From cc85b8a81d4bdc8e8a8e31a6d1f671588346cf50 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 13 Jan 2024 19:18:15 +0100
Subject: [PATCH 01/45] Fix: missing audio commands due to improper
configuration (#39)
This PR adds additional configuration without which the "SetAudioOutputCommand" and "SetApplicationVolume" commands were not available within UI.
In addition:
- it adds ability to configure the device audio name in UI for SetAudioOutputCommand
- it adds translations for audio sensors missing in previous versions
---
.../HASS.Agent.Satellite.Service.csproj | 6 +--
.../HASS.Agent.Shared/Enums/CommandType.cs | 4 ++
.../HASS.Agent.Shared.csproj | 6 +--
.../Localization/Languages.Designer.cs | 9 ++++
.../Resources/Localization/Languages.de.resx | 6 +++
.../Resources/Localization/Languages.en.resx | 6 +++
.../Resources/Localization/Languages.es.resx | 6 +++
.../Resources/Localization/Languages.fr.resx | 6 +++
.../Resources/Localization/Languages.nl.resx | 6 +++
.../Resources/Localization/Languages.pl.resx | 6 +++
.../Localization/Languages.pt-br.resx | 6 +++
.../Resources/Localization/Languages.resx | 3 ++
.../Resources/Localization/Languages.ru.resx | 6 +++
.../Resources/Localization/Languages.sl.resx | 6 +++
.../Resources/Localization/Languages.tr.resx | 6 +++
.../HASS.Agent/Commands/CommandsManager.cs | 20 ++++++++-
.../HASS.Agent/Forms/Commands/CommandsMod.cs | 45 +++++++++++++++++--
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 6 +--
.../Localization/Languages.Designer.cs | 37 +++++++++++++++
.../Resources/Localization/Languages.de.resx | 13 ++++++
.../Resources/Localization/Languages.en.resx | 13 ++++++
.../Resources/Localization/Languages.es.resx | 13 ++++++
.../Resources/Localization/Languages.fr.resx | 13 ++++++
.../Resources/Localization/Languages.nl.resx | 13 ++++++
.../Resources/Localization/Languages.pl.resx | 15 ++++++-
.../Localization/Languages.pt-br.resx | 13 ++++++
.../Resources/Localization/Languages.resx | 13 ++++++
.../Resources/Localization/Languages.ru.resx | 13 ++++++
.../Resources/Localization/Languages.sl.resx | 13 ++++++
.../Resources/Localization/Languages.tr.resx | 13 ++++++
.../HASS.Agent/Settings/StoredCommands.cs | 3 ++
31 files changed, 328 insertions(+), 16 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index 5fbcd2b0..bf279fa5 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -8,7 +8,7 @@
dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154
x64
x64;x86
- 2.0.1
+ 2.0.2-beta1
HASS.Agent Team
HASS.Agent Satellite Service
HASS.Agent.Satellite.Service
@@ -17,9 +17,9 @@
https://github.com/hass-agent/HASS.Agent
https://github.com/hass-agent/HASS.Agent
hass.png
- 2.0.1
+ 2.0.2
hass.ico
- 2.0.1
+ 2.0.2
10.0.17763.0
false
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
index d2b6e148..ee0fa0a0 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
@@ -101,6 +101,10 @@ public enum CommandType
[EnumMember(Value = "SetApplicationVolumeCommand")]
SetApplicationVolumeCommand,
+ [LocalizedDescription("CommandType_SetAudioOutputCommand", typeof(Languages))]
+ [EnumMember(Value = "SetAudioOutputCommand")]
+ SetAudioOutputCommand,
+
[LocalizedDescription("CommandType_ShutdownCommand", typeof(Languages))]
[EnumMember(Value = "ShutdownCommand")]
ShutdownCommand,
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index 9d3b507f..faf7bb08 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -10,9 +10,9 @@
Shared functions and models for the HASS.Agent platform.
https://github.com/hass-agent/HASS.Agent
https://github.com/hass-agent/HASS.Agent
- 2.0.1
- 2.0.1
- 2.0.1
+ 2.0.2
+ 2.0.2
+ 2.0.2-beta1
logo_128.png
True
hassagent.ico
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index fac76b2c..b529d8a8 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -1284,6 +1284,15 @@ internal static string CommandType_SetApplicationVolumeCommand {
}
}
+ ///
+ /// Looks up a localized string similar to SetAudioOutputCommand.
+ ///
+ internal static string CommandType_SetAudioOutputCommand {
+ get {
+ return ResourceManager.GetString("CommandType_SetAudioOutputCommand", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to SetVolume.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index fcd970f4..883f905a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3352,4 +3352,10 @@ Willst Du den Runtime Installer herunterladen?
AktiverDesktop
+
+ Legen Sie die Anwendungslautstärke fest
+
+
+ Legen Sie den Audioausgabebefehl fest
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index 4a7ea851..d635f2ae 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3231,4 +3231,10 @@ Do you want to download the runtime installer?
ActiveDesktop
+
+ SetApplicationVolume
+
+
+ SetAudioOutputCommand
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 1bac329d..b384718b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3228,4 +3228,10 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
EscritorioActivo
+
+ Establecer volumen de aplicación
+
+
+ Establecer comando de salida de audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index f78ca843..677293f0 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3261,4 +3261,10 @@ Do you want to download the runtime installer?
ActiveDesktop
+
+ Définir le volume d'application
+
+
+ Définir la commande de sortie audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 8765dac7..f9248ab5 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3250,4 +3250,10 @@ Wil je de runtime installatie downloaden?
ActiveDesktop
+
+ Stel het toepassingsvolume in
+
+
+ Stel het audio-uitvoercommando in
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index 23625103..563325c4 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3338,4 +3338,10 @@ Czy chcesz pobrać plik instalacyjny?
AktywnyPulpit
+
+ Ustaw głośność aplikacji
+
+
+ Ustaw wyjście audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index 32ffbfff..ac97d593 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3275,4 +3275,10 @@ Deseja baixar o Microsoft WebView2 runtime?
ActiveDesktop
+
+ Definir Volume de Aplicativo
+
+
+ Definir comando de saída de áudio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index f033ea56..311f0b79 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3214,4 +3214,7 @@ Do you want to download the runtime installer?
SwitchDesktop
+
+ SetAudioOutputCommand
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index e448d6b5..2d922706 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3296,4 +3296,10 @@ Home Assistant.
Активдесктоп
+
+ Установить громкость приложения
+
+
+ Установить команду вывода звука
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index d0acb0a5..d9f52478 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3377,4 +3377,10 @@ Ali želite prenesti runtime installer?
ActiveDesktop
+
+ Nastavite glasnost aplikacije
+
+
+ Nastavite ukaz za avdio izhod
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index 3a6a5395..1b6e9a8c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2835,4 +2835,10 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
AktifMasaüstü
+
+ Uygulama Sesini Ayarla
+
+
+ Ses Çıkışı Komutunu Ayarla
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
index 99057184..a0850e70 100644
--- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
@@ -455,10 +455,10 @@ internal static void LoadCommandInfo()
Languages.CommandsManager_SwitchDesktopCommandDescription,
true, false, true);
- // =================================
-
CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
+ // =================================
+
commandInfoCard = new CommandInfoCard(CommandType.SetVolumeCommand,
Languages.CommandsManager_SetVolumeCommandDescription,
true, true, true);
@@ -467,6 +467,22 @@ internal static void LoadCommandInfo()
// =================================
+ commandInfoCard = new CommandInfoCard(CommandType.SetApplicationVolumeCommand,
+ Languages.CommandsManager_SetApplicationVolumeCommandDescription,
+ true, false, true);
+
+ CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
+
+ // =================================
+
+ commandInfoCard = new CommandInfoCard(CommandType.SetAudioOutputCommand,
+ Languages.CommandsManager_SetAudioOutputCommandDescription,
+ true, false, true);
+
+ CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
+
+ // =================================
+
commandInfoCard = new CommandInfoCard(CommandType.ShutdownCommand,
Languages.CommandsManager_ShutdownCommandDescription,
true, true, false);
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
index d279bef5..525bcc87 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
@@ -205,6 +205,7 @@ private void LoadCommand()
case CommandType.SetVolumeCommand:
case CommandType.SetApplicationVolumeCommand:
+ case CommandType.SetAudioOutputCommand:
TbSetting.Text = Command.Command;
break;
}
@@ -455,6 +456,21 @@ private void BtnStore_Click(object sender, EventArgs e)
}
break;
+ case CommandType.SetAudioOutputCommand:
+ var audioDeviceName = TbSetting.Text.Trim();
+ if (string.IsNullOrEmpty(audioDeviceName))
+ {
+ var q = MessageBoxAdv.Show(this, Languages.CommandsMod_BtnStore_MessageBox8, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+ if (q != DialogResult.Yes)
+ {
+ ActiveControl = BtnConfigureCommand;
+ return;
+ }
+ }
+ Command.Command = audioDeviceName;
+ break;
+
+
case CommandType.WebViewCommand:
var webview = TbSetting.Text.Trim();
if (string.IsNullOrEmpty(webview))
@@ -613,6 +629,10 @@ private bool SetType(bool setDefaultValues = true)
SetApplicationVolumeUi();
break;
+ case CommandType.SetAudioOutputCommand:
+ SetAudioOutputUi();
+ break;
+
case CommandType.RadioCommand:
CbConfigDropdown.DataSource = new BindingSource(_radioDevices, null);
SetRadioUi();
@@ -795,10 +815,10 @@ private void SetVolumeUi()
{
SetEmptyGui();
- LblSetting.Text = "volume (between 0 and 100)";
- LblSetting.Visible = true;
+ LblSetting.Text = Languages.CommandsMod_LblSetting_VolumeRange;
+ LblSetting.Visible = true;
- TbSetting.Text = string.Empty;
+ TbSetting.Text = string.Empty;
TbSetting.Visible = true;
}));
}
@@ -812,7 +832,24 @@ private void SetApplicationVolumeUi()
{
SetEmptyGui();
- LblSetting.Text = "JSON Command Payload";
+ LblSetting.Text = Languages.CommandsMod_LblSetting_JsonPayload;
+ LblSetting.Visible = true;
+
+ TbSetting.Text = string.Empty;
+ TbSetting.Visible = true;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'setaudiooutput' type
+ ///
+ private void SetAudioOutputUi()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting.Text = Languages.CommandsMod_LblSetting_AudioDeviceName;
LblSetting.Visible = true;
TbSetting.Text = string.Empty;
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index ec6feb51..5a943915 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -12,7 +12,7 @@
x64
x64;x86
full
- 2.0.1
+ 2.0.2-beta1
HASS.Agent Team
HASS.Agent Team
Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more.
@@ -22,8 +22,8 @@
https://github.com/hass-agent/HASS.Agent
MIT
app.manifest
- 2.0.1
- 2.0.1
+ 2.0.2
+ 2.0.2
HASS.Agent
None
win10-x64;win10-x86
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index e0f8d42d..779cfe72 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -608,6 +608,16 @@ internal static string CommandsManager_SetApplicationVolumeCommandDescription {
}
}
+ ///
+ /// Looks up a localized string similar to Sets the default audio output for the system.
+ ///Requires audio device name as a payload..
+ ///
+ internal static string CommandsManager_SetAudioOutputCommandDescription {
+ get {
+ return ResourceManager.GetString("CommandsManager_SetAudioOutputCommandDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Sets the volume of the current default audiodevice to the specified level..
///
@@ -1038,6 +1048,15 @@ internal static string CommandsMod_LblSetting {
}
}
+ ///
+ /// Looks up a localized string similar to Audio Device Name.
+ ///
+ internal static string CommandsMod_LblSetting_AudioDeviceName {
+ get {
+ return ResourceManager.GetString("CommandsMod_LblSetting_AudioDeviceName", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Command.
///
@@ -1056,6 +1075,15 @@ internal static string CommandsMod_LblSetting_CommandScript {
}
}
+ ///
+ /// Looks up a localized string similar to JSON Command Payload.
+ ///
+ internal static string CommandsMod_LblSetting_JsonPayload {
+ get {
+ return ResourceManager.GetString("CommandsMod_LblSetting_JsonPayload", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Keycode.
///
@@ -1092,6 +1120,15 @@ internal static string CommandsMod_LblSetting_Url {
}
}
+ ///
+ /// Looks up a localized string similar to Volume (between 0 and 100).
+ ///
+ internal static string CommandsMod_LblSetting_VolumeRange {
+ get {
+ return ResourceManager.GetString("CommandsMod_LblSetting_VolumeRange", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to HASS.Agent only!.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index be5cc84d..d378941c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3464,4 +3464,17 @@ aus der Originalversion
Fehler beim Starten des Satellitendienstes!
+
+ Legt die Standard-Audioausgabe für das System fest.
+Erfordert den Namen des Audiogeräts als Nutzlast.
+
+
+ Name des Audiogeräts
+
+
+ JSON-Befehlsnutzlast
+
+
+ Lautstärke (zwischen 0 und 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 0baa62cb..23c49a32 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3341,4 +3341,17 @@ from the original version
Error communicating with the satellite service!
+
+ Sets the default audio output for the system.
+Requires audio device name as a payload.
+
+
+ Audio Device Name
+
+
+ JSON Command Payload
+
+
+ Volume (between 0 and 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 1dce77ca..ff26c4b5 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3340,4 +3340,17 @@ HASS.Agent de la versión original
¡Error al iniciar el servicio satelital!
+
+ Establece la salida de audio predeterminada para el sistema.
+Requiere el nombre del dispositivo de audio como carga útil.
+
+
+ Volumen (entre 0 y 100)
+
+
+ Carga útil del comando JSON
+
+
+ Nombre del dispositivo de audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 03e69a61..5e3fde18 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3373,4 +3373,17 @@ HASS.Agent de la version originale
Erreur lors du démarrage du service satellite !
+
+ Définit la sortie audio par défaut du système.
+Nécessite le nom du périphérique audio comme charge utile.
+
+
+ Volume (entre 0 et 100)
+
+
+ Charge utile de la commande JSON
+
+
+ Nom du périphérique audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 4523dc56..fc9c643e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3361,4 +3361,17 @@ van de originele versie
Fout bij starten satellietservice!
+
+ Stelt de standaard audio-uitvoer voor het systeem in.
+Vereist de naam van het audioapparaat als payload.
+
+
+ Naam audioapparaat
+
+
+ JSON-opdrachtpayload
+
+
+ Volume (tussen 0 en 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 0296c3a4..c2a0e768 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3093,7 +3093,7 @@ Uwaga: wyłączyłeś czyszczenie nazw, więc upewnij się, że nazwa Twojego ur
Stara się wybudzić wszystkie monitory poprzez symulowanie wciśnięcia przycisku "do góry".
- Ustawia poziom głośności domyślnego urządzenia na podaną wartość.
+ Ustawia poziom głośności domyślnego urządzenia na podaną wartość.
Wprowadź wartość pomiędzy 0-100 jako nowy poziom głośności!
@@ -3450,4 +3450,17 @@ z oryginalnej wersji
Błąd podczas uruchamiania usługi satelity!
+
+ Ustawia domyślne wyjście audio dla systemu.
+Wymaga nazwy urządzenia audio jako ładunku.
+
+
+ Głośność (od 0 do 100)
+
+
+ Ładunek polecenia JSON
+
+
+ Nazwa urządzenia audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 483830cc..16e45e80 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3386,4 +3386,17 @@ HASS.Agent da versão original
Erro ao iniciar o serviço de satélite!
+
+ Define a saída de áudio padrão para o sistema.
+Requer o nome do dispositivo de áudio como carga útil.
+
+
+ Nome do dispositivo de áudio
+
+
+ Carga útil do comando JSON
+
+
+ Volume (entre 0 e 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 81c2b7ab..72abfe05 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3353,4 +3353,17 @@ from the original version
Error communicating with the satellite service!
+
+ Sets the default audio output for the system.
+Requires audio device name as a payload.
+
+
+ Audio Device Name
+
+
+ JSON Command Payload
+
+
+ Volume (between 0 and 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index d9f1ac5d..7aecfda6 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3409,4 +3409,17 @@ Home Assistant.
Ошибка при запуске спутниковой службы!
+
+ Устанавливает аудиовыход по умолчанию для системы.
+В качестве полезной нагрузки требуется имя аудиоустройства.
+
+
+ Громкость (от 0 до 100)
+
+
+ Полезная нагрузка команды JSON
+
+
+ Имя аудиоустройства
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index c01e258d..0a7b3781 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3489,4 +3489,17 @@ iz izvirne različice
Napaka pri zagonu satelitske storitve!
+
+ Nastavi privzeti zvočni izhod za sistem.
+Zahteva ime zvočne naprave kot tovor.
+
+
+ Ime zvočne naprave
+
+
+ Tovor ukazov JSON
+
+
+ Glasnost (med 0 in 100)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index f8e566f5..064d8465 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2952,4 +2952,17 @@ taşınması orijinal versiyondan
Uydu hizmeti başlatılırken hata oluştu!
+
+ Sistem için varsayılan ses çıkışını ayarlar.
+Yük olarak ses cihazı adını gerektirir.
+
+
+ Ses Cihazı Adı
+
+
+ JSON Komut Yükü
+
+
+ Hacim (0 ile 100 arasında)
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
index 7639fc74..c277e876 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
@@ -175,6 +175,9 @@ internal static AbstractCommand ConvertConfiguredToAbstract(ConfiguredCommand co
case CommandType.SetApplicationVolumeCommand:
abstractCommand = new SetApplicationVolumeCommand(command.EntityName, command.Name, command.Command, command.EntityType, command.Id.ToString());
break;
+ case CommandType.SetAudioOutputCommand:
+ abstractCommand = new SetAudioOutputCommand(command.EntityName, command.Name, command.Command, command.EntityType, command.Id.ToString());
+ break;
default:
Log.Error("[SETTINGS_COMMANDS] [{name}] Unknown configured command type: {type}", command.EntityName, command.Type.ToString());
break;
From 01463dac928d16579d32a7f27a290f9159f2dc8d Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 15 Jan 2024 15:32:34 +0100
Subject: [PATCH 02/45] updated VirtualDesktop library version and removed
unused one (#40)
This PR attempts to fix HASS.Agent crash when virtual desktops are switched.
Probable root cause was bug in the management library HASS.Agent uses, this PR bumps the dependency version of this library to newest version.
---
.../HASS.Agent.Satellite.Service.csproj | 2 +-
src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj | 2 +-
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 5 ++---
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index bf279fa5..f8cf3dd8 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -8,7 +8,7 @@
dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154
x64
x64;x86
- 2.0.2-beta1
+ 2.0.2-beta2
HASS.Agent Team
HASS.Agent Satellite Service
HASS.Agent.Satellite.Service
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index faf7bb08..ab356af1 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -12,7 +12,7 @@
https://github.com/hass-agent/HASS.Agent
2.0.2
2.0.2
- 2.0.2-beta1
+ 2.0.2-beta2
logo_128.png
True
hassagent.ico
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index 5a943915..04598222 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -12,7 +12,7 @@
x64
x64;x86
full
- 2.0.2-beta1
+ 2.0.2-beta2
HASS.Agent Team
HASS.Agent Team
Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more.
@@ -69,13 +69,12 @@
-
+
-
From 2ac75747bd0f3950dbd8cd0e07ccbc0a1ff32831 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Tue, 13 Feb 2024 21:33:48 +0100
Subject: [PATCH 03/45] Development: 2.1.0-beta1 version change (#46)
* updated versions to 2.1.0-beta1
* updated installer version
---
src/HASS.Agent.Installer/InstallerScript.iss | 2 +-
.../HASS.Agent.Satellite.Service.csproj | 6 +++---
src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj | 6 +++---
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 6 +++---
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript.iss b/src/HASS.Agent.Installer/InstallerScript.iss
index 6b2aa517..8e7190f5 100644
--- a/src/HASS.Agent.Installer/InstallerScript.iss
+++ b/src/HASS.Agent.Installer/InstallerScript.iss
@@ -9,7 +9,7 @@
; Standard installation constants
#define MyAppName "HASS.Agent"
-#define MyAppVersion "2.0.1"
+#define MyAppVersion "2.1.0-beta1"
#define MyAppPublisher "HASS.Agent Team"
#define MyAppURL "https://hass-agent.io"
#define MyAppExeName "HASS.Agent.exe"
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index f8cf3dd8..72f396fe 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -8,7 +8,7 @@
dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154
x64
x64;x86
- 2.0.2-beta2
+ 2.1.0-beta1
HASS.Agent Team
HASS.Agent Satellite Service
HASS.Agent.Satellite.Service
@@ -17,9 +17,9 @@
https://github.com/hass-agent/HASS.Agent
https://github.com/hass-agent/HASS.Agent
hass.png
- 2.0.2
+ 2.1.0
hass.ico
- 2.0.2
+ 2.1.0
10.0.17763.0
false
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index ab356af1..092eaffa 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -10,9 +10,9 @@
Shared functions and models for the HASS.Agent platform.
https://github.com/hass-agent/HASS.Agent
https://github.com/hass-agent/HASS.Agent
- 2.0.2
- 2.0.2
- 2.0.2-beta2
+ 2.1.0
+ 2.1.0
+ 2.1.0-beta1
logo_128.png
True
hassagent.ico
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index 04598222..bbaf5a51 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -12,7 +12,7 @@
x64
x64;x86
full
- 2.0.2-beta2
+ 2.1.0-beta1
HASS.Agent Team
HASS.Agent Team
Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more.
@@ -22,8 +22,8 @@
https://github.com/hass-agent/HASS.Agent
MIT
app.manifest
- 2.0.2
- 2.0.2
+ 2.1.0
+ 2.1.0
HASS.Agent
None
win10-x64;win10-x86
From 952d8ec46c29f46cd9e26de1f51d5b2bdacc7b92 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Tue, 13 Feb 2024 21:37:58 +0100
Subject: [PATCH 04/45] Fix: sensor being updated constantly when value doesn't
change (#45)
This PR fixes issue where the "LastUpdated" value is not updated if sensor value is same as previously.
---
.../HomeAssistant/Sensors/PowershellSensor.cs | 92 ++++----
.../AbstractSingleValueSensor.cs | 210 +++++++++---------
2 files changed, 158 insertions(+), 144 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
index a84e57ea..ccbd2859 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
@@ -5,59 +5,63 @@
using HASS.Agent.Shared.Models.HomeAssistant;
using Serilog;
-namespace HASS.Agent.Shared.HomeAssistant.Sensors
+namespace HASS.Agent.Shared.HomeAssistant.Sensors;
+
+///
+/// Sensor containing the result of the provided Powershell command or script
+///
+public class PowershellSensor : AbstractSingleValueSensor
{
- ///
- /// Sensor containing the result of the provided Powershell command or script
- ///
- public class PowershellSensor : AbstractSingleValueSensor
+ private const string DefaultName = "powershellsensor";
+
+ public string Command { get; private set; }
+ public bool ApplyRounding { get; private set; }
+ public int? Round { get; private set; }
+
+ public PowershellSensor(string command, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
{
- private const string DefaultName = "powershellsensor";
+ Command = command;
+ ApplyRounding = applyRounding;
+ Round = round;
+ }
- public string Command { get; private set; }
- public bool ApplyRounding { get; private set; }
- public int? Round { get; private set; }
+ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
+ {
+ if (Variables.MqttManager == null)
+ return null;
- public PowershellSensor(string command, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
- {
- Command = command;
- ApplyRounding = applyRounding;
- Round = round;
- }
+ var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
+ if (deviceConfig == null)
+ return null;
- public override DiscoveryConfigModel GetAutoDiscoveryConfig()
- {
- if (Variables.MqttManager == null) return null;
-
- var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
- if (deviceConfig == null) return null;
-
- return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
- {
- EntityName = EntityName,
- Name = Name,
- Unique_id = Id,
- Device = deviceConfig,
- State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{EntityName}/state",
- Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
- });
- }
-
- public override string GetState()
+ return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
- var executed = PowershellManager.ExecuteWithOutput(Command, TimeSpan.FromMinutes(5), out var output, out var errors);
- if (!executed) return "error_during_execution";
+ EntityName = EntityName,
+ Name = Name,
+ Unique_id = Id,
+ Device = deviceConfig,
+ State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{EntityName}/state",
+ Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
+ });
+ }
- if (string.IsNullOrWhiteSpace(output) && string.IsNullOrWhiteSpace(errors)) return string.Empty;
- if (string.IsNullOrWhiteSpace(output)) return errors.Trim();
+ public override string GetState()
+ {
+ var executed = PowershellManager.ExecuteWithOutput(Command, TimeSpan.FromMinutes(5), out var output, out var errors);
+ if (!executed)
+ return "error_during_execution";
- // optionally apply rounding
- if (ApplyRounding && Round != null && double.TryParse(output, out var dblValue)) { output = Math.Round(dblValue, (int)Round).ToString(CultureInfo.CurrentCulture); }
+ if (string.IsNullOrWhiteSpace(output) && string.IsNullOrWhiteSpace(errors))
+ return string.Empty;
+ if (string.IsNullOrWhiteSpace(output))
+ return errors.Trim();
- // done
- return string.IsNullOrWhiteSpace(errors) ? output : $"{output} | {errors}";
- }
+ // optionally apply rounding
+ if (ApplyRounding && Round != null && double.TryParse(output, out var dblValue)) { output = Math.Round(dblValue, (int)Round).ToString(CultureInfo.CurrentCulture); }
- public override string GetAttributes() => string.Empty;
+ // done
+ return string.IsNullOrWhiteSpace(errors) ? output : $"{output} | {errors}";
}
+
+ public override string GetAttributes() => string.Empty;
}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
index 63261023..25e41799 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
@@ -3,134 +3,144 @@
using MQTTnet;
using Serilog;
-namespace HASS.Agent.Shared.Models.HomeAssistant
+namespace HASS.Agent.Shared.Models.HomeAssistant;
+
+///
+/// Abstract singlevalue-sensor from which all singlevalue-sensors are derived
+///
+public abstract class AbstractSingleValueSensor : AbstractDiscoverable
{
- ///
- /// Abstract singlevalue-sensor from which all singlevalue-sensors are derived
- ///
- public abstract class AbstractSingleValueSensor : AbstractDiscoverable
- {
- public int UpdateIntervalSeconds { get; protected set; }
- public DateTime? LastUpdated { get; protected set; }
+ public int UpdateIntervalSeconds { get; protected set; }
+ public DateTime? LastUpdated { get; protected set; }
- public string PreviousPublishedState { get; protected set; } = string.Empty;
- public string PreviousPublishedAttributes { get; protected set; } = string.Empty;
+ public string PreviousPublishedState { get; protected set; } = string.Empty;
+ public string PreviousPublishedAttributes { get; protected set; } = string.Empty;
- protected AbstractSingleValueSensor(string entityName, string name, int updateIntervalSeconds = 10, string id = default, bool useAttributes = false)
- {
- Id = id == null || id == Guid.Empty.ToString() ? Guid.NewGuid().ToString() : id;
- EntityName = entityName;
- Name = name;
- UpdateIntervalSeconds = updateIntervalSeconds;
- Domain = "sensor";
- UseAttributes = useAttributes;
- }
+ protected AbstractSingleValueSensor(string entityName, string name, int updateIntervalSeconds = 10, string id = default, bool useAttributes = false)
+ {
+ Id = id == null || id == Guid.Empty.ToString() ? Guid.NewGuid().ToString() : id;
+ EntityName = entityName;
+ Name = name;
+ UpdateIntervalSeconds = updateIntervalSeconds;
+ Domain = "sensor";
+ UseAttributes = useAttributes;
+ }
- protected SensorDiscoveryConfigModel AutoDiscoveryConfigModel;
- protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
- {
- AutoDiscoveryConfigModel = config;
- return config;
- }
+ protected SensorDiscoveryConfigModel AutoDiscoveryConfigModel;
+ protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
+ {
+ AutoDiscoveryConfigModel = config;
+ return config;
+ }
- public override void ClearAutoDiscoveryConfig() => AutoDiscoveryConfigModel = null;
+ public override void ClearAutoDiscoveryConfig() => AutoDiscoveryConfigModel = null;
- // nullable in preparation for possible future "nullable enablement"
- public abstract string? GetState();
+ // nullable in preparation for possible future "nullable enablement"
+ public abstract string? GetState();
- // nullable in preparation for possible future "nullable enablement"
- public abstract string? GetAttributes();
+ // nullable in preparation for possible future "nullable enablement"
+ public abstract string? GetAttributes();
- public void ResetChecks()
- {
- LastUpdated = DateTime.MinValue;
+ public void ResetChecks()
+ {
+ LastUpdated = DateTime.MinValue;
- PreviousPublishedState = string.Empty;
- PreviousPublishedAttributes = string.Empty;
- }
+ PreviousPublishedState = string.Empty;
+ PreviousPublishedAttributes = string.Empty;
+ }
- public async Task PublishStateAsync(bool respectChecks = true)
+ public async Task PublishStateAsync(bool respectChecks = true)
+ {
+ try
{
- try
- {
- if (Variables.MqttManager == null) return;
+ if (Variables.MqttManager == null)
+ return;
- // are we asked to check elapsed time?
- if (respectChecks)
- {
- if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(UpdateIntervalSeconds) > DateTime.Now) return;
- }
-
- // get the current state/attributes
- var state = GetState();
- if (state == null)
+ // are we asked to check elapsed time?
+ if (respectChecks)
+ {
+ if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(UpdateIntervalSeconds) > DateTime.Now)
return;
+ }
- var attributes = GetAttributes();
+ // get the current state/attributes
+ var state = GetState();
+ if (state == null)
+ return;
- // are we asked to check state changes?
- if (respectChecks)
+ var attributes = GetAttributes();
+
+ // are we asked to check state changes?
+ if (respectChecks)
+ {
+ if (PreviousPublishedState == state && PreviousPublishedAttributes == attributes)
{
- if (PreviousPublishedState == state && PreviousPublishedAttributes == attributes) return;
+ LastUpdated = DateTime.Now;
+ return;
}
+ }
- // fetch the autodiscovery config
- var autoDiscoConfig = (SensorDiscoveryConfigModel)GetAutoDiscoveryConfig();
- if (autoDiscoConfig == null) return;
-
- // prepare the state message
- var message = new MqttApplicationMessageBuilder()
- .WithTopic(autoDiscoConfig.State_topic)
- .WithPayload(state)
+ // fetch the autodiscovery config
+ var autoDiscoConfig = (SensorDiscoveryConfigModel)GetAutoDiscoveryConfig();
+ if (autoDiscoConfig == null)
+ return;
+
+ // prepare the state message
+ var message = new MqttApplicationMessageBuilder()
+ .WithTopic(autoDiscoConfig.State_topic)
+ .WithPayload(state)
+ .WithRetainFlag(Variables.MqttManager.UseRetainFlag())
+ .Build();
+
+ // send it
+ var published = await Variables.MqttManager.PublishAsync(message);
+ if (!published)
+ return;
+
+ // optionally prepare and send attributes
+ if (UseAttributes && attributes != null)
+ {
+ message = new MqttApplicationMessageBuilder()
+ .WithTopic(autoDiscoConfig.Json_attributes_topic)
+ .WithPayload(attributes)
.WithRetainFlag(Variables.MqttManager.UseRetainFlag())
.Build();
- // send it
- var published = await Variables.MqttManager.PublishAsync(message);
+ published = await Variables.MqttManager.PublishAsync(message);
if (!published)
- return; // failed, don't store the state
-
- // optionally prepare and send attributes
- if (UseAttributes && attributes != null)
- {
- message = new MqttApplicationMessageBuilder()
- .WithTopic(autoDiscoConfig.Json_attributes_topic)
- .WithPayload(attributes)
- .WithRetainFlag(Variables.MqttManager.UseRetainFlag())
- .Build();
-
- published = await Variables.MqttManager.PublishAsync(message);
- if (!published)
- return; // failed, don't store the state
- }
-
- // only store the values if the checks are respected
- // otherwise, we might stay in 'unknown' state until the value changes
- if (!respectChecks)
return;
+ }
- PreviousPublishedState = state;
- if (attributes != null)
- PreviousPublishedAttributes = attributes;
+ // only store the values if the checks are respected
+ // otherwise, we might stay in 'unknown' state until the value changes
+ if (!respectChecks)
+ return;
- LastUpdated = DateTime.Now;
- }
- catch (Exception ex)
- {
- Log.Fatal("[SENSOR] [{name}] Error publishing state: {err}", EntityName, ex.Message);
- }
- }
+ PreviousPublishedState = state;
+ if (attributes != null)
+ PreviousPublishedAttributes = attributes;
- public async Task PublishAutoDiscoveryConfigAsync()
- {
- if (Variables.MqttManager == null) return;
- await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain);
+ LastUpdated = DateTime.Now;
}
-
- public async Task UnPublishAutoDiscoveryConfigAsync()
+ catch (Exception ex)
{
- if (Variables.MqttManager == null) return;
- await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true);
+ Log.Fatal("[SENSOR] [{name}] Error publishing state: {err}", EntityName, ex.Message);
}
}
+
+ public async Task PublishAutoDiscoveryConfigAsync()
+ {
+ if (Variables.MqttManager == null)
+ return;
+
+ await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain);
+ }
+
+ public async Task UnPublishAutoDiscoveryConfigAsync()
+ {
+ if (Variables.MqttManager == null)
+ return;
+
+ await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true);
+ }
}
From 87887aac22e63be3b3f8ed5fa968f917e00a5599 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Tue, 13 Feb 2024 21:39:35 +0100
Subject: [PATCH 05/45] Feature: screenshot camera sensor (#42)
This PR adds screenshot sensor (camera entity) which was originally proposed and implemented by @denisabt in the PR to original HASS.Agent repository LAB02-Research#8
This sensor is supported only for client part of HASS.Agent.
---
.../HASS.Agent.Shared/Enums/SensorType.cs | 6 +-
.../HASS.Agent.Shared.csproj | 1 +
.../SingleValue/ScreenshotSensor.cs | 93 +++++++++++++++++++
.../HomeAssistant/DiscoveryConfigModel.cs | 14 +++
.../Localization/Languages.Designer.cs | 9 ++
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
.../HASS.Agent/Forms/Sensors/SensorsMod.cs | 43 ++++++++-
.../Localization/Languages.Designer.cs | 19 ++++
.../Resources/Localization/Languages.de.resx | 7 ++
.../Resources/Localization/Languages.en.resx | 7 ++
.../Resources/Localization/Languages.es.resx | 7 ++
.../Resources/Localization/Languages.fr.resx | 7 ++
.../Resources/Localization/Languages.nl.resx | 7 ++
.../Resources/Localization/Languages.pl.resx | 7 ++
.../Localization/Languages.pt-br.resx | 7 ++
.../Resources/Localization/Languages.resx | 7 ++
.../Resources/Localization/Languages.ru.resx | 7 ++
.../Resources/Localization/Languages.sl.resx | 7 ++
.../Resources/Localization/Languages.tr.resx | 7 ++
.../HASS.Agent/Sensors/SensorsManager.cs | 8 ++
.../HASS.Agent/Settings/StoredSensors.cs | 23 ++++-
31 files changed, 318 insertions(+), 8 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
index bf3b31eb..65348afa 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
@@ -165,6 +165,10 @@ public enum SensorType
[LocalizedDescription("SensorType_InternalDeviceSensor", typeof(Languages))]
[EnumMember(Value = "InternalDeviceSensor")]
- InternalDeviceSensor
+ InternalDeviceSensor,
+
+ [LocalizedDescription("SensorType_ScreenshotSensor", typeof(Languages))]
+ [EnumMember(Value = "ScreenshotSensor")]
+ ScreenshotSensor
}
}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index 092eaffa..504a0bcb 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -42,6 +42,7 @@
+
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
new file mode 100644
index 00000000..0fc98195
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.DirectoryServices.ActiveDirectory;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HASS.Agent.Shared.Models.HomeAssistant;
+using Serilog;
+using System.Windows.Forms;
+using System.Xml.Linq;
+using System.Runtime.InteropServices;
+using System.Drawing.Imaging;
+using System.Text.RegularExpressions;
+
+namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue;
+public class ScreenshotSensor : AbstractSingleValueSensor
+{
+ private const string DefaultName = "screenshot";
+
+ public int ScreenIndex;
+
+ public ScreenshotSensor(string screenIndex = "0", int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id)
+ {
+ ScreenIndex = int.TryParse(screenIndex, out var parsedScreenIndex) ? parsedScreenIndex : 0;
+ Domain = "camera";
+ }
+
+ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
+ {
+ if (Variables.MqttManager == null)
+ return null;
+
+ var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
+ if (deviceConfig == null)
+ return null;
+
+ return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new CameraSensorDiscoveryConfigModel()
+ {
+ EntityName = EntityName,
+ Name = Name,
+ Unique_id = Id,
+ Device = deviceConfig,
+ Image_encoding = "b64",
+ Icon = "mdi:camera",
+ State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ Topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability"
+ });
+ }
+
+ public override string GetState()
+ {
+ var screenCount = Screen.AllScreens.Length;
+ if (ScreenIndex >= screenCount || ScreenIndex < 0)
+ {
+ Log.Warning("[SCREENSHOT] Wrong index '{index}' - returning image for screen 0", ScreenIndex);
+ ScreenIndex = 0;
+ }
+
+ var screenImage = CaptureScreen(ScreenIndex);
+ return Convert.ToBase64String(screenImage);
+ }
+
+ private byte[] CaptureScreen(int screenIndex)
+ {
+ try
+ {
+ return CapturePngFile(Screen.AllScreens[screenIndex]);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[SCREENSHOT] Internal Error capturing screen {index}, {ex}", ex.Message);
+ return Array.Empty();
+ }
+ }
+
+ private byte[] CapturePngFile(Screen screen)
+ {
+ var captureRectangle = screen.Bounds;
+ var captureBitmap = new Bitmap(captureRectangle.Width, captureRectangle.Height, PixelFormat.Format32bppArgb);
+ var captureGraphics = Graphics.FromImage(captureBitmap);
+ captureGraphics.CopyFromScreen(captureRectangle.Left, captureRectangle.Top, 0, 0, captureRectangle.Size);
+
+ using var ms = new MemoryStream();
+ captureBitmap.Save(ms, ImageFormat.Png);
+
+ return ms.ToArray();
+ }
+
+ public override string GetAttributes() => string.Empty;
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
index 68c4d4a6..4a98233b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
@@ -143,6 +143,20 @@ public string Object_id
public string Value_template { get; set; }
}
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public class CameraSensorDiscoveryConfigModel : SensorDiscoveryConfigModel
+ {
+ ///
+ /// Messages published to this topic need to contain full contents of an image
+ ///
+ public string Topic { get; set; }
+
+ ///
+ /// (Optional) The encoding of the image payloads received.
+ ///
+ public string Image_encoding { get; set; }
+ }
+
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class CommandDiscoveryConfigModel : DiscoveryConfigModel
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index b529d8a8..16f7dcde 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -6735,6 +6735,15 @@ internal static string SensorType_ProcessActiveSensor {
}
}
+ ///
+ /// Looks up a localized string similar to Screenshot.
+ ///
+ internal static string SensorType_ScreenshotSensor {
+ get {
+ return ResourceManager.GetString("SensorType_ScreenshotSensor", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to ServiceState.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index 883f905a..c20b1ed0 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3358,4 +3358,7 @@ Willst Du den Runtime Installer herunterladen?
Legen Sie den Audioausgabebefehl fest
+
+ Bildschirmfoto
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index d635f2ae..b7f04ec8 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3237,4 +3237,7 @@ Do you want to download the runtime installer?
SetAudioOutputCommand
+
+ Screenshot
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index b384718b..37db7723 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3234,4 +3234,7 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Establecer comando de salida de audio
+
+ Captura de pantalla
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index 677293f0..4dfc6e87 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3267,4 +3267,7 @@ Do you want to download the runtime installer?
Définir la commande de sortie audio
+
+ Capture d'écran
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index f9248ab5..6c01199c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3256,4 +3256,7 @@ Wil je de runtime installatie downloaden?
Stel het audio-uitvoercommando in
+
+ Schermafbeelding
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index 563325c4..cfeff95d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3344,4 +3344,7 @@ Czy chcesz pobrać plik instalacyjny?
Ustaw wyjście audio
+
+ Zrzut ekranu
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index ac97d593..8e8420d5 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3281,4 +3281,7 @@ Deseja baixar o Microsoft WebView2 runtime?
Definir comando de saída de áudio
+
+ Captura de tela
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index 311f0b79..ed3f3224 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3217,4 +3217,7 @@ Do you want to download the runtime installer?
SetAudioOutputCommand
+
+ Screenshot
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index 2d922706..fac40f1d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3302,4 +3302,7 @@ Home Assistant.
Установить команду вывода звука
+
+ Скриншот
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index d9f52478..7a2426ef 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3383,4 +3383,7 @@ Ali želite prenesti runtime installer?
Nastavite ukaz za avdio izhod
+
+ Posnetek zaslona
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index 1b6e9a8c..f5cfa8d2 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2841,4 +2841,7 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
Ses Çıkışı Komutunu Ayarla
+
+ Ekran görüntüsü
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
index ba5c019d..987cbad3 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
@@ -228,7 +228,11 @@ private void LoadSensor()
CbApplyRounding.Checked = Sensor.ApplyRounding;
NumRound.Text = Sensor.Round?.ToString() ?? LastActiveSensor.DefaultTimeWindow.ToString(); ;
break;
- }
+
+ case SensorType.ScreenshotSensor:
+ TbSetting1.Text = Sensor.Query;
+ break;
+ }
}
///
@@ -327,6 +331,10 @@ private bool SetType(bool setDefaultValues = true)
SetLastActiveGui();
break;
+ case SensorType.ScreenshotSensor:
+ SetScreenshotGui();
+ break;
+
default:
SetEmptyGui();
break;
@@ -528,10 +536,24 @@ private void SetLastActiveGui()
}));
}
- ///
- /// Change the UI to a general type
- ///
- private void SetEmptyGui()
+ private void SetScreenshotGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_ScreenNumber;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+
+ BtnTest.Visible = false;
+ }));
+ }
+
+ ///
+ /// Change the UI to a general type
+ ///
+ private void SetEmptyGui()
{
Invoke(new MethodInvoker(delegate
{
@@ -799,6 +821,17 @@ private void BtnStore_Click(object sender, EventArgs e)
return;
}
break;
+
+ case SensorType.ScreenshotSensor:
+ var screenIndex = TbSetting1.Text.Trim();
+ if (string.IsNullOrEmpty(screenIndex))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox10, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.Query = screenIndex;
+ break;
}
// set values
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 779cfe72..8a43773e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -6095,6 +6095,16 @@ internal static string SensorsManager_ProcessActiveSensorDescription {
}
}
+ ///
+ /// Looks up a localized string similar to Provides a screenshot sensor in form of a camera entity.
+ ///Screen number depends on system configuration - starts at 0..
+ ///
+ internal static string SensorsManager_ScreenshotSensorDescription {
+ get {
+ return ResourceManager.GetString("SensorsManager_ScreenshotSensorDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Returns the state of the provided service: NotFound, Stopped, StartPending, StopPending, Running, ContinuePending, PausePending or Paused.
///
@@ -6520,6 +6530,15 @@ internal static string SensorsMod_LblSetting1_Process {
}
}
+ ///
+ /// Looks up a localized string similar to Screen number.
+ ///
+ internal static string SensorsMod_LblSetting1_ScreenNumber {
+ get {
+ return ResourceManager.GetString("SensorsMod_LblSetting1_ScreenNumber", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Service.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index d378941c..376c1f7c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3477,4 +3477,11 @@ Erfordert den Namen des Audiogeräts als Nutzlast.
Lautstärke (zwischen 0 und 100)
+
+ Sie stellen einen Screenshot-Sensor in Form einer Kameraeinheit bereit.
+Die Bildschirmnummer hängt von der Systemkonfiguration ab – beginnt bei 0.
+
+
+ Bildschirmnummer
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 23c49a32..6e767239 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3354,4 +3354,11 @@ Requires audio device name as a payload.
Volume (between 0 and 100)
+
+ Screen number
+
+
+ Provides a screenshot sensor in form of a camera entity.
+Screen number depends on system configuration - starts at 0.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index ff26c4b5..89b491b3 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3353,4 +3353,11 @@ Requiere el nombre del dispositivo de audio como carga útil.
Nombre del dispositivo de audio
+
+ Proporciona un sensor de captura de pantalla en forma de entidad de cámara.
+El número de pantalla depende de la configuración del sistema: comienza en 0.
+
+
+ Número de pantalla
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 5e3fde18..ffe6fbe4 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3386,4 +3386,11 @@ Nécessite le nom du périphérique audio comme charge utile.
Nom du périphérique audio
+
+ Vous fournissez un capteur de capture d’écran sous la forme d’une entité caméra.
+Le numéro d'écran dépend de la configuration du système - commence à 0.
+
+
+ Numéro d'écran
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index fc9c643e..7629e0fd 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3374,4 +3374,11 @@ Vereist de naam van het audioapparaat als payload.
Volume (tussen 0 en 100)
+
+ U levert een screenshot-sensor in de vorm van een camera-entiteit.
+Schermnummer is afhankelijk van de systeemconfiguratie - begint bij 0.
+
+
+ Schermnummer
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index c2a0e768..0f937e96 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3463,4 +3463,11 @@ Wymaga nazwy urządzenia audio jako ładunku.
Nazwa urządzenia audio
+
+ Czujnik zrzutu ekranu w postaci kamery.
+Numer ekranu zależy od konfiguracji systemu – zaczyna się od 0.
+
+
+ Numer ekranu
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 16e45e80..27b31828 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3399,4 +3399,11 @@ Requer o nome do dispositivo de áudio como carga útil.
Volume (entre 0 e 100)
+
+ Você fornece um sensor de captura de tela na forma de uma entidade de câmera.
+O número da tela depende da configuração do sistema – começa em 0.
+
+
+ Número da tela
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 72abfe05..8cf4f2a9 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3366,4 +3366,11 @@ Requires audio device name as a payload.
Volume (between 0 and 100)
+
+ Screen number
+
+
+ Provides a screenshot sensor in form of a camera entity.
+Screen number depends on system configuration - starts at 0.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 7aecfda6..8846437c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3422,4 +3422,11 @@ Home Assistant.
Имя аудиоустройства
+
+ Вы предоставляете датчик скриншота в виде объекта камеры.
+Номер экрана зависит от конфигурации системы — начинается с 0.
+
+
+ Номер экрана
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 0a7b3781..e32b3a8c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3502,4 +3502,11 @@ Zahteva ime zvočne naprave kot tovor.
Glasnost (med 0 in 100)
+
+ Zagotovite senzor posnetka zaslona v obliki entitete kamere.
+Številka zaslona je odvisna od konfiguracije sistema - začne se pri 0.
+
+
+ Številka zaslona
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 064d8465..ebd9375f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2965,4 +2965,11 @@ Yük olarak ses cihazı adını gerektirir.
Hacim (0 ile 100 arasında)
+
+ Bir kamera varlığı biçiminde bir ekran görüntüsü sensörü sağlarsınız.
+Ekran numarası sistem konfigürasyonuna bağlıdır - 0'dan başlar.
+
+
+ Ekran numarası
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
index e01731c8..0f5f4eba 100644
--- a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
@@ -661,6 +661,14 @@ internal static void LoadSensorInfo()
SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard);
// =================================
+
+ sensorInfoCard = new SensorInfoCard(SensorType.ScreenshotSensor,
+ Languages.SensorsManager_ScreenshotSensorDescription,
+ 10, false, true, false);
+
+ SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard);
+
+ // =================================
}
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
index 6dce967d..8c908fd2 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
@@ -3,6 +3,7 @@
using HASS.Agent.Extensions;
using HASS.Agent.HomeAssistant.Sensors.GeneralSensors.MultiValue;
using HASS.Agent.HomeAssistant.Sensors.GeneralSensors.SingleValue;
+using HASS.Agent.Managers.DeviceSensors;
using HASS.Agent.Resources.Localization;
using HASS.Agent.Shared.Enums;
using HASS.Agent.Shared.Extensions;
@@ -196,7 +197,10 @@ internal static AbstractSingleValueSensor ConvertConfiguredToAbstractSingleValue
abstractSensor = new BluetoothLeDevicesSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
break;
case SensorType.InternalDeviceSensor:
- abstractSensor = new InternalDeviceSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new HomeAssistant.Sensors.GeneralSensors.SingleValue.InternalDeviceSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.ScreenshotSensor:
+ abstractSensor = new ScreenshotSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
break;
default:
Log.Error("[SETTINGS_SENSORS] [{name}] Unknown configured single-value sensor type: {type}", sensor.EntityName, sensor.Type.ToString());
@@ -389,7 +393,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
};
}
- case InternalDeviceSensor internalDeviceSensor:
+ case HomeAssistant.Sensors.GeneralSensors.SingleValue.InternalDeviceSensor internalDeviceSensor:
{
_ = Enum.TryParse(internalDeviceSensor.GetType().Name, out var type);
return new ConfiguredSensor
@@ -404,6 +408,21 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
};
}
+ case ScreenshotSensor screenshotSensor:
+ {
+ _ = Enum.TryParse(screenshotSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(screenshotSensor.Id),
+ EntityName = screenshotSensor.EntityName,
+ Name = screenshotSensor.Name,
+ Type = type,
+ UpdateInterval = screenshotSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = screenshotSensor.IgnoreAvailability,
+ Query = screenshotSensor.ScreenIndex.ToString()
+ };
+ }
+
default:
{
_ = Enum.TryParse(sensor.GetType().Name, out var type);
From 7f6b3f536f6c9ad231d7623360b8f57e5946e4f1 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Tue, 13 Feb 2024 22:25:37 +0100
Subject: [PATCH 06/45] Feature: set audio input command (#41)
This PR adds "SetAudioInputCommand" requested by Chrisdegod.
In current state both default audio input and default communications device is set.
Supports setting device configured within UI as well as sent as a payload on the command topic.
---
.../HASS.Agent.Shared/Enums/CommandType.cs | 4 ++
.../InternalCommands/SetAudioInputCommand.cs | 67 +++++++++++++++++++
.../Localization/Languages.Designer.cs | 9 +++
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 6 +-
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
.../HASS.Agent/Commands/CommandsManager.cs | 8 +++
.../HASS.Agent/Forms/Commands/CommandsMod.cs | 3 +
.../Localization/Languages.Designer.cs | 10 +++
.../Resources/Localization/Languages.de.resx | 4 ++
.../Resources/Localization/Languages.en.resx | 4 ++
.../Resources/Localization/Languages.es.resx | 4 ++
.../Resources/Localization/Languages.fr.resx | 4 ++
.../Resources/Localization/Languages.nl.resx | 4 ++
.../Resources/Localization/Languages.pl.resx | 4 ++
.../Localization/Languages.pt-br.resx | 4 ++
.../Resources/Localization/Languages.resx | 4 ++
.../Resources/Localization/Languages.ru.resx | 4 ++
.../Resources/Localization/Languages.sl.resx | 4 ++
.../Resources/Localization/Languages.tr.resx | 4 ++
.../HASS.Agent/Settings/StoredCommands.cs | 3 +
29 files changed, 181 insertions(+), 3 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
index ee0fa0a0..e92db473 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
@@ -105,6 +105,10 @@ public enum CommandType
[EnumMember(Value = "SetAudioOutputCommand")]
SetAudioOutputCommand,
+ [LocalizedDescription("CommandType_SetAudioInputCommand", typeof(Languages))]
+ [EnumMember(Value = "SetAudioInputCommand")]
+ SetAudioInputCommand,
+
[LocalizedDescription("CommandType_ShutdownCommand", typeof(Languages))]
[EnumMember(Value = "ShutdownCommand")]
ShutdownCommand,
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
new file mode 100644
index 00000000..292b09ba
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
@@ -0,0 +1,67 @@
+using CoreAudio;
+using HASS.Agent.Shared.Enums;
+using Newtonsoft.Json;
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands;
+
+public class SetAudioInputCommand : InternalCommand
+{
+ private const string DefaultName = "setaudioinput";
+
+ private string InputDevice { get => CommandConfig; }
+
+ public SetAudioInputCommand(string entityName = DefaultName, string name = DefaultName, string audioDevice = "", CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(entityName ?? DefaultName, name ?? null, audioDevice, entityType, id)
+ {
+ State = "OFF";
+ }
+
+ public override void TurnOn()
+ {
+ if (string.IsNullOrWhiteSpace(InputDevice))
+ {
+ Log.Error("[SETAUDIOIN] Error, input device name cannot be null/blank");
+
+ return;
+ }
+
+ TurnOnWithAction(InputDevice);
+ }
+
+ private MMDevice GetAudioDeviceOrDefault(string playbackDeviceName)
+ {
+ var devices = Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);
+ var playbackDevice = devices.Where(d => d.DeviceFriendlyName == playbackDeviceName).FirstOrDefault();
+
+ return playbackDevice ?? Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
+ }
+
+ public override void TurnOnWithAction(string action)
+ {
+ State = "ON";
+
+ try
+ {
+ var outputDevice = GetAudioDeviceOrDefault(action);
+ if (outputDevice == Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications))
+ return;
+
+ outputDevice.Selected = true;
+ }
+ catch (Exception ex)
+ {
+ Log.Error("[SETAUDIOIN] Error while processing action '{action}': {err}", action, ex.Message);
+ }
+ finally
+ {
+ State = "OFF";
+ }
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index 16f7dcde..be913c63 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -1284,6 +1284,15 @@ internal static string CommandType_SetApplicationVolumeCommand {
}
}
+ ///
+ /// Looks up a localized string similar to SetAudioInputCommand.
+ ///
+ internal static string CommandType_SetAudioInputCommand {
+ get {
+ return ResourceManager.GetString("CommandType_SetAudioInputCommand", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to SetAudioOutputCommand.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index c20b1ed0..736f2957 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3361,4 +3361,7 @@ Willst Du den Runtime Installer herunterladen?
Bildschirmfoto
+
+ Legen Sie den Audioeingabebefehl fest
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index b7f04ec8..decc96d4 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3225,9 +3225,6 @@ Do you want to download the runtime installer?
InternalDeviceSensor
-
- SwitchDesktop
-
ActiveDesktop
@@ -3240,4 +3237,7 @@ Do you want to download the runtime installer?
Screenshot
+
+ SetAudioInputCommand
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 37db7723..58a31529 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3237,4 +3237,7 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Captura de pantalla
+
+ Establecer comando de entrada de audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index 4dfc6e87..32f1ca61 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3270,4 +3270,7 @@ Do you want to download the runtime installer?
Capture d'écran
+
+ Définir la commande d'entrée audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 6c01199c..664534da 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3259,4 +3259,7 @@ Wil je de runtime installatie downloaden?
Schermafbeelding
+
+ Stel het audio-invoercommando in
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index cfeff95d..c6ffb694 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3347,4 +3347,7 @@ Czy chcesz pobrać plik instalacyjny?
Zrzut ekranu
+
+ Ustaw wejście audio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index 8e8420d5..76c7b10d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3284,4 +3284,7 @@ Deseja baixar o Microsoft WebView2 runtime?
Captura de tela
+
+ Definir comando de entrada de áudio
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index ed3f3224..94dad3f7 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3220,4 +3220,7 @@ Do you want to download the runtime installer?
Screenshot
+
+ SetAudioInputCommand
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index fac40f1d..758aa8bd 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3305,4 +3305,7 @@ Home Assistant.
Скриншот
+
+ Установить команду аудиовхода
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index 7a2426ef..77cf6153 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3386,4 +3386,7 @@ Ali želite prenesti runtime installer?
Posnetek zaslona
+
+ Nastavite ukaz za zvočni vhod
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index f5cfa8d2..e2c8d79d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2844,4 +2844,7 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
Ekran görüntüsü
+
+ Ses Giriş Komutunu Ayarla
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
index a0850e70..6dd989f7 100644
--- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
@@ -483,6 +483,14 @@ internal static void LoadCommandInfo()
// =================================
+ commandInfoCard = new CommandInfoCard(CommandType.SetAudioInputCommand,
+ Languages.CommandsManager_SetAudioInputCommandDescription,
+ true, false, true);
+
+ CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
+
+ // =================================
+
commandInfoCard = new CommandInfoCard(CommandType.ShutdownCommand,
Languages.CommandsManager_ShutdownCommandDescription,
true, true, false);
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
index 525bcc87..983b41b8 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
@@ -206,6 +206,7 @@ private void LoadCommand()
case CommandType.SetVolumeCommand:
case CommandType.SetApplicationVolumeCommand:
case CommandType.SetAudioOutputCommand:
+ case CommandType.SetAudioInputCommand:
TbSetting.Text = Command.Command;
break;
}
@@ -457,6 +458,7 @@ private void BtnStore_Click(object sender, EventArgs e)
break;
case CommandType.SetAudioOutputCommand:
+ case CommandType.SetAudioInputCommand:
var audioDeviceName = TbSetting.Text.Trim();
if (string.IsNullOrEmpty(audioDeviceName))
{
@@ -630,6 +632,7 @@ private bool SetType(bool setDefaultValues = true)
break;
case CommandType.SetAudioOutputCommand:
+ case CommandType.SetAudioInputCommand:
SetAudioOutputUi();
break;
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 8a43773e..b84f6070 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -608,6 +608,16 @@ internal static string CommandsManager_SetApplicationVolumeCommandDescription {
}
}
+ ///
+ /// Looks up a localized string similar to Sets the default audio input for the system (including default communication device).
+ ///Requires audio device name as a payload..
+ ///
+ internal static string CommandsManager_SetAudioInputCommandDescription {
+ get {
+ return ResourceManager.GetString("CommandsManager_SetAudioInputCommandDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Sets the default audio output for the system.
///Requires audio device name as a payload..
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 376c1f7c..fbd3e43b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3484,4 +3484,8 @@ Die Bildschirmnummer hängt von der Systemkonfiguration ab – beginnt bei 0.
Bildschirmnummer
+
+ Legt den Standard-Audioeingang für das System fest (einschließlich des Standard-Kommunikationsgeräts).
+Als Payload benötigen Sie den Namen des Audiogeräts.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 6e767239..7be618f1 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3361,4 +3361,8 @@ Requires audio device name as a payload.
Provides a screenshot sensor in form of a camera entity.
Screen number depends on system configuration - starts at 0.
+
+ Sets the default audio input for the system (including default communication device).
+Requires audio device name as a payload.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 89b491b3..7aafaaa8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3360,4 +3360,8 @@ El número de pantalla depende de la configuración del sistema: comienza en 0.<
Número de pantalla
+
+ Establece la entrada de audio predeterminada para el sistema (incluido el dispositivo de comunicación predeterminado).
+Necesita el nombre del dispositivo de audio como carga útil.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index ffe6fbe4..62fb1f9c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3393,4 +3393,8 @@ Le numéro d'écran dépend de la configuration du système - commence à 0.
Numéro d'écran
+
+ Définit l'entrée audio par défaut du système (y compris le périphérique de communication par défaut).
+Vous avez besoin du nom du périphérique audio comme charge utile.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 7629e0fd..a56c0472 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3381,4 +3381,8 @@ Schermnummer is afhankelijk van de systeemconfiguratie - begint bij 0.
Schermnummer
+
+ Stelt de standaardaudio-invoer in voor het systeem (inclusief het standaardcommunicatieapparaat).
+U hebt de naam van het audioapparaat nodig als payload.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 0f937e96..a68dbf65 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3470,4 +3470,8 @@ Numer ekranu zależy od konfiguracji systemu – zaczyna się od 0.
Numer ekranu
+
+ Ustawia domyślne wejście audio dla systemu (w tym domyślne urządzenie komunikacyjne).
+Wymaga nazwy urządzenia audio jako ładunku.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 27b31828..ff0267af 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3406,4 +3406,8 @@ O número da tela depende da configuração do sistema – começa em 0.
Número da tela
+
+ Define a entrada de áudio padrão para o sistema (incluindo o dispositivo de comunicação padrão).
+Você precisa do nome do dispositivo de áudio como carga útil.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 8cf4f2a9..47ff6ae3 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3373,4 +3373,8 @@ Requires audio device name as a payload.
Provides a screenshot sensor in form of a camera entity.
Screen number depends on system configuration - starts at 0.
+
+ Sets the default audio input for the system (including default communication device).
+Requires audio device name as a payload.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 8846437c..9a34e67d 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3429,4 +3429,8 @@ Home Assistant.
Номер экрана
+
+ Устанавливает аудиовход по умолчанию для системы (включая устройство связи по умолчанию).
+В качестве полезной нагрузки требуется имя аудиоустройства.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index e32b3a8c..970518e6 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3509,4 +3509,8 @@ Zahteva ime zvočne naprave kot tovor.
Številka zaslona
+
+ Nastavi privzeti zvočni vhod za sistem (vključno s privzeto komunikacijsko napravo).
+Ime zvočne naprave potrebujete kot obremenitev.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index ebd9375f..a9138f11 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2972,4 +2972,8 @@ Ekran numarası sistem konfigürasyonuna bağlıdır - 0'dan başlar.
Ekran numarası
+
+ Sistem için varsayılan ses girişini ayarlar (varsayılan iletişim cihazı dahil).
+Yük olarak ses cihazı adına ihtiyacınız vardır.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
index c277e876..9ddb228a 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
@@ -178,6 +178,9 @@ internal static AbstractCommand ConvertConfiguredToAbstract(ConfiguredCommand co
case CommandType.SetAudioOutputCommand:
abstractCommand = new SetAudioOutputCommand(command.EntityName, command.Name, command.Command, command.EntityType, command.Id.ToString());
break;
+ case CommandType.SetAudioInputCommand:
+ abstractCommand = new SetAudioInputCommand(command.EntityName, command.Name, command.Command, command.EntityType, command.Id.ToString());
+ break;
default:
Log.Error("[SETTINGS_COMMANDS] [{name}] Unknown configured command type: {type}", command.EntityName, command.Type.ToString());
break;
From dd55e187423ab88b854b97335b117d336f90df1c Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 15 Feb 2024 20:23:20 +0100
Subject: [PATCH 07/45] Fix: Powershell command argument binding (#47)
This PR fixes how action arguments are passed on to the PowershellCommand.
Previously whole action string was passed as a one argument, this prevented parameter binding - after the changes the action string itself defines how the parameter/s will be handled.
Thanks to @shupershuff for reporting :)
---
.../Managers/PowershellManager.cs | 625 +++++++++---------
1 file changed, 313 insertions(+), 312 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
index 576823dc..f28df5d7 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
@@ -2,322 +2,323 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using CliWrap;
using Newtonsoft.Json;
using Serilog;
-namespace HASS.Agent.Shared.Managers
+namespace HASS.Agent.Shared.Managers;
+
+///
+/// Performs Powershell-related actions
+///
+public static class PowershellManager
{
- ///
- /// Performs Powershell-related actions
- ///
- public static class PowershellManager
- {
- ///
- /// Execute a Powershell command without waiting for or checking results
- ///
- ///
- ///
- public static bool ExecuteCommandHeadless(string command) => ExecuteHeadless(command, string.Empty, false);
-
- ///
- /// Executes a Powershell script without waiting for or checking results
- ///
- ///
- ///
- ///
- public static bool ExecuteScriptHeadless(string script, string parameters) => ExecuteHeadless(script, parameters, true);
-
- private static string GetProcessArguments(string command, string parameters, bool isScript)
- {
- if (isScript)
- {
- return string.IsNullOrWhiteSpace(parameters)
- ? $"-File \"{command}\""
- : $"-File \"{command}\" \"{parameters}\"";
- }
- else
- {
- return $@"& {{{command}}}"; //NOTE: place to fix any potential future issues with "command part of the command"
- }
- }
-
- private static bool ExecuteHeadless(string command, string parameters, bool isScript)
- {
- var descriptor = isScript ? "script" : "command";
-
- try
- {
- var workingDir = string.Empty;
- if (isScript)
- {
- // try to get the script's startup path
- var scriptDir = Path.GetDirectoryName(command);
- workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
- }
-
- var psExec = GetPsExecutable();
- if (string.IsNullOrEmpty(psExec))
- return false;
-
- var processInfo = new ProcessStartInfo
- {
- WindowStyle = ProcessWindowStyle.Hidden,
- CreateNoWindow = true,
- FileName = psExec,
- WorkingDirectory = workingDir,
- Arguments = GetProcessArguments(command, parameters, isScript)
- };
-
- using var process = new Process();
- process.StartInfo = processInfo;
- var start = process.Start();
-
- if (!start)
- {
- Log.Error("[POWERSHELL] Unable to start processing {descriptor}: {command}", descriptor, command);
-
- return false;
- }
-
- return true;
- }
- catch (Exception ex)
- {
- Log.Fatal(ex, "[POWERSHELL] Fatal error when executing {descriptor}: {command}", descriptor, command);
-
- return false;
- }
- }
-
- ///
- /// Execute a Powershell command, logs the output if it fails
- ///
- ///
- ///
- ///
- public static bool ExecuteCommand(string command, TimeSpan timeout) => Execute(command, string.Empty, false, timeout);
-
- ///
- /// Executes a Powershell script, logs the output if it fails
- ///
- ///
- ///
- ///
- public static bool ExecuteScript(string script, string parameters, TimeSpan timeout) => Execute(script, parameters, true, timeout);
-
- private static bool Execute(string command, string parameters, bool isScript, TimeSpan timeout)
- {
- var descriptor = isScript ? "script" : "command";
-
- try
- {
- var workingDir = string.Empty;
- if (isScript)
- {
- // try to get the script's startup path
- var scriptDir = Path.GetDirectoryName(command);
- workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
- }
-
- var psExec = GetPsExecutable();
- if (string.IsNullOrEmpty(psExec)) return false;
-
- var processInfo = new ProcessStartInfo
- {
- FileName = psExec,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- UseShellExecute = false,
- WorkingDirectory = workingDir,
- Arguments = GetProcessArguments(command, parameters, isScript)
- };
-
- using var process = new Process();
- process.StartInfo = processInfo;
- var start = process.Start();
-
- if (!start)
- {
- Log.Error("[POWERSHELL] Unable to start processing {descriptor}: {script}", descriptor, command);
-
- return false;
- }
-
- process.WaitForExit(Convert.ToInt32(timeout.TotalMilliseconds));
-
- if (process.ExitCode == 0)
- return true;
-
- // non-zero exitcode, process as failed
- Log.Error("[POWERSHELL] The {descriptor} returned non-zero exitcode: {code}", descriptor, process.ExitCode);
-
- var errors = process.StandardError.ReadToEnd().Trim();
- if (!string.IsNullOrEmpty(errors))
- {
- Log.Error("[POWERSHELL] Error output:\r\n{output}", errors);
- }
- else
- {
- var console = process.StandardOutput.ReadToEnd().Trim();
- if (!string.IsNullOrEmpty(console))
- Log.Error("[POWERSHELL] No error output, console output:\r\n{output}", errors);
- else
- Log.Error("[POWERSHELL] No error and no console output");
- }
-
- return false;
- }
- catch (Exception ex)
- {
- Log.Fatal(ex, "[POWERSHELL] Fatal error when executing {descriptor}: {command}", descriptor, command);
-
- return false;
- }
- }
-
- private static Encoding TryParseCodePage(int codePage)
- {
- Encoding encoding = null;
- try
- {
- encoding = Encoding.GetEncoding(codePage);
- }
- catch
- {
- // best effort
- }
-
- return encoding;
- }
-
- private static Encoding GetEncoding()
- {
- var encoding = TryParseCodePage(CultureInfo.InstalledUICulture.TextInfo.OEMCodePage);
- if (encoding != null)
- return encoding;
-
- encoding = TryParseCodePage(CultureInfo.CurrentCulture.TextInfo.OEMCodePage);
- if (encoding != null)
- return encoding;
-
- encoding = TryParseCodePage(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage);
- if (encoding != null)
- return encoding;
-
- encoding = TryParseCodePage(CultureInfo.InvariantCulture.TextInfo.OEMCodePage);
- if (encoding != null)
- return encoding;
-
- Log.Warning("[POWERSHELL] Cannot parse system text culture to encoding, returning UTF-8 as a fallback, please report this as a GitHub issue");
-
- Log.Debug("[POWERSHELL] currentInstalledUICulture {c}", JsonConvert.SerializeObject(CultureInfo.InstalledUICulture.TextInfo));
- Log.Debug("[POWERSHELL] currentCulture {c}", JsonConvert.SerializeObject(CultureInfo.CurrentCulture.TextInfo));
- Log.Debug("[POWERSHELL] currentUICulture {c}", JsonConvert.SerializeObject(CultureInfo.CurrentUICulture.TextInfo));
- Log.Debug("[POWERSHELL] invariantCulture {c}", JsonConvert.SerializeObject(CultureInfo.InvariantCulture.TextInfo));
-
- return Encoding.UTF8;
- }
-
- ///
- /// Executes the command or script, and returns the standard and error output
- ///
- ///
- ///
- ///
- ///
- ///
- internal static bool ExecuteWithOutput(string command, TimeSpan timeout, out string output, out string errors)
- {
- output = string.Empty;
- errors = string.Empty;
-
- try
- {
- var isScript = command.ToLower().EndsWith(".ps1");
-
- var workingDir = string.Empty;
- if (isScript)
- {
- // try to get the script's startup path
- var scriptDir = Path.GetDirectoryName(command);
- workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
- }
-
- var psExec = GetPsExecutable();
- if (string.IsNullOrEmpty(psExec))
- return false;
-
- var encoding = GetEncoding();
-
- var processInfo = new ProcessStartInfo
- {
- FileName = psExec,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- UseShellExecute = false,
- CreateNoWindow = true,
- WorkingDirectory = workingDir,
- StandardOutputEncoding = encoding,
- StandardErrorEncoding = encoding,
- // set the right type of arguments
- Arguments = isScript
- ? $@"& '{command}'"
- : $@"& {{{command}}}"
- };
-
- using var process = new Process();
- process.StartInfo = processInfo;
-
- var start = process.Start();
- if (!start)
- {
- Log.Error("[POWERSHELL] Unable to begin executing the {type}: {cmd}", isScript ? "script" : "command", command);
-
- return false;
- }
-
- var completed = process.WaitForExit(Convert.ToInt32(timeout.TotalMilliseconds));
- if (!completed)
- Log.Error("[POWERSHELL] Timeout executing the {type}: {cmd}", isScript ? "script" : "command", command);
-
- output = process.StandardOutput.ReadToEnd().Trim();
- errors = process.StandardError.ReadToEnd().Trim();
-
- process.StandardOutput.Dispose();
- process.StandardError.Dispose();
-
- process.Kill();
-
- return completed;
- }
- catch (Exception ex)
- {
- Log.Fatal(ex, ex.Message);
-
- return false;
- }
- }
-
- ///
- /// Attempt to locate powershell.exe
- ///
- ///
- public static string GetPsExecutable()
- {
- // try regular location
- var psExec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "WindowsPowerShell\\v1.0\\powershell.exe");
- if (File.Exists(psExec))
- return psExec;
-
- // try specific
- psExec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "WindowsPowerShell\\v1.0\\powershell.exe");
- if (File.Exists(psExec))
- return psExec;
-
- Log.Error("[POWERSHELL] PS executable not found, make sure you have powershell installed on your system");
- return string.Empty;
- }
- }
+ ///
+ /// Execute a Powershell command without waiting for or checking results
+ ///
+ ///
+ ///
+ public static bool ExecuteCommandHeadless(string command) => ExecuteHeadless(command, string.Empty, false);
+
+ ///
+ /// Executes a Powershell script without waiting for or checking results
+ ///
+ ///
+ ///
+ ///
+ public static bool ExecuteScriptHeadless(string script, string parameters) => ExecuteHeadless(script, parameters, true);
+
+ private static string GetProcessArguments(string command, string parameters, bool isScript)
+ {
+ if (isScript)
+ {
+ return string.IsNullOrWhiteSpace(parameters)
+ ? $"-File \"{command}\""
+ : $"-File \"{command}\" {parameters}";
+ }
+ else
+ {
+ return $@"& {{{command}}}"; //NOTE: place to fix any potential future issues with "command part of the command"
+ }
+ }
+
+ private static bool ExecuteHeadless(string command, string parameters, bool isScript)
+ {
+ var descriptor = isScript ? "script" : "command";
+
+ try
+ {
+ var workingDir = string.Empty;
+ if (isScript)
+ {
+ // try to get the script's startup path
+ var scriptDir = Path.GetDirectoryName(command);
+ workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
+ }
+
+ var psExec = GetPsExecutable();
+ if (string.IsNullOrEmpty(psExec))
+ return false;
+
+ var processInfo = new ProcessStartInfo
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ CreateNoWindow = true,
+ FileName = psExec,
+ WorkingDirectory = workingDir,
+ Arguments = GetProcessArguments(command, parameters, isScript)
+ };
+
+ using var process = new Process();
+ process.StartInfo = processInfo;
+ var start = process.Start();
+
+ if (!start)
+ {
+ Log.Error("[POWERSHELL] Unable to start processing {descriptor}: {command}", descriptor, command);
+
+ return false;
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "[POWERSHELL] Fatal error when executing {descriptor}: {command}", descriptor, command);
+
+ return false;
+ }
+ }
+
+ ///
+ /// Execute a Powershell command, logs the output if it fails
+ ///
+ ///
+ ///
+ ///
+ public static bool ExecuteCommand(string command, TimeSpan timeout) => Execute(command, string.Empty, false, timeout);
+
+ ///
+ /// Executes a Powershell script, logs the output if it fails
+ ///
+ ///
+ ///
+ ///
+ public static bool ExecuteScript(string script, string parameters, TimeSpan timeout) => Execute(script, parameters, true, timeout);
+
+ private static bool Execute(string command, string parameters, bool isScript, TimeSpan timeout)
+ {
+ var descriptor = isScript ? "script" : "command";
+
+ try
+ {
+ var workingDir = string.Empty;
+ if (isScript)
+ {
+ // try to get the script's startup path
+ var scriptDir = Path.GetDirectoryName(command);
+ workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
+ }
+
+ var psExec = GetPsExecutable();
+ if (string.IsNullOrEmpty(psExec)) return false;
+
+ var processInfo = new ProcessStartInfo
+ {
+ FileName = psExec,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ WorkingDirectory = workingDir,
+ Arguments = GetProcessArguments(command, parameters, isScript)
+ };
+
+ using var process = new Process();
+ process.StartInfo = processInfo;
+ var start = process.Start();
+
+ if (!start)
+ {
+ Log.Error("[POWERSHELL] Unable to start processing {descriptor}: {script}", descriptor, command);
+
+ return false;
+ }
+
+ process.WaitForExit(Convert.ToInt32(timeout.TotalMilliseconds));
+
+ if (process.ExitCode == 0)
+ return true;
+
+ // non-zero exitcode, process as failed
+ Log.Error("[POWERSHELL] The {descriptor} returned non-zero exitcode: {code}", descriptor, process.ExitCode);
+
+ var errors = process.StandardError.ReadToEnd().Trim();
+ if (!string.IsNullOrEmpty(errors))
+ {
+ Log.Error("[POWERSHELL] Error output:\r\n{output}", errors);
+ }
+ else
+ {
+ var console = process.StandardOutput.ReadToEnd().Trim();
+ if (!string.IsNullOrEmpty(console))
+ Log.Error("[POWERSHELL] No error output, console output:\r\n{output}", errors);
+ else
+ Log.Error("[POWERSHELL] No error and no console output");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "[POWERSHELL] Fatal error when executing {descriptor}: {command}", descriptor, command);
+
+ return false;
+ }
+ }
+
+ private static Encoding TryParseCodePage(int codePage)
+ {
+ Encoding encoding = null;
+ try
+ {
+ encoding = Encoding.GetEncoding(codePage);
+ }
+ catch
+ {
+ // best effort
+ }
+
+ return encoding;
+ }
+
+ private static Encoding GetEncoding()
+ {
+ var encoding = TryParseCodePage(CultureInfo.InstalledUICulture.TextInfo.OEMCodePage);
+ if (encoding != null)
+ return encoding;
+
+ encoding = TryParseCodePage(CultureInfo.CurrentCulture.TextInfo.OEMCodePage);
+ if (encoding != null)
+ return encoding;
+
+ encoding = TryParseCodePage(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage);
+ if (encoding != null)
+ return encoding;
+
+ encoding = TryParseCodePage(CultureInfo.InvariantCulture.TextInfo.OEMCodePage);
+ if (encoding != null)
+ return encoding;
+
+ Log.Warning("[POWERSHELL] Cannot parse system text culture to encoding, returning UTF-8 as a fallback, please report this as a GitHub issue");
+
+ Log.Debug("[POWERSHELL] currentInstalledUICulture {c}", JsonConvert.SerializeObject(CultureInfo.InstalledUICulture.TextInfo));
+ Log.Debug("[POWERSHELL] currentCulture {c}", JsonConvert.SerializeObject(CultureInfo.CurrentCulture.TextInfo));
+ Log.Debug("[POWERSHELL] currentUICulture {c}", JsonConvert.SerializeObject(CultureInfo.CurrentUICulture.TextInfo));
+ Log.Debug("[POWERSHELL] invariantCulture {c}", JsonConvert.SerializeObject(CultureInfo.InvariantCulture.TextInfo));
+
+ return Encoding.UTF8;
+ }
+
+ ///
+ /// Executes the command or script, and returns the standard and error output
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static bool ExecuteWithOutput(string command, TimeSpan timeout, out string output, out string errors)
+ {
+ output = string.Empty;
+ errors = string.Empty;
+
+ try
+ {
+ var isScript = command.ToLower().EndsWith(".ps1");
+
+ var workingDir = string.Empty;
+ if (isScript)
+ {
+ // try to get the script's startup path
+ var scriptDir = Path.GetDirectoryName(command);
+ workingDir = !string.IsNullOrEmpty(scriptDir) ? scriptDir : string.Empty;
+ }
+
+ var psExec = GetPsExecutable();
+ if (string.IsNullOrEmpty(psExec))
+ return false;
+
+ var encoding = GetEncoding();
+
+ var processInfo = new ProcessStartInfo
+ {
+ FileName = psExec,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = workingDir,
+ StandardOutputEncoding = encoding,
+ StandardErrorEncoding = encoding,
+ // set the right type of arguments
+ Arguments = isScript
+ ? $@"& '{command}'"
+ : $@"& {{{command}}}"
+ };
+
+ using var process = new Process();
+ process.StartInfo = processInfo;
+
+ var start = process.Start();
+ if (!start)
+ {
+ Log.Error("[POWERSHELL] Unable to begin executing the {type}: {cmd}", isScript ? "script" : "command", command);
+
+ return false;
+ }
+
+ var completed = process.WaitForExit(Convert.ToInt32(timeout.TotalMilliseconds));
+ if (!completed)
+ Log.Error("[POWERSHELL] Timeout executing the {type}: {cmd}", isScript ? "script" : "command", command);
+
+ output = process.StandardOutput.ReadToEnd().Trim();
+ errors = process.StandardError.ReadToEnd().Trim();
+
+ process.StandardOutput.Dispose();
+ process.StandardError.Dispose();
+
+ process.Kill();
+
+ return completed;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, ex.Message);
+
+ return false;
+ }
+ }
+
+ ///
+ /// Attempt to locate powershell.exe
+ ///
+ ///
+ public static string GetPsExecutable()
+ {
+ // try regular location
+ var psExec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "WindowsPowerShell\\v1.0\\powershell.exe");
+ if (File.Exists(psExec))
+ return psExec;
+
+ // try specific
+ psExec = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "WindowsPowerShell\\v1.0\\powershell.exe");
+ if (File.Exists(psExec))
+ return psExec;
+
+ Log.Error("[POWERSHELL] PS executable not found, make sure you have powershell installed on your system");
+ return string.Empty;
+ }
}
From d1af9d6cd3a3ed6294f159d55d311924233df10f Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 24 Feb 2024 11:44:05 +0100
Subject: [PATCH 08/45] Fix: active window title encoding (#50)
* added charset to the dllimport
* added check for the window name length
---
.../SingleValue/ActiveWindowSensor.cs | 22 ++++++++++++-------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
index c6711c3e..713a50e7 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
@@ -16,10 +16,12 @@ public ActiveWindowSensor(int? updateInterval = null, string entityName = Defaul
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
- if (Variables.MqttManager == null) return null;
+ if (Variables.MqttManager == null)
+ return null;
var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
- if (deviceConfig == null) return null;
+ if (deviceConfig == null)
+ return null;
return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
@@ -43,16 +45,20 @@ public override string GetState()
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
- [DllImport("user32.dll")]
- private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ private static extern int GetWindowTextLength(IntPtr hWnd);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ private static extern int GetWindowText(IntPtr hWnd, StringBuilder builder, int count);
private static string GetActiveWindowTitle()
{
- const int nChars = 256;
- var buff = new StringBuilder(nChars);
- var handle = GetForegroundWindow();
+ var windowHandle = GetForegroundWindow();
+ var titleLength = GetWindowTextLength(windowHandle) + 1;
+ var builder = new StringBuilder(titleLength);
+ var windowTitle = GetWindowText(windowHandle, builder, titleLength) > 0 ? builder.ToString() : string.Empty;
- return GetWindowText(handle, buff, nChars) > 0 ? buff.ToString() : string.Empty;
+ return windowTitle.Length > 255 ? windowTitle[..255]: windowTitle; //Note(Amadeo): to make sure we don't exceed HA limitation of 255 payload length
}
}
}
From a426aee510551dfffaeeb655acbae843acfaf638 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 1 Mar 2024 12:35:40 +0100
Subject: [PATCH 09/45] Fix: constant MQTT discovery payload publish (#54)
* discovery payload is sent only once per run and config change
---
.../Commands/CommandsManager.cs | 19 ++++++-----
.../Sensors/SensorsManager.cs | 32 +++++++++++--------
.../HASS.Agent/Commands/CommandsManager.cs | 22 +++++++++----
.../HASS.Agent/Sensors/SensorsManager.cs | 32 +++++++++++--------
4 files changed, 63 insertions(+), 42 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs
index d65f8dba..872371fd 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs
@@ -15,6 +15,8 @@ internal static class CommandsManager
private static bool _active = true;
private static bool _pause;
+ private static bool _discoveryPublished = false;
+
private static DateTime _lastAutoDiscoPublish = DateTime.MinValue;
///
@@ -122,20 +124,22 @@ private static async void Process()
// do we have commands?
if (!CommandsPresent()) continue;
- // publish availability & sensor autodisco's every 30 sec
+ // publish availability & autodiscovery every 30 sec
if ((DateTime.Now - _lastAutoDiscoPublish).TotalSeconds > 30)
{
- // let hass know we're still here
await Variables.MqttManager.AnnounceAvailabilityAsync();
- // publish command autodisco's
- foreach (var command in Variables.Commands.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ if (!_discoveryPublished)
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
- await command.PublishAutoDiscoveryConfigAsync();
+ foreach (var command in Variables.Commands.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
+ await command.PublishAutoDiscoveryConfigAsync();
+ }
+
+ _discoveryPublished = true;
}
- // are we subscribed?
if (!_subscribed)
{
foreach (var command in Variables.Commands.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
@@ -146,7 +150,6 @@ private static async void Process()
_subscribed = true;
}
- // log moment
_lastAutoDiscoPublish = DateTime.Now;
}
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs
index 8a7e3623..10122601 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs
@@ -14,6 +14,8 @@ internal static class SensorsManager
private static bool _active = true;
private static bool _pause;
+ private static bool _discoveryPublished = false;
+
private static DateTime _lastAutoDiscoPublish = DateTime.MinValue;
///
@@ -116,32 +118,34 @@ private static async void Process()
// optionally flag as the first real run
if (!firstRunDone) firstRunDone = true;
- // publish availability & sensor autodisco's every 30 sec
+ // publish availability & autodiscovery every 30 sec
if ((DateTime.Now - _lastAutoDiscoPublish).TotalSeconds > 30)
{
- // let hass know we're still here
await Variables.MqttManager.AnnounceAvailabilityAsync();
- // publish the autodisco's
- if (SingleValueSensorsPresent())
+ if (!_discoveryPublished)
{
- foreach (var sensor in Variables.SingleValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ if (SingleValueSensorsPresent())
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
- await sensor.PublishAutoDiscoveryConfigAsync();
+ foreach (var sensor in Variables.SingleValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
+ await sensor.PublishAutoDiscoveryConfigAsync();
+ }
}
- }
- if (MultiValueSensorsPresent())
- {
- foreach (var sensor in Variables.MultiValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ if (MultiValueSensorsPresent())
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
- await sensor.PublishAutoDiscoveryConfigAsync();
+ foreach (var sensor in Variables.MultiValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
+ await sensor.PublishAutoDiscoveryConfigAsync();
+ }
}
+
+ _discoveryPublished = true;
}
- // log moment
_lastAutoDiscoPublish = DateTime.Now;
}
diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
index 6dd989f7..0d4dbc69 100644
--- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
@@ -17,6 +17,8 @@ internal static class CommandsManager
private static bool _active = true;
private static bool _pause;
+ private static bool _discoveryPublished = false;
+
private static DateTime _lastAutoDiscoPublish = DateTime.MinValue;
///
@@ -66,6 +68,8 @@ internal static async Task UnpublishAllCommands()
await command.UnPublishAutoDiscoveryConfigAsync();
await Variables.MqttManager.UnsubscribeAsync(command);
}
+
+ _discoveryPublished = false;
}
///
@@ -103,18 +107,24 @@ private static async void Process()
firstRun = false;
+ // publish availability & autodiscovery every 30 sec
if ((DateTime.Now - _lastAutoDiscoPublish).TotalSeconds > 30)
{
await Variables.MqttManager.AnnounceAvailabilityAsync();
- foreach (var command in Variables.Commands
- .TakeWhile(_ => !_pause)
- .TakeWhile(_ => _active))
+ if (!_discoveryPublished)
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected)
- continue;
+ foreach (var command in Variables.Commands
+ .TakeWhile(_ => !_pause)
+ .TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected)
+ continue;
+
+ await command.PublishAutoDiscoveryConfigAsync();
+ }
- await command.PublishAutoDiscoveryConfigAsync();
+ _discoveryPublished = true;
}
if (!subscribed)
diff --git a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
index 0f5f4eba..f8cc5b7a 100644
--- a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
@@ -20,6 +20,8 @@ internal static class SensorsManager
private static bool _active = true;
private static bool _pause;
+ private static bool _discoveryPublished = false;
+
private static DateTime _lastAutoDiscoPublish = DateTime.MinValue;
///
@@ -96,32 +98,34 @@ private static async void Process()
// optionally flag as the first real run
if (!firstRunDone) firstRunDone = true;
- // publish availability & sensor autodisco's every 30 sec
+ // publish availability & autodiscovery every 30 sec
if ((DateTime.Now - _lastAutoDiscoPublish).TotalSeconds > 30)
{
- // let hass know we're still here
await Variables.MqttManager.AnnounceAvailabilityAsync();
- // publish the autodisco's
- if (SingleValueSensorsPresent())
+ if (!_discoveryPublished)
{
- foreach (var sensor in Variables.SingleValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ if (SingleValueSensorsPresent())
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
- await sensor.PublishAutoDiscoveryConfigAsync();
+ foreach (var sensor in Variables.SingleValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
+ await sensor.PublishAutoDiscoveryConfigAsync();
+ }
}
- }
- if (MultiValueSensorsPresent())
- {
- foreach (var sensor in Variables.MultiValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ if (MultiValueSensorsPresent())
{
- if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
- await sensor.PublishAutoDiscoveryConfigAsync();
+ foreach (var sensor in Variables.MultiValueSensors.TakeWhile(_ => !_pause).TakeWhile(_ => _active))
+ {
+ if (_pause || Variables.MqttManager.GetStatus() != MqttStatus.Connected) continue;
+ await sensor.PublishAutoDiscoveryConfigAsync();
+ }
}
+
+ _discoveryPublished = true;
}
- // log moment
_lastAutoDiscoPublish = DateTime.Now;
}
From b828cf814d1548d70b2912111229297efa17d767 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 1 Mar 2024 12:36:26 +0100
Subject: [PATCH 10/45] Feature: ability to ignore MQTT grace period after
waking up from hibernation (#51)
This PR adds an option to ignore grace period if system was woken up from the hibernation not later than the grace period itself - disabled by default.
---
.../Configuration/ConfigMqtt.Designer.cs | 99 +++++++++++--------
.../Controls/Configuration/ConfigMqtt.cs | 5 +
.../Controls/Configuration/ConfigMqtt.resx | 59 ++++++-----
.../HASS.Agent/Forms/Configuration.cs | 2 +
src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs | 37 +++++--
.../HASS.Agent/Managers/SystemStateManager.cs | 21 +++-
.../HASS.Agent/Models/Config/AppSettings.cs | 2 +
.../Localization/Languages.Designer.cs | 9 ++
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
19 files changed, 192 insertions(+), 75 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs
index 48c9bfbb..670ddf22 100644
--- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs
@@ -56,6 +56,7 @@ private void InitializeComponent()
this.LblClientId = new System.Windows.Forms.Label();
this.NumMqttPort = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
this.PbShow = new System.Windows.Forms.PictureBox();
+ this.CbIgnoreGracePeriod = new System.Windows.Forms.CheckBox();
this.CbEnableMqtt = new System.Windows.Forms.CheckBox();
this.LblMqttDisabledWarning = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.NumMqttPort)).BeginInit();
@@ -460,51 +461,68 @@ private void InitializeComponent()
this.LblMqttDisabledWarning.Text = Languages.ConfigMqtt_LblMqttDisabledWarning;
this.LblMqttDisabledWarning.Visible = false;
//
+ // CbIgnoreGracePeriod
+ //
+ this.CbIgnoreGracePeriod.AccessibleDescription = "Ignore grace period after waking up from hibernation.";
+ this.CbIgnoreGracePeriod.AccessibleName = "Ignore grace period";
+ this.CbIgnoreGracePeriod.AccessibleRole = AccessibleRole.CheckButton;
+ this.CbIgnoreGracePeriod.AutoSize = true;
+ this.CbIgnoreGracePeriod.Checked = false;
+ this.CbIgnoreGracePeriod.CheckState = CheckState.Unchecked;
+ this.CbIgnoreGracePeriod.Font = new Font("Segoe UI", 10F);
+ this.CbIgnoreGracePeriod.Location = new Point(59, 612);
+ this.CbIgnoreGracePeriod.Name = "CbIgnoreGracePeriod";
+ this.CbIgnoreGracePeriod.Size = new Size(354, 23);
+ this.CbIgnoreGracePeriod.TabIndex = 103;
+ this.CbIgnoreGracePeriod.Text = Languages.ConfigMqtt_CbIgnoreGracePeriod;
+ this.CbIgnoreGracePeriod.UseVisualStyleBackColor = true;
+ this.CbIgnoreGracePeriod.CheckedChanged += new System.EventHandler(this.CbIgnoreGracePeriod_CheckedChanged);
+ //
// ConfigMqtt
//
this.AccessibleDescription = "Panel containing the MQTT client configuration.";
this.AccessibleName = "MQTT";
- this.AccessibleRole = System.Windows.Forms.AccessibleRole.Pane;
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(48)))));
- this.Controls.Add(this.LblMqttDisabledWarning);
- this.Controls.Add(this.CbEnableMqtt);
- this.Controls.Add(this.PbShow);
- this.Controls.Add(this.NumMqttPort);
- this.Controls.Add(this.LblTip2);
- this.Controls.Add(this.TbMqttClientId);
- this.Controls.Add(this.LblClientId);
- this.Controls.Add(this.LblTip3);
- this.Controls.Add(this.TbMqttClientCertificate);
- this.Controls.Add(this.LblClientCert);
- this.Controls.Add(this.TbMqttRootCertificate);
- this.Controls.Add(this.LblRootCert);
- this.Controls.Add(this.CbUseRetainFlag);
- this.Controls.Add(this.CbAllowUntrustedCertificates);
- this.Controls.Add(this.BtnMqttClearConfig);
- this.Controls.Add(this.LblTip1);
- this.Controls.Add(this.LblInfo1);
- this.Controls.Add(this.TbMqttDiscoveryPrefix);
- this.Controls.Add(this.LblDiscoPrefix);
- this.Controls.Add(this.TbMqttPassword);
- this.Controls.Add(this.TbMqttUsername);
- this.Controls.Add(this.TbMqttAddress);
- this.Controls.Add(this.CbMqttTls);
- this.Controls.Add(this.LblBrokerPassword);
- this.Controls.Add(this.LblBrokerUsername);
- this.Controls.Add(this.LblBrokerPort);
- this.Controls.Add(this.LblBrokerIp);
- this.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.Margin = new System.Windows.Forms.Padding(4);
+ this.AccessibleRole = AccessibleRole.Pane;
+ this.AutoScaleDimensions = new SizeF(96F, 96F);
+ this.AutoScaleMode = AutoScaleMode.Dpi;
+ this.BackColor = Color.FromArgb(45, 45, 48);
+ this.Controls.Add(CbIgnoreGracePeriod);
+ this.Controls.Add(LblMqttDisabledWarning);
+ this.Controls.Add(CbEnableMqtt);
+ this.Controls.Add(PbShow);
+ this.Controls.Add(NumMqttPort);
+ this.Controls.Add(LblTip2);
+ this.Controls.Add(TbMqttClientId);
+ this.Controls.Add(LblClientId);
+ this.Controls.Add(LblTip3);
+ this.Controls.Add(TbMqttClientCertificate);
+ this.Controls.Add(LblClientCert);
+ this.Controls.Add(TbMqttRootCertificate);
+ this.Controls.Add(LblRootCert);
+ this.Controls.Add(CbUseRetainFlag);
+ this.Controls.Add(CbAllowUntrustedCertificates);
+ this.Controls.Add(BtnMqttClearConfig);
+ this.Controls.Add(LblTip1);
+ this.Controls.Add(LblInfo1);
+ this.Controls.Add(TbMqttDiscoveryPrefix);
+ this.Controls.Add(LblDiscoPrefix);
+ this.Controls.Add(TbMqttPassword);
+ this.Controls.Add(TbMqttUsername);
+ this.Controls.Add(TbMqttAddress);
+ this.Controls.Add(CbMqttTls);
+ this.Controls.Add(LblBrokerPassword);
+ this.Controls.Add(LblBrokerUsername);
+ this.Controls.Add(LblBrokerPort);
+ this.Controls.Add(LblBrokerIp);
+ this.ForeColor = Color.FromArgb(241, 241, 241);
+ this.Margin = new Padding(4);
this.Name = "ConfigMqtt";
- this.Size = new System.Drawing.Size(700, 614);
- this.Load += new System.EventHandler(this.ConfigMqtt_Load);
- ((System.ComponentModel.ISupportInitialize)(this.NumMqttPort)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.PbShow)).EndInit();
- this.ResumeLayout(false);
- this.PerformLayout();
-
+ this.Size = new Size(700, 658);
+ this.Load += ConfigMqtt_Load;
+ ((System.ComponentModel.ISupportInitialize)NumMqttPort).EndInit();
+ ((System.ComponentModel.ISupportInitialize)PbShow).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
}
#endregion
@@ -536,5 +554,6 @@ private void InitializeComponent()
private PictureBox PbShow;
internal CheckBox CbEnableMqtt;
private Label LblMqttDisabledWarning;
+ internal CheckBox CbIgnoreGracePeriod;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs
index c39635d7..83354e26 100644
--- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs
@@ -60,5 +60,10 @@ private void CbEnableMqtt_CheckedChanged(object sender, EventArgs e)
{
LblMqttDisabledWarning.Visible = CbEnableMqtt.CheckState != CheckState.Checked;
}
+
+ private void CbIgnoreGracePeriod_CheckedChanged(object sender, EventArgs e)
+ {
+
+ }
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.resx
index c037b6da..1435c3aa 100644
--- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.resx
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.resx
@@ -1,17 +1,17 @@
-
@@ -117,11 +117,18 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Commands and sensors use MQTT, as well as notifications and media player functions when using the new integration.
+
+Please provide credentials for your broker, if you're using the HA Mosquitto addon, you can probably use the preset address.
+
+Note: these settings (excluding the Client ID) will also be applied to the satellite service.
+
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wAAADsABataJCQAAAZZJREFUSEvtk61TAlEUxTcQDAajwWAgGgwG/gCCgUAwGA0GgsFIZMZANBgMBgOR
+ vgAADr4B6kKxwAAAAZZJREFUSEvtk61TAlEUxTcQDAajwWAgGgwG/gCCgUAwGA0GgsFIZMZANBgMBgOR
YDQYNliAhdkgMwQCwUAgGAgGgv7Oeh/zdmdRscqZObPv3Y9z37v3bbDBBgkGg0Gx1+uV+/3+mdjtdo/Z
H5j7bwjDcAuxOozhRx6jKBrzvYrjeMfSfgdOd0rixBf7jhSawRqHKphEPnRtEp6yAmswgiWTSwNHlVPM
XTDrB/js9qtIzAjeebYF+5rJfoGTX3oBUw1RdtahZ88lYmOLLWnt7DyMa9mz4nGn09lLHEBBnm8V2xYe
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
index dedf1cf2..c11841ec 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
@@ -323,6 +323,7 @@ private void LoadSettings()
_mqtt.TbMqttClientCertificate.Text = Variables.AppSettings.MqttClientCertificate;
_mqtt.CbAllowUntrustedCertificates.CheckState = Variables.AppSettings.MqttAllowUntrustedCertificates ? CheckState.Checked : CheckState.Unchecked;
_mqtt.CbUseRetainFlag.CheckState = Variables.AppSettings.MqttUseRetainFlag ? CheckState.Checked : CheckState.Unchecked;
+ _mqtt.CbIgnoreGracePeriod.CheckState = Variables.AppSettings.MqttIgnoreGracePeriod ? CheckState.Checked : CheckState.Unchecked;
// updates
_updates.CbUpdates.CheckState = Variables.AppSettings.CheckForUpdates ? CheckState.Checked : CheckState.Unchecked;
@@ -425,6 +426,7 @@ private async Task StoreSettingsAsync()
Variables.AppSettings.MqttClientCertificate = _mqtt.TbMqttClientCertificate.Text;
Variables.AppSettings.MqttAllowUntrustedCertificates = _mqtt.CbAllowUntrustedCertificates.CheckState == CheckState.Checked;
Variables.AppSettings.MqttUseRetainFlag = _mqtt.CbUseRetainFlag.CheckState == CheckState.Checked;
+ Variables.AppSettings.MqttIgnoreGracePeriod = _mqtt.CbIgnoreGracePeriod.CheckState == CheckState.Checked;
// mqtt -> service
await SettingsManager.SendMqttSettingsToServiceAsync();
diff --git a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
index ff56e52f..3e99155b 100644
--- a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
+++ b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
@@ -14,6 +14,7 @@
using HASS.Agent.Resources.Localization;
using HASS.Agent.Settings;
using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Managers;
using HASS.Agent.Shared.Models.HomeAssistant;
using HASS.Agent.Shared.Mqtt;
using MQTTnet;
@@ -130,10 +131,14 @@ private async Task OnMqttDisconnected(MqttClientDisconnectedEventArgs arg)
Variables.MainForm?.SetMqttStatus(ComponentStatus.Connecting);
+ var gracePeriod = Variables.AppSettings.DisconnectedGracePeriodSeconds;
+
// give the connection the grace period to recover
var runningTimer = Stopwatch.StartNew();
- while (runningTimer.Elapsed.TotalSeconds < Variables.AppSettings.DisconnectedGracePeriodSeconds)
+ while (runningTimer.Elapsed.TotalSeconds < gracePeriod)
{
+ await Task.Delay(TimeSpan.FromSeconds(5));
+
if (IsConnected())
{
_isReady = true;
@@ -143,12 +148,20 @@ private async Task OnMqttDisconnected(MqttClientDisconnectedEventArgs arg)
_status = MqttStatus.Connected;
Variables.MainForm?.SetMqttStatus(ComponentStatus.Ok);
- Log.Information("[MQTT] Connected");
+ Log.Information("[MQTT] Reconnected from disconnection");
return;
}
- await Task.Delay(TimeSpan.FromSeconds(5));
+ if (Variables.AppSettings.MqttIgnoreGracePeriod)
+ {
+ var lastResumed = SystemStateManager.LastEventOccurrence.TryGetValue(SystemStateEvent.Resume, out var lastResumeEventDate);
+ if (lastResumed && DateTime.Now < lastResumeEventDate.AddSeconds(gracePeriod))
+ {
+ Log.Information("[MQTT] System resumed less than {gracePeriod} seconds ago, ignoring grace period on disconnection");
+ break;
+ }
+ }
}
// nope, call it
@@ -172,10 +185,14 @@ private async Task OnMqttConnectionFailed(ConnectingFailedEventArgs arg)
{
Variables.MainForm?.SetMqttStatus(ComponentStatus.Connecting);
+ var gracePeriod = Variables.AppSettings.DisconnectedGracePeriodSeconds;
+
// give the connection the grace period to recover
var runningTimer = Stopwatch.StartNew();
- while (runningTimer.Elapsed.TotalSeconds < Variables.AppSettings.DisconnectedGracePeriodSeconds)
+ while (runningTimer.Elapsed.TotalSeconds < gracePeriod)
{
+ await Task.Delay(TimeSpan.FromSeconds(5));
+
if (IsConnected())
{
// recovered
@@ -184,12 +201,20 @@ private async Task OnMqttConnectionFailed(ConnectingFailedEventArgs arg)
_status = MqttStatus.Connected;
Variables.MainForm?.SetMqttStatus(ComponentStatus.Ok);
- Log.Information("[MQTT] Connected");
+ Log.Information("[MQTT] Reconnected from failed connection");
return;
}
- await Task.Delay(TimeSpan.FromSeconds(5));
+ if (Variables.AppSettings.MqttIgnoreGracePeriod)
+ {
+ var lastResumed = SystemStateManager.LastEventOccurrence.TryGetValue(SystemStateEvent.Resume, out var lastResumeEventDate);
+ if (lastResumed && DateTime.Now < lastResumeEventDate.AddSeconds(gracePeriod))
+ {
+ Log.Information("[MQTT] System resumed more than {gracePeriod} seconds ago, ignoring grace period on connection failed");
+ break;
+ }
+ }
}
// nope, call it
diff --git a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
index ca4c4ded..22fbfb1e 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
@@ -12,7 +12,7 @@ internal static class SystemStateManager
/// The pointer to unregister the monitor power notifications
///
internal static IntPtr UnRegPowerNotify { get; set; } = IntPtr.Zero;
-
+
///
/// Notes the last time something happened to the system's state, eg. user logged on, session locked, etc
///
@@ -21,7 +21,21 @@ internal static class SystemStateManager
///
/// Contains the last event that happened to the system, eg. user logged on, session locked, etc
///
- internal static SystemStateEvent LastSystemStateEvent { get; private set; } = SystemStateEvent.ApplicationStarted;
+ private static SystemStateEvent s_lastSystemStateEvent = SystemStateEvent.ApplicationStarted;
+ internal static SystemStateEvent LastSystemStateEvent
+ {
+ get => s_lastSystemStateEvent;
+ private set
+ {
+ s_lastSystemStateEvent = value;
+ LastEventOccurrence[s_lastSystemStateEvent] = DateTime.Now;
+ }
+ }
+
+ ///
+ /// Contains the key value pair with SystemStateEvent and the last time it occurred
+ ///
+ public static Dictionary LastEventOccurrence = new();
///
/// Contains the last event that happened to the monitors, eg. power on
@@ -33,6 +47,7 @@ internal static class SystemStateManager
///
internal static async void Initialize()
{
+ LastSystemStateEvent = SystemStateEvent.ApplicationStarted;
await Task.Run(Monitor);
}
@@ -67,7 +82,7 @@ internal static void ProcessSessionEnd()
Log.Information("[SYSTEMSTATE] Session ending: system shutting down");
Task.Run(() => HelperFunctions.ShutdownAsync(TimeSpan.Zero));
LastSystemStateEvent = SystemStateEvent.SystemShutdown;
-
+
}
else
{
diff --git a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
index caccd39e..5222a21c 100644
--- a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
+++ b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
@@ -78,5 +78,7 @@ public AppSettings()
public bool MqttUseRetainFlag { get; set; } = true;
public string MqttRootCertificate { get; set; } = string.Empty;
public string MqttClientCertificate { get; set; } = string.Empty;
+
+ public bool MqttIgnoreGracePeriod { get; set; } = false;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index b84f6070..0a90d4a8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -2362,6 +2362,15 @@ internal static string ConfigMqtt_CbEnableMqtt {
}
}
+ ///
+ /// Looks up a localized string similar to Ignore grace period after waking up from hibernation.
+ ///
+ internal static string ConfigMqtt_CbIgnoreGracePeriod {
+ get {
+ return ResourceManager.GetString("ConfigMqtt_CbIgnoreGracePeriod", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to &TLS.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index fbd3e43b..d93caf19 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3488,4 +3488,7 @@ Die Bildschirmnummer hängt von der Systemkonfiguration ab – beginnt bei 0.Legt den Standard-Audioeingang für das System fest (einschließlich des Standard-Kommunikationsgeräts).
Als Payload benötigen Sie den Namen des Audiogeräts.
+
+ Ignorieren Sie die Schonfrist nach dem Aufwachen aus dem Ruhezustand
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 7be618f1..8b21c62b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3365,4 +3365,7 @@ Screen number depends on system configuration - starts at 0.
Sets the default audio input for the system (including default communication device).
Requires audio device name as a payload.
+
+ Ignore grace period after waking up from hibernation
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 7aafaaa8..1c609964 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3364,4 +3364,7 @@ El número de pantalla depende de la configuración del sistema: comienza en 0.<
Establece la entrada de audio predeterminada para el sistema (incluido el dispositivo de comunicación predeterminado).
Necesita el nombre del dispositivo de audio como carga útil.
+
+ Ignorar el período de gracia después de despertar de la hibernación
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 62fb1f9c..4f459b6e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3397,4 +3397,7 @@ Le numéro d'écran dépend de la configuration du système - commence à 0.Définit l'entrée audio par défaut du système (y compris le périphérique de communication par défaut).
Vous avez besoin du nom du périphérique audio comme charge utile.
+
+ Ignorer la période de grâce après la sortie de l'hibernation
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index a56c0472..3556e82b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3385,4 +3385,7 @@ Schermnummer is afhankelijk van de systeemconfiguratie - begint bij 0.
Stelt de standaardaudio-invoer in voor het systeem (inclusief het standaardcommunicatieapparaat).
U hebt de naam van het audioapparaat nodig als payload.
+
+ Negeer de respijtperiode na het ontwaken uit de winterslaap
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index a68dbf65..e888467f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3474,4 +3474,7 @@ Numer ekranu zależy od konfiguracji systemu – zaczyna się od 0.
Ustawia domyślne wejście audio dla systemu (w tym domyślne urządzenie komunikacyjne).
Wymaga nazwy urządzenia audio jako ładunku.
+
+ Ignoruj okres karencji po przebudzeniu ze stanu hibernacji
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index ff0267af..cc835f66 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3410,4 +3410,7 @@ O número da tela depende da configuração do sistema – começa em 0.
Define a entrada de áudio padrão para o sistema (incluindo o dispositivo de comunicação padrão).
Você precisa do nome do dispositivo de áudio como carga útil.
+
+ Ignorar o período de carência após sair da hibernação
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 47ff6ae3..fa71846e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3377,4 +3377,7 @@ Screen number depends on system configuration - starts at 0.
Sets the default audio input for the system (including default communication device).
Requires audio device name as a payload.
+
+ Ignore grace period after waking up from hibernation
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 9a34e67d..4eb99872 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3433,4 +3433,7 @@ Home Assistant.
Устанавливает аудиовход по умолчанию для системы (включая устройство связи по умолчанию).
В качестве полезной нагрузки требуется имя аудиоустройства.
+
+ Игнорировать льготный период после выхода из спящего режима
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 970518e6..732c2f7e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3513,4 +3513,7 @@ Zahteva ime zvočne naprave kot tovor.
Nastavi privzeti zvočni vhod za sistem (vključno s privzeto komunikacijsko napravo).
Ime zvočne naprave potrebujete kot obremenitev.
+
+ Po prebujanju iz mirovanja prezrite obdobje odloga
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index a9138f11..dea9fd38 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2976,4 +2976,7 @@ Ekran numarası sistem konfigürasyonuna bağlıdır - 0'dan başlar.
Sistem için varsayılan ses girişini ayarlar (varsayılan iletişim cihazı dahil).
Yük olarak ses cihazı adına ihtiyacınız vardır.
+
+ Hazırda bekletme modundan uyandıktan sonra ek süreyi göz ardı et
+
\ No newline at end of file
From dc6b04e02b64f846ede5e573d5974817e9053408 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 1 Mar 2024 12:41:16 +0100
Subject: [PATCH 11/45] Feature: modern/transparent tray icon option (#55)
This PR adds modern transparent tray icon option
---
.../Configuration/ConfigTrayIcon.Designer.cs | 528 +++++++++---------
.../Configuration/ConfigTrayIcon.resx | 62 +-
.../HASS.Agent/Forms/Configuration.cs | 2 +
src/HASS.Agent/HASS.Agent/Forms/Main.cs | 17 +-
src/HASS.Agent/HASS.Agent/Forms/Main.resx | 156 ++++--
.../HASS.Agent/Models/Config/AppSettings.cs | 1 +
.../Localization/Languages.Designer.cs | 9 +
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
.../HASS.Agent/Resources/modern-tray-icon.ico | Bin 0 -> 52111 bytes
19 files changed, 491 insertions(+), 317 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent/Resources/modern-tray-icon.ico
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs
index 53fdff2f..8fec92e9 100644
--- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs
@@ -30,320 +30,318 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
- this.LblInfo1 = new System.Windows.Forms.Label();
- this.CbDefaultMenu = new System.Windows.Forms.CheckBox();
- this.CbShowWebView = new System.Windows.Forms.CheckBox();
- this.TbWebViewUrl = new System.Windows.Forms.TextBox();
- this.LblWebViewUrl = new System.Windows.Forms.Label();
- this.LblX = new System.Windows.Forms.Label();
- this.LblWebViewSize = new System.Windows.Forms.Label();
- this.BtnShowWebViewPreview = new Syncfusion.WinForms.Controls.SfButton();
- this.NumWebViewWidth = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
- this.NumWebViewHeight = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
- this.BtnWebViewReset = new Syncfusion.WinForms.Controls.SfButton();
- this.CbWebViewKeepLoaded = new System.Windows.Forms.CheckBox();
- this.LblInfo2 = new System.Windows.Forms.Label();
- this.CbWebViewShowMenuOnLeftClick = new System.Windows.Forms.CheckBox();
- ((System.ComponentModel.ISupportInitialize)(this.NumWebViewWidth)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.NumWebViewHeight)).BeginInit();
- this.SuspendLayout();
+ LblInfo1 = new Label();
+ CbUseModernIcon = new CheckBox();
+ CbDefaultMenu = new CheckBox();
+ CbShowWebView = new CheckBox();
+ TbWebViewUrl = new TextBox();
+ LblWebViewUrl = new Label();
+ LblX = new Label();
+ LblWebViewSize = new Label();
+ BtnShowWebViewPreview = new Syncfusion.WinForms.Controls.SfButton();
+ NumWebViewWidth = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
+ NumWebViewHeight = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
+ BtnWebViewReset = new Syncfusion.WinForms.Controls.SfButton();
+ CbWebViewKeepLoaded = new CheckBox();
+ LblInfo2 = new Label();
+ CbWebViewShowMenuOnLeftClick = new CheckBox();
+ ((System.ComponentModel.ISupportInitialize)NumWebViewWidth).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)NumWebViewHeight).BeginInit();
+ SuspendLayout();
//
// LblInfo1
//
- this.LblInfo1.AccessibleDescription = "Tray icon information.";
- this.LblInfo1.AccessibleName = "Information";
- this.LblInfo1.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
- this.LblInfo1.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.LblInfo1.Location = new System.Drawing.Point(70, 36);
- this.LblInfo1.Name = "LblInfo1";
- this.LblInfo1.Size = new System.Drawing.Size(541, 78);
- this.LblInfo1.TabIndex = 31;
- this.LblInfo1.Text = Languages.ConfigTrayIcon_LblInfo1;
+ LblInfo1.AccessibleDescription = "Tray icon information.";
+ LblInfo1.AccessibleName = "Information";
+ LblInfo1.AccessibleRole = AccessibleRole.StaticText;
+ LblInfo1.Font = new Font("Segoe UI", 10F);
+ LblInfo1.Location = new Point(70, 102);
+ LblInfo1.Name = "LblInfo1";
+ LblInfo1.Size = new Size(541, 36);
+ LblInfo1.TabIndex = 31;
+ LblInfo1.Text = "Control the behaviour of the tray icon when it is right-clicked.";
+ //
+ // CbUseModernIcon
+ //
+ CbUseModernIcon.AccessibleDescription = "If enabled, modern white and transparent icon will be used";
+ CbUseModernIcon.AccessibleName = "Use modern icon";
+ CbUseModernIcon.AccessibleRole = AccessibleRole.CheckButton;
+ CbUseModernIcon.AutoSize = true;
+ CbUseModernIcon.Font = new Font("Segoe UI", 10F);
+ CbUseModernIcon.Location = new Point(70, 42);
+ CbUseModernIcon.Name = "CbUseModernIcon";
+ CbUseModernIcon.Size = new Size(160, 23);
+ CbUseModernIcon.TabIndex = 50;
+ CbUseModernIcon.Text = Languages.ConfigTrayIcon_CbUseModernIcon;
+ CbUseModernIcon.UseVisualStyleBackColor = true;
//
// CbDefaultMenu
//
- this.CbDefaultMenu.AccessibleDescription = "If enabled, right clicking the system tray icon will show the default menu.";
- this.CbDefaultMenu.AccessibleName = "Show default menu";
- this.CbDefaultMenu.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton;
- this.CbDefaultMenu.AutoSize = true;
- this.CbDefaultMenu.Checked = true;
- this.CbDefaultMenu.CheckState = System.Windows.Forms.CheckState.Checked;
- this.CbDefaultMenu.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.CbDefaultMenu.Location = new System.Drawing.Point(70, 117);
- this.CbDefaultMenu.Name = "CbDefaultMenu";
- this.CbDefaultMenu.Size = new System.Drawing.Size(145, 23);
- this.CbDefaultMenu.TabIndex = 0;
- this.CbDefaultMenu.Text = global::HASS.Agent.Resources.Localization.Languages.ConfigTrayIcon_CbDefaultMenu;
- this.CbDefaultMenu.UseVisualStyleBackColor = true;
- this.CbDefaultMenu.CheckedChanged += new System.EventHandler(this.CbDefaultMenu_CheckedChanged);
+ CbDefaultMenu.AccessibleDescription = "If enabled, right clicking the system tray icon will show the default menu.";
+ CbDefaultMenu.AccessibleName = "Show default menu";
+ CbDefaultMenu.AccessibleRole = AccessibleRole.CheckButton;
+ CbDefaultMenu.AutoSize = true;
+ CbDefaultMenu.Checked = true;
+ CbDefaultMenu.CheckState = CheckState.Checked;
+ CbDefaultMenu.Font = new Font("Segoe UI", 10F);
+ CbDefaultMenu.Location = new Point(70, 141);
+ CbDefaultMenu.Name = "CbDefaultMenu";
+ CbDefaultMenu.Size = new Size(149, 23);
+ CbDefaultMenu.TabIndex = 0;
+ CbDefaultMenu.Text = Languages.ConfigTrayIcon_CbDefaultMenu;
+ CbDefaultMenu.UseVisualStyleBackColor = true;
+ CbDefaultMenu.CheckedChanged += CbDefaultMenu_CheckedChanged;
//
// CbShowWebView
//
- this.CbShowWebView.AccessibleDescription = "If enabled, right clicking the system tray icon will show a webview with the url " +
- "you provide.";
- this.CbShowWebView.AccessibleName = "Show webview";
- this.CbShowWebView.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton;
- this.CbShowWebView.AutoSize = true;
- this.CbShowWebView.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.CbShowWebView.Location = new System.Drawing.Point(70, 209);
- this.CbShowWebView.Name = "CbShowWebView";
- this.CbShowWebView.Size = new System.Drawing.Size(116, 23);
- this.CbShowWebView.TabIndex = 1;
- this.CbShowWebView.Text = global::HASS.Agent.Resources.Localization.Languages.ConfigTrayIcon_CbShowWebView;
- this.CbShowWebView.UseVisualStyleBackColor = true;
- this.CbShowWebView.CheckedChanged += new System.EventHandler(this.CbShowWebView_CheckedChanged);
+ CbShowWebView.AccessibleDescription = "If enabled, right clicking the system tray icon will show a webview with the url you provide.";
+ CbShowWebView.AccessibleName = "Show webview";
+ CbShowWebView.AccessibleRole = AccessibleRole.CheckButton;
+ CbShowWebView.AutoSize = true;
+ CbShowWebView.Font = new Font("Segoe UI", 10F);
+ CbShowWebView.Location = new Point(70, 209);
+ CbShowWebView.Name = "CbShowWebView";
+ CbShowWebView.Size = new Size(121, 23);
+ CbShowWebView.TabIndex = 1;
+ CbShowWebView.Text = Languages.ConfigTrayIcon_CbShowWebView;
+ CbShowWebView.UseVisualStyleBackColor = true;
+ CbShowWebView.CheckedChanged += CbShowWebView_CheckedChanged;
//
// TbWebViewUrl
//
- this.TbWebViewUrl.AccessibleDescription = "The URL to show. Defaults to the Home Assistant API\'s URL.";
- this.TbWebViewUrl.AccessibleName = "URL";
- this.TbWebViewUrl.AccessibleRole = System.Windows.Forms.AccessibleRole.Text;
- this.TbWebViewUrl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.TbWebViewUrl.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
- this.TbWebViewUrl.Enabled = false;
- this.TbWebViewUrl.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.TbWebViewUrl.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.TbWebViewUrl.Location = new System.Drawing.Point(90, 280);
- this.TbWebViewUrl.Name = "TbWebViewUrl";
- this.TbWebViewUrl.Size = new System.Drawing.Size(521, 25);
- this.TbWebViewUrl.TabIndex = 2;
+ TbWebViewUrl.AccessibleDescription = "The URL to show. Defaults to the Home Assistant API's URL.";
+ TbWebViewUrl.AccessibleName = "URL";
+ TbWebViewUrl.AccessibleRole = AccessibleRole.Text;
+ TbWebViewUrl.BackColor = Color.FromArgb(63, 63, 70);
+ TbWebViewUrl.BorderStyle = BorderStyle.FixedSingle;
+ TbWebViewUrl.Enabled = false;
+ TbWebViewUrl.Font = new Font("Segoe UI", 10F);
+ TbWebViewUrl.ForeColor = Color.FromArgb(241, 241, 241);
+ TbWebViewUrl.Location = new Point(90, 280);
+ TbWebViewUrl.Name = "TbWebViewUrl";
+ TbWebViewUrl.Size = new Size(521, 25);
+ TbWebViewUrl.TabIndex = 2;
//
// LblWebViewUrl
//
- this.LblWebViewUrl.AccessibleDescription = "URL textbox description";
- this.LblWebViewUrl.AccessibleName = "URL info";
- this.LblWebViewUrl.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
- this.LblWebViewUrl.AutoSize = true;
- this.LblWebViewUrl.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.LblWebViewUrl.Location = new System.Drawing.Point(87, 258);
- this.LblWebViewUrl.Name = "LblWebViewUrl";
- this.LblWebViewUrl.Size = new System.Drawing.Size(349, 19);
- this.LblWebViewUrl.TabIndex = 49;
- this.LblWebViewUrl.Text = Languages.ConfigTrayIcon_LblWebViewUrl;
+ LblWebViewUrl.AccessibleDescription = "URL textbox description";
+ LblWebViewUrl.AccessibleName = "URL info";
+ LblWebViewUrl.AccessibleRole = AccessibleRole.StaticText;
+ LblWebViewUrl.AutoSize = true;
+ LblWebViewUrl.Font = new Font("Segoe UI", 10F);
+ LblWebViewUrl.Location = new Point(87, 258);
+ LblWebViewUrl.Name = "LblWebViewUrl";
+ LblWebViewUrl.Size = new Size(415, 19);
+ LblWebViewUrl.TabIndex = 49;
+ LblWebViewUrl.Text = "&WebView URL (For instance, your Home Assistant Dashboard URL)";
//
// LblX
//
- this.LblX.AccessibleDescription = "Shows X, meaning \'by\' in this context.";
- this.LblX.AccessibleName = "X info";
- this.LblX.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
- this.LblX.AutoSize = true;
- this.LblX.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.LblX.Location = new System.Drawing.Point(184, 350);
- this.LblX.Name = "LblX";
- this.LblX.Size = new System.Drawing.Size(17, 19);
- this.LblX.TabIndex = 53;
- this.LblX.Text = "X";
+ LblX.AccessibleDescription = "Shows X, meaning 'by' in this context.";
+ LblX.AccessibleName = "X info";
+ LblX.AccessibleRole = AccessibleRole.StaticText;
+ LblX.AutoSize = true;
+ LblX.Font = new Font("Segoe UI", 10F);
+ LblX.Location = new Point(184, 350);
+ LblX.Name = "LblX";
+ LblX.Size = new Size(17, 19);
+ LblX.TabIndex = 53;
+ LblX.Text = "X";
//
// LblWebViewSize
//
- this.LblWebViewSize.AccessibleDescription = "Size description.";
- this.LblWebViewSize.AccessibleName = "Size info";
- this.LblWebViewSize.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
- this.LblWebViewSize.AutoSize = true;
- this.LblWebViewSize.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.LblWebViewSize.Location = new System.Drawing.Point(85, 326);
- this.LblWebViewSize.Name = "LblWebViewSize";
- this.LblWebViewSize.Size = new System.Drawing.Size(31, 19);
- this.LblWebViewSize.TabIndex = 51;
- this.LblWebViewSize.Text = Languages.ConfigTrayIcon_LblWebViewSize;
+ LblWebViewSize.AccessibleDescription = "Size description.";
+ LblWebViewSize.AccessibleName = "Size info";
+ LblWebViewSize.AccessibleRole = AccessibleRole.StaticText;
+ LblWebViewSize.AutoSize = true;
+ LblWebViewSize.Font = new Font("Segoe UI", 10F);
+ LblWebViewSize.Location = new Point(85, 326);
+ LblWebViewSize.Name = "LblWebViewSize";
+ LblWebViewSize.Size = new Size(58, 19);
+ LblWebViewSize.TabIndex = 51;
+ LblWebViewSize.Text = "Size (px)";
//
// BtnShowWebViewPreview
//
- this.BtnShowWebViewPreview.AccessibleDescription = "Shows the webview, using the currently configured values.";
- this.BtnShowWebViewPreview.AccessibleName = "Webview preview";
- this.BtnShowWebViewPreview.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton;
- this.BtnShowWebViewPreview.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnShowWebViewPreview.Enabled = false;
- this.BtnShowWebViewPreview.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.BtnShowWebViewPreview.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnShowWebViewPreview.Location = new System.Drawing.Point(405, 348);
- this.BtnShowWebViewPreview.Name = "BtnShowWebViewPreview";
- this.BtnShowWebViewPreview.Size = new System.Drawing.Size(206, 26);
- this.BtnShowWebViewPreview.Style.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnShowWebViewPreview.Style.FocusedBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnShowWebViewPreview.Style.FocusedForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnShowWebViewPreview.Style.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnShowWebViewPreview.Style.HoverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnShowWebViewPreview.Style.HoverForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnShowWebViewPreview.Style.PressedForeColor = System.Drawing.Color.Black;
- this.BtnShowWebViewPreview.TabIndex = 6;
- this.BtnShowWebViewPreview.Text = global::HASS.Agent.Resources.Localization.Languages.ConfigTrayIcon_BtnShowWebViewPreview;
- this.BtnShowWebViewPreview.UseVisualStyleBackColor = false;
- this.BtnShowWebViewPreview.Click += new System.EventHandler(this.BtnShowWebViewPreview_Click);
+ BtnShowWebViewPreview.AccessibleDescription = "Shows the webview, using the currently configured values.";
+ BtnShowWebViewPreview.AccessibleName = "Webview preview";
+ BtnShowWebViewPreview.AccessibleRole = AccessibleRole.PushButton;
+ BtnShowWebViewPreview.BackColor = Color.FromArgb(63, 63, 70);
+ BtnShowWebViewPreview.Enabled = false;
+ BtnShowWebViewPreview.Font = new Font("Segoe UI", 10F);
+ BtnShowWebViewPreview.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnShowWebViewPreview.Location = new Point(405, 348);
+ BtnShowWebViewPreview.Name = "BtnShowWebViewPreview";
+ BtnShowWebViewPreview.Size = new Size(206, 26);
+ BtnShowWebViewPreview.Style.BackColor = Color.FromArgb(63, 63, 70);
+ BtnShowWebViewPreview.Style.FocusedBackColor = Color.FromArgb(63, 63, 70);
+ BtnShowWebViewPreview.Style.FocusedForeColor = Color.FromArgb(241, 241, 241);
+ BtnShowWebViewPreview.Style.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnShowWebViewPreview.Style.HoverBackColor = Color.FromArgb(63, 63, 70);
+ BtnShowWebViewPreview.Style.HoverForeColor = Color.FromArgb(241, 241, 241);
+ BtnShowWebViewPreview.Style.PressedForeColor = Color.Black;
+ BtnShowWebViewPreview.TabIndex = 6;
+ BtnShowWebViewPreview.Text = Languages.ConfigTrayIcon_BtnShowWebViewPreview;
+ BtnShowWebViewPreview.UseVisualStyleBackColor = false;
+ BtnShowWebViewPreview.Click += BtnShowWebViewPreview_Click;
//
// NumWebViewWidth
//
- this.NumWebViewWidth.AccessibleDescription = "The width of the webview. Only accepts numeric values.";
- this.NumWebViewWidth.AccessibleName = "Width";
- this.NumWebViewWidth.AccessibleRole = System.Windows.Forms.AccessibleRole.Text;
- this.NumWebViewWidth.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.NumWebViewWidth.BeforeTouchSize = new System.Drawing.Size(83, 25);
- this.NumWebViewWidth.Border3DStyle = System.Windows.Forms.Border3DStyle.Flat;
- this.NumWebViewWidth.BorderColor = System.Drawing.SystemColors.WindowFrame;
- this.NumWebViewWidth.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
- this.NumWebViewWidth.Enabled = false;
- this.NumWebViewWidth.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.NumWebViewWidth.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.NumWebViewWidth.Location = new System.Drawing.Point(87, 348);
- this.NumWebViewWidth.Maximum = new decimal(new int[] {
- 65535,
- 0,
- 0,
- 0});
- this.NumWebViewWidth.MaxLength = 10;
- this.NumWebViewWidth.MetroColor = System.Drawing.SystemColors.WindowFrame;
- this.NumWebViewWidth.Name = "NumWebViewWidth";
- this.NumWebViewWidth.Size = new System.Drawing.Size(83, 25);
- this.NumWebViewWidth.TabIndex = 3;
- this.NumWebViewWidth.ThemeName = "Metro";
- this.NumWebViewWidth.Value = new decimal(new int[] {
- 700,
- 0,
- 0,
- 0});
- this.NumWebViewWidth.VisualStyle = Syncfusion.Windows.Forms.VisualStyle.Metro;
+ NumWebViewWidth.AccessibleDescription = "The width of the webview. Only accepts numeric values.";
+ NumWebViewWidth.AccessibleName = "Width";
+ NumWebViewWidth.AccessibleRole = AccessibleRole.Text;
+ NumWebViewWidth.BackColor = Color.FromArgb(63, 63, 70);
+ NumWebViewWidth.BeforeTouchSize = new Size(83, 25);
+ NumWebViewWidth.Border3DStyle = Border3DStyle.Flat;
+ NumWebViewWidth.BorderColor = SystemColors.WindowFrame;
+ NumWebViewWidth.BorderStyle = BorderStyle.FixedSingle;
+ NumWebViewWidth.Enabled = false;
+ NumWebViewWidth.Font = new Font("Segoe UI", 10F);
+ NumWebViewWidth.ForeColor = Color.FromArgb(241, 241, 241);
+ NumWebViewWidth.Location = new Point(87, 348);
+ NumWebViewWidth.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
+ NumWebViewWidth.MaxLength = 10;
+ NumWebViewWidth.MetroColor = SystemColors.WindowFrame;
+ NumWebViewWidth.Name = "NumWebViewWidth";
+ NumWebViewWidth.Size = new Size(83, 25);
+ NumWebViewWidth.TabIndex = 3;
+ NumWebViewWidth.ThemeName = "Metro";
+ NumWebViewWidth.Value = new decimal(new int[] { 700, 0, 0, 0 });
+ NumWebViewWidth.VisualStyle = Syncfusion.Windows.Forms.VisualStyle.Metro;
//
// NumWebViewHeight
//
- this.NumWebViewHeight.AccessibleDescription = "The height of the webview. Only accepts numeric values.";
- this.NumWebViewHeight.AccessibleName = "Height";
- this.NumWebViewHeight.AccessibleRole = System.Windows.Forms.AccessibleRole.Text;
- this.NumWebViewHeight.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.NumWebViewHeight.BeforeTouchSize = new System.Drawing.Size(83, 25);
- this.NumWebViewHeight.Border3DStyle = System.Windows.Forms.Border3DStyle.Flat;
- this.NumWebViewHeight.BorderColor = System.Drawing.SystemColors.WindowFrame;
- this.NumWebViewHeight.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
- this.NumWebViewHeight.Enabled = false;
- this.NumWebViewHeight.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.NumWebViewHeight.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.NumWebViewHeight.Location = new System.Drawing.Point(218, 348);
- this.NumWebViewHeight.Maximum = new decimal(new int[] {
- 65535,
- 0,
- 0,
- 0});
- this.NumWebViewHeight.MaxLength = 10;
- this.NumWebViewHeight.MetroColor = System.Drawing.SystemColors.WindowFrame;
- this.NumWebViewHeight.Name = "NumWebViewHeight";
- this.NumWebViewHeight.Size = new System.Drawing.Size(83, 25);
- this.NumWebViewHeight.TabIndex = 4;
- this.NumWebViewHeight.ThemeName = "Metro";
- this.NumWebViewHeight.Value = new decimal(new int[] {
- 560,
- 0,
- 0,
- 0});
- this.NumWebViewHeight.VisualStyle = Syncfusion.Windows.Forms.VisualStyle.Metro;
+ NumWebViewHeight.AccessibleDescription = "The height of the webview. Only accepts numeric values.";
+ NumWebViewHeight.AccessibleName = "Height";
+ NumWebViewHeight.AccessibleRole = AccessibleRole.Text;
+ NumWebViewHeight.BackColor = Color.FromArgb(63, 63, 70);
+ NumWebViewHeight.BeforeTouchSize = new Size(83, 25);
+ NumWebViewHeight.Border3DStyle = Border3DStyle.Flat;
+ NumWebViewHeight.BorderColor = SystemColors.WindowFrame;
+ NumWebViewHeight.BorderStyle = BorderStyle.FixedSingle;
+ NumWebViewHeight.Enabled = false;
+ NumWebViewHeight.Font = new Font("Segoe UI", 10F);
+ NumWebViewHeight.ForeColor = Color.FromArgb(241, 241, 241);
+ NumWebViewHeight.Location = new Point(218, 348);
+ NumWebViewHeight.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
+ NumWebViewHeight.MaxLength = 10;
+ NumWebViewHeight.MetroColor = SystemColors.WindowFrame;
+ NumWebViewHeight.Name = "NumWebViewHeight";
+ NumWebViewHeight.Size = new Size(83, 25);
+ NumWebViewHeight.TabIndex = 4;
+ NumWebViewHeight.ThemeName = "Metro";
+ NumWebViewHeight.Value = new decimal(new int[] { 560, 0, 0, 0 });
+ NumWebViewHeight.VisualStyle = Syncfusion.Windows.Forms.VisualStyle.Metro;
//
// BtnWebViewReset
//
- this.BtnWebViewReset.AccessibleDescription = "Resets the width and height values to their defaults.";
- this.BtnWebViewReset.AccessibleName = "Reset webview";
- this.BtnWebViewReset.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton;
- this.BtnWebViewReset.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnWebViewReset.Enabled = false;
- this.BtnWebViewReset.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.BtnWebViewReset.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnWebViewReset.ImageSize = new System.Drawing.Size(24, 24);
- this.BtnWebViewReset.Location = new System.Drawing.Point(317, 348);
- this.BtnWebViewReset.Name = "BtnWebViewReset";
- this.BtnWebViewReset.Size = new System.Drawing.Size(51, 26);
- this.BtnWebViewReset.Style.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnWebViewReset.Style.FocusedBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnWebViewReset.Style.FocusedForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnWebViewReset.Style.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnWebViewReset.Style.HoverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(70)))));
- this.BtnWebViewReset.Style.HoverForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.BtnWebViewReset.Style.Image = global::HASS.Agent.Properties.Resources.reset_24;
- this.BtnWebViewReset.Style.PressedForeColor = System.Drawing.Color.Black;
- this.BtnWebViewReset.TabIndex = 5;
- this.BtnWebViewReset.TextImageRelation = System.Windows.Forms.TextImageRelation.Overlay;
- this.BtnWebViewReset.UseVisualStyleBackColor = false;
- this.BtnWebViewReset.Click += new System.EventHandler(this.BtnWebViewReset_Click);
+ BtnWebViewReset.AccessibleDescription = "Resets the width and height values to their defaults.";
+ BtnWebViewReset.AccessibleName = "Reset webview";
+ BtnWebViewReset.AccessibleRole = AccessibleRole.PushButton;
+ BtnWebViewReset.BackColor = Color.FromArgb(63, 63, 70);
+ BtnWebViewReset.Enabled = false;
+ BtnWebViewReset.Font = new Font("Segoe UI", 10F);
+ BtnWebViewReset.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnWebViewReset.ImageSize = new Size(24, 24);
+ BtnWebViewReset.Location = new Point(317, 348);
+ BtnWebViewReset.Name = "BtnWebViewReset";
+ BtnWebViewReset.Size = new Size(51, 26);
+ BtnWebViewReset.Style.BackColor = Color.FromArgb(63, 63, 70);
+ BtnWebViewReset.Style.FocusedBackColor = Color.FromArgb(63, 63, 70);
+ BtnWebViewReset.Style.FocusedForeColor = Color.FromArgb(241, 241, 241);
+ BtnWebViewReset.Style.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnWebViewReset.Style.HoverBackColor = Color.FromArgb(63, 63, 70);
+ BtnWebViewReset.Style.HoverForeColor = Color.FromArgb(241, 241, 241);
+ BtnWebViewReset.Style.Image = Properties.Resources.reset_24;
+ BtnWebViewReset.Style.PressedForeColor = Color.Black;
+ BtnWebViewReset.TabIndex = 5;
+ BtnWebViewReset.TextImageRelation = TextImageRelation.Overlay;
+ BtnWebViewReset.UseVisualStyleBackColor = false;
+ BtnWebViewReset.Click += BtnWebViewReset_Click;
//
// CbWebViewKeepLoaded
//
- this.CbWebViewKeepLoaded.AccessibleDescription = "Keeps the webview loaded in the background, resulting in faster loading when invo" +
- "ked.";
- this.CbWebViewKeepLoaded.AccessibleName = "Background loading";
- this.CbWebViewKeepLoaded.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton;
- this.CbWebViewKeepLoaded.AutoSize = true;
- this.CbWebViewKeepLoaded.Checked = true;
- this.CbWebViewKeepLoaded.CheckState = System.Windows.Forms.CheckState.Checked;
- this.CbWebViewKeepLoaded.Enabled = false;
- this.CbWebViewKeepLoaded.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.CbWebViewKeepLoaded.Location = new System.Drawing.Point(90, 405);
- this.CbWebViewKeepLoaded.Name = "CbWebViewKeepLoaded";
- this.CbWebViewKeepLoaded.Size = new System.Drawing.Size(252, 23);
- this.CbWebViewKeepLoaded.TabIndex = 7;
- this.CbWebViewKeepLoaded.Text = global::HASS.Agent.Resources.Localization.Languages.ConfigTrayIcon_CbWebViewKeepLoaded;
- this.CbWebViewKeepLoaded.UseVisualStyleBackColor = true;
+ CbWebViewKeepLoaded.AccessibleDescription = "Keeps the webview loaded in the background, resulting in faster loading when invoked.";
+ CbWebViewKeepLoaded.AccessibleName = "Background loading";
+ CbWebViewKeepLoaded.AccessibleRole = AccessibleRole.CheckButton;
+ CbWebViewKeepLoaded.AutoSize = true;
+ CbWebViewKeepLoaded.Checked = true;
+ CbWebViewKeepLoaded.CheckState = CheckState.Checked;
+ CbWebViewKeepLoaded.Enabled = false;
+ CbWebViewKeepLoaded.Font = new Font("Segoe UI", 10F);
+ CbWebViewKeepLoaded.Location = new Point(90, 405);
+ CbWebViewKeepLoaded.Name = "CbWebViewKeepLoaded";
+ CbWebViewKeepLoaded.Size = new Size(253, 23);
+ CbWebViewKeepLoaded.TabIndex = 7;
+ CbWebViewKeepLoaded.Text = Languages.ConfigTrayIcon_CbWebViewKeepLoaded;
+ CbWebViewKeepLoaded.UseVisualStyleBackColor = true;
//
// LblInfo2
//
- this.LblInfo2.AccessibleDescription = "Background loading information.";
- this.LblInfo2.AccessibleName = "Background loading info";
- this.LblInfo2.AccessibleRole = System.Windows.Forms.AccessibleRole.StaticText;
- this.LblInfo2.AutoSize = true;
- this.LblInfo2.Enabled = false;
- this.LblInfo2.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.LblInfo2.Location = new System.Drawing.Point(107, 435);
- this.LblInfo2.Name = "LblInfo2";
- this.LblInfo2.Size = new System.Drawing.Size(320, 19);
- this.LblInfo2.TabIndex = 76;
- this.LblInfo2.Text = Languages.ConfigTrayIcon_LblInfo2;
+ LblInfo2.AccessibleDescription = "Background loading information.";
+ LblInfo2.AccessibleName = "Background loading info";
+ LblInfo2.AccessibleRole = AccessibleRole.StaticText;
+ LblInfo2.AutoSize = true;
+ LblInfo2.Enabled = false;
+ LblInfo2.Font = new Font("Segoe UI", 10F);
+ LblInfo2.Location = new Point(107, 435);
+ LblInfo2.Name = "LblInfo2";
+ LblInfo2.Size = new Size(330, 19);
+ LblInfo2.TabIndex = 76;
+ LblInfo2.Text = "(This uses extra resources, but reduces loading time.)";
//
// CbWebViewShowMenuOnLeftClick
//
- this.CbWebViewShowMenuOnLeftClick.AccessibleDescription = "If enabled, left clicking the system tray icon will show the default menu.";
- this.CbWebViewShowMenuOnLeftClick.AccessibleName = "Show default menu on left click";
- this.CbWebViewShowMenuOnLeftClick.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton;
- this.CbWebViewShowMenuOnLeftClick.AutoSize = true;
- this.CbWebViewShowMenuOnLeftClick.Enabled = false;
- this.CbWebViewShowMenuOnLeftClick.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
- this.CbWebViewShowMenuOnLeftClick.Location = new System.Drawing.Point(90, 487);
- this.CbWebViewShowMenuOnLeftClick.Name = "CbWebViewShowMenuOnLeftClick";
- this.CbWebViewShowMenuOnLeftClick.Size = new System.Drawing.Size(304, 23);
- this.CbWebViewShowMenuOnLeftClick.TabIndex = 77;
- this.CbWebViewShowMenuOnLeftClick.Text = Languages.ConfigTrayIcon_CbWebViewShowMenuOnLeftClick;
- this.CbWebViewShowMenuOnLeftClick.UseVisualStyleBackColor = true;
+ CbWebViewShowMenuOnLeftClick.AccessibleDescription = "If enabled, left clicking the system tray icon will show the default menu.";
+ CbWebViewShowMenuOnLeftClick.AccessibleName = "Show default menu on left click";
+ CbWebViewShowMenuOnLeftClick.AccessibleRole = AccessibleRole.CheckButton;
+ CbWebViewShowMenuOnLeftClick.AutoSize = true;
+ CbWebViewShowMenuOnLeftClick.Enabled = false;
+ CbWebViewShowMenuOnLeftClick.Font = new Font("Segoe UI", 10F);
+ CbWebViewShowMenuOnLeftClick.Location = new Point(90, 487);
+ CbWebViewShowMenuOnLeftClick.Name = "CbWebViewShowMenuOnLeftClick";
+ CbWebViewShowMenuOnLeftClick.Size = new Size(265, 23);
+ CbWebViewShowMenuOnLeftClick.TabIndex = 77;
+ CbWebViewShowMenuOnLeftClick.Text = Languages.ConfigTrayIcon_CbWebViewShowMenuOnLeftClick;
+ CbWebViewShowMenuOnLeftClick.UseVisualStyleBackColor = true;
//
// ConfigTrayIcon
//
- this.AccessibleDescription = "Panel containing the tray icon configuration.";
- this.AccessibleName = "Tray icon";
- this.AccessibleRole = System.Windows.Forms.AccessibleRole.Pane;
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(48)))));
- this.Controls.Add(this.CbWebViewShowMenuOnLeftClick);
- this.Controls.Add(this.LblInfo2);
- this.Controls.Add(this.CbWebViewKeepLoaded);
- this.Controls.Add(this.BtnWebViewReset);
- this.Controls.Add(this.NumWebViewHeight);
- this.Controls.Add(this.NumWebViewWidth);
- this.Controls.Add(this.BtnShowWebViewPreview);
- this.Controls.Add(this.LblX);
- this.Controls.Add(this.LblWebViewSize);
- this.Controls.Add(this.TbWebViewUrl);
- this.Controls.Add(this.LblWebViewUrl);
- this.Controls.Add(this.CbShowWebView);
- this.Controls.Add(this.LblInfo1);
- this.Controls.Add(this.CbDefaultMenu);
- this.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
- this.Margin = new System.Windows.Forms.Padding(4);
- this.Name = "ConfigTrayIcon";
- this.Size = new System.Drawing.Size(700, 544);
- this.Load += new System.EventHandler(this.ConfigTrayIcon_Load);
- ((System.ComponentModel.ISupportInitialize)(this.NumWebViewWidth)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.NumWebViewHeight)).EndInit();
- this.ResumeLayout(false);
- this.PerformLayout();
-
+ AccessibleDescription = "Panel containing the tray icon configuration.";
+ AccessibleName = "Tray icon";
+ AccessibleRole = AccessibleRole.Pane;
+ AutoScaleDimensions = new SizeF(96F, 96F);
+ AutoScaleMode = AutoScaleMode.Dpi;
+ BackColor = Color.FromArgb(45, 45, 48);
+ Controls.Add(CbWebViewShowMenuOnLeftClick);
+ Controls.Add(LblInfo2);
+ Controls.Add(CbWebViewKeepLoaded);
+ Controls.Add(BtnWebViewReset);
+ Controls.Add(NumWebViewHeight);
+ Controls.Add(NumWebViewWidth);
+ Controls.Add(BtnShowWebViewPreview);
+ Controls.Add(LblX);
+ Controls.Add(LblWebViewSize);
+ Controls.Add(TbWebViewUrl);
+ Controls.Add(LblWebViewUrl);
+ Controls.Add(CbShowWebView);
+ Controls.Add(LblInfo1);
+ Controls.Add(CbDefaultMenu);
+ Controls.Add(CbUseModernIcon);
+ ForeColor = Color.FromArgb(241, 241, 241);
+ Margin = new Padding(4);
+ Name = "ConfigTrayIcon";
+ Size = new Size(700, 544);
+ Load += ConfigTrayIcon_Load;
+ ((System.ComponentModel.ISupportInitialize)NumWebViewWidth).EndInit();
+ ((System.ComponentModel.ISupportInitialize)NumWebViewHeight).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
}
#endregion
private System.Windows.Forms.Label LblInfo1;
+ internal System.Windows.Forms.CheckBox CbUseModernIcon;
internal System.Windows.Forms.CheckBox CbDefaultMenu;
internal CheckBox CbShowWebView;
internal TextBox TbWebViewUrl;
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.resx
index f298a7be..af32865e 100644
--- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.resx
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.resx
@@ -1,4 +1,64 @@
-
+
+
+
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
index c11841ec..6c25679f 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
@@ -352,6 +352,7 @@ private void LoadSettings()
_mediaPlayer.CbEnableMediaPlayer.CheckState = Variables.AppSettings.MediaPlayerEnabled ? CheckState.Checked : CheckState.Unchecked;
// tray icon
+ _trayIcon.CbUseModernIcon.CheckState = Variables.AppSettings.TrayIconUseModern ? CheckState.Checked : CheckState.Unchecked;
_trayIcon.CbDefaultMenu.CheckState = Variables.AppSettings.TrayIconShowDefaultMenu ? CheckState.Checked : CheckState.Unchecked;
_trayIcon.CbShowWebView.CheckState = Variables.AppSettings.TrayIconShowWebView ? CheckState.Checked : CheckState.Unchecked;
_trayIcon.NumWebViewWidth.Value = Variables.AppSettings.TrayIconWebViewWidth;
@@ -458,6 +459,7 @@ private async Task StoreSettingsAsync()
Variables.AppSettings.MediaPlayerEnabled = _mediaPlayer.CbEnableMediaPlayer.CheckState == CheckState.Checked;
// tray icon
+ Variables.AppSettings.TrayIconUseModern = _trayIcon.CbUseModernIcon.CheckState == CheckState.Checked;
Variables.AppSettings.TrayIconShowDefaultMenu = _trayIcon.CbDefaultMenu.CheckState == CheckState.Checked;
Variables.AppSettings.TrayIconShowWebView = _trayIcon.CbShowWebView.CheckState == CheckState.Checked;
Variables.AppSettings.TrayIconWebViewWidth = (int)_trayIcon.NumWebViewWidth.Value;
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.cs b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
index 3eb10a0f..7e971535 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Main.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
@@ -89,11 +89,11 @@ private async void Main_Load(object sender, EventArgs e)
// core components initialization - required for loading the entities
await RadioManager.Initialize();
await InternalDeviceSensorsManager.Initialize();
- InitializeHardwareManager();
- InitializeVirtualDesktopManager();
+ InitializeHardwareManager();
+ InitializeVirtualDesktopManager();
- // load entities
- var loaded = await SettingsManager.LoadEntitiesAsync();
+ // load entities
+ var loaded = await SettingsManager.LoadEntitiesAsync();
if (!loaded)
{
MessageBoxAdv.Show(this, Languages.Main_Load_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
@@ -245,8 +245,15 @@ private void CheckDpiScalingFactor()
MessageBoxAdv.Show(this, Languages.Main_CheckDpiScalingFactor_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
- private static void ProcessTrayIcon()
+ private void ProcessTrayIcon()
{
+ if (Variables.AppSettings.TrayIconUseModern)
+ {
+ var icon = (Icon)new System.Resources.ResourceManager(typeof(Main)).GetObject("ModernNotifyIcon");
+ if (icon != null)
+ NotifyIcon.Icon = icon;
+ }
+
// are we set to show the webview and keep it loaded?
if (!Variables.AppSettings.TrayIconShowWebView)
return;
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.resx b/src/HASS.Agent/HASS.Agent/Forms/Main.resx
index 3676f237..df700854 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Main.resx
+++ b/src/HASS.Agent/HASS.Agent/Forms/Main.resx
@@ -1,4 +1,64 @@
-
+
+
+
@@ -57,17 +117,17 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
+
+
17, 17
-
-
+
+
125, 17
-
-
+
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAnRJREFUWEftlt+HVVEUx/vXIiIi5ikiIiKGISKip+glIiIiehoieomYmhpNv9TU
+ wAAADsABataJCQAAAnRJREFUWEftlt+HVVEUx/vXIiIi5ikiIiKGISKip+glIiIiehoieomYmhpNv9TU
ZDI1PzI1mn7o53TPvefcc85qPvtY2nvffebuY+41ZBZf99599t7rs9daZ6+7a/dES7ZTOwD/D8C+yZbc
+tiVuV+FjD5rB+eENBAAnM/+KCQvRdbapSS5RENsGcB2fuZVR0amEllpFQbi2JP+EFsC2HvXda7jQKx3
S5n6kjvzQ2oMwIn33Km+n51LBTv3OnXmXH/fNeMX3rjjITUCYMO0EPmWlibHhx+2zemJAmDMUed8+utD
@@ -84,7 +144,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAiNJREFUWEftlc1L3FAUxfuvCV0VBKFQKLgtFISuCkKhK/eCIBQEQSh0JbgpCIJi
+ wAAADsABataJCQAAAiNJREFUWEftlc1L3FAUxfuvCV0VBKFQKLgtFISuCkKhK/eCIBQEQSh0JbgpCIJi
nSqoFT+oYLVa60et0qlfnSSTSebW35tcTKZp5mWy0MUcOMy8l5d3z7vv3JsHXdMVuUt2BHQE3H8BL1c9
2bwMZfY0SH1elJkCni26EtRFJn/UpH/Nk545J3VdEWYKGNvzZfV3IH3Lrpy4dRn56ieeP5xJrm+HLTPw
YsWVxV+BPF9yE88elxwjjuzE5/PSyoQI4ffpvCPv9n15te7JmXdzNxGGt6v/vGNLKwHKNzt+FDIJfNKc
@@ -99,7 +159,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAnlJREFUWEftlu1LFUEUxv3XAiEIAkEQhCAIAiEQBEEQgsBPgV+CIAiCIAgEQYhA
+ wAAADsABataJCQAAAnlJREFUWEftlu1LFUEUxv3XAiEIAkEQhCAIAiEQBEEQgsBPgV+CIAiCIAgEQYhA
CDQrM+3N0lDKtCxT8jWQfd97T/1m73inZXbv3BvRh+4DD3d3Z2bnmXOec/Z2nJry5F+yLaAtwFnA1ZVI
wJZftY63ylIBV5ZDub0Rq2t+wU6QCTj/LJAHO4mcmbGvdWWhgL7ngaRVtacs7KdynGQ3PJvbS8VP1a3M
fE+t611ZKKBz2pOXB7VdCoCY/leBdb0rG6ZAY2wzlp5ZX0behSoNYOmoYl3XDK0Cbn6Mldl0mPMbaUOC
@@ -116,7 +176,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAiNJREFUWEftlc1L3FAUxfuvCV0VBKFQKLgtFISuCkKhK/eCIBQEQSh0JbgpCIJi
+ wAAADsABataJCQAAAiNJREFUWEftlc1L3FAUxfuvCV0VBKFQKLgtFISuCkKhK/eCIBQEQSh0JbgpCIJi
nSqoFT+oYLVa60et0qlfnSSTSebW35tcTKZp5mWy0MUcOMy8l5d3z7vv3JsHXdMVuUt2BHQE3H8BL1c9
2bwMZfY0SH1elJkCni26EtRFJn/UpH/Nk545J3VdEWYKGNvzZfV3IH3Lrpy4dRn56ieeP5xJrm+HLTPw
YsWVxV+BPF9yE88elxwjjuzE5/PSyoQI4ffpvCPv9n15te7JmXdzNxGGt6v/vGNLKwHKNzt+FDIJfNKc
@@ -131,7 +191,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAe9JREFUWEftlttHRFEUxvvXIiIiIiIiIiIiIiIiIhE9RfTUU0QiopruN92bmm6j
+ wAAADsABataJCQAAAe9JREFUWEftlttHRFEUxvvXIiIiIiIiIiIiIiIiIhE9RfTUU0QiopruN92bmm6j
y9Qo0mU6tzkzs/PtzjTn7LP2zJ4xGTEf38vZa9b57b3WrH0qKhc0VkqXAcoA/w+gY8/gFp/Xreisddv/
PJeVAZA89Jlk0KuV4i90r0/exvlaRE+x3mPTs5bNSgB9JyZLpHj+Xx2/JVn7rsEa13U2dh1nesJZcDR+
EydziVYCGAhaTlp1TYSLCFC1qDHr5/SVVbvsLZHMygDvcaEGOYTSULlEKwGgqUTNPdm8NOiPqXvbd0LD
@@ -145,7 +205,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAwJJREFUWEftlttrE1EQxv3XBEEQBEHwqVAoCIIgCAXBJ0EoCH0RBEEQBF8FwRdB
+ wAAADsABataJCQAAAwJJREFUWEftlttrE1EQxv3XBEEQBEHwqVAoCIIgCAXBJ0EoCH0RBEEQBF8FwRdB
UKsttbZqvdW7FVQsrdRbm2Q3u8m0v92OTs6Z3cQHKUIHPpKdnD3zzTcz52TX7psN2U7sEPi/COybaMjY
Qio3ljJ5+r0jS61ugYWfHZlYzmX8ZSoH7jbdd6swEAECX1psS9qRHvvR7spq2t16Ki3ffLz6KRuYSF8C
R2ZbspKUQSBw5WMmxx62onWH77fk8oe2rGXlWj5PPE6idSFqCZx+nvzOGpmH78WBQxyaasr017x4BzXO
@@ -164,7 +224,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAkVJREFUWEftlt9LFUEUx/vXhCDwKQiCQOgpCIJAEIKeBJ+CEAKfAkEIAkEQfAkC
+ wAAADsABataJCQAAAkVJREFUWEftlt9LFUEUx/vXhCDwKQiCQOgpCIJAEIKeBJ+CEAKfAkEIAkEQfAkC
S/wVZv7IpMwfpSVZlhbK7t79cXe6n7NMzO6Ou7qtSeCBL5d77pk7n5k5c85caBt11FnqHOD/Bbg66aon
X0K15TTVykFT9a366uIze2yRKgEw+a8gVtjMj0ggsKc7oTW+SJUAWDl2Y8b743u8GYivc76Rii1TJYBt
N1bT36OU78qEKwAP14OUv0yVANYOm7Ltpu/mS08AyAXTX6ZKAKwSe/QxUJfHXZn81V6k/FYqXJtyrWOO
@@ -180,7 +240,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAbxJREFUWEftlu9HBEEYx/vXIiIiehUR/QHR24heRfQyIiJ6Fb2MiKur01X0Q5eK
+ wAAADsABataJCQAAAbxJREFUWEftlu9HBEEYx/vXIiIiehUR/QHR24heRfQyIiJ6Fb2MiKur01X0Q5eK
ri6lurq6iuv25+w+9Z01uRuzu7PuUrTLhzMzZj7P7LPPcx2dmTr9JqlAKpAKJBbo3TRotuhQ5pFRseZR
2fRp74XR0o1DQztm09qu9ToNbBtNYzLaAths+tymqu1T4c2juUuHxgsWjR5afHyt7BLzicv054JDcTge
ea9GtAVWH1we7dixpZwHODhXYVSxfH4bbRNYvHbozvC/I4tj+TaQHd41WxcYOTDp3QkiUs2HcfTKKP/M
@@ -221,7 +281,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAXxJREFUWEftlUFLw0AQhf1rgidPglevnvwHXr2K4NWrIAiC4MWTUiwUigdRClUQ
+ wAAADsABataJCQAAAXxJREFUWEftlUFLw0AQhf1rgidPglevnvwHXr2K4NWrIAiC4MWTUiwUigdRClUQ
tVoUq6igSdMkzdiXbMgmHc10Gy2FPHhQkunMt5Od3ZnZI4sm6RKgBBgZYLnepb17j2ovPrXtgFpWP/yN
Z3jH/ec3iwEWqzZVOz7l6fDRC2O5HJxFAHPHFl2891WJfNVffTYPZxHAzp2rUif69AI6aHth6/EZstq8
6rG5shYBnL2lW//UDWjhJGnzfMWim680BDqm5/jJIoAPN1BpI21du0MxG5c99TaSPWDOxnAWb8I8r547
@@ -233,7 +293,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAJtJREFUWEdjcNz79f9A4lEHjDpg6Dog5ti3/yff/Pl//t2f/6knv2NVQwwm2wFr
+ wAAADsABataJCQAAAJtJREFUWEdjcNz79f9A4lEHjDpg6Dog5ti3/yff/Pl//t2f/6knv2NVQwwm2wFr
Hv3+DwN7X/zBqoYYTLYDJt78CbX+//9lD35hVUMMJtsB7vu//m++8uN/57WfYDY2NcTgoZsIqYXJdgAo
4T3//g+MByQRgrIfDIDY2NQQg0cdMOqAUQeMOmDUAaMOINsB1MKjDhh1wAA74Ot/AF92BBjcG70TAAAA
AElFTkSuQmCC
@@ -242,7 +302,7 @@
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAg5JREFUWEftls8rBVEUx/1ryspKKStlpaxsla2yslRWSikrZakUCT1RChGR3+Vn
+ wAAADsABataJCQAAAg5JREFUWEftls8rBVEUx/1ryspKKStlpaxsla2yslRWSikrZakUCT1RChGR3+Vn
Epr33ng/Dh+6GveeMzPPj6R867uZuXfO55577rnT1DwbyW/6H+BvAbTORzK4HcvMRUU272pyUaq/evu+
JvNXVRnaiaVtoajOtZwLgMBjh08S1yRT1brI1FklN0gmQM9qSa7LL19tUI+VuvStl9VvJp0KMLBVzrVq
S2RjeC9Wv+1sArDyrwRPqn/DzoQKwJ5/Ju2WilWRjiW9JlQACs4SFX8Sham5jetSuHmJZGj6vBLEwQEA
@@ -283,7 +343,7 @@
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAmFJREFUSEutlStwU1EQhiMQCAQCgUAgEBHIioqKCiQCUYmojEAgEBERnYlAIBCR
+ wAAADsABataJCQAAAmFJREFUSEutlStwU1EQhiMQCAQCgUAgEBHIioqKCiQCUYmojEAgEBERnYlAIBCR
yMqKiIoKRERF3hNBZioqIiIqKioiKiLa7zvsCW24hEC6M/+ce/bx7949r9K6MhgMdvr9/jHjiPHzeDx+
EqbNBdJtMAcXoAVuTBbmzQWyb2CWqyZB3SSdTudFcthUIDsC01ar9cg5ySom6PV6r5PDpgLhnoSMTXAA
Lpn/CPPDCIRViGeRqM1YDtP6QmANXEmyhDn64263uzUajZ6G+78JJPtBdgiZbVgAXZ3xHPgHOxGyvrgb
@@ -299,7 +359,7 @@
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAA9xJREFUaEPVmi1Q3FAUhREIBAKBqKhAICoQCAQCgahAIBAVCAQCgahAICoQzCAq
+ wAAADsABataJCQAAA9xJREFUaEPVmi1Q3FAUhREIBAKBqKhAICoQCAQCgahAIBAVCAQCgahAICoQzCAq
KhAIRAUCgahAIBAIBKKiAoGoQCAQCAQCgWBm6Xf2HXZnySZ5+WGy/WbuJPvevSd33ybvLztUF61WaxZb
wbawn9gZdo3dYH+xc+wI23x5efmMjTh0MCCxe5KKBv9FhzYP+YyHtOLhC2w4vHlIZtZ5RUPMvsObh2TW
nFc0xJw7vHlI5ofzioaYO4c3B3mMkMgm9hDSKgZxB9ik5eoF/TnETzguYcMu7kDdMnatRKqAxjN2wOkH
@@ -322,7 +382,7 @@
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAABTdJREFUaEPVmS1QHTEQxxEIBKICUVGBqKhAIBAVFQgEEoFAIBAViIoKZAUzFQgE
+ wAAADsABataJCQAABTdJREFUaEPVmS1QHTEQxxEIBKICUVGBqKhAIBAVFQgEEoFAIBAViIoKZAUzFQgE
ogKBQCAQCERFBQKBqKioQCAQiIqKioqKis7Q/v65vZBccveO93Hv9T+zk2R3k93cy8du3tRjcX9/f/k3
AO0/Jvo/gMO35nuIGRNPDnD0DY7NWtNDX7zwOcK8iT3q+ncCjB/IK8rPFE+NPaW6+FWgt2Qq0pmmfWb8
K4puJ1E6X4L2DfSc6gy065gVIN+neALNUv/omAba3U0CY/rZE8D/Af20ZhbIf0O5/SHZmZkYLbD1FGPf
@@ -350,7 +410,7 @@
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAABH1JREFUaEPNmS9Q3UAQhxEIBAKBQFQgEAgEAoFAIBAIBAKJQCAqKxAIBDMIBAJR
+ wAAADsABataJCQAABH1JREFUaEPNmS9Q3UAQhxEIBAKBQFQgEAgEAoFAIBAIBAKJQCAqKxAIBDMIBAJR
gUBUICoQCAQCgahAVCAqKioQiIoKBKKCGej3u+w7Eu4uSSEvL9/MTpLdzd6+vNv7kwyleH5+Hn16etpC
LpBfyB9k3czdhuSnlTTHAujmzaW7kOcEid5mKQeMm1t3IfkzS1ZPXN1mT0+ey0lz6S4kOpWlniXPYdpM
riaQDWQXWTV1tyDpHZJzcL5launXkN9mkm3NTN2CxI4sR7EgHcdV9I9ScLxBviAf3A0VcMuonbaDkrNE
@@ -375,7 +435,7 @@
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAAuVJREFUaEPVma9zE0EUxyMqEMiKigpERUUlojKiAlFRgUAgIpEV/QOYQVZUVCAQ
+ wAAADsABataJCQAAAuVJREFUaEPVma9zE0EUxyMqEMiKigpERUUlojKiAlFRgUAgIpEV/QOYQVZUVCAQ
CGRFRAWiAhGBQCIQFREREUgkMwmf9/aba0M2vST3YzefmTd3+31vd793c7m92XSaYjqddieTySVxRwyJ
AXGFfqaSPMHgc4x+4rgU8n0Ou+qSD5gy87/cZQnUjYl9dc2D/+887c/EKacviBPOr4m/ngTOv6prevDT
DbYCmHur1Byk7LdRXAT0lEoLpi5lyO+85CjkP6jUam8kpwUjd/Jkpk4lRyF/pFKrvZecFoyM5MlMlf44
@@ -393,7 +453,7 @@
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAABpVJREFUaEPFmi9QHTsUxhEIBKICUYGoQCAqEE9UVCAQCASiAlGBqKh4oqICUcEM
+ wAAADsABataJCQAABpVJREFUaEPFmi9QHTsUxhEIBKICUYGoQCAqEE9UVCAQCASiAlGBqKh4oqICUcEM
ouIJBAKBQCAqKhAIBAKBqEAgKioQFYgnKioQCGagvy97NpvsbrJ7917e+2bObHLOl7PJ5t9J7p2aFB4e
Hl49Pj5u8dxFviIXyE+Tb8gxsod8greMTFvR/wdUYIbKrCIHyA35kUCZX8gR8obsrLn9b8ALN4dUOgV8
/UY+kHzaXuEFy7zoyr01Azh3yDVybvIDuTVzElbmjb1ucsD3LI6/FK9pAtsNsktyBXlmxRrANou8hruD
@@ -427,7 +487,7 @@
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wQAADsEBuJFr7QAAARtJREFUSEvdlK8SAVEUxjcIgiCKgigIHkUQBEEQPMQ2QfAIoiCIHkAQBA8gegRB
+ wAAADsABataJCQAAARtJREFUSEvdlK8SAVEUxjcIgiCKgigIHkUQBEEQPMQ2QfAIoiCIHkAQBA8gegRB
MLPrd/d8u2OMsbhrBr+ZM+d+97vn8++u4P+J47gWRdGEWlF7lVtPqYaOvQfhISEn+l2cR411/DUYHCkn
F84ONfYczJQYOtu4gV5TA5Z9+sZ2DfSRVtZ4Pgw0bdRA72kl2Qnsbc010G1Z+XC4wUyYFrorK4O9iQtO
QfdkFQOBS2UnoDuy/CGvQmB2u1i736su2x8C5xZtoGey/CFsqtwEtLsAVdnvQ4i7urfvfEf5Pc0pd8JX
@@ -437,28 +497,28 @@
- iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAIAAAC1JZyVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAASpSURBVEhLvZZ/TJVVGMe/cEEQLlyueH/B5d7Lr4v8CgPM
- CqqJWoRrkIpglBrKMMdyLU3+sc1kkFbKlmlruYhlscw23FyL6cC2XI7Vao1loFCKFiQ/TQZFnJ7z9hrv
- fe4F2xidfcbe7+U833Pec57nnBfiyv8B1/ME1zPSDfETxM8QVxXoucerz8xw7UGn4jsBIRTGIW5BDEOM
- QIxBTN75vU/pyWI94XqaLohe6dt+Jqiu2rgu35adHpPgdDqiXS67M83tWJ0b/fyzpk/eDpPdRiEueTlo
- 4HqaaxA38fR6C5ACuDMyHEUFti0bzJVlpm2l5tIiyyM50ZGmOCA12e3q69CJIS8HDVxPc0MO4451AEuB
- xI0bLAdfNjbWh508qm86oj9aZygvtVRsMp9+KwRIO/CiUa4ec9DAtQqtGEGRkzj1jv6ZddY4u8vfL4HG
- ozdTSAxekLitxEx9bGbXE6ussjOlCUUxKwWuVS7LXfn60wUt74fKvKKdH4D4FeK6kma0Gb9B/C4nMT7m
- 9/B90QE6d+dXATJHKJBZKXAtoa5/ovXkQiBZ2ZglNP0IY1yK2/Fgtn1lbtTKnKgHsuxLEh36MNob+WZK
- n6Qvm4MpkLspcC2hgphCVmqM3Rbb2RH48TH9nu2L1xdYc7PtlGAJLidBDySL11irn4tsPh7a+X2gKTJu
- +T12uXS+6olrCa1MP2iOBStsMuy/kbvMDiRQoAxnhr6HoQqfwI5NJkohXUBCkttRkBdFcv+uRW/uM7x7
- IJw48oqhZrexaoupcLWtcI0tOd4JpFc+ZZK1TOHMcMa9+UXW9vG68N2Vi9c+bo0IpT2gDUjyhHYlQR9O
- /5Kbd6zOIF+FAn1lAdcqPyp1I9DeHHT2RAht7ORlv6Fv/a9fCOg5H9Ddht7zGLyISTpjJpAcb8lMi5VL
- R+k3w1nA9TT07gLxzljAJZeCipxcKK1vKs9jqeKvnUK8IcRes2Vp/kMGyhpZN8zkDlxPQzEC5cWLgAxx
- LUb0BYtuvbiaLvqrxFib0DT4F+woVRKBOWjg2gOBmp2UcvePDI2IqX4xOaQaa9rAwDDN49Vdcxum8TUa
- xnWpa1B19WodHV2A88PDcxlmEq0ndEBEa1u76urVzp67CIR/8ZFOXj8sXAPXHtzGDy00jK7xg89UV6/2
- XsMZwL/znE4ecSxcA9ceDGL4Gz9atZraRtXVq+3b30AdRr/zk0nIwjVw7QHdvj1kgortlLi+W3nF6zQR
- mf10frNwDVx7QBfoAEx6rMqvVl29Wt6jeyzh8r1lZxaugWsPaI7juNeNpLStqqtXS0wpz0pSvkZ8HWX/
- wrUHVKFTKFqBUONa1dWrhUQUPZknu81yBBBccwSqymh3clRXrwbkVZbcpWgIrjkCDbVBgKGp6bRqrGmf
- t1wALKfqdXMeph/jN3RZGQYgNnP5C8UbD5ZtPrR5a31J2cH0zCo6/wsfWyhu+/u8yrRwzaEb4RamegNr
- XwpalgKXGbYImMJgNcjUOLRXJ0Z1Ms2oGwv0hGsfkAVdAbQs9DlBnzh0C1Al0t8/lB/pWrrbGATXs0Hf
- EvRJTfXxD7NmMIPreYLreeEK/gbPMONd+jRQGwAAAABJRU5ErkJggg==
+ iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAIAAAC1JZyVAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL
+ EQAACxEBf2RfkQAABKlJREFUSEu9ln9MlVUYx79wQRAuXK54f8Hl3suvi/wKA8wKqolahGuQimCUGsow
+ x3ItTf6xzWSQVsqWaWu5iGWxzDbcXIvpwLZcjtVqjWWgUIoWJD9NBkWcnvP2Gu997gXbGJ19xt7v5Tzf
+ c95znuecF+LK/wHX8wTXM9IN8RPEzxBXFei5x6vPzHDtQafiOwEhFMYhbkEMQ4xAjEFM3vm9T+nJYj3h
+ epouiF7p234mqK7auC7flp0ek+B0OqJdLrszze1YnRv9/LOmT94Ok91GIS55OWjgepprEDfx9HoLkAK4
+ MzIcRQW2LRvMlWWmbaXm0iLLIznRkaY4IDXZ7err0IkhLwcNXE9zQw7jjnUAS4HEjRssB182NtaHnTyq
+ bzqiP1pnKC+1VGwyn34rBEg78KJRrh5z0MC1Cq0YQZGTOPWO/pl11ji7y98vgcajN1NIDF6QuK3ETH1s
+ ZtcTq6yyM6UJRTErBa5VLstd+frTBS3vh8q8op0fgPgV4rqSZrQZv0H8LicxPub38H3RATp351cBMkco
+ kFkpcC2hrn+i9eRCIFnZmCU0/QhjXIrb8WC2fWVu1MqcqAey7EsSHfow2hv5ZkqfpC+bgymQuylwLaGC
+ mEJWaozdFtvZEfjxMf2e7YvXF1hzs+2UYAkuJ0EPJIvXWKufi2w+Htr5faApMm75PXa5dL7qiWsJrUw/
+ aI4FK2wy7L+Ru8wOJFCgDGeGvoehCp/Ajk0mSiFdQEKS21GQF0Vy/65Fb+4zvHsgnDjyiqFmt7Fqi6lw
+ ta1wjS053gmkVz5lkrVM4cxwxr35Rdb28brw3ZWL1z5ujQilPaANSPKEdiVBH07/kpt3rM4gX4UCfWUB
+ 1yo/KnUj0N4cdPZECG3s5GW/oW/9r18I6Dkf0N2G3vMYvIhJOmMmkBxvyUyLlUtH6TfDWcD1NPTuAvHO
+ WMAll4KKnFworW8qz2Op4q+dQrwhxF6zZWn+QwbKGlk3zOQOXE9DMQLlxYuADHEtRvQFi269uJou+qvE
+ WJvQNPgX7ChVEoE5aODaA4GanZRy948MjYipfjE5pBpr2sDAMM3j1V1zG6bxNRrGdalrUHX1ah0dXYDz
+ w8NzGWYSrSd0QERrW7vq6tXOnrsIhH/xkU5ePyxcA9ce3MYPLTSMrvGDz1RXr/ZewxnAv/OcTh5xLFwD
+ 1x4MYvgbP1q1mtpG1dWr7dvfQB1Gv/OTScjCNXDtAd2+PWSCiu2UuL5becXrNBGZ/XR+s3ANXHtAF+gA
+ THqsyq9WXb1a3qN7LOHyvWVnFq6Baw9ojuO4142ktK2qq1dLTCnPSlK+RnwdZf/CtQdUoVMoWoFQ41rV
+ 1auFRBQ9mSe7zXIEEFxzBKrKaHdyVFevBuRVltylaAiuOQINtUGAoanptGqsaZ+3XAAsp+p1cx6mH+M3
+ dFkZBiA2c/kLxRsPlm0+tHlrfUnZwfTMKjr/Cx9bKG77+7zKtHDNoRvhFqZ6A2tfClqWApcZtgiYwmA1
+ yNQ4tFcnRnUyzagbC/SEax+QBV0BtCz0OUGfOHQLUCXS3z+UH+lautsYBNezQd8S9ElN9fEPs2Ywg+t5
+ gut54Qr+Bs8w4136NFAbAAAAAElFTkSuQmCC
@@ -487,4 +547,8 @@
NLGJMOvFeKEqY0ecbYgf8dDTDERBTzMQBX0DvaZvoNdMcQMi/wKHssFyJvHDuwAAAABJRU5ErkJggg==
+
+
+ ..\Resources\modern-tray-icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
index 5222a21c..ee404285 100644
--- a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
+++ b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
@@ -37,6 +37,7 @@ public AppSettings()
public string TrayIconWebViewUrl { get; set; } = string.Empty;
public bool TrayIconWebViewBackgroundLoading { get; set; } = false;
public bool TrayIconWebViewShowMenuOnLeftClick { get; set; } = false;
+ public bool TrayIconUseModern { get; set; } = false;
public string ServiceAuthId { get; set; } = string.Empty;
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 0a90d4a8..b9cc36b9 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -3055,6 +3055,15 @@ internal static string ConfigTrayIcon_CbShowWebView {
}
}
+ ///
+ /// Looks up a localized string similar to Use modern tray icon.
+ ///
+ internal static string ConfigTrayIcon_CbUseModernIcon {
+ get {
+ return ResourceManager.GetString("ConfigTrayIcon_CbUseModernIcon", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to &Keep page loaded in the background.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index d93caf19..e7b8bf5b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3491,4 +3491,7 @@ Als Payload benötigen Sie den Namen des Audiogeräts.
Ignorieren Sie die Schonfrist nach dem Aufwachen aus dem Ruhezustand
+
+ Verwenden Sie ein modernes Taskleistensymbol
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 8b21c62b..859e0535 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3368,4 +3368,7 @@ Requires audio device name as a payload.
Ignore grace period after waking up from hibernation
+
+ Use modern tray icon
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 1c609964..1c58dc96 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3367,4 +3367,7 @@ Necesita el nombre del dispositivo de audio como carga útil.
Ignorar el período de gracia después de despertar de la hibernación
+
+ Utilice el icono de bandeja moderno
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 4f459b6e..9570fa4f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3400,4 +3400,7 @@ Vous avez besoin du nom du périphérique audio comme charge utile.
Ignorer la période de grâce après la sortie de l'hibernation
+
+ Utiliser l'icône de la barre d'état moderne
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 3556e82b..9a8ec312 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3388,4 +3388,7 @@ U hebt de naam van het audioapparaat nodig als payload.
Negeer de respijtperiode na het ontwaken uit de winterslaap
+
+ Gebruik het moderne ladepictogram
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index e888467f..5e45586f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3477,4 +3477,7 @@ Wymaga nazwy urządzenia audio jako ładunku.
Ignoruj okres karencji po przebudzeniu ze stanu hibernacji
+
+ Użyj nowoczesnej ikony w zasobniku
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index cc835f66..c12f692d 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3413,4 +3413,7 @@ Você precisa do nome do dispositivo de áudio como carga útil.
Ignorar o período de carência após sair da hibernação
+
+ Use o ícone moderno da bandeja
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index fa71846e..5f839459 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3380,4 +3380,7 @@ Requires audio device name as a payload.
Ignore grace period after waking up from hibernation
+
+ Use modern tray icon
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 4eb99872..fd37d175 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3436,4 +3436,7 @@ Home Assistant.
Игнорировать льготный период после выхода из спящего режима
+
+ Использовать современный значок в трее
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 732c2f7e..3a216e73 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3516,4 +3516,7 @@ Ime zvočne naprave potrebujete kot obremenitev.
Po prebujanju iz mirovanja prezrite obdobje odloga
+
+ Uporabi sodobno ikono pladnja
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index dea9fd38..5796ca75 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2979,4 +2979,7 @@ Yük olarak ses cihazı adına ihtiyacınız vardır.
Hazırda bekletme modundan uyandıktan sonra ek süreyi göz ardı et
+
+ Modern tepsi simgesini kullan
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/modern-tray-icon.ico b/src/HASS.Agent/HASS.Agent/Resources/modern-tray-icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..4bc498569af998717d123e342e879a1b7957a362
GIT binary patch
literal 52111
zcmeFadsI``_At5w3L+}Cz7>hqR@}?}h)N#3T3SIc5cDWL)h6xfj*nl>!&;@?QiA1dgMq-Eb0-_)cfT>t9ruqr?jIj#
zbhMdk&9&y7Yp(fPYp+8P9QdE)V*>tW5xcGt#ANudPopCv#*Uso8vb+a>bKsEB?xy3
z{PXcBH-LQOKk7&Lmq+$n>vITV+-3ZS(~%{6mmmsGCKDV?>?|FDm#IX-!G==}R
zsbl3EU;StC(Yu+y+{s@!>!-Kf_8Rki<(s4FKXI2ZqG9$4gcXXpa3d@Y{uyM>fN+4~VINA1H6;FI8F#T#3L~Tk6)P(6Zhe
zzXbk8+jjku-2L$HX3OLNcQy1=uO>skS5THoM0yl0RNgHM?QB~#)Z5uOZ%FW`_F+hi
zw7k$q->EHE^-F8CcW-~#CTisia)>d2yH?S?u2v@2atN_3mYZ^9LDCdgZ!AC#B56Ot&Mv3!5t~cd#&Xs9>f7m%0rPGi4Mc3d$)UUKjKWNI(K6(7>k&hmR73QfjEJIz7
z#D960r4DvA^FOjvH73zAdthT|I6-Jh!w(!kpu_Ji
z{od?@qFRC&j4uqB-u?KuA@?3*Sf0R^aWik{$-+TfK%?!K&PQE~x2fuMdOPv%iWaY{
z@{F;Bsdg~(&F}h$N=~|_!(izd0^{sygMMUNEFGDY?Zb3F6*}Q*|$RfLH0(v%Z1pgYLRvWEI|q4-Nd3s0G1?%ae*gHi4TF}d$3JY&rGv{2hB0;RQI%WL
zw>Z|^Rglpx&^34wrN4Z#Xuj9bF6}R&e+f70uU0h%{7Mg!2a7-W#?G0^OTgz8DjNu*
z^yi<_qRTdl`yZu^%u;m)+${J{uGnx@TPPNA>oP63Z6SGMw3)(dIfXl)9P6-jS;M&3
zj&<}p
zu&w`(hjiCpQ5)|GZ{D^Bg^I>SXzxea$`2Xtp-L)aP
zDdJ}TE`51PXtQ)-+fdh&!HfCVHtTn&7M2uRkB99#UKEXYdwiKqz5Ak^NJ`Nfdz2bTMf
zAGDq4QNqG|_Cb!l@y)vu5AuX7A)+ng9lECeoYHXK77P-SNL%QL9rlH(|Dt)TWIefl
z1Tmc!KF9WajVj}8iSK-#n5rD?=3KDz7^rGalib!#8%bQ1w+UHgkF4pQY+687yAYd0
z`d{d23v+G#sB4Ebqs{XBu8S=LM}G;TU^@={Wcy)@c9-?RlJgh0xObu@3jD5drfbT2
zX)<9-(zX&I1D)S1JL>N*9e839mV`B{GTM4Ijm4WD-cRn%ajSdq$Il%tv~BP=p4|kS
zKH|MPQBo-PA47O|{^UNSTr_gXx@hUp#jei##hK<#L7Htq`*7f*jb9R$*QM>x_*X
zGY-@g-)$^uvp#9KDR{WGKhw<+Y7n+H-d}n#Z=<>Fm!n2^AT09N`)$9wp46>(BMv|M
zJ$&eP$?l@ULgAB!@-S{po(=*rF5K%)*E7B^!TU*!ZaH
z>zcZu8%qZs@O*6DoZBui;<%o_(*D|AM_zCv9@D>H9=u&5`CmrNyii=zXrY!4+A_2q
zJut-0!pFTeTARMN;G>qhjR%CFhEq3&HkOwZ2F-D7snLsuMsoYU3^EdGu}{H|Vf`*W
zJ!Z2nAo2?f1yW&K8@;`7M_#$1bA0h#y>>{f9~|iI5;nF9>wJt24>Joc)@AnV%-b#u
zoV=Mc^gj4CB5&7|MUT~)RJAM7pZlNLLp^?fb_$HCgTH4OQ-<_{GMgsAGW4hU;@2Hp
z>n)oHe#oBH+HB?h1s~n6d>GdJ
zr?9Z_BX0Sf2bCRk*W#=TgAK-A$TBsUrYbJKV{GYcx!+tK(wY%gD6rM^KK@;l^XwTic88r8Tr(ecJ84_ZJfvxx6iy)$bjGFydmO
zc@&Y}GSu6_EgbAc9r=WGPgXCX!4=_O`VM;c=clL9ktlu$cVfBmoDP}L)84pO=N_;AikgXD82tV$
zx_zSGD%DndV_;IjtdR#3%-;5&<6^mQat?_s_Fwf4R$}z~y5LdFPl=Rtj{<9vI)eGB
zUZiu6RHsl<$IlXK@7(lAIumu3AWY#jXM$g&D8f!seM~|9h|+MCl4&GzD|yi_nzM%W
z_Mag!+!r~W@n&E9PqEDFh$KfT#Bc`wh7!2moUJn9H0GE3zRrD>dIKfJV2L2I*VD_#
z*PjzL+UH4$ELb+8y2h}bX(UQwxMMgsqs>9gPYIx|P=8AXdoe%L0rk1;b#%8~FiDXG
z#E6t+m8*TEV=>$T;*PH6Ii|1aKa?w15~d3tZ~yrXwbw@yLFw(1$qu49Uh4VuVY?^-
z4_VXQ(+`OFc2TUYN(zV&eRISx6Ci1Na^)1
zu}UGd%T&FI+B;RULX~HyVJDxfCcdC%q6sC4dZo2zPa^4yM>!U!Z4?BsyeaX$-EUox1En98M_I*eBjg7ClcKj43qOf4VEZ=AfIsW53y(VgR%j**qV^rmlua`I$p;V+XH?5zEvdyjW8F(WgI-s-V0NXT^bt7-mc(NEO%
znHA!7h+qevZB{ctx%k7Uz%QGd5O^rM?W*4f=~AZW@fE4JF265$uu74(b!v^qE|oLX
zX`fX}f4(d&XBhS(wTFQz((;C3V-lwH!blgUk!B9iWv6_<%ySp?xU-hBxWFLx%~PZ}
zkc+^Blx4(Ipv;p2UZ*T>s}#L1Tqn7+}x0&F>~DhX8lSQ@^&rv%5y4#3`+}>?)J>l>KZjF4N(r5j
zeJ04h!Bdu{?#G_Fb!FoNW2K9jo|6+xOgPPXWyZ+Eu^FBS1amer5SH+;R_E^;A#Y$d
zp86f?*BL-~hlRSmyOLz$MIwE?;Oh@k0x!z7utY9}{SMs&9y3T5zXF490A7GTcSC%pU
zadaoMbtRvYXWu~1pOF3@d9obFUa3b=Ur$HQFH*NZHY)rX;Ie981x|Clt}LdnCMcX4
z@=abuJ%dA*
zriEVasR^>XA@6G6(+I4tEPJnSmDH~6m4+$EG&u}rw{q57N
z5>znA8Pqm)uwWI_F}Kq9bL6onQaV2Otj2C67LhN#hGyivqQON=-ex+QK5brx3*KHu
zU03@HmN3A+3g5#FHi)d}Eh(eH#9(v@W7cqKdg7#Zf(Dho2eD^;NzfV=2p;0VJ4j_$
zjb`G2Tw|BeS=~#EadO-dgNYe-VSG*PnF?Zak5Kx%F=Y#wK6|NPF~d&`Ro$K{Ww0e{
zFlUfLgF4mF{B3
zTcGrnU`M}`K}S>gq5TZk*W?G%aE|K=&D@*uTE?NMW2t{nK?)m`{@>#|V$b@6%{}{2
zW{^DJ-$3nRq?@!N_3Dlq
zBXagiLHGKo(4P?o0q%ih@QZurD|@xTBBT1p-%kCX6&0G9rrshO*}$xV-=XIEeJiBF
zrXi&TqWw-L_)i}#SdRm$+jM!mdzBdsa29{cx6!g3_Yl6>T~k-K2`7=PP5luE?AfTA
z7uYBgGfMHiU@3#VlWsMEd0;H%)LP#yNOoU}Dj={*l*x45uUNAlU3V+r?4_wHW5Nr`
zCThjx(SmM?QE3^~8LOR-)2N>lOk;-RQK^?MkiA$(CbFBFKS9bd0lS+*?PBH$T$`#!
zf}qscnC(MDWdr`aPy43f!CeM8o$lV0N#`^3be;B1Mx|UT!{DKNG~&h*Tr4?hcxl?Ip~sk~B3*(1_1<*JbzQm@O))*w()MEx~!mAt{Nl&@+#9v51IKw>C%N)<*4raRQktL*fB
zMa{481B1coOcH-ga&e2WBM4gKpMLb-0
ziKpB%CUAvxK2*0=9e1cF_CZhkhTk0}sFeVlJ~>Kz0?~A&`MG^6-3gakxuJm7Fw+av
zYZD(QW{hsC6%7p!dO&t9`c|BD9@Nrp9Rn1U2;tB7l<$g75K~CDDuuIHrB6yp-E*mo
z3gO&LG%wRsl*M7s^z8zV(RuO)CWl|@*jovU&c<1v&bLzTK4647TX5F{gNi1laQ0S3Cu81|-b&5AG+7YI%+pNWw$Bqg
zr3_;OX2|oRR%XbR?pAb0YLuCe&z$Cqyy7SU}0go9H
z_+b*01*d(3@16*nfu#Qi{4ic|a!iR7BM4?OSg+_QI}n+BMl({Ks`9*WOk^0I9K#x%
zJS8f$7x{rS|LX~Z@tqt77#C_`fG*VO|2vTB31J1()8Ww>^lr+V5&KqUCl}F^mZg4l
z3Bn4bXoVD~CLp^0$wT0V+>C;M!Jh<0aWhdHh9ust)iB-aCC$J8_CM4UcNE1yjfPPy
z1_)Ap7WheX*KhxzF4%#N2&f)`BI<-42%O?XNX?E=`noIDti%d?u?Z!~$OQ>1CNE$|
z0wtLe5&96t`!R%i3=lS7WNk%R4l;Ae5xv=IkJc__X#X;Z;TlD86eHGbzIg>35Y``Q
z?s@k^I-i-G4JQ|z@eNte5B-IiXCYNOJkN~;?GhYv(}AFQ2vo12E**VCmopZJyrs}E
zUX>X#lwQjWiE$?OL``46CRED|8AaO5E~x1kfitAz-oQW9^cykS*BH`qZ@{1A3VC)O
zh8DQAJ%@q<5D2N$zmMYfdIq?M`t>O8P6p@*_tgl*ef$jYH1*d}FyNCJAesQ2Xr%7S
zyv}&-LS}Y0uRzOz(FH?imTnru5n7JYsL%$2_8CrJ7F7E#V+<1&I*QG67#JEgXIQQ8
z@}+p3h%hjGIQ0StY&Y6VG<*om<_N}rj~2DI#;}`B#?-%3&wr64>zs&2xE-x2cAXME
zr2^9?G{WwAN`J<@L2~->Yi*)cyTMy=F&1YA9WR*nJ=%V>?Aad&@e!y7b$=Oce^Z&r
zJe*2SiJfv99SXV&_Xt^zebBY3e}4@EiQQY}LLcw58a$pSvKI?p`5wJgZKXk&{Y6kR
zGbo7jW%N=_`$ad~W2kL*N)t*d(I28?bB|E;+6RqKJ^Wgg$agob%NvId67iern`ga;
z{uyNA?Omf`qDb_s-=otR5Aw-_;XMw?${
zYd7k-f)k9G9`Mbt3en1P_m;Xwt)ve1wco%#t$-Q*_5cmCsv&VTgll-|`b9t0H2G^fSo
zCdirmQKuhKDzbvp>#dv2M#M`#*DzdK~=ax(hrJm
zE`tCcnxK60ReKt>R5DF>;Wuw;>Bz$g=BbWSV5H3V_1u~=uj4DFD>ap638txZ=59z9
z0L}C!^}=2=nd5pksIW95RH+`MR4(>nq1uBS>DI64^ZuD1D5A?RNzul>92gOm@UG
zzOQ1OkCuT!%b=tz+KbfZdvfHRlb{Im3EI9}=M0k^X#b>k>_NjRuGDySCTbV;Y*5W{
zR7FSWu8hE>ur;Q0KX%EjHjH&(y-CIHVG16y-b>S|rEY_AgNLJO1hrxhVELSvo^4chwKVc@PZJJ3hxUAC!6OfyMG@vm4nhQGiGI>Kgq?h}ZiUuzS%gY4g
zIS_D2%Bi*W5bWIj8+AS0zG8lFrLIuVqb{nA)B-p>s=fOvRn0anOI@ah0F7aMlq*T|AE
z-FXBsN*~HHW@^r97O+?{Pxs^Nq^mK*zZt6yE?q6&nwdCe`bA*ikT
zY*|LFvaI0vYH5Ts8dM9mK;W4JrIO2IC~vGu;DXK6t#qwEr+FP{KY^hXTqbbX0QF(&
zGzW_j7}yHjP?sG0xl86d-87mi&B>$(Fyul*gc&M~cPl3Kb@i9zu1;`8`&xqB(eAQT
zR5rganXkyvf&ucxq7ZrshZ)!zTQVkFK#v1kd0}6eZWkP@UD(4iCST)*=4QApRJZGy
zwsK0U20QN=raK`CC6_+ac0m+|`TnIj0$&q&ezk*ZDfyBsQ=9-Fe)qGWDHwh@>|y#m
z!M})Ye6uHJGP&n8ZuzG*o-B8(Q(q&>l@_rr0l
z{T@VXXXh(5<1nyuX1p`TJYPhQxs*jd!SWBgtAbIB@+s9yWmy;|jOb9Drz?e0#QVBl
z_qK$LPcG5D8`D&pAm^
zs++%)4ek{uA8L@ID1X(o<=1WsG(vMuat@;SI-NK?&h~GRGYv$Tus?f}Q8WAnYJt
z>)j>SOxUdJb#o?>Ci99PQ787o-gxJrNaL({Oje&5npNKz6z8ohTjV^X%rXl{g2?j9
zV(OV4Cu!65AK6Z=-#drA@J<>X;&AeelBlETjqk$gf(gnCw<1C#oaNZFR>hrGRrUtf
zBg9o@T<6(xs|-gE#%QNucxjY(x;d#iab970X)~GQ24^T&GN@gwpT}jBMCtO5E~b|5
zg=)w
zsDf+TsoQ(TQ>CfvY~p9|-lq-s@mL4K(OQUwoR#QQtU2v)oOUt%aFRiS){O1{;R=6TK=_p_$PooQ5JaRsPD^K#`F@!LoO`8JP$735G
ze?$-M{QxeZg2K6NaWZqb-pcchtscC#_ZF
zwVbpTs=sN*NY`}4`W;cf+IB8yV`!XTO=h<*HAMVOF}5$9n&S$m>Az@p!I|4P%@;P<
zY@0T~eNG4`-zs>QxK%B_$Iy`&z=w-OYn3N47-j@geoFk%(G*ZKXn=9i4+fs$y+-7J
zZvCP2qUc=u2K}8ctihL^$msc$YAi?Ufg`@MZP7C$RvS2_eCzw1mP%
zFOzxN0Ja8sR9=#E%l
zHmll{Z6m*+2BArU;w6lH^K&@Y+yApS@-|j4vZFz?rcNl{qts{qKK8*{+6~|-)gZj=
zNL5@QXiU?Df_6cq1@p-_agw_usTPXAI$dIf~T$@ZdoSfOB4E+k6T5~wVj2RA`|p91-+%M1``ehN%eU1NX`
zbS=0S8MUmBQ2lHNlEF`bQL0}UU@hN_nXaAwjr8Gr)uJ*+xBz4*y>p_>ba4LTDN=w3
z))^K8oDpmHo13$1=1;GT(@r2d$zWs`r&G^-f83UJOFC$3mkX{n(uQI3tudz1A2gepYha^l4m2@yO{BWTia{}K*~1M;U7M~2*AuB;
zR?*!JTlNxcSq&t9%=UsNTolO)z233=N)v$Bow(f9F~Cu@Q23>_g~c%)g|x_NxWCbs
zeXwH@M?Ftr#L^TaWY}M?(8B%0O>$$tQxnYev`J9`49#bo-lk`uHDQ!9
zTG*IsD`dqREi-O(q9Q70C)c6qRE|d(bUu1S)N4}zha;F0dJbxpTT7m7(nqPz_W~YEo53CIhV|AcUYAtX^^QV@g|6_JUn9lry
z6B@!;)&|dty;xx(Y&xC7s?I5~yK~D12-+N+^`+4LZ1jO-!(14v;J7`zC?THrqrRaa
zwm<~zkgzF-G&40M+~?5!b?O3(Ri9>Kjtg*$fn*~fk6;Cyr!%u22a)cChZeCcKb9Nu
z+7m0pCd?5WLhOoZ8}>dZyeK+_H66LFR_1;%E8je*r#a-S&e55tI0-5Q^B9K#E2|PW
zLKCz7P*Ce3{b70brt??_gyPUb*5r_{q+e-##fe66fMS-$JA4~+#OPqUV;t>`Q4Fe1
zQ@NF~m3)oH8*J`yv6T4f+8DfO&
zCNn*xgvj*ph<|U!K$fvAW~CA9<*e$Mf~ia-70vy?dfO@-(H(AZLU;GD7>g^**-eVM
zd>ts`y^aI~R&yKzsOlO)g*zUmi!jK3JNoVaiO)K=1P&pv-0Vifd
z0*iDVZV@fH2dmW4MlaNH(1ev@p4L4*}~kYR99h#Y)fM2z
ztA#WxWVs%t`IDe`Vl-g5m?Sf$NS59paG2!NJlGTvJCmsG3iD5=7|jn>etNJbLg
zp|`u>oaM4^ADpPJY-!)oW;C59H_CNn%Gpd$-gUC~jqFQjgWSd!pL)7%3qK=K4vQaYRo&
z!H6MK!P~%~8I?wNT=-Bj=R05f5qbchyU6rS^D8DT72pDq-kpfh10l(wZ8)SW={^SK
zk{e}9L~ofV@BAzSYUj|Fd&GK#Gfx-FNsDWnhC!p4I`Lbp{WgY=jQ75JwIE7uA+y{e
zLWF32$%P`(IMg$^*gYfrZT<5-{`3qN@Uq=-o9BU!IpbIm%?&ht(fpF>pmpKy75bT4
zs1V*%FehF~r+GWnuL#zt*V88vtL$tUUn65~Q6|ZFxoQtVg!&DtaU{?m0>=dT>p~YH
z{qN9sT_a_|F0~15i;+f<;{BTxb7;(avtY#Ta>WdnEo9bMpz&5k*}W+KP$8h1E|VS4
z^~sxOrzhGTAln7h2}A85BatFy@tmFsg0N+k;yegeJK%~?R`gIfU`dAJxNn>0QDRA=
z&22egX=Aijy>1AX&D02&-SheSagc)~j{y34a10M|E-v=P8r~VTQ1>TciEUdAMDA@<
z<^`S|JPb`lSRywGjg}-&VVsK_y@g5~JUfWlF;SxQ^6N(O%%mE}*b`Vneabq^!z>Ht?35c{ckDvghMQl!7#rZy72ozDuz{=*zKPt~
zcfV{NpWbLa#106Cf$%7dwQ%zHTWf}ayRAK=7~nDM55vHuf{#8n%6}nvakj@GQHE_W
z%7K^a$W)@dM(kSQAly5eJN+)7@`cYH+V-)Ni)FmmE`{mV5d*2Vxhov#eY>sdQH6X9
z(KDa^WfZIgl4+VjU48-gp1PdOdXbgTB1Q4vdlm^i3A4=jHf$P3MENlV@gKL5Gl~3_
zwk>G#82okg;_-vALZW9T{fh@!4FpO8$f7r8Gl;%Vtz*$*Aq+0~kN-@IcPBjP5FguJNMn;75@UjPAVN4qCx~{KXE`I>Fb5sLxn`V9|RXQFCUOpDhzgR@ff1#P_{V?>+*$8njF<-pgX=pRxLV
z*9m7arg2o`R4^uJ%hVn6;ywBMMH&t!2g6;zu+I8UOdoW;6JuNAsLv36Q|ZNw@o8$X
z9zxs8kTjkOpOvR;7~3;~PDbO3!1Qme#CNcZTtimdwy@|)O5WgCaiKSfsQ5NLgelC*
zCn~%OyWk44`lv1|5DX@2m_$HTa$CsWG4;!B$A+;hd5^!sI~6C%tRS}IBr2Sn2RAe8
zV{GTyj=2}D-(fuFx-GHo5j%}f4X@`>K0U@Ye~-1Mh$TIV-d>!6*C&pWSv+>k2!&T(
z2tTxiD3^)*S$TxKUGU?*3vfBJ?_GKv#;LXFGc8l_e%xM2A9U%-p?w`yMdFCOIOh&tuq=%;Ph_z_
zwfc3a;5-0~_eQp(UoG$Z?P+koGW(FIjpb-6d`O1Af`f%9mdu0l;_Y|%p(lwHrNN(F
zq*Y{5U^iZc+(Pza+Qg{mS<2PhlAxVSuH2Ev)46+Lq~5~PpPn0%80pyx;%}M
zy*AG5%kn>TmiG@vq&;ilQU^^I!Cf_L2BHH99B`69z=A+^~tYNC1Z
zaMw!S7xu3Cv@hG$qY)L@?Pu3~Z=4N;!wxZcMo*JJqAeurNLM1E!6_i|ST3C5SmN{(R@0SUo&Oj$5AFgv#J(Vw+XCUx=4U#7YW?ww
z552(knGwkd-<|T#Y0gvUHd)nIFtn_XQyJN*z4+XwTX3k(6qX%Rn_wj-_mlo()seKA
zojN4G;MO};qFT-^xgpE34J7W0;tgGJ!;T2n^$!be869vfU+t3++Ae0u#Se!`l51>Da61K?-Yq>%#GXE)+oCh35M!eD|Z6519V0u!)kv(DZ{%4T)>)c;q
z5+^*GV3jwz;NTuUwd$Ffm(zStg_#$G`F&Daa5<{~UNuHdbw+XqU<0J9q)
z$VHfN6%7ac8REI0Kizxwl5Ed8>yf76b>C9SJkqc2g*~<#veL6EZet^OG=yy*CwAaSr%%+05-hw6-BMImS
zheqKOSPFaM1mOT9yyLkihbhGJD-L+ddTV&Vfj9M79f#8r_j$tL&YKqNNY5yLbb5#zYw5uw7$j40#r
zo_36}Al>RKlq*wt#U<#eZN{=V;YLQhJWO}0053KWV`RnASe0ZpM=%wMFt~7YdRP!b
zQi?FYGrTv4C#fS3F)$f#)i4YzqySXcTW7(YBKwjKtkyF%p3Br;rW(KmzGm`6M1D?I
z9E({L6W6uHH6rq|Q@ppCgb5?*fw%Rjb;M^@KPHvO7``V;Facp7Q}><@h;>x>drnb|
z*S_onVi^_wmR0kjpANW2kD?(0qPcQrw_xrucs3Jfmdub9$3MlEGfQQfONHY}nJd!0
zT#vX!KrB}|j7;I)C7Lk5VN4rP$P`Nv21
z32EHZo{lf@FyZ?zz{8>&h@J~cT6}|&
zI6!s`cf9w6nud#({+adG@Q98hC1ODw_aeKthp=t|9VPpy$|dI!y6$~$O;bIU|q7$R$V5{5|ha7=LpdU8+k_B;h*
z!3Ke+c^})^fe)XUDh>kA@b*6iVi5>|=Xjqmz`?NKoqP0ejhhx-OQT?_w@B`9^A*~I
zHr+J1VGC&Itv;JK3h)#7^iQm=`4RMqud9M?G8EoOFmAf8)XW96JEsRtyqJOqU6mSm
zEJHXB9`dwI=~Xm%KwF6{to})y)E%CWfoCg#ND7Cr$UJP-yb~$#1u{)*
z=m`INs7gKzRL0GXz2x@d7Pyk(J3t%XlJI*H@_f{4XcK_xJxw`_3gmUV*66Kn8`5IBF&
zQJCl97{J|LlG_d+S}*REHH>1WMm>vGLYK;sNqSms@S!J_xc3k=qe3iE6SiC1*o^F2qK=N#l`zy~P7?#rrL_d3X!oKX1V
zr3OmlmnaQjI6Gt=++>$=(puYW&$bv#(O1+(xVzp-Os#9pW6@#?)tA1cc#9*YZ!l>0
zNO;15Q3bUNRm^W%qhEMcmSflagVxqfcI`TzWcz9K95QtC!JrIAySn+tHyqk^R1ubh
zLb_9@naXHaKPeo8YY1BMF6EW*vd(r-EV!DEx_qayHO-;Rb7Kq7Tv|Z+?x+h&V}`8d
z8y7mZ)C5^rzcy#U-uOCv?YUcrr?2CM}~tGvI!}vNMCT_D#~M?(+oa8A>kJwuT>1lg0p=3*GT@
zb#?b53%p^3Yh&6)>)WSOrJ&}1Nw(ReTVyTQL#Y&Iw%o{qtor>yRe&}owor2EFtstK
zIEc?M;1g$@QeCM*>*Pvu%i)V9ACKZoAA9WLrNB{hbIQ{ga7PGYvqK<1Y!9@-3j>4N5ko&l=whLVe6x$EF1Qas2leBXsjg_=Zya}3+_lxE^a0$Oj0r;d99
zzAa0A+NNkM|5o<|>3D&5pWE}Th}*PL`U)<9dNuV!MuA{I?BJ$lw(TAR)dr7Reg;E-
z_8!s0+u_p9Fr8RFSUTaVv;Nz{#_e~m3*BQe{k7GNrZHkWj1>6x>cz1)KjA0>I)>vvIPk^T~jiftX%&rl|
zH6=>tF=T#Ejm?M>)B>5gv9{SBcPb5&YZLI}#bEdP)1sE!anh|oN?Cm2TP_wwzNAFIkKrRam~I}){Y*WTzL8xM6os;sOd^wiFi`p6sC!OXU7w$-GLG|
zQ30Rm!Vs53qP$^B4pJX(DHL2qN|xCkjrxWf`zy(Q~OI_`V$rEkqJEpy0ZPo+E<{BiaH>t@fS2o*o2PBRg8
z))Yx!y_88s0_PXc3?6s65oeyvu4_}cswVsI%@2Wdqay4d5`nJ&3QetLwn%M!)~luu
zfsnPQ2K`)a@Xb?)u~w+gsyitRamU3Kj^OmmEC*R_7+9sMQ0GuTK--dp!Y-m;E!EoQ&PKi%U)=TifC?vJ&
z->BlrCU4rXU1i@Q@EGC@kJqU`VaU00g=dIPxv-d>F8`ciRk{!D18o)7y(0qos={HJ
z7^!t59we9{(DsYAaa^L!vsmVGMhIl_*Zp$vE~iLZhr6_OOM
zF_IyL@U!7IMWffPaMgRzwn)dFM5M_rPut`HUbo^@ATae&s(6&7oDBY#gE@&mne1L#
zVF+ThD8f};NvVEw>~gh%I^?Q}Gy8zfergTpnAX#oI~x7Fvc{VPIMvE
zkEw_-wT?=?&rQ9&4l`2S+>ZR7tys}F~X%`$erT&eIPn8=|;K|xpTeQ1O7kr%+08ri|BEj
z>!%Fw;MVclA-PSq3}Z#A!U)*PV56R=!@biJXSb}I&aPJUGndKj7g;wZ;O=4Pu?~KA~H>KFVb4yyM`XxoF
zft1$3vB)hW(-Ua1E1`am>Ucw~pls}jO&eA739yN9H$Z$=9PL_NYuJt^W3<35c}V8D
z)SndHgcpQApyy0apF)?hG%_Cpv?wey!FDBvm2q~)nS*XBdLf?|svlA|B5=8i2KA}2
zwvVY-ii{V)!oEzZT+8@oH}wY;q^^=SviMzzUKgzWmcm?oW>1r75fPbS4g=L{vu+=g
z{dbX>MH45QM-Ya!=%U>hBYvkt9(@~BUPQhOT8VK9VIOl4pCto)6FzBd1MOck)#5Ifo+|4`lVh^TL8a
zsS|<;Jt^ll5&5;PTr{D;*xm^`mAgqm&jhWSOTjy{My1)qAx6`S0wV{*3{c{F>tav!
zW~zUWgAjD&L~Vj`KGht1)X4HQbuzQ-|+fezx0(=$DyaCy0&lVJ~uy
zCl^Gqj*kf)`{m{(=<>s6agxFd(uUj4cEibu!W;lYJ``9=X~
z@+q;O3p@oo$a>7|lICQhw6e7qXjx&49+Cbb9eNd5K-Xvbo1lsaj4K%85Iu|Nd(Xiq
z*_LU@I)rk2m1L@mX&W6nick;r!P?$bnBQPsds~8V6k#AOvkCZ+vSSvJr!WVwZFPzk
zS7Ip9cz}Sf#<{zRuA0_P)FNaqMASnLEj!gXA!UzsHYep3@W=v5EoCkf_|Kj4eLG?Z!!DRSuS5T=MDFW
z5yJm7M`bI#y?s79j5-x7G|!{Wu1-CuaRgj&?VpI*^5$``3M^$YLy55D9S)HO^5Kp2
zrr3fy7h-{2{7$#M+Xbd-N;)#}RW#%R)j;QXSL5;_52~*I`oQ;N)d
z&e65OGor%74psiDHRv+ox7W&Z17}kV*Q8bR$+o4=9cqJG2*0D&jnIKC_QMXkw$Ov|
z!_m-JWkC&uU!_&-Mod+R^HEx3D1V^4m5qCD#AHk&wj>&VhWpoKu`dj9a&^IEzgT^M
zBb@WL$R&i7Qq-WaNOyCWgH
zichcvE6hSdQl;~P=V7|P{rp;M}7dU)7C_Hd>%a
zui&nQ1-~#;mw~A;>=P!Fyi)}a29=&^g$i$LeApM5q690olUK>|t`<<+kZ@$02#hHh
zawP`Nif17xGP->6!D!3De)2Zva@(arYl!Txt>BD%hNzxh+VM^hzD1}P6
z6+p~Ta079>;Z)tR!UKz`Qlz&pI)ei*XpYU7H4sL5KZn?MMtlng%uSI_aw=wA?pAN3
z)|oN{t4vU8AtYl-ujxpTR_X65l($@&Du9OrnseqUwLZE0EKZ#~$8{(XmNY5Fb{sy)
z*F0(t3eGNS<{ym97z^|XPg}3w{fKr$-`jJpn?uygtm7GmE!3Fynbbx{edW$-s$oBT
zN74xekicYVApwuR-y4Yx3aABel^}Aly?k@WgYw@GAxKqB=iwaPZFhJYEo=i4gaL6%
zP4~pjx?NZt`m>ss9JpF9d=QiInZNFWJ296ZmWcgO3!VGPcyWO%^@0Zp8Ap4`6VNXq
zESBj9MH;yOFyWr=1T)?l-Wm*HZp3zenM1CJesN*&$Ut)r@(JHReEJBUgav8tj4q$u
zRofl7RoQ!O8dd5_9F=ij!sWm{#+SFeS*e)`3YYmFeY?Gu%4{eP`nju46G%L#GVA;q;`Z*3ocEfe9!`bjwLTISOs7HYfEc1Vf~7Ru~3)^3ql^y3=Jge
zXK%oFU7BAJp}=J0Q*0pu0UUDxUE*YeMG
zkF@tYBYv0Z2j!x^L(O51%HNjQ@{wJqx)+C>?)K=HLxDb}TAYIYk|LxD&i$gxIn}k@
zeiu)7kIj?!j)B7~aXixxiZ{6l`k4=+JD&r#_=>{3r1knVBn4i_@F#lK(3{~o^x%$W
zs_T-H>h>!NDnhxeu${d8JvE4ByswIbt`X`3RKy5$6SQvHez@_p$wx35eHXfyYDKs2
zowMDO`(+Jdh!D9L(j`IXtqAFRxhwTEK#SlBgx~=1mzV!e87{pnxOoc;Ii}@wB|O9u
zj6Ur$))-5IigDs#UE4woC2=lzZN`ldM~6*D(RL{{OE4D6k@sRLwI$qk^3vZZ%?f!p
z7oGsQ9`c(<`fL0#{?%u?z0uWbW&M{N#oF5Vp=0&`Xm0R3(0rh}PUAQBw#)HbpM19e
z1S!1-b&$K$1Zl^i>RSqc%y`bnm2cBn<-b)cYzB^tJh3gKUCvTN4xgDsz
zG@2^=8eDKnmFC}bDC4N~SLv7Gt%^?Ex4H59NwAkDK!75S9pYioTz|T`vARaHs4cFu
za}u59c0^gk6wsl_tDZxZVM#Jt-eNgn4cM(CrTf0r+1~q5Al|i3deSH>43?}7RpZ9!
zYi#!F`J{Os7CzzoZ5R3}
zttln68tKhlrJvIGwtg%VM-0XoKh6HU*^FW^Q{EFPqqpfb`V>Z1rb&Vuh@p$<*{z!A{PtxPmqgR8!Lz3Ce_t<$sQ*{;$5iJub>>`}-LXP(jcv#pF_1
zS!qa-0fQiBislXEGMIp4beuYxP@Hjaa9|LXN`h|3OEI9LG8b;jNx3*MXh-Tu!|8Ws
z5>sGIi+VB!}Da(+;FS1rebU$R0Oz6|deP~=r$H|NxP!iAzkWS&cOFf{}n|5|3
zE1KqM+9zAQY)?^7$jIV7sp-rCr1Y}+)f^PIH|pAPC%|FBy;8Xc6&I}?YQ4hWtIZd-
zauu4U=5e~Y>h?^c^G+*_m3eN79{8}@OAys2Mz(Sm840V1m3zP*?lH!+jmK9SDa>R+(;>K3)2j>
zjqnAv&B7bF5~VxXkT3V@Sf%QTHTN{t+PCGdF{P4YB@f2`7L*mbRF%Q?#&-ve(|ux=
zG!xwv(^5vKG7oP-7pRF`Z%p@sKhWjuwio2?H9Ir9>85b%Dx)JWilI^NPWArF+{5UW
znQxFcA@38Df>;;2N+si}V!NLnqm%qaz6Yhbm@hCYs39~Eq3cvTx$NquRcr}i>izuh
zr);6l`!!L_w=!2Z)Q;($aZ)(IkuPPO>YOek{bPGY?lMy<^I%*HhY1xV(PnrV`uRp@
zQm(KGw&e@m=W5y?PK*qXqt#;po1ta%hkXbX48Q8A|yqL0<%+
z0e4?jeef?+&y>|C{hlC^(sxcCoG4Ny4@Mz2qsu%Xup|?<(f@^T_FQed&!H9JD_B>h
zw(Odd!n1v8Qh%=RMdhn{0qyqPy8gGJROYTUrLs!1tGlL=up=Ye@HHRCtrewl#JP69
zpjaJ|A$GD&WgG#c4WHrmm~D_$
z2tY`)FTh83ejQ)P-Ky%^8M2`>2TSa5k7;LUyrMVqh@T%ZCB0cLNnEOm&3w6Yx-!{3
zB48;t%E9NO6hObv^P^LeKP)c$uqMYAd|(9^b`^t1=o{aq4_l+`oC>*WlH^j$MsQ
zK$_NKb#FKohb<^{gW?W+dLNOMAjnXkMGCdMdqn0=0pYjl#~KD`bYZ>y{JQOmbFkKp
zl}COZ(kIARGQ7fnv+rLf6?M0MxvGr1drj@tM>Y2X%V+YWPPFdcyAar5M~D1ewHU~bq3hqfb3?I
zOZ|L2Tt#i#E(sYEG;Dw%gw&BK{N`N11%iH^jNI#c=9v{vht}vX(Kb&V0h;xf2K&9-
z{pKyg8_vv6T
zGrASwPcpiQUA
z5}lXr0MXI^mDBjjy(G#pzO%}VS6FwWy(Yo__vnbyOQ^flb>?p~^4Y`|m|wpU`Z{chPu
z{iYHQKwcnfAAzgsC*-l4qoma|Y%u3!r|q|LH>uqw^d`tWIz;+3syB}B6(k+)TFVLlvYEbZEU^9?>c{rFLb&m((kWnXt+SD4fa$ft+u(QJOQn+Vog^Bi7V(!50a;D
z4_Osd@jBJltm3179tfp<+LB}&c@73h&HD{HE{%Wok91H9ycQAN`p&tGu31>doB%e@
z^IE?5G-efe^&`y|zqle70t?d5$ejI+IFV}MkOl`boa{xU8Fv*t=h<9F49Zn1LB2J31f3YPe1GfXD@B5Q&sf9#3Nlx
zzKia85KQx?5hOE_n1X-~IWfh}v{WEc!*geSX-
zIrB%A^N^*2{If)dXbz8C)m@>92oxEoqe(z?-P*7TeJ8tK`fh#CLzqjmX!;Wmsakx?
zs`WR`5fEN)S)<1#1;+WePWHZlixIDqw}g6uZ*4UBywv$lOuT%@tc$h
zwBSBnW5W%oLeJ)rl}mK7s;fk|tITgAp%0wR^E3>pba?3vNTI&_f05lUPVTm0lWH^V
z1L8Hd9W537cGfzbv3I<61YBUqD2}5Tu_?%4d{&I$9M)NfN$1?Hc5_OJ8F0mr!HAl*
zHg3Q#5upTuWeo?3cYuwuXvm0n1p8sgusL~@<1*`tqg~G&TGP3bh5{N}1Pw(npR1IZ
zRRXo_L#&p4|1!_!+N!&rKD4qkipT&x{D;h>ATo*v*%sEPmzRlWNM};Z(2Tn@nKr3g
z1tl>9F4VHPo`*^Qq{xZ?O-3Ccor1_<4GhNWC*elXHM?h-!il*kk+f;X+)Pqs>c%RKmDtBC^@|ypK&2)0%pQc3GqSqtQMK8Z6}C?G%XndfD?6uA
zVTdBJNNj64l{^Q2lp+!m(A1fb*emz3%*A;w-uI@5)R^09+9-f_llw|%I57i`3C4_>
z{M(3W80NqX0`f>_MMB#!a;0EjtgRMarTuAedH7VO0YWIS(k`2>3JPKeq`a{5VB)ayUJCc@JKQjngv`zP(k=?(nNTOv
zKSyRRH4pG!RQu*vZ6SsnvkFHL(jFy$O;TCc0~Q2r7zlB~U>T!(?v3+}DZErI><`PU
zwv*gtBx58rowoNv;jj0rCH=X}wDGIM{59tR^;ws?<@zURzolxyf~am*?P6q?IHx)L
zA)%_hI9lJAWh*oQGN;}apD+%M_x4^{uM_Jn98SonzEyeIJR|@#{&%YOfYAE$qSZP-
zH(2f*EquZn0~7S2uxUDJ>v#=wgc|&BzmxGr&PX#sJ;gs4krFa9Nk5akhO`i12j$R0
zSYW!rN!_UV6M+}es4Gj&VFH;ti`56C>Stst@N+XJ^!QVKgfUZ`kh0o5#EEpBAG{pJ
zajQI6zPuzZjD1lh9QqmAXz)e6#PLN@gFDS&r;!A!6A5wB27wfmRz>6>ekddzU$Ctq
zM>oW^X}X>y6D<9&4Xao)b^S$Cly91E7>K!G-&6uk1DoaL+`z&tb=H?)%F<53EsoU~
zEAeA(+$5c=l$x?!Xjwxr6VZjw8{Ed{9k?Av9Hv-DrOnjnQmzT}f+T&H);LVC8kfLI
zxlag;Qxb8^o}tK+wJ|bZlA|OrV1X&aS-V1UsWY{UVrwaSrUufmPumbittVV(n-gnV
zbdL{f8jK!8HlLgab%-IP7_RHxe1v$q_|LY;8nV}Em!$PWD$1?ny+H(LvMS?cPL15Z
zw5_>>RMW7M+cbTj{8!x8UPSf1Gq
zf6$#CUKZw3q|R!gexPgOd1AS3MGb*d#9xx>-bcH>KRo!&opi}2V1#q2D+I3Ia#>yN
zH;ICfSy$d~27d;lS3eFY5*Iy?@ggg-#=BqNPS9@ibGZbWPv4u(_KpB5DjF?OW9PFyWF
zrv*T}6V0lWc$piEZh%!Tkh-x(CUnl~GKT{4E}F1ao8eAs0^4TWDx1m&Q&e79s$@^o
z^lI5*cz{1D4wr6WyhJFjoARz%_7F;IspoHz;!bn5!z=Nn@CPeS80WFpo#j0Rfz9@+
zAGS(g41pAa9GiuPLJB^5!iYM8kYYqsj
zvGtRGL{PKO-o#3SWTMh&8PVuLHQ9~WY!|g`D(P8Rw6YoS%(alCh1#}Jnon%bj6~~T
zlVk5E!N66NHPw=z&KM(6QBR>&oj+iBovW7xU16W3XcGpS{GoF-o~{=eMjW;4?)>viY8nff|?S0
zFZH4W8!q*tYQ5||;t7=E7AVb5J=y`Uss!0&$Q0R3>P8d4jk=M3qG27-D2|j}Qkp|z
z^$O$;i)A<|^#l6AXVed!S@i=_`CBI0#DUP(*{a1eCm4^u7ccYMYRH=fxkCOmV&6J#
zR31ghQ-mGi$Si7w1_OnW8E_pcJ>%a?;ogMnryr2_lbX*ZZd4TA)(ibflcI@7jz{FR
zH%#YS#4z4-YfYcK;h76Sf?~xFT1wPm0UB!pQ#Nz7uu-X;-z;
zjofm?LbzlZ3y;fRAk={Dqn2cTJ3EzdBTE{Q2zZKE*c&&npIA84bfM)Ozs+e#C-cNJ
zoQgKd9wruk#$O{XFdgPFvGCjxKTopWJ2vSvm??ehMAKzrArPtq#KLKO39ERwJcP_o
zq!GW^g2o7HO6}WkVC)j%2B3;*AEzF}AQZ!1%vG3&TxGg2R|x0E4=`!OzS0Ehu{WY~
zh}8?Ut$7sXedA}?lvWPiGa+n$wQ+BHP!Y)YTzLpuwV_;4qXH`X;@%#K2K)>5TfdVM!O=UGOLgp
zh;d>Kgn%Wq_YaCfoL`9dPk!N3W?L3v{8GgBNPrx20g+z
z2Yg8$B=L)g)1;@ddd+iKy|nW!*EG5sX7NLXG!xPPPwOKS;ADJR}`Q
zENFxeEmbY0e!x*hu5rR6$nwS2B8_73iYq?RG(gfyE_J2iC^mno%H@7>a=z1193q~G
zEpM3brcy5wWFJy5_M1{2tqcu8ll;%NE-Y?{o9yTWIENS!?=%%BBl;yr&qYII6JDvze=
zj7xWxw*bWAjx@R7MISC|`$;Lh;KBR-7g>ref{|I)DExoNas!z10nGvE@;9loui4aW
zodYx5O<0#n`!-094P_D${d|_S(B(IFUx$FG{enLO8xKBXsiqmAl!g7IYJ+~zQgwHn;5ma
zDAha~HSF9U6JtI%-8fI8tx}%np!=)6kkKKMWSkC=H@AI#kvpBV!1WXiegqhi^)Y%v
z5s0*D9g68(MFX-u&dY;jF9oKuo@B4@{www5vZj`)X$5==>&XXvF2Sa`r}%Fi1>uzNaCxTG3$$fk)&^6+g&Wr#(_KbQEYj67
zJqva?O`E{z@DSRA(?5`}axiIqw1ls8hA(EIapwU}&5)H%zaZJn&q`1Td*P3Wc^O1)G;b1OWd~^90K_NHBE{k>3ARQoPu77}Q|NVw
zmT@{bOBfxLH#j}kIOsb0Mxt9Hs!LEw5&`qjkW4f?e;L8gcee7+I;ftqsVS>()M^Jcl$nhe(5jI-6#o?WADXr$|v+dpy-2!1B6xXkzh
zA@>eaoi&9>5%YAPw9+_X@h8)^i}DMA01$}Ij>S5ku2OXe)b)_NJI6+n@9q^YDn4oqtw8%2|F*<+GcOJtsOJ6w
zixB|Qe@qwxb_**IFybJO@BWsYJ^_5jeexvL{#bKQ1R@gpcMD%2gRbIa?|cV2VhV{I
z?q5beS6WJ?gFHo(BP)q>+Xu&Oe!MA}-2MWD#5aBhSJ8$IiXFt2TgQ8u+YJt@tu|}>
zbiO9NMA#qL*ign+TH-Q=q`yu$Pp55Xk{s~zPlzmA3qS3AKEtv^8XXd=$~pw$#73y%
zc+=UPYgGr$rXDhmFW?YMKbBOvrakLL+s;0v@bO}uF@n%w*7Stf?#@N!#!Ij8wM<$A
z3#X=I_Jo(&S7%21K1fFOmKYfY%eZm#wAQo|kz@-(8VN+GDQmb)GixPZTe3m%;-Vv-
zlO(!+uP++%98KS#C?aQ9t*)I3M$C(!5Pz{2p6y$F|8+NMoP
z9499)8Do0L5=D3aXTsnzb8|3BW_X82ns0SX+VObk$1k_kk7|12YjuZLU+le<@BCO)
zdh8dG-UH?cuKbI^}yOy7!5ugsIZ6N^doS7@`H`|5>ZQJUs?a1VIQ(+n9E(ianA+IJ?{
zlIo9ox-5#7P3YA$3M6c@>beEnVtXWMwt?3Ju;>>`d+Mu=7oXun+qEV0WTqCSgfGM?
zz_o@eSe9JZ5s|1zvcM+$-^3Lq&l0f+kwQ72SJ?HC>Mi7DeYfcg4&0
z!6Mwpnx2Bh${kUvvM4_70ZiUSC{<@&K>XVU#&WBs+Y_M=
z-#%*#-b_9HlXR4N`e%M;_3@I0>#*e4v5>w4j-C#ft!{T>dVa=$iiw5KtvcGavIJ|JFv`peb)BIi5ypTU!pB?IsChx=dlo!W7%*XuGO5*SZ9s#iaeYvH4xFJKjJR!C{E8cc(
z=MvusSgG*xAkLgq4x6l#?>rHfWIlYwrB^2tw3}ybKuhDz89gr26=Ge((gN7ibRY^x
zH@ausTDQIKv!LhQ7m2J+CAy%~$K*fhv^~cC{1HQfG~XMBD8l=kxx$#{=Q>Kyr9Tw4
z<)O@Vdaq2d7k#R(C<$Mn&2u6=Gm&3}OvAohda+&AGV=P~c_Gh~8-{oVYiwNmu0Pps
z?Of_Rqho33oQsIoWR1OHwobV-cTT}J>n-qA3pF`b8XKWsaWhKErRugoXBAu
z4jz76F}{b+A8^Wkv(>9`&h!FtyTWCg$T|VS{dI6vl+a${Wp2q0H!FVdkdl~I!V#JbtgQh`ykZ7s_931a#Y^IpPif@ZtHUDrQ)E6
zjTI;0qg!J5?YHqED=um2@D>4Xr4t!aZH2H=)+UJBbELL=p(}jnhrH7HtRH|wR-n{m
zongVJrkr~Syo7!2iN!DI#SGy
zt-9{#4Vk=t0@lVPsupegHunkBqj_a!-;K%PIcx;0sA42
za&27xl)?nrHq0gf7@pjrsNpKX#c1nemU{WRz7pYrpI29wES{@#c8k)5oq^n8ooG9e
zeU-zdxQL#Yxqzdm(Se^0@9>6}S$s+5v63LRa5J>sA(7=>iw#@(xL;>Y&IY
zp1#FLd#~K$@3M6mSZ!B1(_OjeaLenta-;hu-lw9fWbskquJnBkl3H!gWX9cl{D5bA
zmAr?m)(udVyNw076ZMo($*43vlNi;*e6SZB&E4Zv=MM_qem?&@&z;}ESABD=WDaZC
zR!w(=;Wi)ce6Ff%<~dlCV|C`K^Cn6(jXS_Www4lOhh3ctW34mc6l=05q+5vT$rdlE
z_x1VG%T?Nv*{rxuEz4
zuWA|M+xjYv=Xw5a&=KLM=_eb~L^17jkdp)S(EJ|6TaFTkbb5iL!xee
zj%|O21Y6eRLEHmukno)?P|Er?swVw=x3B3@DkyCI@LOyX=I%r{Ym{lW2rfco^`d^@
zxXO{4w~l*0k7BNM@m4nP09JM>1QTb=S)#m-%Rx{{(t$$h=BW)mw6*j=EZRWsJ4-dCz_7tPy5-Y_}4b*oCWIa
zqLyvQGjP=uw&yqAym@@{CHU+Q=Dx-Io{!*ti0I#W
ziOHPzVZWA$!h62XT-|5(w_Fifm2h#)%_rX|p-QSGbNV39yE^QC?J2B#eSSo3}vfY02TA=K_oD
z!BaR~{u?|y2{9)3Nkozf;hQ6+Pjhum_Ol+G`KJuFrSEsf6x|L}gCs9}6-+QkIaMTH-
zU{U;aYH!vag?Z->*wS}tGPnhr{Z58KzWW(YISXm@y%95k
z3`PN)7fTNyC&=`_?A@<%cQ(}V-A{4K!=|(_?T+;s
zb&IHZzyc*5zJ*5CP^mP5B<72{RTCo1M{r3D?5p0qT2r#~ofzu_z&GB%Y~FBvmCCte
zS@PqYQ8ao7cg&s49&Py+H{P&z;L6rt`3h>sEp5Pq$lZ{W!67WO+4=fY(zC3dVe{c1
z!lfxx&koVS$;ulz3d3Dg$B0YnR
zb!L-!gEH2%%vs!MKZ_g+L;Tstb|I{$rbvD;y;!($Y=1Cl`169kdV4TPdidZu=gNBB
z|44#M!VSOR*6lvbG-nPW3TaW9%Q~mCiV~$wnVVFzX#7?uM{p^k(JeSl*kqqFsfgU#
zU~OT1&;E8(J+XF$*=2&I-hS4JQ?{F|E9%O7U0FR9!cDtQ8mFM1$f|%xxdC(YG@PT4
zZJv8Q4bFWULFkD;kSqgd_r)BXrtIdsk%{F_O!K_!o1{o)4w4(@SvSVEihb*6J^(?P
zhWs^9*bhd_IGQFQDXQm4YNknNh(n}L3;H4j+@bhk{$nTPc)81Q
z#k%aA=i%>X!xzdIpMt=pvALwaG`VKC{IWCmPj&ZEfb=gieiRBN+eDIg(NN2J{=8FL
zMb~YP`x`&-R6h=U-74#PqPX1nFbT(vDwn$B4Ru_QRu;x_sp{?%Z3t&)Wt0fFj+b$o
z|J|wWc-Jo+S8yV`^c}eOSa=tI2hnafDk!@9dpwGdVx
zHAFmF`jMcjqU$;esm!i^2VQ1YNQQamcp)ca4$iW?&v#DY3=2)xgev4Ds!7&Ig=|uV
zQX$xM4RQQS&PglvwF0j0fPMHMgE;g{f~4PkI2DqDT5>XO=4=SNWGcjLO}vO`WfE7#
z)kdEocnz>2i(i%23zjEXaZ6!UWp?}XCBjb(J8XVOafWf&wBZM^vp887hHPE`xnK{w%6srP#Zek`Bs%mp5_D^}qEAhi_d1XDLjO9p`i&o2uI
zGfd=@JvhWT%K~sT@O~Xw3PMKq}zIF#WItEwk!@S|KD@3w1@>%3)4muf1coot_;li?3zyi}qWeMCT
z%=bYiDT3~M)d!ss_LmHjvnK27z+aqqo$T^0IBI;f757I|MQ*%{pk|qVDYAZiWY@Pr
z9b@`YsygGVxA|?iq0(61ldt4<9qUp-LcDM9Y=auk!Gd$Wp|1Vv6mFA-_GVdTJI!TA
z>4Q*;uWjh?WmBktv^I?4M9>KHaK&ZDS;Uqd(F+~`TN|fxTw!!`EsRBf
z(v-b%cF-8pQ$WXw!!!pc)tr&Lp|v0J19!B1Jq)}7msD-W*E{(N>a$p4TQB%7&KiNN
zB1?yRQ`Us1k+v#cEe$|$*Fq>}0-qx(^AX{oLJARyw!0hXk_VX!1@P+iXlgG4l*Sq9
zOD@P?^DD<4iEh3CC~Rit8vQ(PIGBxaL{dxKbO${G&&pc`9PaKZA|S5sGr1`6jIfl%
z2~;SYIA8Fvc&%+ag#0v<^>^TguU28dIYJ>UsE|Fp%Y>v@eIwEgP3C1ouv?t`bg~JTqkBGGt_mZTcpOQ2
z+3>8Zl|B)Y+{?RV7~OoAmy5h*g0sczdJD
zUHpjcB_}d~ks0K)J$$iN9$-+|vMo5-@;~Z~R+={g^ZrTRvHH)E>(JHKnEirn5n#Vg
zgUxjKM69F;IxLr`Jvcq^fPAiiGpyzZhM(d?xwGcRTsZAkK^zcYT}2C?7JJau!OB@?
z>pfg`o!iapA>isyz=cg^%#2>}%#nEOBgmq%VYsW>)a^T1+2x*u0A4Kix=N!<`lGh<
zP7m8~<{No@tRuDN?%8B9B*Bpfr+Ph6JZJkKCu6)78F>;{4v#EeHLL>gr7{5AJRy>N
zIK*$`X2vZD04h{Pg_uX33lnlSSvKmu>JYF?E18-aP>~`woZ_L_B2mJqm(PijD#Ka1
ze>x74&VtAQyeUXZ(toK;MI;>>Yh5fG4$oa+3$WOEk01z)Ya)sBp-BB#&|9)VFqkm9
zxMS6@Z|=@JlPZO+1!cy^po()(#w-XDFSb#>%52@}+->nWi!Ju7J>&4?5=5$Lj-WGw--DAB(fDRG~L~jBz+OUtsBgVOH*+G5h;2?
z@<$&IEU#_+M$Q)mVRge2aE2+dcOpQ0Pv|0-a;6-*v_Z8cS$c66!~>O`9;^4+yh94n
zpxih5aJnLDzs>5Znfa@U*cp?%)Xn2a4Gt@FHZl|
z<`<>q`1rX&ygnt{bp0ctl@>fK3_<2f{rt_Y(jvURJ=iR`aaC8|_rB1Ygou6i^%?ow
z=Mk-lUSIri)V1KvqAc!QnQ;*%j{YaPYHdb4LUDeiwta3|
z^>_|rBukT((au{R8GNC1QcebH8?by03-7iIdcuB>;Mz@hd*Gx0
zJxMi9nQUJ5&~ijBVwwevhFTUyb-P&(H#jFnsp3;&dnQ<>+TPiV_!6vs&k>p7wz1(M
zL%7t9M93moV3ugx$9oYHjn@Hq{hYCBCcGM+>ClegH<}LJ`_c>}JYiTV2S@#CO8M>B
zP_jsP{0Q)lTN}S1IpkGj{o%yfBUyk*f8$2tWw{9Sy9A4edz}B#JqzbAFyf06x;-uH
zr2cg{!gmh2Q6se}=gA(xD?owxEJ3#WS8Xl(%;RXv#Xb%4J>#9i*2!(xWVJvDup0Np
zF4&R@9}wPIn(W)T0aK?1;fKEi`-9Fq^Mz}%G>5U|nr0j3NEg@PWWc#9<7B9^%ckUL
zhwzHKChDGI`KZA^3Gw@6by=S0_fC=0w1i;%TmV1N9{2%pYhPi*
zf;I%N5nLLmlAp?Oq=aPEk*$KXE=-qLU77NG5<e(KyR&B2Hpj@+}?
z61qJsu~HeiY`S85T*NDFze^x?AOTpB+mOQ)4j0zS!NrS9=4sh|lhkkW1+Xhmh%!ii9%cKM58zl96(m6-~ZSo&0cQ=rS2xh%UPOd)jK3pVhkfRA
zd?a_nlJHb2GTip8pfGyieZ&y4&q7uX=LKP(rXnT$=S~DXBYNi>QqZPqbM{mnDS@>G
z9LQdUR8K>HN8Ai6(?dP-_A};g2ozXPic}>K+|(&j<6WSDT)^>>Z@-TnrljxK34+~1
zmUaf=&`dGxJc0Omo7dG+J`L}<;_{}Yh}LR4h0L|^oI?8Gx2q!J^aXl|u=@WzJRVU)
z0M#i3y^}3yeGbMo*2T^&pI5HG&-b$DD52-!c)Fo9S_YFg^i?>ag3R3y(;M%X6%~8tsk0`Js$q&q_raId2tblv;Yy{a
zt|P4Id^19j;V^{Dugcu*Pp?8^6d&$^*q=PAZG`RV130{&^b`8IX}vxq!FnBHg&c?Y
z%x`5l)G2UXV*7Sl6;f{8)ATySL-IE`90{DIciZCpXI;lhtn={Rl8XODGLn8#V9U4W655ZyiquP5Wa%+$K{5K>Rn
zo#=G|+Vi*F@{6L_cGi-?YJ1|uAkB~m&;-PiAArO8E_{^qnc;U_PuYfJP>a?N>kdnd!f`{I4MvDR^;EO#)$e^C68
z^z!w1si)pH%A9s-
zdp%pV{a?VXQmbRP=yHv^qV0w>TZ-~oek2_j4Y^v;Z~ClegW`3j@NC0L3K(Xu#)S8S
zlf-;wktu&6xw}Z{y^YT>!JE)I`mFD?{g5m9hRCG}*|`nNLbSe}&2T?lAIKCj@{Gb5
zuGE$cq2si44GV1FdLYx&0)&Jd42e?J!Zo`oCY;UwbniXx+5y*FIN%3U=nR~OsE-7V
z;TW#zBRdwRZN%GvL&)=o*VK;rNZ8Jd?OO=0rq9UVrtM*$-Q=CU2{yJ&lvanVQ+!$!pUT!lM@T)p(!
z%mjbngXS=oLIgm=kb)zOyl1p7EU)o-GisXU)i4gT-)<6S{G1FI7yBs2|1?vg3ln4o
zIQWVWq8uyfScF(Gq#KwF&(*9CgtDKzCi1igae->wTHsUG^(=XzVBj3hDpNBS8I*k4
zL#z@wp1y@$14k5rT=*Fx`BcX%2xMe7qE|n&b)KUxiZ?XzDkq6{XgTl%K-L6((non}
zuXuot3Uby&;T6D)@mJ2cvw=V^a%&>7(d|m36W-ECQ*~Z(grU$0GFU8k6C}BSmFUex
zbL-RM&*k}%$f-zZ!C9d@VKn&RrtGzBn1BU82jc?%qVbR3HRV5T39|JnA<*F){}4C^
zd4K3DCtJ+q%uV`W@dT+C*(|UYTL4hdtB?(f51qPVtsaCw6JP+l7``vo;ki1m^~gc0
z5R|DKzk+A}cOVu7vEYYH2zd{Zvr_!H)PEL|y2C_mK{M%HaYe)ma*H}GM;4RYro%Vk
zTtz#HQOfD&)~DD?0Afx!6|9y#0$lJ*KW6uKxtCyd@kX%hn4vM=fwgh|WlETCu%ENO
zuH(181Fyq)OWxyCA0!)rbXqe1VNF?pT~Lw8^fAZ`xwly~sE$T0ciXJoS!RP{cU
zs_Xw6P73l1(PwitUNJRD7?6&Wq+}12=z`9VvnvKy<{`>69~%#Wb)+$q?TxPh1f)Jl
z0+DkZ)4ww&L$Cy)*G#6U1>C*}F3`)BMEpM#j>>iaBd<1(1
z{gqx3(1EY$)so8IN7@jLn1Q@iF
zdBeX5^DTuNa;(#|o=71(6yNz4a%~-MH(wcVAYIZzj{P>c#t@g&2Bn3-1uF2UJ;a5K
z4J5h$22ny#b^OQMM>n>l@Bu9t(jXj4Ay|356jo~#2ZL3KH6ixP0Y
zCKwry1*ByX-iuH;dgUFbsCz8}C*B%W%T-zAQzEk-zrY&Y_UXg>INu3X35N(&B@_fd
zak@>gABLOzE{#k;Y9#rGux{y{tH{5m4jH8k2jw#n&sG>K*-vN$e(BR}kM6&XD?MoA
zHu+k}+XD=MUd_a|;SI>aa#p^8J`za^#y@(_wq!pLmI;JY+$-x9wBkGg1j)(`B?FooVAgB`!5B!%TQxE_?3O^r?n8f+mX6tachJsIs^Pmu-$
zfhwc;>~!qhWNPD;a-}IhfXpjW1z@Gm!9F{iAO^e>KW00%pQ^TMWUgd`!ly7^-DRh?x8gTn8&QzFBzz75X1SHT=eN3`ug2O@Q)iES
z@tFKcwj0Bad>Nh$h(ETUjmG%@$CH2RmXr=VH?(2?WS(GkQBXy0)_}6PXTs7?_Sh+Z6n8i
z`Z;+!&K;FJhmDeAZ|L2vi@8%mUOBZ1S6_WVdn*A4_*nuA|7`Fp{Qil+&lQeX=1yZ=mMkM=^O
z0X;(i2rHC(&mKvE)dXnWrz_Wb^~280Cm0K2nMnq2ZH)g-G9&A8RR_IIL(3GfUa*ttWa6Rp$lfS
z?@vI2Ym-%vvWGK7{{JBGk9IEvM=*s0in(*>_+|(B&e~aX$R6kkL$3`0FYO2H>-onb
z-o1cBLP5wkc?$sx?BR4>ktGn>Dky3hTY#fTY;3`8$P`MOS~fqQ6&ZNg-Zus2oRPLO
z$aePi>LU?5E_l#oO8Fq_=4=ks19OOEy{$ZtpGb@mUZ=(LUP*x$%4$LjH@h-=HL#5jgE+g!jx*8&$ZgwD%Oqnq3n`R2%
zgB|NX4-*w%?pxovnBp&BHLw+-Tw-?HhTy3@f>0MppL8^c2oXKT9>SC8c$lR43I}46
z^C*ga{9&EaerO5;DqRUKJ;birVj9Mh*@Z}AL^0-Y1F*KUiB1;tF2s<)ieMX;VVTM8
zEea4G#K8p^0*FL`)N$7oeP0vb`6$I%-(XiEVY>X?l>Zh{jR#*lD78gb3Flw|;W!XS
z58Y(0~`~#hd
zqo=UYJ7{7tycKvjTNO|nzWp43JYzt71
zs=a=qPzywN&W7kAJcQ#$WBtDgg(DiVoWZ0MvxnK5q8=Y)f2IIN(xKVIW>aeh$xpcjW+<}nIh2nBaifM(9R8^}EidgWln1lwYyM`B8hOelLn7dz
zJu*`xW52S}wD?YSbk5SmWPMDgNbeZYj)dV)M&oS*h&v72yUv89=J+XpGcO*>S)b$I
zu}mL77^i=fl^U{kxPMe<-)IQnE0OYnZb(A+zny?o17E}-)rMnNy;fK{QnN2+$=h
zWfJ9o|MmYW^S^7Lzy8yf|8Cj;{g;_v!@A8yo-CB6pFq$s`+cZM^-a=@ae#ZBPeh(_
zhev?p>V@C8jB|^NtSa1r?7T}IpVbryyISVr0vz^3c8g56QuTmivcPxc?02rL%b`wk
z^UDh}?~gB6b6j|{Wgf2|`|vb>$gNt%MPIz$p(S!1ulnADb;-7Mi79p5Q$n5KP*8%F%tJFYM;KeG2klVd;TLSFTqP@iviz>Az^
z1|Rs5Uv-tj8SfmhJ47dE`HmNVHH!~(EC_C7
zn)2>yMYDs_FjE@&`Z`;uLlAH|G5IgwiS3^6c!^N;=~to@x-n8OUj3DwpY2#|TuQXt
z^6%ohpBN+ECw$>_iQTcw(q+`AK9ZWwI#gh|?+Oe5&Ced=Tfq9(D4~rm`=Xz->Cunt
z`K+-qb=BrQo?()WwRClK{>qD5>ljZDsXad}i0x+JDBa4t^af`onQ=e?tjfo7dBhvT9yD18P$4kQ&{;RJ?&B0@B
z+omm4-P1LVb=bm52hTOyhaE2wStR&(6;Ia}(a$r!^`BuHhf@mDJy%#zQkA{Q@y@cG
zkCbce6ib1h@^fwKFDv=+**`URaa{9w_5S?34Q2N6milS)_yAruHZ+;9_=-plI?6O`
z3`!spC=G(@3dg!F*-^xtg4kep=B&PNI
zdFF113BidcNzW-n!<}P=9*B4Al$g86SS+3`wJ45{1dP4Bs?D>e*~TU+7xMnkV+~7x
tq-AD@c>$
Date: Fri, 1 Mar 2024 16:07:29 +0100
Subject: [PATCH 12/45] updated description to better reflect sensor's
functionality (#57)
---
.../Resources/Localization/Languages.Designer.cs | 2 +-
.../Resources/Localization/Languages.de.resx | 10 +++++-----
.../Resources/Localization/Languages.en.resx | 10 +++++-----
.../Resources/Localization/Languages.es.resx | 10 +++++-----
.../Resources/Localization/Languages.fr.resx | 10 +++++-----
.../Resources/Localization/Languages.nl.resx | 10 +++++-----
.../Resources/Localization/Languages.pl.resx | 10 +++++-----
.../Resources/Localization/Languages.pt-br.resx | 10 +++++-----
.../HASS.Agent/Resources/Localization/Languages.resx | 2 +-
.../Resources/Localization/Languages.ru.resx | 10 +++++-----
.../Resources/Localization/Languages.sl.resx | 10 +++++-----
.../Resources/Localization/Languages.tr.resx | 8 +++++---
12 files changed, 52 insertions(+), 50 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index b9cc36b9..eb72a3ef 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -6034,7 +6034,7 @@ internal static string SensorsManager_MicrophoneActiveSensorDescription {
}
///
- /// Looks up a localized string similar to Provides the name of the process that's currently using the microphone.
+ /// Looks up a localized string similar to Provides the number of the processes that currently use the microphone - additionally provides name of the processes in the sensor's attributes.
///
///Note: if used in the satellite service, it won't detect userspace applications..
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index e7b8bf5b..65f05b22 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3252,11 +3252,6 @@ Zeigt nur Geräte an, die seit dem letzten Bericht gesehen wurden, d. h. Wenn de
Stelle sicher, dass die Ortungsdienste von Windows aktiviert sind!
Je nach Windows-Version finden Sie diese in der neuen Systemsteuerung -> „Datenschutz und Sicherheit“ -> „Standort“.
-
-
- Stellt den Namen des Prozesses bereit, der das Mikrofon derzeit verwendet.
-
-Hinweis: Bei Verwendung im Satellitenservice werden keine Userspace-Anwendungen erkannt.
Zeigt die letzte Änderung des Monitor-Energiezustands:
@@ -3494,4 +3489,9 @@ Als Payload benötigen Sie den Namen des Audiogeräts.
Verwenden Sie ein modernes Taskleistensymbol
+
+ Gibt die Anzahl der Prozesse an, die derzeit das Mikrofon verwenden. Zusätzlich werden die Namen der Prozesse in den Attributen des Sensors angegeben.
+
+Hinweis: Bei Verwendung im Satellitendienst werden keine Userspace-Anwendungen erkannt.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 859e0535..35b55d94 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3130,11 +3130,6 @@ Only shows devices that were seen since the last report, ie. when the sensor pub
Make sure Windows' location services are enabled!
Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'.
-
-
- Provides the name of the process that's currently using the microphone.
-
-Note: if used in the satellite service, it won't detect userspace applications.
Provides the last monitor power state change:
@@ -3371,4 +3366,9 @@ Requires audio device name as a payload.
Use modern tray icon
+
+ Provides the number of the processes that currently use the microphone - additionally provides name of the processes in the sensor's attributes.
+
+Note: if used in the satellite service, it won't detect userspace applications.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 1c58dc96..b94b7fbd 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3129,11 +3129,6 @@ Sólo muestra los dispositivos que fueron vistos desde el último informe, es de
Asegúrese de que los servicios de localización de Windows están activados.
Dependiendo de su versión de Windows, esto se puede encontrar en el nuevo panel de control -> 'privacidad y seguridad' -> 'ubicación'.
-
-
- Proporciona el nombre del proceso que está usando actualmente el micrófono.
-
-Nota: si se usa en el servicio de satélite, no detectará las aplicaciones del espacio de usuario.
Proporciona el último cambio de estado de energía del monitor:
@@ -3370,4 +3365,9 @@ Necesita el nombre del dispositivo de audio como carga útil.
Utilice el icono de bandeja moderno
+
+ Proporciona el número de procesos que utilizan actualmente el micrófono; además, proporciona el nombre de los procesos en los atributos del sensor.
+
+Nota: si se usa en el servicio satelital, no detectará aplicaciones del espacio de usuario.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 9570fa4f..6974fc0d 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3162,11 +3162,6 @@ Affiche uniquement les appareils qui ont été vus depuis le dernier rapport, c'
Assurez-vous que les services de localisation de Windows sont activés !
Selon votre version de Windows, cela peut être trouvé dans le nouveau panneau de configuration -> 'confidentialité et sécurité' -> 'emplacement'.
-
-
- Provides the name of the process that's currently using the microphone.
-
-Note: if used in the satellite service, it won't detect userspace applications.
Provides the last monitor power state change:
@@ -3403,4 +3398,9 @@ Vous avez besoin du nom du périphérique audio comme charge utile.
Utiliser l'icône de la barre d'état moderne
+
+ Fournit le nombre de processus qui utilisent actuellement le microphone - fournit en outre le nom des processus dans les attributs du capteur.
+
+Remarque : s'il est utilisé dans le service satellite, il ne détectera pas les applications de l'espace utilisateur.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 9a8ec312..b3718a8f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3151,11 +3151,6 @@ Toont alleen apparaten die zijn gezien sinds het laatste rapport, oftewel, zodra
Verzeker dat Windows' localisatieservices ingeschakeld zijn!
Afhankelijk van je Windows versie, kan dit gevonden worden in het nieuwe configuratiescherm -> 'privacy en beveiliging' -> 'locatie'.
-
-
- Geeft de naam van het proces dat momenteel de microfoon gebruikt.
-
-Notitie: als hij in de satellietservice gebruikt wordt, zal hij geen gebruikerapplicaties detecteren.
Geeft de laatste beeldscherm energiemodus status verandering:
@@ -3391,4 +3386,9 @@ U hebt de naam van het audioapparaat nodig als payload.
Gebruik het moderne ladepictogram
+
+ Geeft het aantal processen weer die momenteel de microfoon gebruiken - geeft bovendien de naam van de processen in de attributen van de sensor.
+
+Opmerking: indien gebruikt in de satellietdienst, zal het geen gebruikersruimtetoepassingen detecteren.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 5e45586f..c0aa8938 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3239,11 +3239,6 @@ Pokazuje tylko te urządzenia które zgłaszały się w okresie od ostatniego ra
Upewnij się że lokalizacja jest włączona w systemie Windows!
W zalezności od Twojej wersji Windows'a opcje te możesz znaleźć w Ustawienia -> Prywatność i Zabezpieczenia -> Lokalizacja
-
-
- Zwraca nazwę procesu który obecnie używa mikrofonu.
-
-Wskazówka: jeżeli korzystasz z usługi Satellite, nie będziesz miał informacji o aplikacji w przestrzeni użytkownika.
Zwraca ostatnią zmianę stanu ekranu:
@@ -3480,4 +3475,9 @@ Wymaga nazwy urządzenia audio jako ładunku.
Użyj nowoczesnej ikony w zasobniku
+
+ Podaje liczbę procesów aktualnie korzystających z mikrofonu - dodatkowo podaje nazwę procesów w atrybutach czujnika.
+
+Uwaga: jeśli jest używany w usłudze satelitarnej, nie wykryje aplikacji przestrzeni użytkownika.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index c12f692d..82742fe5 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -2250,11 +2250,6 @@ HassAgentStarted, Logoff, SystemShutdown, Resume, Suspend, ConsoleConnect, Conso
Fornece um valor bool com base em se o microfone está sendo usado no momento.
Fuzzy
-
- Fornece o nome do processo que está usando o microfone no momento.
-
-Observação: se usado no serviço satélite, ele não detectará aplicativos de espaço de usuário.
-
Fornece a última alteração do estado de energia do monitor:
@@ -3416,4 +3411,9 @@ Você precisa do nome do dispositivo de áudio como carga útil.
Use o ícone moderno da bandeja
+
+ Fornece o número de processos que utilizam atualmente o microfone - fornece adicionalmente o nome dos processos nos atributos do sensor.
+
+Nota: se usado no serviço de satélite, não detectará aplicações no espaço do usuário.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 5f839459..a3d68008 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -2951,7 +2951,7 @@ Hidden, Maximized, Minimized, Normal and Unknown.
Note: if used in the satellite service, it won't detect userspace applications.
- Provides the name of the process that's currently using the microphone.
+ Provides the number of the processes that currently use the microphone - additionally provides name of the processes in the sensor's attributes.
Note: if used in the satellite service, it won't detect userspace applications.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index fd37d175..730a6e59 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3197,11 +3197,6 @@ Home Assistant.
Убедитесь, что службы определения местоположения Windows включены!
В зависимости от вашей версии Windows, это можно найти в новой панели управления -> 'конфиденциальность и безопасность' -> 'местоположение'.
-
-
- Указывает имя процесса, который в данный момент использует микрофон.
-
-Примечание: если он используется в спутниковой службе, он не будет обнаруживать приложения пользовательского пространства.
Обеспечивает последнее изменение состояния питания монитора:
@@ -3439,4 +3434,9 @@ Home Assistant.
Использовать современный значок в трее
+
+ Предоставляет количество процессов, которые в данный момент используют микрофон. Дополнительно указывается имя процесса в атрибутах датчика.
+
+Примечание. При использовании в спутниковой службе он не обнаружит приложения пользовательского пространства.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 3a216e73..1b8cf9ba 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -2328,11 +2328,6 @@ HassAgentStarted, Odjava, SystemShutdown, Resume, Suspend, ConsoleConnect, Conso
Zagotavlja bool vrednost glede na to, ali se mikrofon trenutno uporablja.
Fuzzy
-
- Vrne ime procesa, ki trenutno uporablja mikrofon.
-
-Opomba: če se uporablja v satelitski storitvi
-
Vrne zadnjo spremembo načina monitorja:
@@ -3519,4 +3514,9 @@ Ime zvočne naprave potrebujete kot obremenitev.
Uporabi sodobno ikono pladnja
+
+ Zagotavlja število procesov, ki trenutno uporabljajo mikrofon - dodatno podaja ime procesov v atributih senzorja.
+
+Opomba: če se uporablja v satelitski storitvi, ne bo zaznal aplikacij uporabniškega prostora.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 5796ca75..52515f4c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2764,9 +2764,6 @@ Daha fazla tuşa ve/veya CTRL gibi değiştiricilere ihtiyacınız varsa, Multip
Geçerli enlem, boylam ve yüksekliğinizi virgülle ayrılmış bir değer olarak döndürür. Windows'un konum hizmetlerinin etkinleştirildiğinden emin olun! Windows sürümünüze bağlı olarak bu, yeni kontrol panelinde -> 'gizlilik ve güvenlik' -> 'konum'da bulunabilir.
-
- Şu anda mikrofonu kullanan işlemin adını sağlar. Not: uydu hizmetinde kullanılırsa, kullanıcı alanı uygulamalarını algılamaz.
-
Son monitör güç durumu değişikliğini sağlar: Soluk, Güç Kapalı, Güç Açık ve Bilinmiyor.
@@ -2982,4 +2979,9 @@ Yük olarak ses cihazı adına ihtiyacınız vardır.
Modern tepsi simgesini kullan
+
+ Halihazırda mikrofonu kullanan işlemlerin sayısını sağlar; ayrıca sensörün özniteliklerinde süreçlerin adını da sağlar.
+
+Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılamaz.
+
\ No newline at end of file
From 3478aa0d4f4d9ada0cf53e0023889c4a054ab4e6 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:02:02 +0100
Subject: [PATCH 13/45] dependency bump (#56)
---
.../HASS.Agent.Satellite.Service.csproj | 16 ++++++++--------
.../HASS.Agent.Shared/HASS.Agent.Shared.csproj | 10 +++++-----
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 18 +++++++++---------
3 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index 72f396fe..e9e2d70f 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -29,22 +29,22 @@
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
-
+
+
-
+
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index 504a0bcb..aa05de02 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -30,19 +30,19 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index bbaf5a51..d5bd7361 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -49,27 +49,27 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
WV2
-
-
-
+
+
+
-
+
-
+
-
+
From 2067440601ed1209803ba6749e5680d2a0b95de9 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:18:52 +0100
Subject: [PATCH 14/45] added note regarding HA 255 payload character limit
(#58)
---
.../HASS.Agent/Resources/Localization/Languages.Designer.cs | 1 +
.../HASS.Agent/Resources/Localization/Languages.de.resx | 1 +
.../HASS.Agent/Resources/Localization/Languages.en.resx | 1 +
.../HASS.Agent/Resources/Localization/Languages.es.resx | 1 +
.../HASS.Agent/Resources/Localization/Languages.fr.resx | 5 +++--
.../HASS.Agent/Resources/Localization/Languages.nl.resx | 5 +++--
.../HASS.Agent/Resources/Localization/Languages.pl.resx | 5 +++--
.../HASS.Agent/Resources/Localization/Languages.pt-br.resx | 1 +
.../HASS.Agent/Resources/Localization/Languages.resx | 1 +
.../HASS.Agent/Resources/Localization/Languages.ru.resx | 3 ++-
.../HASS.Agent/Resources/Localization/Languages.sl.resx | 5 +++--
.../HASS.Agent/Resources/Localization/Languages.tr.resx | 5 ++++-
12 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index eb72a3ef..ffd2a2ed 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -6094,6 +6094,7 @@ internal static string SensorsManager_PerformanceCounterSensorDescription {
///
/// Looks up a localized string similar to Returns the result of the provided Powershell command or script.
+ ///Note: please keep in mind that Home Assistant accepts payload up to 255 characters.
///
///Converts the outcome to text..
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 65f05b22..ef8af3e2 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3260,6 +3260,7 @@ Gedimmt, PowerOff, PowerOn und Unbekannt.
Gibt das Ergebnis des bereitgestellten Powershell-Befehls oder -Skripts zurück.
+Hinweis: Bitte beachten Sie, dass Home Assistant Nutzdaten von bis zu 255 Zeichen akzeptiert.
Konvertiert das Ergebnis in Text.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 35b55d94..2d827e9e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3138,6 +3138,7 @@ Dimmed, PowerOff, PowerOn and Unkown.
Returns the result of the provided Powershell command or script.
+Note: please keep in mind that Home Assistant accepts payload up to 255 characters.
Converts the outcome to text.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index b94b7fbd..88070530 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3137,6 +3137,7 @@ Atenuado, Apagado, Encendido y Desconocido.
Devuelve el resultado del comando o script de Powershell proporcionado.
+Nota: tenga en cuenta que Home Assistant acepta una carga útil de hasta 255 caracteres.
Convierte el resultado en texto.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 6974fc0d..f8295066 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3169,9 +3169,10 @@ Selon votre version de Windows, cela peut être trouvé dans le nouveau panneau
Dimmed, PowerOff, PowerOn and Unkown.
- Returns the result of the provided Powershell command or script.
+ Renvoie le résultat de la commande ou du script Powershell fourni.
+Remarque : veuillez garder à l'esprit que Home Assistant accepte des charges utiles allant jusqu'à 255 caractères.
-Converts the outcome to text.
+Convertit le résultat en texte.
Fournit des informations sur toutes les imprimantes installées et leurs files d'attente.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index b3718a8f..f810730d 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3158,9 +3158,10 @@ Afhankelijk van je Windows versie, kan dit gevonden worden in het nieuwe configu
Gedimmed, Uitgeschakeld, Ingeschakeld en Onbekend
- Geeft het resultaat van het opgegeven Powershell commando of script.
+ Retourneert het resultaat van de opgegeven Powershell-opdracht of -script.
+Opmerking: houd er rekening mee dat Home Assistant een payload van maximaal 255 tekens accepteert.
-Converteert de uitkomst naar text.
+Converteert de uitkomst naar tekst.
Geeft informatie over alle geïnstalleerde printers en hun wachtrijen.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index c0aa8938..8adea682 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3246,9 +3246,10 @@ W zalezności od Twojej wersji Windows'a opcje te możesz znaleźć w Ustawienia
Przyciemnienie, Wyłączenie, Włączenie, Nieznany
- Zwraca wynik poprzedniej komendy Powershell lub skryptu.
+ Zwraca wynik podanego polecenia lub skryptu programu PowerShell.
+Uwaga: pamiętaj, że Home Assistant akceptuje ładunek o długości do 255 znaków.
-Konwertuje wynik do tekstu.
+Konwertuje wynik na tekst.
Zwraca informacje na temat wszystkich zainstalowanych drukarek i ich kolejek.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 82742fe5..92c42b50 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -2276,6 +2276,7 @@ Você pode explorar os contadores através da ferramenta 'perfmon.exe' do Window
Retorna o resultado do comando ou script do Powershell fornecido.
+Observação: lembre-se de que o Home Assistant aceita carga útil de até 255 caracteres.
Converte o resultado em texto.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index a3d68008..69340051 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -2925,6 +2925,7 @@ Dimmed, PowerOff, PowerOn and Unkown.
Returns the result of the provided Powershell command or script.
+Note: please keep in mind that Home Assistant accepts payload up to 255 characters.
Converts the outcome to text.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 730a6e59..5ecfc113 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3204,7 +3204,8 @@ Home Assistant.
Затемненный, Выключенный, Включенный и Неизвестный.
- Возвращает результат предоставленной команды Powershell или сценария.
+ Возвращает результат предоставленной команды или сценария Powershell.
+Примечание. Имейте в виду, что Home Assistant принимает полезную нагрузку длиной до 255 символов.
Преобразует результат в текст.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 1b8cf9ba..b50418be 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -2353,9 +2353,10 @@ Primer: _Skupaj
Številke lahko raziščete z orodjem Windows 'perfmon.exe'.
- Vrne rezultat Powershell ukaza ali skripta.
+ Vrne rezultat podanega ukaza ali skripta Powershell.
+Opomba: ne pozabite, da Home Assistant sprejme vsebino do 255 znakov.
-Pretvori rezultat v tekst.
+Pretvori rezultat v besedilo.
Vrne informacijo o nameščenih tiskalnikih in čakalni vrsti za njih.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 52515f4c..140a75ac 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2768,7 +2768,10 @@ Daha fazla tuşa ve/veya CTRL gibi değiştiricilere ihtiyacınız varsa, Multip
Son monitör güç durumu değişikliğini sağlar: Soluk, Güç Kapalı, Güç Açık ve Bilinmiyor.
- Sağlanan Powershell komutunun veya komut dosyasının sonucunu döndürür. Sonucu metne dönüştürür.
+ Sağlanan Powershell komutunun veya betiğinin sonucunu döndürür.
+Not: Home Assistant'ın 255 karaktere kadar yükü kabul ettiğini lütfen unutmayın.
+
+Sonucu metne dönüştürür.
Yüklü tüm yazıcılar ve sıraları hakkında bilgi sağlar.
From 1f2a40900d2a6767a47c12463006a143725f7285 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 8 Apr 2024 20:05:25 +0200
Subject: [PATCH 15/45] Feature: NFC tag scanning (#69)
---
.../ConfigGeneral - Copy.de.resx | 14 ++
.../ConfigGeneral - Copy.en.resx | 14 ++
.../ConfigGeneral - Copy.es.resx | 14 ++
.../ConfigGeneral - Copy.fr.resx | 14 ++
.../ConfigGeneral - Copy.nl.resx | 14 ++
.../ConfigGeneral - Copy.pl.resx | 14 ++
.../ConfigGeneral - Copy.pt-br.resx | 14 ++
.../ConfigGeneral - Copy.ru.resx | 14 ++
.../ConfigGeneral - Copy.sl.resx | 14 ++
.../ConfigGeneral - Copy.tr.resx | 14 ++
.../Configuration/ConfigNFC.Designer.cs | 144 ++++++++++++++++++
.../Controls/Configuration/ConfigNFC.cs | 29 ++++
.../Controls/Configuration/ConfigNFC.resx | 124 +++++++++++++++
.../Forms/Configuration.Designer.cs | 20 +++
.../HASS.Agent/Forms/Configuration.cs | 10 ++
.../HASS.Agent/Forms/Configuration.resx | 62 +++++++-
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 34 +++++
.../HASS.Agent/Managers/RadioManager.cs | 116 +++++++++++++-
.../HASS.Agent/Models/Config/AppSettings.cs | 4 +-
.../Localization/Languages.Designer.cs | 9 ++
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
.../HASS.Agent/Sensors/SensorsManager.cs | 20 +++
32 files changed, 739 insertions(+), 6 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.de.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.en.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.es.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.fr.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.nl.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pl.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pt-br.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.ru.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.sl.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.tr.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.Designer.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.resx
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.de.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.de.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.de.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.en.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.en.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.en.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.es.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.es.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.es.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.fr.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.fr.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.fr.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.nl.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.nl.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.nl.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pl.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pl.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pl.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pt-br.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pt-br.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.pt-br.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.ru.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.ru.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.ru.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.sl.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.sl.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.sl.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.tr.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.tr.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigGeneral - Copy.tr.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.Designer.cs
new file mode 100644
index 00000000..c353bf48
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.Designer.cs
@@ -0,0 +1,144 @@
+using HASS.Agent.Resources.Localization;
+
+namespace HASS.Agent.Controls.Configuration
+{
+ partial class ConfigNFC
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ var resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigNFC));
+ LblInfo1 = new Label();
+ LblSelectedScanner = new Label();
+ CbNfcScanner = new ComboBox();
+ PbLine1 = new PictureBox();
+ CbEnableNfc = new CheckBox();
+ ((System.ComponentModel.ISupportInitialize)PbLine1).BeginInit();
+ SuspendLayout();
+ //
+ // LblInfo1
+ //
+ LblInfo1.AccessibleDescription = "NFC scanning ability description";
+ LblInfo1.AccessibleName = "NFC information";
+ LblInfo1.AccessibleRole = AccessibleRole.StaticText;
+ LblInfo1.AutoEllipsis = true;
+ LblInfo1.Font = new Font("Segoe UI", 10F);
+ LblInfo1.Location = new Point(70, 36);
+ LblInfo1.Name = "LblInfo1";
+ LblInfo1.Size = new Size(583, 74);
+ LblInfo1.TabIndex = 57;
+ LblInfo1.Text = resources.GetString("LblInfo1.Text");
+ //
+ // LblSelectedScanner
+ //
+ LblSelectedScanner.AccessibleDescription = "NFC Scanner";
+ LblSelectedScanner.AccessibleName = "NFC Scanner";
+ LblSelectedScanner.AccessibleRole = AccessibleRole.StaticText;
+ LblSelectedScanner.AutoSize = true;
+ LblSelectedScanner.Font = new Font("Segoe UI", 10F);
+ LblSelectedScanner.Location = new Point(70, 188);
+ LblSelectedScanner.Name = "LblSelectedScanner";
+ LblSelectedScanner.Size = new Size(87, 19);
+ LblSelectedScanner.TabIndex = 65;
+ LblSelectedScanner.Text = "NFC &Scanner";
+ //
+ // CbNfcScanner
+ //
+ CbNfcScanner.AccessibleDescription = "Selected NFC scanner";
+ CbNfcScanner.AccessibleName = "Selected NFC scanner";
+ CbNfcScanner.AccessibleRole = AccessibleRole.DropList;
+ CbNfcScanner.BackColor = Color.FromArgb(63, 63, 70);
+ CbNfcScanner.DrawMode = DrawMode.OwnerDrawFixed;
+ CbNfcScanner.DropDownHeight = 300;
+ CbNfcScanner.DropDownStyle = ComboBoxStyle.DropDownList;
+ CbNfcScanner.Font = new Font("Segoe UI", 9.75F);
+ CbNfcScanner.ForeColor = Color.FromArgb(241, 241, 241);
+ CbNfcScanner.FormattingEnabled = true;
+ CbNfcScanner.IntegralHeight = false;
+ CbNfcScanner.Location = new Point(73, 210);
+ CbNfcScanner.Name = "CbNfcScanner";
+ CbNfcScanner.Size = new Size(358, 26);
+ CbNfcScanner.Sorted = true;
+ CbNfcScanner.TabIndex = 0;
+ //
+ // PbLine1
+ //
+ PbLine1.AccessibleDescription = "Seperator line.";
+ PbLine1.AccessibleName = "Seperator";
+ PbLine1.AccessibleRole = AccessibleRole.Graphic;
+ PbLine1.Image = Properties.Resources.line;
+ PbLine1.Location = new Point(73, 130);
+ PbLine1.Name = "PbLine1";
+ PbLine1.Size = new Size(576, 1);
+ PbLine1.SizeMode = PictureBoxSizeMode.AutoSize;
+ PbLine1.TabIndex = 67;
+ PbLine1.TabStop = false;
+ //
+ // CbEnableNfc
+ //
+ CbEnableNfc.AccessibleDescription = "Enable NFC tag scanning";
+ CbEnableNfc.AccessibleName = "Enable NFC";
+ CbEnableNfc.AccessibleRole = AccessibleRole.CheckButton;
+ CbEnableNfc.AutoSize = true;
+ CbEnableNfc.Font = new Font("Segoe UI", 10F);
+ CbEnableNfc.Location = new Point(73, 150);
+ CbEnableNfc.Name = "CbEnableNfc";
+ CbEnableNfc.Size = new Size(180, 23);
+ CbEnableNfc.TabIndex = 72;
+ CbEnableNfc.Text = "Enable &NFC tag scanning";
+ CbEnableNfc.UseVisualStyleBackColor = true;
+ //
+ // ConfigNFC
+ //
+ AccessibleDescription = "Panel containing general configuration options.";
+ AccessibleName = "General configuration";
+ AccessibleRole = AccessibleRole.Pane;
+ AutoScaleDimensions = new SizeF(96F, 96F);
+ AutoScaleMode = AutoScaleMode.Dpi;
+ BackColor = Color.FromArgb(45, 45, 48);
+ Controls.Add(CbEnableNfc);
+ Controls.Add(PbLine1);
+ Controls.Add(CbNfcScanner);
+ Controls.Add(LblSelectedScanner);
+ Controls.Add(LblInfo1);
+ ForeColor = Color.FromArgb(241, 241, 241);
+ Margin = new Padding(4);
+ Name = "ConfigNFC";
+ Size = new Size(700, 275);
+ Load += ConfigNFC_Load;
+ ((System.ComponentModel.ISupportInitialize)PbLine1).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+ private System.Windows.Forms.Label LblInfo1;
+ private Label LblSelectedScanner;
+ internal ComboBox CbNfcScanner;
+ private PictureBox PbLine1;
+ internal CheckBox CbEnableNfc;
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.cs
new file mode 100644
index 00000000..f3ffd70d
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.cs
@@ -0,0 +1,29 @@
+using System.Globalization;
+using HASS.Agent.Functions;
+using HASS.Agent.Managers;
+using HASS.Agent.Resources.Localization;
+using Syncfusion.Windows.Forms;
+
+namespace HASS.Agent.Controls.Configuration;
+
+public partial class ConfigNFC : UserControl
+{
+ public ConfigNFC()
+ {
+ InitializeComponent();
+
+ BindComboBoxTheme();
+ }
+
+ private void BindComboBoxTheme() => CbNfcScanner.DrawItem += ComboBoxTheme.DrawItem;
+
+ private void ConfigNFC_Load(object sender, EventArgs e)
+ {
+ CbNfcScanner.Items.Add(Languages.SensorsMod_None);
+
+ foreach (var nfcReaderName in RadioManager.AvailableNFCReaderNames)
+ CbNfcScanner.Items.Add(nfcReaderName);
+
+ CbNfcScanner.SelectedItem = string.IsNullOrWhiteSpace(Variables.AppSettings.NfcSelectedScanner) ? Languages.SensorsMod_None : Variables.AppSettings.NfcSelectedScanner;
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.resx b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.resx
new file mode 100644
index 00000000..6b83a3b0
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigNFC.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ If your device is equipped with NFC scanner, you can enable the scanning functionality and select one of the available scanners.
+This functionality works like the Home Assistant Compaion App NFC tag scanning.
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.Designer.cs b/src/HASS.Agent/HASS.Agent/Forms/Configuration.Designer.cs
index 0b47fbcc..150e1c3a 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.Designer.cs
@@ -46,6 +46,7 @@ private void InitializeComponent()
this.TabStartup = new Syncfusion.Windows.Forms.Tools.TabPageAdv();
this.TabTrayIcon = new Syncfusion.Windows.Forms.Tools.TabPageAdv();
this.TabUpdates = new Syncfusion.Windows.Forms.Tools.TabPageAdv();
+ this.TabNFC = new Syncfusion.Windows.Forms.Tools.TabPageAdv();
this.BtnAbout = new Syncfusion.WinForms.Controls.SfButton();
this.BtnHelp = new Syncfusion.WinForms.Controls.SfButton();
this.BtnStore = new Syncfusion.WinForms.Controls.SfButton();
@@ -80,6 +81,7 @@ private void InitializeComponent()
this.ConfigTabs.Controls.Add(this.TabStartup);
this.ConfigTabs.Controls.Add(this.TabTrayIcon);
this.ConfigTabs.Controls.Add(this.TabUpdates);
+ this.ConfigTabs.Controls.Add(this.TabNFC);
this.ConfigTabs.Dock = System.Windows.Forms.DockStyle.Fill;
this.ConfigTabs.FixedSingleBorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
this.ConfigTabs.FocusOnTabClick = false;
@@ -355,6 +357,23 @@ private void InitializeComponent()
this.TabUpdates.Text = global::HASS.Agent.Resources.Localization.Languages.Configuration_TabUpdates;
this.TabUpdates.ThemesEnabled = false;
//
+ // TabNFC
+ //
+ this.TabNFC.AccessibleDescription = "Contains the \'NFC\' controls.";
+ this.TabNFC.AccessibleName = "NFC";
+ this.TabNFC.AccessibleRole = System.Windows.Forms.AccessibleRole.PageTab;
+ this.TabNFC.Image = null;
+ this.TabNFC.ImageSize = new System.Drawing.Size(16, 16);
+ this.TabNFC.Location = new System.Drawing.Point(142, 2);
+ this.TabNFC.Name = "TabNFC";
+ this.TabNFC.ShowCloseButton = true;
+ this.TabNFC.Size = new System.Drawing.Size(740, 544);
+ this.TabNFC.TabBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(48)))));
+ this.TabNFC.TabForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241)))));
+ this.TabNFC.TabIndex = 15;
+ this.TabNFC.Text = Languages.Configuration_NFC;
+ this.TabNFC.ThemesEnabled = false;
+ //
// BtnAbout
//
this.BtnAbout.AccessibleDescription = "Opens the about window.";
@@ -523,6 +542,7 @@ private void InitializeComponent()
private Syncfusion.Windows.Forms.Tools.TabPageAdv TablLocalApi;
private Syncfusion.Windows.Forms.Tools.TabPageAdv TabMediaPlayer;
private Syncfusion.Windows.Forms.Tools.TabPageAdv TabTrayIcon;
+ private Syncfusion.Windows.Forms.Tools.TabPageAdv TabNFC;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
index 6c25679f..2f83e22a 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs
@@ -36,6 +36,7 @@ public partial class Configuration : MetroForm
private readonly ConfigLocalApi _localApi = new();
private readonly ConfigMediaPlayer _mediaPlayer = new();
private readonly ConfigTrayIcon _trayIcon = new();
+ private readonly ConfigNFC _nfc = new();
private bool _initializing = true;
@@ -67,6 +68,8 @@ private void Configuration_Load(object sender, EventArgs e)
TablLocalApi.Controls.Add(_localApi);
TabMediaPlayer.Controls.Add(_mediaPlayer);
TabTrayIcon.Controls.Add(_trayIcon);
+ TabNFC.Controls.Add(_nfc);
+
// bind events
BindEvents();
@@ -103,6 +106,7 @@ private void Configuration_FormClosing(object sender, FormClosingEventArgs e)
_localApi.Dispose();
_mediaPlayer.Dispose();
_trayIcon.Dispose();
+ _nfc.Dispose();
}
private void BindEvents()
@@ -361,6 +365,8 @@ private void LoadSettings()
_trayIcon.CbWebViewKeepLoaded.CheckState = Variables.AppSettings.TrayIconWebViewBackgroundLoading ? CheckState.Checked : CheckState.Unchecked;
_trayIcon.CbWebViewShowMenuOnLeftClick.CheckState = Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick ? CheckState.Checked : CheckState.Unchecked;
+ _nfc.CbEnableNfc.CheckState = Variables.AppSettings.NfcScanningEnabled ? CheckState.Checked : CheckState.Unchecked;
+
// done
_initializing = false;
}
@@ -468,6 +474,10 @@ private async Task StoreSettingsAsync()
Variables.AppSettings.TrayIconWebViewBackgroundLoading = _trayIcon.CbWebViewKeepLoaded.CheckState == CheckState.Checked;
Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick = _trayIcon.CbWebViewShowMenuOnLeftClick.CheckState == CheckState.Checked;
+ // nfc
+ Variables.AppSettings.NfcScanningEnabled = _nfc.CbEnableNfc.CheckState == CheckState.Checked;
+ Variables.AppSettings.NfcSelectedScanner = _nfc.CbNfcScanner.SelectedItem == null ? string.Empty : _nfc.CbNfcScanner.SelectedItem.ToString();
+
// save to file
SettingsManager.StoreAppSettings();
}
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.resx b/src/HASS.Agent/HASS.Agent/Forms/Configuration.resx
index d1f11844..9cfd6d5f 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.resx
+++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.resx
@@ -1,4 +1,64 @@
-
+
+
+
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index d5bd7361..fb9c0c56 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -63,6 +63,7 @@
+
@@ -91,6 +92,9 @@
+
+ UserControl
+
UserControl
@@ -169,6 +173,36 @@
ConfigExternalTools.resx
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
+
+ ConfigGeneral.resx
+
ConfigGeneral.resx
diff --git a/src/HASS.Agent/HASS.Agent/Managers/RadioManager.cs b/src/HASS.Agent/HASS.Agent/Managers/RadioManager.cs
index 035aa8c9..c4792838 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/RadioManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/RadioManager.cs
@@ -1,20 +1,52 @@
-using Serilog;
+using MQTTnet;
+using NdefLibrary.Ndef;
+using Newtonsoft.Json;
+using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
+using Windows.Devices.Enumeration;
using Windows.Devices.Radios;
+using Windows.Devices.SmartCards;
+using Windows.Networking.Proximity;
namespace HASS.Agent.Managers
{
internal static class RadioManager
{
public static List AvailableRadio { get; private set; } = new();
- public static List AvailableRadioNames { get => AvailableRadio.Select(r => r.Name).ToList(); }
+ public static List AvailableRadioNames => AvailableRadio.Select(r => r.Name).ToList();
+
+ public static Dictionary AvailableNFCReader { get; private set; } = new();
+ public static List AvailableNFCReaderNames => AvailableNFCReader.Keys.ToList();
+
+ private static long s_subscriptionId = -1;
+ private static ProximityDevice s_selectedNFCReader = null;
+ public static ProximityDevice SelectedNFCReader
+ {
+ get => s_selectedNFCReader;
+ set
+ {
+ if (s_selectedNFCReader == value)
+ return;
+
+ if (s_selectedNFCReader != null && s_subscriptionId != -1)
+ {
+ s_selectedNFCReader.StopSubscribingForMessage(s_subscriptionId);
+ }
+
+ s_selectedNFCReader = value;
+ s_subscriptionId = s_selectedNFCReader.SubscribeForMessage("NDEF", MessageReceivedHandler);
+ }
+ }
public static async Task Initialize()
{
+ Log.Debug("[RADIOMGR] Initialization started");
+
var accessStatus = await Radio.RequestAccessAsync();
if (accessStatus == RadioAccessStatus.Allowed)
{
@@ -23,12 +55,88 @@ public static async Task Initialize()
AvailableRadio.Add(radio);
}
- Log.Information("[RADIOMGR] Ready");
+ Log.Information("[RADIOMGR] Radio management permission granted");
}
else
{
- Log.Fatal("[RADIOMGR] No permission granted for Bluetooth radio management");
+ Log.Fatal("[RADIOMGR] No permission granted for radio management - privacy settings may be restricting the access");
+ }
+
+ Log.Debug("[RADIOMGR] Enumerating proximity/NFC devices");
+ try
+ {
+ var proximityDevices = await DeviceInformation.FindAllAsync(ProximityDevice.GetDeviceSelector());
+ foreach (var device in proximityDevices)
+ {
+ var proximityReader = ProximityDevice.FromId(device.Id);
+ AvailableNFCReader.Add(device.Name, proximityReader);
+ }
+ }
+ catch
+ {
+ Log.Fatal("[RADIOMGR] Error initializing proximity/NFC devices");
+ }
+
+ if (!string.IsNullOrEmpty(Variables.AppSettings.NfcSelectedScanner))
+ {
+ if (AvailableNFCReader.TryGetValue(Variables.AppSettings.NfcSelectedScanner, out var selectedScanner))
+ {
+ Log.Debug("[RADIOMGR] Selected NFC reader: '{nfcScanner}'", Variables.AppSettings.NfcSelectedScanner);
+ SelectedNFCReader = selectedScanner;
+ }
+ else
+ {
+ Log.Warning("[RADIOMGR] Selected NFC reader: '{nfcScanner}' not available", Variables.AppSettings.NfcSelectedScanner);
+ }
}
+
+ Log.Information("[RADIOMGR] Ready");
+ }
+
+ private static void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
+ {
+ if(!Variables.AppSettings.NfcScanningEnabled)
+ return;
+
+ try
+ {
+ var rawMsg = message.Data.ToArray();
+ var ndefMessage = NdefMessage.FromByteArray(rawMsg);
+
+ foreach (var record in ndefMessage)
+ {
+ Log.Debug("Record type: " + Encoding.UTF8.GetString(record.Type, 0, record.Type.Length));
+ if (record.CheckSpecializedType(false) == typeof(NdefUriRecord))
+ {
+ var uriRecord = new NdefUriRecord(record);
+ Log.Debug($"URI: {uriRecord.Uri}");
+
+ if (uriRecord.Uri.StartsWith("https://www.home-assistant.io/tag/"))
+ {
+ var tagId = uriRecord.Uri.Split('/').LastOrDefault();
+ if(string.IsNullOrWhiteSpace(tagId))
+ return;
+
+ var tagScannedMessage = new MqttApplicationMessageBuilder()
+ .WithTopic($"hass.agent/devices/{Variables.DeviceConfig.Name}/tag_scanned")
+ .WithPayload(JsonConvert.SerializeObject(new
+ {
+ Time = DateTime.UtcNow.ToString("s"),
+ UID = tagId
+ }))
+ .Build();
+
+ Variables.MqttManager.PublishAsync(tagScannedMessage);
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.Message);
+ }
+
}
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
index ee404285..a7f705c7 100644
--- a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
+++ b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs
@@ -79,7 +79,9 @@ public AppSettings()
public bool MqttUseRetainFlag { get; set; } = true;
public string MqttRootCertificate { get; set; } = string.Empty;
public string MqttClientCertificate { get; set; } = string.Empty;
-
public bool MqttIgnoreGracePeriod { get; set; } = false;
+
+ public bool NfcScanningEnabled { get; set; } = false;
+ public string NfcSelectedScanner { get; set; } = string.Empty;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index ffd2a2ed..6aa0e753 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -3257,6 +3257,15 @@ internal static string Configuration_MessageBox_RestartManually {
}
}
+ ///
+ /// Looks up a localized string similar to NFC.
+ ///
+ internal static string Configuration_NFC {
+ get {
+ return ResourceManager.GetString("Configuration_NFC", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to You've changed your device's name.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index ef8af3e2..667b8615 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3495,4 +3495,7 @@ Als Payload benötigen Sie den Namen des Audiogeräts.
Hinweis: Bei Verwendung im Satellitendienst werden keine Userspace-Anwendungen erkannt.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 2d827e9e..9ad44e64 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3372,4 +3372,7 @@ Requires audio device name as a payload.
Note: if used in the satellite service, it won't detect userspace applications.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 88070530..f4714e88 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3371,4 +3371,7 @@ Necesita el nombre del dispositivo de audio como carga útil.
Nota: si se usa en el servicio satelital, no detectará aplicaciones del espacio de usuario.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index f8295066..a3c2f2f1 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3404,4 +3404,7 @@ Vous avez besoin du nom du périphérique audio comme charge utile.
Remarque : s'il est utilisé dans le service satellite, il ne détectera pas les applications de l'espace utilisateur.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index f810730d..e87c832b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3392,4 +3392,7 @@ U hebt de naam van het audioapparaat nodig als payload.
Opmerking: indien gebruikt in de satellietdienst, zal het geen gebruikersruimtetoepassingen detecteren.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 8adea682..a209df91 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3481,4 +3481,7 @@ Wymaga nazwy urządzenia audio jako ładunku.
Uwaga: jeśli jest używany w usłudze satelitarnej, nie wykryje aplikacji przestrzeni użytkownika.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 92c42b50..04f553a7 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3417,4 +3417,7 @@ Você precisa do nome do dispositivo de áudio como carga útil.
Nota: se usado no serviço de satélite, não detectará aplicações no espaço do usuário.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 69340051..a91866c5 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3384,4 +3384,7 @@ Requires audio device name as a payload.
Use modern tray icon
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 5ecfc113..2e7a6713 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3440,4 +3440,7 @@ Home Assistant.
Примечание. При использовании в спутниковой службе он не обнаружит приложения пользовательского пространства.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index b50418be..57350977 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3520,4 +3520,7 @@ Ime zvočne naprave potrebujete kot obremenitev.
Opomba: če se uporablja v satelitski storitvi, ne bo zaznal aplikacij uporabniškega prostora.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 140a75ac..21bd0270 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2987,4 +2987,7 @@ Yük olarak ses cihazı adına ihtiyacınız vardır.
Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılamaz.
+
+ NFC
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
index f8cc5b7a..1d8f2848 100644
--- a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs
@@ -1,4 +1,5 @@
using HASS.Agent.Extensions;
+using HASS.Agent.Managers;
using HASS.Agent.Models.Internal;
using HASS.Agent.Resources.Localization;
using HASS.Agent.Settings;
@@ -6,7 +7,10 @@
using HASS.Agent.Shared.Extensions;
using HASS.Agent.Shared.Models.Config;
using HASS.Agent.Shared.Models.HomeAssistant;
+using MQTTnet;
+using Newtonsoft.Json;
using Serilog;
+using static HASS.Agent.Shared.Functions.Inputs;
namespace HASS.Agent.Sensors
{
@@ -123,6 +127,22 @@ private static async void Process()
}
}
+ //if (RadioManager.AvailableNFCReader.Any())
+ if (true)
+ {
+ var tagScannerConfigMesage = new MqttApplicationMessageBuilder()
+ .WithTopic($"{Variables.AppSettings.MqttDiscoveryPrefix}/tag/{Variables.DeviceConfig.Name}/config")
+ .WithPayload(JsonConvert.SerializeObject(new
+ {
+ topic = $"hass.agent/devices/{Variables.DeviceConfig.Name}/tag_scanned",
+ value_template = "{{ value_json.UID }}"
+ }))
+ .WithRetainFlag(Variables.AppSettings.MqttUseRetainFlag)
+ .Build();
+
+ await Variables.MqttManager.PublishAsync(tagScannerConfigMesage);
+ }
+
_discoveryPublished = true;
}
From 5e480f4f9570431f9cd50bc0d154725eb584d53f Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 8 Apr 2024 20:10:39 +0200
Subject: [PATCH 16/45] Fix: close all subwindows when main one is closed (#70)
---
src/HASS.Agent/HASS.Agent/Forms/Main.cs | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.cs b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
index 7e971535..583c9197 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Main.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
@@ -344,7 +344,19 @@ private void Main_FormClosing(object sender, FormClosingEventArgs e)
if (_isClosing)
return;
- Invoke(new MethodInvoker(Hide));
+ Invoke(() =>
+ {
+ HelperFunctions.GetForm("QuickActions")?.Close();
+ HelperFunctions.GetForm("Configuration")?.Close();
+ HelperFunctions.GetForm("QuickActionsConfig")?.Close();
+ HelperFunctions.GetForm("SensorsConfig")?.Close();
+ HelperFunctions.GetForm("ServiceConfig")?.Close();
+ HelperFunctions.GetForm("CommandsConfig")?.Close();
+ HelperFunctions.GetForm("Help")?.Close();
+ HelperFunctions.GetForm("Donate")?.Close();
+
+ new MethodInvoker(Hide).Invoke();
+ });
if (!Variables.ShuttingDown)
{
From 9e106747d4dee729707800079b1d0e7b3b5f1c21 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 8 Apr 2024 20:29:36 +0200
Subject: [PATCH 17/45] Fix: device rename MQTT messages cleanup (#71)
---
src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs | 31 +++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
index 3e99155b..8507183f 100644
--- a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
+++ b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs
@@ -562,11 +562,38 @@ public async Task ClearDeviceConfigAsync()
{
if (string.IsNullOrEmpty(Variables.AppSettings.MqttDiscoveryPrefix))
Variables.AppSettings.MqttDiscoveryPrefix = "homeassistant";
-
var messageBuilder = new MqttApplicationMessageBuilder()
.WithTopic($"{Variables.AppSettings.MqttDiscoveryPrefix}/sensor/{Variables.DeviceConfig.Name}/availability")
.WithPayload(Array.Empty())
- .WithRetainFlag(Variables.AppSettings.MqttUseRetainFlag);
+ .WithRetainFlag(false);
+
+ await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build());
+
+ messageBuilder = new MqttApplicationMessageBuilder()
+ .WithTopic($"hass.agent/devices/{Variables.DeviceConfig.Name}")
+ .WithPayload(Array.Empty())
+ .WithRetainFlag(false);
+
+ await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build());
+
+ messageBuilder = new MqttApplicationMessageBuilder()
+ .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}")
+ .WithPayload(Array.Empty())
+ .WithRetainFlag(false);
+
+ await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build());
+
+ messageBuilder = new MqttApplicationMessageBuilder()
+ .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}/thumbnail")
+ .WithPayload(Array.Empty())
+ .WithRetainFlag(false);
+
+ await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build());
+
+ messageBuilder = new MqttApplicationMessageBuilder()
+ .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}/state")
+ .WithPayload(Array.Empty())
+ .WithRetainFlag(false);
await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build());
}
From 571c8fab980940190053a920e8acadd26374fadf Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Wed, 1 May 2024 22:58:48 +0200
Subject: [PATCH 18/45] Fix: added additional check for mixed german keyboard
layouts (#74)
This PR adds additional check for the german input language being installed on system.
Should remediate cases where mixed language systems (English UI with German keyboard layout) do not present the notification regarding HotKey collision - #62
---
.../HASS.Agent/Functions/HelperFunctions.cs | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
index b1f7bc85..877b4e57 100644
--- a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
+++ b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
@@ -546,13 +546,24 @@ internal static bool InputLanguageCheckDiffers(out bool knownToCollide, out stri
var inputLanguage = InputLanguage.CurrentInputLanguage.Handle;
// check for known OK languages
- if (KnownOkInputLanguage.ContainsKey(inputLanguage)) return false;
-
+ if (KnownOkInputLanguage.ContainsKey(inputLanguage))
+ return false;
+
// check for known NOT OK languages
- if (KnownNotOkInputLanguage.ContainsKey(inputLanguage))
+ var germanLayoutDetected = false;
+ foreach(InputLanguage language in InputLanguage.InstalledInputLanguages)
+ {
+ if(language.Culture.TwoLetterISOLanguageName == "de")
+ {
+ germanLayoutDetected = true;
+ break;
+ }
+ }
+
+ if (KnownNotOkInputLanguage.ContainsKey(inputLanguage) || germanLayoutDetected)
{
// get human-readable name
- var langName = KnownNotOkInputLanguage[inputLanguage];
+ var langName = germanLayoutDetected ? "German" : KnownNotOkInputLanguage[inputLanguage];
message = string.Format(Languages.HelperFunctions_InputLanguageCheckDiffers_ErrorMsg1, langName);
return true;
From b2fb3c9a582b5fda9c4ce7ab0f80ed44f0cca968 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 2 May 2024 11:16:54 +0200
Subject: [PATCH 19/45] Fix: attributes are now cleared if no process uses the
microphone (#76)
This PR makes the microphone process sensors attributes to be cleared when no process is using the microphone.
---
.../SingleValue/MicrophoneProcessSensor.cs | 47 +++++++------------
1 file changed, 18 insertions(+), 29 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
index e2cbf944..7c92e3e3 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
@@ -13,21 +13,16 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue;
public class MicrophoneProcessSensor : AbstractSingleValueSensor
{
private const string DefaultName = "microphoneprocess";
-
- private const string LastUsedTimeStop = "LastUsedTimeStop";
- public MicrophoneProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool useAttributes = true) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, useAttributes)
- {
- //
- }
+
+ private const string _lastUsedTimeStop = "LastUsedTimeStop";
+ private const string _regKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone";
+
+ public MicrophoneProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, true) { }
private readonly Dictionary _processes = new();
private string _attributes = string.Empty;
- public override string GetState() => MicrophoneProcess();
- public void SetAttributes(string value) => _attributes = string.IsNullOrWhiteSpace(value) ? "{}" : value;
- public override string GetAttributes() => _attributes;
-
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
if (Variables.MqttManager == null)
@@ -49,40 +44,31 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability",
- Icon = "mdi:microphone"
+ Icon = "mdi:microphone",
+ Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes"
};
- if (UseAttributes)
- {
- model.Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes";
- }
-
return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(model);
}
private string MicrophoneProcess()
{
- const string regKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone";
-
_processes.Clear();
// first local machine
- using (var key = Registry.LocalMachine.OpenSubKey(regKey))
+ using (var key = Registry.LocalMachine.OpenSubKey(_regKey))
{
CheckRegForMicrophoneInUse(key);
}
// then current user
- using (var key = Registry.CurrentUser.OpenSubKey(regKey))
+ using (var key = Registry.CurrentUser.OpenSubKey(_regKey))
{
CheckRegForMicrophoneInUse(key);
}
// add processes as attributes
- if (_processes.Count > 0)
- {
- _attributes = JsonConvert.SerializeObject(_processes, Formatting.Indented);
- }
+ _attributes = _processes.Count > 0 ? JsonConvert.SerializeObject(_processes, Formatting.Indented) : "{}";
// return the count
return _processes.Count.ToString();
@@ -109,13 +95,13 @@ private void CheckRegForMicrophoneInUse(RegistryKey key)
foreach (var nonpackagedSubKeyName in nonpackagedkey.GetSubKeyNames())
{
using var subKey = nonpackagedkey.OpenSubKey(nonpackagedSubKeyName);
- if (subKey == null || !subKey.GetValueNames().Contains(LastUsedTimeStop))
+ if (subKey == null || !subKey.GetValueNames().Contains(_lastUsedTimeStop))
{
continue;
}
- var endTime = subKey.GetValue(LastUsedTimeStop) is long
- ? (long)(subKey.GetValue(LastUsedTimeStop) ?? -1)
+ var endTime = subKey.GetValue(_lastUsedTimeStop) is long
+ ? (long)(subKey.GetValue(_lastUsedTimeStop) ?? -1)
: -1;
if (endTime <= 0)
@@ -127,12 +113,12 @@ private void CheckRegForMicrophoneInUse(RegistryKey key)
else
{
using var subKey = key.OpenSubKey(subKeyName);
- if (subKey == null || !subKey.GetValueNames().Contains(LastUsedTimeStop))
+ if (subKey == null || !subKey.GetValueNames().Contains(_lastUsedTimeStop))
{
continue;
}
- var endTime = subKey.GetValue(LastUsedTimeStop) is long ? (long)(subKey.GetValue(LastUsedTimeStop) ?? -1) : -1;
+ var endTime = subKey.GetValue(_lastUsedTimeStop) is long ? (long)(subKey.GetValue(_lastUsedTimeStop) ?? -1) : -1;
if (endTime <= 0)
{
_processes[SharedHelperFunctions.ParseRegWebcamMicApplicationName(subKey.Name)] = "on";
@@ -140,4 +126,7 @@ private void CheckRegForMicrophoneInUse(RegistryKey key)
}
}
}
+
+ public override string GetState() => MicrophoneProcess();
+ public override string GetAttributes() => _attributes;
}
From 07a76e5e99afa87004c308c14b048f7649dbcd8a Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 2 May 2024 11:35:36 +0200
Subject: [PATCH 20/45] Feature: ActiveWindowSensor process name attribute
(#77)
This PR adds "ProcessName" attribute to the ActiveWindowSensor.
---
.../SingleValue/ActiveWindowSensor.cs | 33 +++++++++++++++----
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
index 713a50e7..9751eef4 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
@@ -1,7 +1,9 @@
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using HASS.Agent.Shared.Models.HomeAssistant;
+using Newtonsoft.Json;
namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue
{
@@ -12,7 +14,9 @@ public class ActiveWindowSensor : AbstractSingleValueSensor
{
private const string DefaultName = "activewindow";
- public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id) { }
+ private string _processName = string.Empty;
+
+ public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, true) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
@@ -31,16 +35,29 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
Icon = "mdi:window-maximize",
- Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
+ Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability",
+ Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes"
});
}
public override string GetState()
{
- return GetActiveWindowTitle();
+ var windowHandle = GetForegroundWindow();
+
+ var returnValue = GetWindowThreadProcessId(windowHandle, out var processId);
+ if (returnValue != 0 && processId != 0)
+ {
+ using var process = Process.GetProcessById(Convert.ToInt32(processId));
+ _processName = process.ProcessName ?? string.Empty;
+ }
+
+ return GetWindowTitle(windowHandle);
}
- public override string GetAttributes() => string.Empty;
+ public override string GetAttributes() => JsonConvert.SerializeObject(new
+ {
+ processName = _processName
+ });
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
@@ -51,14 +68,16 @@ public override string GetState()
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder builder, int count);
- private static string GetActiveWindowTitle()
+ [DllImport("user32.dll", SetLastError = true)]
+ static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
+
+ private static string GetWindowTitle(IntPtr windowHandle)
{
- var windowHandle = GetForegroundWindow();
var titleLength = GetWindowTextLength(windowHandle) + 1;
var builder = new StringBuilder(titleLength);
var windowTitle = GetWindowText(windowHandle, builder, titleLength) > 0 ? builder.ToString() : string.Empty;
- return windowTitle.Length > 255 ? windowTitle[..255]: windowTitle; //Note(Amadeo): to make sure we don't exceed HA limitation of 255 payload length
+ return windowTitle.Length > 255 ? windowTitle[..255] : windowTitle; //Note(Amadeo): to make sure we don't exceed HA limitation of 255 payload length
}
}
}
From d5d84ae8912184aae8c8f80026cf362cb62b3eeb Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 2 May 2024 20:54:39 +0200
Subject: [PATCH 21/45] Fix: added additional checks for management object
property or its value being null (#78)
This PR adds additional checks for WMI sensor where one of the property values can be null.
---
.../HomeAssistant/Sensors/WmiQuerySensor.cs | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
index 62858511..2e829aae 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
@@ -31,14 +31,14 @@ public WmiQuerySensor(string query, string scope = "", bool applyRounding = fals
ObjectQuery = new ObjectQuery(Query);
// use either default or provided scope
- var managementscope = !string.IsNullOrWhiteSpace(scope)
- ? new ManagementScope(scope)
+ var managementscope = !string.IsNullOrWhiteSpace(scope)
+ ? new ManagementScope(scope)
: new ManagementScope(@"\\localhost\");
// prepare searcher
Searcher = new ManagementObjectSearcher(managementscope, ObjectQuery);
}
-
+
public void Dispose() => Searcher?.Dispose();
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
@@ -58,7 +58,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
-
+
public override string GetState()
{
using var collection = Searcher.Get();
@@ -68,12 +68,13 @@ public override string GetState()
{
try
{
- if (!string.IsNullOrEmpty(retValue)) continue;
+ if (!string.IsNullOrEmpty(retValue))
+ continue;
using var managementObject = (ManagementObject)managementBaseObject;
foreach (var property in managementObject.Properties)
{
- retValue = property.Value.ToString();
+ retValue = property?.Value?.ToString() ?? string.Empty;
break;
}
}
@@ -84,7 +85,10 @@ public override string GetState()
}
// optionally apply rounding
- if (ApplyRounding && Round != null && double.TryParse(retValue, out var dblValue)) { retValue = Math.Round(dblValue, (int)Round).ToString(CultureInfo.CurrentCulture); }
+ if (ApplyRounding && Round != null && double.TryParse(retValue, out var dblValue))
+ {
+ retValue = Math.Round(dblValue, (int)Round).ToString(CultureInfo.CurrentCulture);
+ }
// done
return retValue;
From d8dd96a59b34e6080972d1ebf9351625813abad6 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 2 May 2024 23:00:53 +0200
Subject: [PATCH 22/45] Feature: advanced sensor settings (device_class,
unit_of_measurement, state_class) (#79)
This PR adds feature requested via #66 - ability to override device_class, unit_of_measurement and state_class for the sensors.
Important Note
Due to "tech debt" restrictions, despite advanced options being enabled for multiple value sensors (like audio sensor), the advanced settings will work only for single value sensors.
---
.../Settings/StoredSensors.cs | 72 +-
.../SingleValue/ActiveWindowSensor.cs | 2 +-
.../SingleValue/CurrentVolumeSensor.cs | 2 +-
.../GeneralSensors/SingleValue/DummySensor.cs | 2 +-
.../SingleValue/GpuLoadSensor.cs | 2 +-
.../SingleValue/GpuTemperatureSensor.cs | 2 +-
.../SingleValue/LastActiveSensor.cs | 2 +-
.../SingleValue/LastBootSensor.cs | 2 +-
.../LastSystemStateChangeSensor.cs | 2 +-
.../SingleValue/LoggedUserSensor.cs | 2 +-
.../SingleValue/LoggedUsersSensor.cs | 2 +-
.../SingleValue/MicrophoneActiveSensor.cs | 2 +-
.../SingleValue/MicrophoneProcessSensor.cs | 7 +-
.../SingleValue/NamedWindowSensor.cs | 2 +-
.../SingleValue/ProcessActiveSensor.cs | 2 +-
.../SingleValue/ScreenshotSensor.cs | 2 +-
.../SingleValue/ServiceStateSensor.cs | 2 +-
.../SingleValue/SessionStateSensor.cs | 2 +-
.../UserNotificationStateSensor.cs | 2 +-
.../SingleValue/WebcamActiveSensor.cs | 2 +-
.../SingleValue/WebcamProcessSensor.cs | 2 +-
.../SingleValue/WindowStateSensor.cs | 2 +-
.../SingleValue/CpuLoadSensor.cs | 2 +-
.../Sensors/PerformanceCounterSensor.cs | 2 +-
.../HomeAssistant/Sensors/PowershellSensor.cs | 2 +-
.../HomeAssistant/Sensors/WmiQuerySensor.cs | 13 +-
.../SingleValue/CurrentClockSpeedSensor.cs | 2 +-
.../SingleValue/MemoryUsageSensor.cs | 2 +-
.../Models/Config/ConfiguredSensor.cs | 1 +
.../AbstractSingleValueSensor.cs | 27 +-
.../HomeAssistant/DiscoveryConfigModel.cs | 6 +
.../Models/Internal/SensorAdvancedSettings.cs | 13 +
.../AdvancedSensorSettings.Designer.cs | 209 ++
.../SensorConfig/AdvancedSensorSettings.cs | 58 +
.../AdvancedSensorSettings.de.resx | 14 +
.../AdvancedSensorSettings.es.resx | 14 +
.../AdvancedSensorSettings.fr.resx | 14 +
.../AdvancedSensorSettings.nl.resx | 14 +
.../SensorConfig/AdvancedSensorSettings.resx | 147 ++
.../AdvancedSensorSettings.ru.resx | 14 +
.../AdvancedSensorSettings.tr.resx | 14 +
.../Forms/Sensors/SensorsMod.Designer.cs | 39 +-
.../HASS.Agent/Forms/Sensors/SensorsMod.cs | 2048 +++++++++--------
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 21 +
.../SingleValue/ActiveDesktopSensor.cs | 2 +-
.../SingleValue/BluetoothDevicesSensor.cs | 2 +-
.../SingleValue/BluetoothLeDevicesSensor.cs | 2 +-
.../SingleValue/GeoLocationSensor.cs | 2 +-
.../SingleValue/InternalDeviceSensor.cs | 2 +-
.../SingleValue/MonitorPowerStateSensor.cs | 2 +-
.../Localization/Languages.Designer.cs | 54 +
.../Resources/Localization/Languages.de.resx | 9 +
.../Resources/Localization/Languages.en.resx | 18 +
.../Resources/Localization/Languages.es.resx | 9 +
.../Resources/Localization/Languages.fr.resx | 9 +
.../Resources/Localization/Languages.nl.resx | 9 +
.../Resources/Localization/Languages.pl.resx | 9 +
.../Localization/Languages.pt-br.resx | 9 +
.../Resources/Localization/Languages.resx | 18 +
.../Resources/Localization/Languages.ru.resx | 9 +
.../Resources/Localization/Languages.sl.resx | 9 +
.../Resources/Localization/Languages.tr.resx | 9 +
.../HASS.Agent/Settings/StoredSensors.cs | 101 +-
63 files changed, 1944 insertions(+), 1135 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Models/Internal/SensorAdvancedSettings.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.Designer.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.de.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.es.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.fr.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.nl.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.ru.resx
create mode 100644 src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.tr.resx
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
index e2f2765d..5dfdb27a 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
@@ -90,82 +90,82 @@ await Task.Run(delegate
switch (sensor.Type)
{
case SensorType.UserNotificationStateSensor:
- abstractSensor = new UserNotificationStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new UserNotificationStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.DummySensor:
- abstractSensor = new DummySensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new DummySensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.CurrentClockSpeedSensor:
- abstractSensor = new CurrentClockSpeedSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CurrentClockSpeedSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.CpuLoadSensor:
- abstractSensor = new CpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.MemoryUsageSensor:
- abstractSensor = new MemoryUsageSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MemoryUsageSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.ActiveWindowSensor:
- abstractSensor = new ActiveWindowSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ActiveWindowSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.NamedWindowSensor:
- abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString());
+ abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastActiveSensor:
- abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastSystemStateChangeSensor:
- abstractSensor = new LastSystemStateChangeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastSystemStateChangeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastBootSensor:
- abstractSensor = new LastBootSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastBootSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WebcamActiveSensor:
- abstractSensor = new WebcamActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WebcamActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.MicrophoneActiveSensor:
- abstractSensor = new MicrophoneActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MicrophoneActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.SessionStateSensor:
- abstractSensor = new SessionStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new SessionStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.CurrentVolumeSensor:
- abstractSensor = new CurrentVolumeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CurrentVolumeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.GpuLoadSensor:
- abstractSensor = new GpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new GpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.GpuTemperatureSensor:
- abstractSensor = new GpuTemperatureSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new GpuTemperatureSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WmiQuerySensor:
- abstractSensor = new WmiQuerySensor(sensor.Query, sensor.Scope, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WmiQuerySensor(sensor.Query, sensor.Scope, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.PerformanceCounterSensor:
- abstractSensor = new PerformanceCounterSensor(sensor.Category, sensor.Counter, sensor.Instance, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new PerformanceCounterSensor(sensor.Category, sensor.Counter, sensor.Instance, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ProcessActiveSensor:
- abstractSensor = new ProcessActiveSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ProcessActiveSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ServiceStateSensor:
- abstractSensor = new ServiceStateSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ServiceStateSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LoggedUsersSensor:
- abstractSensor = new LoggedUsersSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LoggedUsersSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LoggedUserSensor:
- abstractSensor = new LoggedUserSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LoggedUserSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.PowershellSensor:
- abstractSensor = new PowershellSensor(sensor.Query, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new PowershellSensor(sensor.Query, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WindowStateSensor:
- abstractSensor = new WindowStateSensor(sensor.Query, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString());
+ abstractSensor = new WindowStateSensor(sensor.Query, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.MicrophoneProcessSensor:
- abstractSensor = new MicrophoneProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MicrophoneProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WebcamProcessSensor:
- abstractSensor = new WebcamProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WebcamProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
default:
Log.Error("[SETTINGS_SENSORS] [{name}] Unknown configured single-value sensor type: {type}", sensor.EntityName, sensor.Type.ToString());
@@ -235,6 +235,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Query = wmiSensor.Query,
ApplyRounding = wmiSensor.ApplyRounding,
Round = wmiSensor.Round,
+ AdvancedSettings = wmiSensor.AdvancedSettings
};
}
@@ -248,7 +249,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = namedWindowSensor.EntityName,
Type = type,
UpdateInterval = namedWindowSensor.UpdateIntervalSeconds,
- WindowName = namedWindowSensor.WindowName
+ WindowName = namedWindowSensor.WindowName,
+ AdvancedSettings = namedWindowSensor.AdvancedSettings
};
}
@@ -267,6 +269,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Instance = performanceCounterSensor.InstanceName,
ApplyRounding = performanceCounterSensor.ApplyRounding,
Round = performanceCounterSensor.Round,
+ AdvancedSettings = performanceCounterSensor.AdvancedSettings
};
}
@@ -280,7 +283,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = processActiveSensor.EntityName,
Type = type,
UpdateInterval = processActiveSensor.UpdateIntervalSeconds,
- Query = processActiveSensor.ProcessName
+ Query = processActiveSensor.ProcessName,
+ AdvancedSettings = processActiveSensor.AdvancedSettings
};
}
@@ -294,7 +298,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = serviceStateSensor.EntityName,
Type = type,
UpdateInterval = serviceStateSensor.UpdateIntervalSeconds,
- Query = serviceStateSensor.ServiceName
+ Query = serviceStateSensor.ServiceName,
+ AdvancedSettings = serviceStateSensor.AdvancedSettings
};
}
@@ -310,7 +315,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
UpdateInterval = powershellSensor.UpdateIntervalSeconds,
Query = powershellSensor.Command,
ApplyRounding = powershellSensor.ApplyRounding,
- Round = powershellSensor.Round
+ Round = powershellSensor.Round,
+ AdvancedSettings = powershellSensor.AdvancedSettings
};
}
@@ -324,7 +330,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = windowStateSensor.EntityName,
Type = type,
UpdateInterval = windowStateSensor.UpdateIntervalSeconds,
- Query = windowStateSensor.ProcessName
+ Query = windowStateSensor.ProcessName,
+ AdvancedSettings = windowStateSensor.AdvancedSettings
};
}
@@ -337,7 +344,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
EntityName = sensor.EntityName,
Name = sensor.EntityName,
Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ AdvancedSettings = sensor.AdvancedSettings
};
}
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
index 9751eef4..849a1a28 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
@@ -16,7 +16,7 @@ public class ActiveWindowSensor : AbstractSingleValueSensor
private string _processName = string.Empty;
- public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, true) { }
+ public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
index 7481c3e9..b327c891 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
@@ -12,7 +12,7 @@ public class CurrentVolumeSensor : AbstractSingleValueSensor
{
private const string DefaultName = "currentvolume";
- public CurrentVolumeSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id) { }
+ public CurrentVolumeSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
index 767f17ca..2e298eab 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
@@ -9,7 +9,7 @@ public class DummySensor : AbstractSingleValueSensor
{
private const string DefaultName = "dummy";
- public DummySensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 5, id) { }
+ public DummySensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 5, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
index 1e075b67..16d1ce0b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
@@ -14,7 +14,7 @@ public class GpuLoadSensor : AbstractSingleValueSensor
private const string DefaultName = "gpuload";
private readonly IHardware _gpu;
- public GpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public GpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
_gpu = HardwareManager.Hardware.FirstOrDefault(
h => h.HardwareType == HardwareType.GpuAmd ||
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
index 574f1429..96a209ea 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
@@ -14,7 +14,7 @@ public class GpuTemperatureSensor : AbstractSingleValueSensor
private const string DefaultName = "gputemperature";
private readonly IHardware _gpu;
- public GpuTemperatureSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public GpuTemperatureSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
_gpu = HardwareManager.Hardware.FirstOrDefault(
h => h.HardwareType == HardwareType.GpuAmd ||
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs
index 80473337..bd67b9d6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs
@@ -23,7 +23,7 @@ public class LastActiveSensor : AbstractSingleValueSensor
public bool ApplyRounding { get; private set; }
public int Round { get; private set; }
- public LastActiveSensor(bool updateOnResume, int? updateOnResumeTimeWindow, int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public LastActiveSensor(bool updateOnResume, int? updateOnResumeTimeWindow, int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
ApplyRounding = updateOnResume;
Round = updateOnResumeTimeWindow ?? 30;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs
index 2f24b0a2..c8e6429c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs
@@ -13,7 +13,7 @@ public class LastBootSensor : AbstractSingleValueSensor
{
private const string DefaultName = "lastboot";
- public LastBootSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id) { }
+ public LastBootSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs
index 1e24dbf9..482c0fad 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs
@@ -10,7 +10,7 @@ public class LastSystemStateChangeSensor : AbstractSingleValueSensor
{
private const string DefaultName = "lastsystemstatechange";
- public LastSystemStateChangeSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id) { }
+ public LastSystemStateChangeSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs
index 487377d8..ba14ba5b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs
@@ -10,7 +10,7 @@ public class LoggedUserSensor : AbstractSingleValueSensor
{
private const string DefaultName = "loggeduser";
- public LoggedUserSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id) { }
+ public LoggedUserSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs
index d0940f9e..e10c958e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs
@@ -18,7 +18,7 @@ public class LoggedUsersSensor : AbstractSingleValueSensor
{
private const string DefaultName = "loggedusers";
- public LoggedUsersSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id) { }
+ public LoggedUsersSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs
index 4198a9fe..5d630ef6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs
@@ -11,7 +11,7 @@ public class MicrophoneActiveSensor : AbstractSingleValueSensor
{
private const string DefaultName = "microphoneactive";
- public MicrophoneActiveSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public MicrophoneActiveSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
Domain = "binary_sensor";
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
index 7c92e3e3..0b57d35c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
@@ -16,8 +16,11 @@ public class MicrophoneProcessSensor : AbstractSingleValueSensor
private const string _lastUsedTimeStop = "LastUsedTimeStop";
private const string _regKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone";
-
- public MicrophoneProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, true) { }
+
+ public MicrophoneProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, true, advancedSettings: advancedSettings)
+ {
+ //
+ }
private readonly Dictionary _processes = new();
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs
index 2d18cb25..b3f494c7 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs
@@ -12,7 +12,7 @@ public class NamedWindowSensor : AbstractSingleValueSensor
private const string DefaultName = "namedwindow";
public string WindowName { get; protected set; }
- public NamedWindowSensor(string windowName, string entityName = DefaultName, string name = DefaultName, int? updateInterval = 10, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public NamedWindowSensor(string windowName, string entityName = DefaultName, string name = DefaultName, int? updateInterval = 10, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
Domain = "binary_sensor";
WindowName = windowName;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
index 731f05a4..594e2c5d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
@@ -13,7 +13,7 @@ public class ProcessActiveSensor : AbstractSingleValueSensor
private const string DefaultName = "processactive";
public string ProcessName { get; protected set; }
- public ProcessActiveSensor(string processName, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public ProcessActiveSensor(string processName, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
ProcessName = processName;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
index 0fc98195..f3305b50 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
@@ -21,7 +21,7 @@ public class ScreenshotSensor : AbstractSingleValueSensor
public int ScreenIndex;
- public ScreenshotSensor(string screenIndex = "0", int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id)
+ public ScreenshotSensor(string screenIndex = "0", int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings)
{
ScreenIndex = int.TryParse(screenIndex, out var parsedScreenIndex) ? parsedScreenIndex : 0;
Domain = "camera";
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs
index 0b11de55..f3655606 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs
@@ -12,7 +12,7 @@ public class ServiceStateSensor : AbstractSingleValueSensor
private const string DefaultName = "servicestate";
public string ServiceName { get; protected set; }
- public ServiceStateSensor(string serviceName, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public ServiceStateSensor(string serviceName, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
ServiceName = serviceName;
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs
index 71671e8d..9f698e0e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs
@@ -10,7 +10,7 @@ public class SessionStateSensor : AbstractSingleValueSensor
{
private const string DefaultName = "sessionstate";
- public SessionStateSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id) { }
+ public SessionStateSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs
index 6567af7c..ee66f883 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs
@@ -11,7 +11,7 @@ public class UserNotificationStateSensor : AbstractSingleValueSensor
{
private const string DefaultName = "notificationstate";
- public UserNotificationStateSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id) { }
+ public UserNotificationStateSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs
index f1729cf9..372f8a1c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs
@@ -11,7 +11,7 @@ public class WebcamActiveSensor : AbstractSingleValueSensor
{
private const string DefaultName = "webcamactive";
- public WebcamActiveSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public WebcamActiveSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
Domain = "binary_sensor";
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
index 6f98ca4e..c698f90c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
@@ -14,7 +14,7 @@ public class WebcamProcessSensor : AbstractSingleValueSensor
{
private const string DefaultName = "webcamprocess";
- public WebcamProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool useAttributes = true) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, useAttributes)
+ public WebcamProcessSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, true, advancedSettings: advancedSettings)
{
//
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs
index 17ba6c70..b07eccc6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs
@@ -15,7 +15,7 @@ public class WindowStateSensor : AbstractSingleValueSensor
private const string DefaultName = "windowstate";
public string ProcessName { get; protected set; }
- public WindowStateSensor(string processName, string entityName = DefaultName, string name = DefaultName, int? updateInterval = 10, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public WindowStateSensor(string processName, string entityName = DefaultName, string name = DefaultName, int? updateInterval = 10, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
ProcessName = processName;
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
index f7ab6dbd..d55b4664 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
@@ -9,7 +9,7 @@ public class CpuLoadSensor : PerformanceCounterSensor
{
private const string DefaultName = "cpuload";
- public CpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null) : base("Processor", "% Processor Time", "_Total", applyRounding, round, updateInterval ?? 30, entityName ?? DefaultName, name ?? null, id) { }
+ public CpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null, string advancedSettings = default) : base("Processor", "% Processor Time", "_Total", applyRounding, round, updateInterval ?? 30, entityName ?? DefaultName, name ?? null, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
index 3c429b8d..2aa2f9d1 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
@@ -22,7 +22,7 @@ public class PerformanceCounterSensor : AbstractSingleValueSensor
public bool ApplyRounding { get; private set; }
public int? Round { get; private set; }
- public PerformanceCounterSensor(string categoryName, string counterName, string instanceName, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public PerformanceCounterSensor(string categoryName, string counterName, string instanceName, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
CategoryName = categoryName;
CounterName = counterName;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
index ccbd2859..832f7e66 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs
@@ -18,7 +18,7 @@ public class PowershellSensor : AbstractSingleValueSensor
public bool ApplyRounding { get; private set; }
public int? Round { get; private set; }
- public PowershellSensor(string command, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public PowershellSensor(string command, bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, advancedSettings: advancedSettings)
{
Command = command;
ApplyRounding = applyRounding;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
index 2e829aae..14bf596d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
@@ -2,6 +2,8 @@
using System.Globalization;
using System.Management;
using HASS.Agent.Shared.Models.HomeAssistant;
+using HASS.Agent.Shared.Models.Internal;
+using Newtonsoft.Json;
namespace HASS.Agent.Shared.HomeAssistant.Sensors
{
@@ -17,15 +19,18 @@ public class WmiQuerySensor : AbstractSingleValueSensor
public bool ApplyRounding { get; private set; }
public int? Round { get; private set; }
+ public string AdvancedSettings { get; private set; }
+
protected readonly ObjectQuery ObjectQuery;
protected readonly ManagementObjectSearcher Searcher;
- public WmiQuerySensor(string query, string scope = "", bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id)
+ public WmiQuerySensor(string query, string scope = "", bool applyRounding = false, int? round = null, int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 10, id, false, advancedSettings)
{
Query = query;
Scope = scope;
ApplyRounding = applyRounding;
Round = round;
+ AdvancedSettings = advancedSettings;
// prepare query
ObjectQuery = new ObjectQuery(Query);
@@ -43,10 +48,12 @@ public WmiQuerySensor(string query, string scope = "", bool applyRounding = fals
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
- if (Variables.MqttManager == null) return null;
+ if (Variables.MqttManager == null)
+ return null;
var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
- if (deviceConfig == null) return null;
+ if (deviceConfig == null)
+ return null;
return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
index b431dc14..82e34202 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
@@ -17,7 +17,7 @@ public class CurrentClockSpeedSensor : WmiQuerySensor
private protected DateTime LastFetched = DateTime.MinValue;
private protected string LastValue = string.Empty;
- public CurrentClockSpeedSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null) : base(string.Empty, string.Empty, applyRounding, round, updateInterval ?? 300, entityName ?? DefaultName, name ?? null, id)
+ public CurrentClockSpeedSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null, string advancedSettings = default) : base(string.Empty, string.Empty, applyRounding, round, updateInterval ?? 300, entityName ?? DefaultName, name ?? null, id, advancedSettings: advancedSettings)
=> _managementObject = new ManagementObject("Win32_Processor.DeviceID='CPU0'");
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
index fa87542b..77b61ba6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
@@ -11,7 +11,7 @@ public class MemoryUsageSensor : WmiQuerySensor
{
private const string DefaultName = "memoryusage";
- public MemoryUsageSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null) : base("SELECT FreePhysicalMemory,TotalVisibleMemorySize FROM Win32_OperatingSystem", string.Empty, applyRounding, round, updateInterval ?? 30, entityName ?? DefaultName, name ?? null, id) { }
+ public MemoryUsageSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, bool applyRounding = false, int? round = null, string advancedSettings = default) : base("SELECT FreePhysicalMemory,TotalVisibleMemorySize FROM Win32_OperatingSystem", string.Empty, applyRounding, round, updateInterval ?? 30, entityName ?? DefaultName, name ?? null, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/Config/ConfiguredSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/Config/ConfiguredSensor.cs
index 1ee66b4e..13bc204b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/Config/ConfiguredSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/Config/ConfiguredSensor.cs
@@ -26,6 +26,7 @@ public class ConfiguredSensor
public bool IgnoreAvailability { get; set; } = false;
public bool ApplyRounding { get; set; } = false;
public int? Round { get; set; }
+ public string AdvancedSettings { get; set; } = string.Empty;
public static ConfiguredSensor FromLAB02(ConfiguredSensorLAB02 oldConfig)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
index 25e41799..eb7ee64c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
@@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
+using HASS.Agent.Shared.Models.Internal;
using MQTTnet;
+using Newtonsoft.Json;
using Serilog;
namespace HASS.Agent.Shared.Models.HomeAssistant;
@@ -10,13 +12,17 @@ namespace HASS.Agent.Shared.Models.HomeAssistant;
///
public abstract class AbstractSingleValueSensor : AbstractDiscoverable
{
+ private SensorAdvancedSettings _advancedInfo;
+
public int UpdateIntervalSeconds { get; protected set; }
public DateTime? LastUpdated { get; protected set; }
public string PreviousPublishedState { get; protected set; } = string.Empty;
public string PreviousPublishedAttributes { get; protected set; } = string.Empty;
- protected AbstractSingleValueSensor(string entityName, string name, int updateIntervalSeconds = 10, string id = default, bool useAttributes = false)
+ public string AdvancedSettings { get; private set; }
+
+ protected AbstractSingleValueSensor(string entityName, string name, int updateIntervalSeconds = 10, string id = default, bool useAttributes = false, string advancedSettings = default)
{
Id = id == null || id == Guid.Empty.ToString() ? Guid.NewGuid().ToString() : id;
EntityName = entityName;
@@ -24,13 +30,30 @@ protected AbstractSingleValueSensor(string entityName, string name, int updateIn
UpdateIntervalSeconds = updateIntervalSeconds;
Domain = "sensor";
UseAttributes = useAttributes;
+
+ if (!string.IsNullOrWhiteSpace(advancedSettings))
+ {
+ _advancedInfo = JsonConvert.DeserializeObject(advancedSettings);
+ }
}
protected SensorDiscoveryConfigModel AutoDiscoveryConfigModel;
protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
{
AutoDiscoveryConfigModel = config;
- return config;
+
+ // overwrite with advanced settings
+ if (_advancedInfo != null)
+ {
+ if (!string.IsNullOrWhiteSpace(_advancedInfo.DeviceClass))
+ AutoDiscoveryConfigModel.Device_class = _advancedInfo.DeviceClass;
+ if (!string.IsNullOrWhiteSpace(_advancedInfo.UnitOfMeasurement))
+ AutoDiscoveryConfigModel.Unit_of_measurement = _advancedInfo.UnitOfMeasurement;
+ if (!string.IsNullOrWhiteSpace(_advancedInfo.StateClass))
+ AutoDiscoveryConfigModel.State_class = _advancedInfo.StateClass;
+ }
+
+ return AutoDiscoveryConfigModel;
}
public override void ClearAutoDiscoveryConfig() => AutoDiscoveryConfigModel = null;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
index 4a98233b..8d2cd9f5 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs
@@ -52,6 +52,12 @@ public class SensorDiscoveryConfigModel : DiscoveryConfigModel
///
public string Device_class { get; set; }
+ ///
+ /// (Optional) The state class of the sensor. See https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes for options.
+ ///
+ ///
+ public string State_class { get; set; }
+
///
/// (Optional) Defines the number of seconds after the sensor’s state expires, if it’s not updated. After expiry, the sensor’s state becomes unavailable. Defaults to 0 in hass.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/SensorAdvancedSettings.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/SensorAdvancedSettings.cs
new file mode 100644
index 00000000..77aed989
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/SensorAdvancedSettings.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Models.Internal;
+public class SensorAdvancedSettings
+{
+ public string DeviceClass { get; set; }
+ public string UnitOfMeasurement { get; set; }
+ public string StateClass { get; set; }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.Designer.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.Designer.cs
new file mode 100644
index 00000000..0f922ff9
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.Designer.cs
@@ -0,0 +1,209 @@
+
+using HASS.Agent.Resources.Localization;
+
+namespace HASS.Agent.Forms.Commands.CommandConfig
+{
+ partial class AdvancedSensorSettings
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ var resources = new System.ComponentModel.ComponentResourceManager(typeof(AdvancedSensorSettings));
+ BtnSave = new Syncfusion.WinForms.Controls.SfButton();
+ LblInfo1 = new Label();
+ LblDeviceClass = new Label();
+ TbDeviceClass = new TextBox();
+ LblUnitOfMeasurement = new Label();
+ TbUnitOfMeasurement = new TextBox();
+ LblStateClass = new Label();
+ TbStateClass = new TextBox();
+ SuspendLayout();
+ //
+ // BtnSave
+ //
+ BtnSave.AccessibleDescription = "Saves and closes the window.";
+ BtnSave.AccessibleName = "Save";
+ BtnSave.AccessibleRole = AccessibleRole.PushButton;
+ BtnSave.BackColor = Color.FromArgb(63, 63, 70);
+ BtnSave.Dock = DockStyle.Bottom;
+ BtnSave.Font = new Font("Segoe UI", 10F);
+ BtnSave.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnSave.Location = new Point(0, 299);
+ BtnSave.Name = "BtnSave";
+ BtnSave.Size = new Size(349, 37);
+ BtnSave.Style.BackColor = Color.FromArgb(63, 63, 70);
+ BtnSave.Style.FocusedBackColor = Color.FromArgb(63, 63, 70);
+ BtnSave.Style.FocusedForeColor = Color.FromArgb(241, 241, 241);
+ BtnSave.Style.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnSave.Style.HoverBackColor = Color.FromArgb(63, 63, 70);
+ BtnSave.Style.HoverForeColor = Color.FromArgb(241, 241, 241);
+ BtnSave.Style.PressedForeColor = Color.Black;
+ BtnSave.TabIndex = 8;
+ BtnSave.Text = Languages.WebViewCommandConfig_BtnSave;
+ BtnSave.UseVisualStyleBackColor = false;
+ BtnSave.Click += BtnSave_Click;
+ //
+ // LblInfo1
+ //
+ LblInfo1.AccessibleDescription = "Warning regarding advanced settings";
+ LblInfo1.AccessibleName = "Advanced settings warning";
+ LblInfo1.AccessibleRole = AccessibleRole.StaticText;
+ LblInfo1.Font = new Font("Segoe UI", 10F);
+ LblInfo1.Location = new Point(12, 13);
+ LblInfo1.Name = "LblInfo1";
+ LblInfo1.Size = new Size(325, 45);
+ LblInfo1.TabIndex = 3;
+ LblInfo1.Text = Languages.AdvancedSensorConfig_Warning;
+ LblInfo1.TextAlign = ContentAlignment.MiddleCenter;
+ //
+ // LblDeviceClass
+ //
+ LblDeviceClass.AccessibleDescription = "Device class textbox";
+ LblDeviceClass.AccessibleName = "Device class";
+ LblDeviceClass.AccessibleRole = AccessibleRole.StaticText;
+ LblDeviceClass.AutoSize = true;
+ LblDeviceClass.Font = new Font("Segoe UI", 10F);
+ LblDeviceClass.Location = new Point(34, 81);
+ LblDeviceClass.Name = "LblDeviceClass";
+ LblDeviceClass.Size = new Size(81, 19);
+ LblDeviceClass.TabIndex = 5;
+ LblDeviceClass.Text = Languages.AdvancedSensorConfig_LblDeviceClass;
+ //
+ // TbDeviceClass
+ //
+ TbDeviceClass.AccessibleDescription = "Device class of the sensor";
+ TbDeviceClass.AccessibleName = "URL";
+ TbDeviceClass.AccessibleRole = AccessibleRole.Text;
+ TbDeviceClass.BackColor = Color.FromArgb(63, 63, 70);
+ TbDeviceClass.BorderStyle = BorderStyle.FixedSingle;
+ TbDeviceClass.Font = new Font("Segoe UI", 10F);
+ TbDeviceClass.ForeColor = Color.FromArgb(241, 241, 241);
+ TbDeviceClass.Location = new Point(34, 103);
+ TbDeviceClass.Name = "TbDeviceClass";
+ TbDeviceClass.Size = new Size(282, 25);
+ TbDeviceClass.TabIndex = 0;
+ //
+ // LblUnitOfMeasurement
+ //
+ LblUnitOfMeasurement.AccessibleDescription = "Unit of measurement textbox";
+ LblUnitOfMeasurement.AccessibleName = "Unit of measurement";
+ LblUnitOfMeasurement.AccessibleRole = AccessibleRole.StaticText;
+ LblUnitOfMeasurement.AutoSize = true;
+ LblUnitOfMeasurement.Font = new Font("Segoe UI", 10F);
+ LblUnitOfMeasurement.Location = new Point(34, 143);
+ LblUnitOfMeasurement.Name = "LblUnitOfMeasurement";
+ LblUnitOfMeasurement.Size = new Size(139, 19);
+ LblUnitOfMeasurement.TabIndex = 10;
+ LblUnitOfMeasurement.Text = Languages.AdvancedSensorConfig_LblUnitOfMeasurement;
+ //
+ // TbUnitOfMeasurement
+ //
+ TbUnitOfMeasurement.AccessibleDescription = "Device class of the sensor";
+ TbUnitOfMeasurement.AccessibleName = "URL";
+ TbUnitOfMeasurement.AccessibleRole = AccessibleRole.Text;
+ TbUnitOfMeasurement.BackColor = Color.FromArgb(63, 63, 70);
+ TbUnitOfMeasurement.BorderStyle = BorderStyle.FixedSingle;
+ TbUnitOfMeasurement.Font = new Font("Segoe UI", 10F);
+ TbUnitOfMeasurement.ForeColor = Color.FromArgb(241, 241, 241);
+ TbUnitOfMeasurement.Location = new Point(34, 165);
+ TbUnitOfMeasurement.Name = "TbUnitOfMeasurement";
+ TbUnitOfMeasurement.Size = new Size(282, 25);
+ TbUnitOfMeasurement.TabIndex = 9;
+ //
+ // LblStateClass
+ //
+ LblStateClass.AccessibleDescription = "State class textbox";
+ LblStateClass.AccessibleName = "State class";
+ LblStateClass.AccessibleRole = AccessibleRole.StaticText;
+ LblStateClass.AutoSize = true;
+ LblStateClass.Font = new Font("Segoe UI", 10F);
+ LblStateClass.Location = new Point(34, 206);
+ LblStateClass.Name = "LblStateClass";
+ LblStateClass.Size = new Size(72, 19);
+ LblStateClass.TabIndex = 12;
+ LblStateClass.Text = Languages.AdvancedSensorConfig_LblStateClass;
+ //
+ // TbStateClass
+ //
+ TbStateClass.AccessibleDescription = "State class of the sensor";
+ TbStateClass.AccessibleName = "URL";
+ TbStateClass.AccessibleRole = AccessibleRole.Text;
+ TbStateClass.BackColor = Color.FromArgb(63, 63, 70);
+ TbStateClass.BorderStyle = BorderStyle.FixedSingle;
+ TbStateClass.Font = new Font("Segoe UI", 10F);
+ TbStateClass.ForeColor = Color.FromArgb(241, 241, 241);
+ TbStateClass.Location = new Point(34, 228);
+ TbStateClass.Name = "TbStateClass";
+ TbStateClass.Size = new Size(282, 25);
+ TbStateClass.TabIndex = 11;
+ //
+ // AdvancedSensorConfig
+ //
+ AccessibleDescription = "Configuration for the webview command, like position and url.";
+ AccessibleName = "Webview configuration";
+ AccessibleRole = AccessibleRole.Window;
+ AutoScaleDimensions = new SizeF(96F, 96F);
+ AutoScaleMode = AutoScaleMode.Dpi;
+ BackColor = Color.FromArgb(45, 45, 48);
+ CaptionBarColor = Color.FromArgb(63, 63, 70);
+ CaptionFont = new Font("Segoe UI", 10F);
+ CaptionForeColor = Color.FromArgb(241, 241, 241);
+ ClientSize = new Size(349, 336);
+ Controls.Add(LblStateClass);
+ Controls.Add(TbStateClass);
+ Controls.Add(LblUnitOfMeasurement);
+ Controls.Add(TbUnitOfMeasurement);
+ Controls.Add(BtnSave);
+ Controls.Add(LblDeviceClass);
+ Controls.Add(TbDeviceClass);
+ Controls.Add(LblInfo1);
+ DoubleBuffered = true;
+ ForeColor = Color.FromArgb(241, 241, 241);
+ Icon = (Icon)resources.GetObject("$this.Icon");
+ MaximizeBox = false;
+ MetroColor = Color.FromArgb(63, 63, 70);
+ Name = "AdvancedSensorConfig";
+ ShowMaximizeBox = false;
+ ShowMinimizeBox = false;
+ StartPosition = FormStartPosition.CenterScreen;
+ Text = Languages.AdvancedSensorConfig_Title;
+ Load += AdvancedSensorConfig_Load;
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+ private Syncfusion.WinForms.Controls.SfButton BtnSave;
+ private System.Windows.Forms.Label LblInfo1;
+ private Label LblDeviceClass;
+ private TextBox TbDeviceClass;
+ private Label LblUnitOfMeasurement;
+ private TextBox TbUnitOfMeasurement;
+ private Label LblStateClass;
+ private TextBox TbStateClass;
+ }
+}
+
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.cs
new file mode 100644
index 00000000..abff8f7c
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.cs
@@ -0,0 +1,58 @@
+using HASS.Agent.Functions;
+using HASS.Agent.Models.Internal;
+using HASS.Agent.Resources.Localization;
+using HASS.Agent.Shared.Models.Internal;
+using Newtonsoft.Json;
+using Serilog;
+using Syncfusion.Windows.Forms;
+using Syncfusion.WinForms.Controls.Styles;
+
+namespace HASS.Agent.Forms.Commands.CommandConfig;
+
+public partial class AdvancedSensorSettings : MetroForm
+{
+
+ public SensorAdvancedSettings AdvancedSettings { get; private set; } = null;
+
+ public AdvancedSensorSettings(string wmiAdvancedInfo = "")
+ {
+ InitializeComponent();
+
+ if (string.IsNullOrEmpty(wmiAdvancedInfo))
+ return;
+
+ var advancedInfo = JsonConvert.DeserializeObject(wmiAdvancedInfo);
+ if (advancedInfo == null)
+ return;
+
+ AdvancedSettings = advancedInfo;
+ }
+
+ private void AdvancedSensorConfig_Load(object sender, EventArgs e)
+ {
+ CaptionBarHeight = 26;
+
+ if (AdvancedSettings != null)
+ SetStoredVariables();
+
+ Opacity = 100;
+ }
+
+ private void SetStoredVariables()
+ {
+ TbDeviceClass.Text = AdvancedSettings.DeviceClass;
+ TbUnitOfMeasurement.Text = AdvancedSettings.UnitOfMeasurement;
+ TbStateClass.Text = AdvancedSettings.StateClass;
+ }
+
+ private void BtnSave_Click(object sender, EventArgs e)
+ {
+ AdvancedSettings ??= new SensorAdvancedSettings();
+
+ AdvancedSettings.DeviceClass = TbDeviceClass.Text;
+ AdvancedSettings.UnitOfMeasurement = TbUnitOfMeasurement.Text;
+ AdvancedSettings.StateClass = TbStateClass.Text;
+
+ DialogResult = DialogResult.OK;
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.de.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.de.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.de.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.es.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.es.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.es.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.fr.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.fr.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.fr.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.nl.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.nl.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.nl.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.resx
new file mode 100644
index 00000000..9cfd6d5f
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.resx
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAEAMDAAAAEAIAAQBQAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAwAAAAMAgGAAAAVwL5hwAAAAFz
+ UkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEpUlEQVRoQ+2Ya4hU
+ ZRjHn7nujFnGZmksbVlecLMkpTXCiCQoiD5VHyzoQgRlEdEHMQoiKBI/2AW6YHQlCKIoqCCICEnKUCIr
+ VyzLsix101xT5z7T+3vPvOPb2TMz55w5zrQwPxjPOHP2nP//ub3vnNhVnx6ryRQmXj9OWfoGek3fQK/p
+ GwjKlWcl5JklGXl00YAMT+v89l1dB244Jyn3zkvX/ydyqFiTB78uyJ7j1fonwelaBmzxm8YrWvRgOibr
+ l3SWia4YsMVvPFCRx74vNCLfqYmTbsAt/vHtBamoorXLBxOr5qX0OUE5qQZGTot7ijdg4rP9Ff1++JT/
+ QQZmpGL1dw7XnJ3Ux+8OVyeJB7Jz2xwn8m/sLuljUCIxQP2+cGlG3rsiKy8vy8hFpzuX3XbYie7IjLgs
+ PzOh3xvs0nrux6J8/GdZvw9KxwYQTxMuODWuIzxHlcLaxY4JyoZXQiXmkQsH9BoAbvHv/hZOPHRkwIin
+ CWnG2zfndLlklU5MEHlKxzaxemE6MvEQ2oBbPBNlb64ma7blW5q4tt4XUYiH0CvxhtGMzJ0eb4hnohiM
+ eMoop9oAU2MTVS3+8pkJ+fCPsnz5l9MfnRLKANF/7bKMrnnKhsi78TJBZqImVAkRdURTEqsXDmixbmzR
+ tpmoCX3FdTsKWiSiENcrE6GvhijEhTHBqI2KwFfi5ozBFbMSujHDmLjj/HD7Hi8CGWAhYvqwEDHTefk1
+ YTM0LaYnWBT4nkKIRzCNu+totSHAbNKY90a8XV5gSsdd/3z/9p6SvPlLadI+yS++wmCLR/A9W/J6IbK/
+ a5YJL/GsGZzHd2zm1l/inMv1pyfVPwFoa8At3uwqWUVtEytmJT0b2xa/L1/T5m/clJOHv528O127eEDe
+ WZ6V2Rn/JloaGD3DW7wBE/w8hGXqXHCbsCP/vDK885+q/uyJi53rmvPT8ZgsHUyoY/1kn7Q8/VaV3mbi
+ gWY22+TPx0/saxB139a8fLC3rBc9GC/UtFmTGUrGNrt08ESWePmlpYGJ+v6GDZs7MvaWmExg0mb3sao8
+ tbMoT253yozabiYebhp2RuuWg/+9TjtaGuBXkl3P3Bjc4k0veIER4G+biV95bkr/niiqU9/6taQb+6ER
+ 5/rtaGmAerXrGQHczK94QNT7vzvl5SWeYNx1gRN9RiqZwgBDwc9EamkA7BtiwtzMj3jDSz8VdS/Y16K3
+ 7GBQgq+rjNN38Mm+shwtt+8F3wsZTxhWzU/rEYd4Uh0E+iinpgCDgOjecl5KhrJOhBG7bkdRDwQeOXIO
+ 2/RELKbe1zy364auPVok4verAFw9O9noJSJM1AmIvd4wvfiMBwVw3cbj+uhF2xKKCjJw/ZAjnpH6ys8l
+ ufmLvGxWv8xoWCJvRvazPxTlgQUpfe7YRL1ZmtC1DAA7WcYxwwFoVvNcCD5SPzWfVqP37rlp3dyU0p1f
+ 5RtriRddywAwUo14mK9GJ3zzd0UvfK+qrFBGiAeGRCvx0NUMuGFMmqcaNPSG0awuGyL/4i5/Ty26mgE3
+ NLGJMOvFeKEqY0ecbYgf8dDTDERBTzMQBX0DvaZvoNdMcQMi/wKHssFyJvHDuwAAAABJRU5ErkJggg==
+
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.ru.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.ru.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.ru.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.tr.resx b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.tr.resx
new file mode 100644
index 00000000..deb06fac
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorConfig/AdvancedSensorSettings.tr.resx
@@ -0,0 +1,14 @@
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.Designer.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.Designer.cs
index ee6b3fed..e2070687 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.Designer.cs
@@ -66,7 +66,8 @@ private void InitializeComponent()
PbService = new PictureBox();
LblSpecificClient = new Label();
BtnTest = new Syncfusion.WinForms.Controls.SfButton();
- CbNetworkCard = new ComboBox();
+ BtnAdvSettings = new Syncfusion.WinForms.Controls.SfButton();
+ CbNetworkCard = new ComboBox();
NumRound = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt();
LblDigits = new Label();
CbApplyRounding = new CheckBox();
@@ -521,10 +522,34 @@ private void InitializeComponent()
BtnTest.UseVisualStyleBackColor = false;
BtnTest.Visible = false;
BtnTest.Click += BtnTest_Click;
- //
- // CbNetworkCard
- //
- CbNetworkCard.AccessibleDescription = "List of available network cards.";
+ //
+ // BtnAdvSettings
+ //
+ BtnAdvSettings.AccessibleDescription = "Shows popup with advanced sensor settings.";
+ BtnAdvSettings.AccessibleName = "Advanced settings";
+ BtnAdvSettings.AccessibleRole = AccessibleRole.PushButton;
+ BtnAdvSettings.BackColor = Color.FromArgb(63, 63, 70);
+ BtnAdvSettings.Font = new Font("Segoe UI", 10F, FontStyle.Regular, GraphicsUnit.Point);
+ BtnAdvSettings.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnAdvSettings.Location = new Point(955, 446);
+ BtnAdvSettings.Name = "BtnTest";
+ BtnAdvSettings.Size = new Size(354, 25);
+ BtnAdvSettings.Style.BackColor = Color.FromArgb(63, 63, 70);
+ BtnAdvSettings.Style.FocusedBackColor = Color.FromArgb(63, 63, 70);
+ BtnAdvSettings.Style.FocusedForeColor = Color.FromArgb(241, 241, 241);
+ BtnAdvSettings.Style.ForeColor = Color.FromArgb(241, 241, 241);
+ BtnAdvSettings.Style.HoverBackColor = Color.FromArgb(63, 63, 70);
+ BtnAdvSettings.Style.HoverForeColor = Color.FromArgb(241, 241, 241);
+ BtnAdvSettings.Style.PressedForeColor = Color.Black;
+ BtnAdvSettings.TabIndex = 48;
+ BtnAdvSettings.Text = Languages.SensorsMod_BtnAdvancedSettings;
+ BtnAdvSettings.UseVisualStyleBackColor = false;
+ BtnAdvSettings.Visible = false;
+ BtnAdvSettings.Click += BtnAdvSettings_Click;
+ //
+ // CbNetworkCard
+ //
+ CbNetworkCard.AccessibleDescription = "List of available network cards.";
CbNetworkCard.AccessibleName = "Network cards";
CbNetworkCard.AccessibleRole = AccessibleRole.DropList;
CbNetworkCard.BackColor = Color.FromArgb(63, 63, 70);
@@ -688,6 +713,7 @@ private void InitializeComponent()
Controls.Add(LblDescription);
Controls.Add(LblSetting1);
Controls.Add(BtnStore);
+ Controls.Add(BtnAdvSettings);
Controls.Add(TbSetting1);
Controls.Add(LblType);
Controls.Add(LblSeconds);
@@ -755,7 +781,8 @@ private void InitializeComponent()
private Label LblSpecificClient;
private ColumnHeader ClmId;
private Syncfusion.WinForms.Controls.SfButton BtnTest;
- private ComboBox CbNetworkCard;
+ private Syncfusion.WinForms.Controls.SfButton BtnAdvSettings;
+ private ComboBox CbNetworkCard;
private Syncfusion.Windows.Forms.Tools.NumericUpDownExt NumRound;
private Label LblDigits;
internal CheckBox CbApplyRounding;
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
index 987cbad3..e852dbbf 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs
@@ -13,528 +13,530 @@
using HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue;
using HASS.Agent.Managers.DeviceSensors;
using HASS.Agent.Managers;
+using HASS.Agent.Forms.Commands.CommandConfig;
+using Newtonsoft.Json;
namespace HASS.Agent.Forms.Sensors
{
- public partial class SensorsMod : MetroForm
- {
- internal readonly ConfiguredSensor Sensor;
-
- private readonly bool _serviceMode;
- private readonly string _serviceDeviceName;
+ public partial class SensorsMod : MetroForm
+ {
+ internal readonly ConfiguredSensor Sensor;
- private bool _interfaceLockedWrongType;
- private bool _loading = true;
+ private readonly bool _serviceMode;
+ private readonly string _serviceDeviceName;
- private readonly Dictionary _networkCards = new();
- private readonly Dictionary _internalSensors = new();
-
- private SensorType _selectedSensorType = SensorType.ActiveWindowSensor;
+ private bool _interfaceLockedWrongType;
+ private bool _loading = true;
- public SensorsMod(ConfiguredSensor sensor, bool serviceMode = false, string serviceDeviceName = "")
- {
- Sensor = sensor;
+ private readonly Dictionary _networkCards = new();
+ private readonly Dictionary _internalSensors = new();
- _serviceMode = serviceMode;
- _serviceDeviceName = serviceDeviceName;
-
- InitializeComponent();
-
- BindListViewTheme();
-
- BindComboBoxTheme();
- }
-
- public SensorsMod(bool serviceMode = false, string serviceDeviceName = "")
- {
- Sensor = new ConfiguredSensor();
-
- _serviceMode = serviceMode;
- _serviceDeviceName = serviceDeviceName;
-
- InitializeComponent();
-
- BindListViewTheme();
-
- BindComboBoxTheme();
- }
-
- private void CbIgnoreAvailability_CheckedChanged(object sender, EventArgs e)
- {
- if ((sender as CheckBox).Checked)
- MessageBoxAdv.Show(this, Languages.SensorsMod_IgnoreAvailability_Info, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
-
- private void SetCbIgnoreAvailability(bool check)
- {
- CbIgnoreAvailability.CheckedChanged -= CbIgnoreAvailability_CheckedChanged;
- CbIgnoreAvailability.Checked = check;
- CbIgnoreAvailability.CheckedChanged += CbIgnoreAvailability_CheckedChanged;
- }
-
- private void BindListViewTheme()
- {
- LvSensors.DrawItem += ListViewTheme.DrawItem;
- LvSensors.DrawSubItem += ListViewTheme.DrawSubItem;
- LvSensors.DrawColumnHeader += ListViewTheme.DrawColumnHeader;
- }
-
- private void BindComboBoxTheme() => CbNetworkCard.DrawItem += ComboBoxTheme.DrawDictionaryStringStringItem;
-
- private void SensorMod_Load(object sender, EventArgs e)
- {
- // catch all key presses
- KeyPreview = true;
-
- // load sensors
- LvSensors.BeginUpdate();
- foreach (var sensor in SensorsManager.SensorInfoCards.Select(x => x.Value))
- {
- var lvSensor = new ListViewItem(sensor.Key.ToString());
- lvSensor.SubItems.Add(sensor.Name);
- lvSensor.SubItems.Add(sensor.MultiValue ? "√" : string.Empty);
- lvSensor.SubItems.Add(sensor.AgentCompatible ? "√" : string.Empty);
- lvSensor.SubItems.Add(sensor.SatelliteCompatible ? "√" : string.Empty);
- LvSensors.Items.Add(lvSensor);
- }
- LvSensors.EndUpdate();
-
- _networkCards.Add("*", Languages.SensorsMod_All);
- foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
- _networkCards.Add(nic.Id, nic.Name);
-
- _internalSensors.Add("none", Languages.SensorsMod_None);
- foreach (var internalSensor in InternalDeviceSensorsManager.AvailableSensors)
- {
- var internalSensorType = internalSensor.Type.ToString();
- _internalSensors.Add(internalSensorType, internalSensorType);
- }
-
- CbIgnoreAvailability.CheckedChanged += CbIgnoreAvailability_CheckedChanged;
-
- // load in gui
- CbNetworkCard.DataSource = new BindingSource(_networkCards, null);
-
- // load or set sensor
- if (Sensor.Id == Guid.Empty)
- {
- Sensor.Id = Guid.NewGuid();
- Text = Languages.SensorsMod_Title_New;
-
- // done
- _loading = false;
- return;
- }
-
- // we're modding, load it
- LoadSensor();
- Text = Languages.SensorsMod_Title_Mod;
-
- // done
- _loading = false;
- }
-
- ///
- /// Loads the to-be-modded sensor
- ///
- private void LoadSensor()
- {
- // load the card
- var sensorCard = SensorsManager.SensorInfoCards[Sensor.Type];
-
- // set type
- _selectedSensorType = sensorCard.SensorType;
-
- // load the type
- TbSelectedType.Text = _selectedSensorType.ToString();
-
- // select it as well
- foreach (ListViewItem lvi in LvSensors.Items)
- {
- if (lvi.Text != sensorCard.Key.ToString()) continue;
- lvi.Selected = true;
- LvSensors.SelectedItems[0].EnsureVisible();
- break;
- }
-
- // set gui
- var guiOk = SetType(false);
- if (!guiOk) return;
-
- // set the name
- TbName.Text = Sensor.EntityName;
- if (!string.IsNullOrWhiteSpace(TbName.Text)) TbName.SelectionStart = TbName.Text.Length;
-
- // set the friendly name
- TbFriendlyName.Text = Sensor.Name;
-
- // set interval
- NumInterval.Text = Sensor.UpdateInterval?.ToString() ?? "10";
-
-
- SetCbIgnoreAvailability(Sensor.IgnoreAvailability);
-
- // set optional setting
- switch (_selectedSensorType)
- {
- case SensorType.NamedWindowSensor:
- TbSetting1.Text = Sensor.WindowName;
- break;
-
- case SensorType.WmiQuerySensor:
- TbSetting1.Text = Sensor.Query;
- TbSetting2.Text = Sensor.Scope;
- CbApplyRounding.Checked = Sensor.ApplyRounding;
- NumRound.Text = Sensor.Round?.ToString() ?? "2";
- break;
-
- case SensorType.PerformanceCounterSensor:
- TbSetting1.Text = Sensor.Category;
- TbSetting2.Text = Sensor.Counter;
- TbSetting3.Text = Sensor.Instance;
- CbApplyRounding.Checked = Sensor.ApplyRounding;
- NumRound.Text = Sensor.Round?.ToString() ?? "2";
- break;
-
- case SensorType.ProcessActiveSensor:
- TbSetting1.Text = Sensor.Query;
- break;
-
- case SensorType.ServiceStateSensor:
- TbSetting1.Text = Sensor.Query;
- break;
-
- case SensorType.PowershellSensor:
- TbSetting1.Text = Sensor.Query;
- CbApplyRounding.Checked = Sensor.ApplyRounding;
- NumRound.Text = Sensor.Round?.ToString() ?? "2";
- break;
-
- case SensorType.NetworkSensors:
- if (_networkCards.ContainsKey(Sensor.Query))
- CbNetworkCard.SelectedItem = new KeyValuePair(Sensor.Query, _networkCards[Sensor.Query]);
- break;
-
- case SensorType.InternalDeviceSensor:
- if (_internalSensors.ContainsKey(Sensor.Query))
- CbNetworkCard.SelectedItem = new KeyValuePair(Sensor.Query, _internalSensors[Sensor.Query]);
- break;
-
- case SensorType.WindowStateSensor:
- TbSetting1.Text = Sensor.Query;
- break;
-
- case SensorType.LastActiveSensor:
- CbApplyRounding.Checked = Sensor.ApplyRounding;
- NumRound.Text = Sensor.Round?.ToString() ?? LastActiveSensor.DefaultTimeWindow.ToString(); ;
- break;
+ private SensorType _selectedSensorType = SensorType.ActiveWindowSensor;
+
+ public SensorsMod(ConfiguredSensor sensor, bool serviceMode = false, string serviceDeviceName = "")
+ {
+ Sensor = sensor;
+
+ _serviceMode = serviceMode;
+ _serviceDeviceName = serviceDeviceName;
+
+ InitializeComponent();
+
+ BindListViewTheme();
+
+ BindComboBoxTheme();
+ }
+
+ public SensorsMod(bool serviceMode = false, string serviceDeviceName = "")
+ {
+ Sensor = new ConfiguredSensor();
+
+ _serviceMode = serviceMode;
+ _serviceDeviceName = serviceDeviceName;
+
+ InitializeComponent();
+
+ BindListViewTheme();
+
+ BindComboBoxTheme();
+ }
+
+ private void CbIgnoreAvailability_CheckedChanged(object sender, EventArgs e)
+ {
+ if ((sender as CheckBox).Checked)
+ MessageBoxAdv.Show(this, Languages.SensorsMod_IgnoreAvailability_Info, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ private void SetCbIgnoreAvailability(bool check)
+ {
+ CbIgnoreAvailability.CheckedChanged -= CbIgnoreAvailability_CheckedChanged;
+ CbIgnoreAvailability.Checked = check;
+ CbIgnoreAvailability.CheckedChanged += CbIgnoreAvailability_CheckedChanged;
+ }
+
+ private void BindListViewTheme()
+ {
+ LvSensors.DrawItem += ListViewTheme.DrawItem;
+ LvSensors.DrawSubItem += ListViewTheme.DrawSubItem;
+ LvSensors.DrawColumnHeader += ListViewTheme.DrawColumnHeader;
+ }
+
+ private void BindComboBoxTheme() => CbNetworkCard.DrawItem += ComboBoxTheme.DrawDictionaryStringStringItem;
+
+ private void SensorMod_Load(object sender, EventArgs e)
+ {
+ // catch all key presses
+ KeyPreview = true;
+
+ // load sensors
+ LvSensors.BeginUpdate();
+ foreach (var sensor in SensorsManager.SensorInfoCards.Select(x => x.Value))
+ {
+ var lvSensor = new ListViewItem(sensor.Key.ToString());
+ lvSensor.SubItems.Add(sensor.Name);
+ lvSensor.SubItems.Add(sensor.MultiValue ? "√" : string.Empty);
+ lvSensor.SubItems.Add(sensor.AgentCompatible ? "√" : string.Empty);
+ lvSensor.SubItems.Add(sensor.SatelliteCompatible ? "√" : string.Empty);
+ LvSensors.Items.Add(lvSensor);
+ }
+ LvSensors.EndUpdate();
+
+ _networkCards.Add("*", Languages.SensorsMod_All);
+ foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
+ _networkCards.Add(nic.Id, nic.Name);
+
+ _internalSensors.Add("none", Languages.SensorsMod_None);
+ foreach (var internalSensor in InternalDeviceSensorsManager.AvailableSensors)
+ {
+ var internalSensorType = internalSensor.Type.ToString();
+ _internalSensors.Add(internalSensorType, internalSensorType);
+ }
+
+ CbIgnoreAvailability.CheckedChanged += CbIgnoreAvailability_CheckedChanged;
+
+ // load in gui
+ CbNetworkCard.DataSource = new BindingSource(_networkCards, null);
+
+ // load or set sensor
+ if (Sensor.Id == Guid.Empty)
+ {
+ Sensor.Id = Guid.NewGuid();
+ Text = Languages.SensorsMod_Title_New;
+
+ // done
+ _loading = false;
+ return;
+ }
+
+ // we're modding, load it
+ LoadSensor();
+ Text = Languages.SensorsMod_Title_Mod;
+
+ // done
+ _loading = false;
+ }
+
+ ///
+ /// Loads the to-be-modded sensor
+ ///
+ private void LoadSensor()
+ {
+ // load the card
+ var sensorCard = SensorsManager.SensorInfoCards[Sensor.Type];
+
+ // set type
+ _selectedSensorType = sensorCard.SensorType;
+
+ // load the type
+ TbSelectedType.Text = _selectedSensorType.ToString();
+
+ // select it as well
+ foreach (ListViewItem lvi in LvSensors.Items)
+ {
+ if (lvi.Text != sensorCard.Key.ToString()) continue;
+ lvi.Selected = true;
+ LvSensors.SelectedItems[0].EnsureVisible();
+ break;
+ }
+
+ // set gui
+ var guiOk = SetType(false);
+ if (!guiOk) return;
+
+ // set the name
+ TbName.Text = Sensor.EntityName;
+ if (!string.IsNullOrWhiteSpace(TbName.Text)) TbName.SelectionStart = TbName.Text.Length;
+
+ // set the friendly name
+ TbFriendlyName.Text = Sensor.Name;
+
+ // set interval
+ NumInterval.Text = Sensor.UpdateInterval?.ToString() ?? "10";
+
+
+ SetCbIgnoreAvailability(Sensor.IgnoreAvailability);
+
+ // set optional setting
+ switch (_selectedSensorType)
+ {
+ case SensorType.NamedWindowSensor:
+ TbSetting1.Text = Sensor.WindowName;
+ break;
+
+ case SensorType.WmiQuerySensor:
+ TbSetting1.Text = Sensor.Query;
+ TbSetting2.Text = Sensor.Scope;
+ CbApplyRounding.Checked = Sensor.ApplyRounding;
+ NumRound.Text = Sensor.Round?.ToString() ?? "2";
+ break;
+
+ case SensorType.PerformanceCounterSensor:
+ TbSetting1.Text = Sensor.Category;
+ TbSetting2.Text = Sensor.Counter;
+ TbSetting3.Text = Sensor.Instance;
+ CbApplyRounding.Checked = Sensor.ApplyRounding;
+ NumRound.Text = Sensor.Round?.ToString() ?? "2";
+ break;
+
+ case SensorType.ProcessActiveSensor:
+ TbSetting1.Text = Sensor.Query;
+ break;
+
+ case SensorType.ServiceStateSensor:
+ TbSetting1.Text = Sensor.Query;
+ break;
+
+ case SensorType.PowershellSensor:
+ TbSetting1.Text = Sensor.Query;
+ CbApplyRounding.Checked = Sensor.ApplyRounding;
+ NumRound.Text = Sensor.Round?.ToString() ?? "2";
+ break;
+
+ case SensorType.NetworkSensors:
+ if (_networkCards.ContainsKey(Sensor.Query))
+ CbNetworkCard.SelectedItem = new KeyValuePair(Sensor.Query, _networkCards[Sensor.Query]);
+ break;
+
+ case SensorType.InternalDeviceSensor:
+ if (_internalSensors.ContainsKey(Sensor.Query))
+ CbNetworkCard.SelectedItem = new KeyValuePair(Sensor.Query, _internalSensors[Sensor.Query]);
+ break;
+
+ case SensorType.WindowStateSensor:
+ TbSetting1.Text = Sensor.Query;
+ break;
+
+ case SensorType.LastActiveSensor:
+ CbApplyRounding.Checked = Sensor.ApplyRounding;
+ NumRound.Text = Sensor.Round?.ToString() ?? LastActiveSensor.DefaultTimeWindow.ToString(); ;
+ break;
case SensorType.ScreenshotSensor:
TbSetting1.Text = Sensor.Query;
break;
}
- }
-
- ///
- /// Change the UI depending on the selected type
- ///
- ///
- private bool SetType(bool setDefaultValues = true)
- {
- if (LvSensors.SelectedItems.Count == 0)
- {
- // was the interface locked?
- if (_interfaceLockedWrongType) UnlockWrongClient();
- return false;
- }
-
- // find the sensor card
- var sensorId = int.Parse(LvSensors.SelectedItems[0].Text);
- var sensorCard = SensorsManager.SensorInfoCards.Where(card => card.Value.Key == sensorId)
- .Select(card => card.Value).FirstOrDefault();
- if (sensorCard == null) return false;
-
- // can the current client load this type?
- if (_serviceMode && !sensorCard.SatelliteCompatible)
- {
- LockWrongClient();
- return false;
- }
-
- if (!_serviceMode && !sensorCard.AgentCompatible)
- {
- LockWrongClient();
- return false;
- }
-
- // was the interface locked?
- if (_interfaceLockedWrongType) UnlockWrongClient();
-
- // set default values
- if (setDefaultValues)
- {
- TbName.Text = sensorCard.SensorType.GetSensorName();
- NumInterval.Text = sensorCard.RefreshTimer.ToString();
- _selectedSensorType = sensorCard.SensorType;
- }
-
- SetCbIgnoreAvailability(false);
-
- TbSelectedType.Text = sensorCard.SensorType.ToString();
- TbDescription.Text = sensorCard.Description;
- CbApplyRounding.Visible = false;
- NumRound.Visible = false;
- LblDigits.Visible = false;
-
- // process the interface
- switch (sensorCard.SensorType)
- {
- case SensorType.NamedWindowSensor:
- SetWindowGui();
- break;
-
- case SensorType.WmiQuerySensor:
- SetWmiGui();
- break;
-
- case SensorType.PerformanceCounterSensor:
- SetPerformanceCounterGui();
- break;
-
- case SensorType.ProcessActiveSensor:
- SetProcessGui();
- break;
-
- case SensorType.ServiceStateSensor:
- SetServiceStateGui();
- break;
-
- case SensorType.NetworkSensors:
- CbNetworkCard.DataSource = new BindingSource(_networkCards, null);
- SetNetworkGui();
- break;
-
- case SensorType.InternalDeviceSensor:
- CbNetworkCard.DataSource = new BindingSource(_internalSensors, null);
- SetInternalSensorGui();
- break;
-
- case SensorType.PowershellSensor:
- SetPowershellGui();
- break;
-
- case SensorType.WindowStateSensor:
- SetWindowGui();
- break;
-
- case SensorType.LastActiveSensor:
- SetLastActiveGui();
- break;
+ }
+
+ ///
+ /// Change the UI depending on the selected type
+ ///
+ ///
+ private bool SetType(bool setDefaultValues = true)
+ {
+ if (LvSensors.SelectedItems.Count == 0)
+ {
+ // was the interface locked?
+ if (_interfaceLockedWrongType) UnlockWrongClient();
+ return false;
+ }
+
+ // find the sensor card
+ var sensorId = int.Parse(LvSensors.SelectedItems[0].Text);
+ var sensorCard = SensorsManager.SensorInfoCards.Where(card => card.Value.Key == sensorId)
+ .Select(card => card.Value).FirstOrDefault();
+ if (sensorCard == null) return false;
+
+ // can the current client load this type?
+ if (_serviceMode && !sensorCard.SatelliteCompatible)
+ {
+ LockWrongClient();
+ return false;
+ }
+
+ if (!_serviceMode && !sensorCard.AgentCompatible)
+ {
+ LockWrongClient();
+ return false;
+ }
+
+ // was the interface locked?
+ if (_interfaceLockedWrongType) UnlockWrongClient();
+
+ // set default values
+ if (setDefaultValues)
+ {
+ TbName.Text = sensorCard.SensorType.GetSensorName();
+ NumInterval.Text = sensorCard.RefreshTimer.ToString();
+ _selectedSensorType = sensorCard.SensorType;
+ }
+
+ SetCbIgnoreAvailability(false);
+
+ TbSelectedType.Text = sensorCard.SensorType.ToString();
+ TbDescription.Text = sensorCard.Description;
+ CbApplyRounding.Visible = false;
+ NumRound.Visible = false;
+ LblDigits.Visible = false;
+
+ // process the interface
+ switch (sensorCard.SensorType)
+ {
+ case SensorType.NamedWindowSensor:
+ SetWindowGui();
+ break;
+
+ case SensorType.WmiQuerySensor:
+ SetWmiGui();
+ break;
+
+ case SensorType.PerformanceCounterSensor:
+ SetPerformanceCounterGui();
+ break;
+
+ case SensorType.ProcessActiveSensor:
+ SetProcessGui();
+ break;
+
+ case SensorType.ServiceStateSensor:
+ SetServiceStateGui();
+ break;
+
+ case SensorType.NetworkSensors:
+ CbNetworkCard.DataSource = new BindingSource(_networkCards, null);
+ SetNetworkGui();
+ break;
+
+ case SensorType.InternalDeviceSensor:
+ CbNetworkCard.DataSource = new BindingSource(_internalSensors, null);
+ SetInternalSensorGui();
+ break;
+
+ case SensorType.PowershellSensor:
+ SetPowershellGui();
+ break;
+
+ case SensorType.WindowStateSensor:
+ SetWindowGui();
+ break;
+
+ case SensorType.LastActiveSensor:
+ SetLastActiveGui();
+ break;
case SensorType.ScreenshotSensor:
SetScreenshotGui();
break;
- default:
- SetEmptyGui();
- break;
- }
-
- return true;
- }
-
- ///
- /// Change the UI to a 'named window' type
- ///
- private void SetWindowGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_WindowName;
- LblSetting1.Visible = true;
- TbSetting1.Visible = true;
-
- BtnTest.Visible = false;
- }));
- }
-
- ///
- /// Change the UI to a 'wmi query' type
- ///
- [SuppressMessage("ReSharper", "InvertIf")]
- private void SetWmiGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Wmi;
- LblSetting1.Visible = true;
- TbSetting1.Visible = true;
-
- LblSetting2.Text = Languages.SensorsMod_LblSetting2_Wmi;
- LblSetting2.Visible = true;
- TbSetting2.Visible = true;
-
- BtnTest.Text = Languages.SensorsMod_BtnTest_Wmi;
- BtnTest.Visible = true;
-
- CbApplyRounding.Visible = true;
- if (CbApplyRounding.Checked)
- {
- NumRound.Visible = true;
- LblDigits.Visible = true;
- }
- }));
- }
-
- ///
- /// Change the UI to a 'powershell command' type
- ///
- [SuppressMessage("ReSharper", "InvertIf")]
- private void SetPowershellGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Powershell;
- LblSetting1.Visible = true;
- TbSetting1.Visible = true;
-
- BtnTest.Text = Languages.SensorsMod_SensorsMod_BtnTest_Powershell;
- BtnTest.Visible = true;
-
- CbApplyRounding.Visible = true;
- if (CbApplyRounding.Checked)
- {
- NumRound.Visible = true;
- LblDigits.Visible = true;
- }
- }));
- }
-
- ///
- /// Change the UI to a 'performance counter' type
- ///
- [SuppressMessage("ReSharper", "InvertIf")]
- private void SetPerformanceCounterGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Category;
- LblSetting1.Visible = true;
- TbSetting1.Text = string.Empty;
- TbSetting1.Visible = true;
-
- LblSetting2.Text = Languages.SensorsMod_LblSetting2_Counter;
- LblSetting2.Visible = true;
- TbSetting2.Text = string.Empty;
- TbSetting2.Visible = true;
-
- LblSetting3.Text = Languages.SensorsMod_LblSetting3_Instance;
- LblSetting3.Visible = true;
- TbSetting3.Text = string.Empty;
- TbSetting3.Visible = true;
-
- BtnTest.Text = Languages.SensorsMod_BtnTest_PerformanceCounter;
- BtnTest.Visible = true;
-
- CbApplyRounding.Visible = true;
- if (CbApplyRounding.Checked)
- {
- NumRound.Visible = true;
- LblDigits.Visible = true;
- }
- }));
- }
-
- ///
- /// Change the UI to a 'process active' type
- ///
- private void SetProcessGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Process;
- LblSetting1.Visible = true;
- TbSetting1.Visible = true;
- }));
- }
-
- ///
- /// Change the UI to a 'service state' type
- ///
- private void SetServiceStateGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Service;
- LblSetting1.Visible = true;
- TbSetting1.Visible = true;
- }));
- }
-
- ///
- /// Change the UI to a 'network' type
- ///
- private void SetNetworkGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_Network;
- LblSetting1.Visible = true;
-
- CbNetworkCard.Visible = true;
- }));
- }
-
- ///
- /// Change the UI to a 'internal sensor' type
- ///
- private void SetInternalSensorGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- LblSetting1.Text = Languages.SensorsMod_LblSetting1_InternalSensor;
- LblSetting1.Visible = true;
-
- CbNetworkCard.Visible = true;
- }));
- }
-
- ///
- /// Change the UI to a 'lastactive' type
- ///
- private void SetLastActiveGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- SetEmptyGui();
-
- CbApplyRounding.Text = Languages.SensorsMod_CbApplyRounding_LastActive;
- LblDigits.Text = Languages.SensorsMod_LblSeconds;
- NumRound.Text = LastActiveSensor.DefaultTimeWindow.ToString();
- CbApplyRounding.Visible = true;
- if (CbApplyRounding.Checked)
- {
- NumRound.Visible = true;
- LblDigits.Visible = true;
- }
- }));
- }
+ default:
+ SetEmptyGui();
+ break;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Change the UI to a 'named window' type
+ ///
+ private void SetWindowGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_WindowName;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+
+ BtnTest.Visible = false;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'wmi query' type
+ ///
+ [SuppressMessage("ReSharper", "InvertIf")]
+ private void SetWmiGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Wmi;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+
+ LblSetting2.Text = Languages.SensorsMod_LblSetting2_Wmi;
+ LblSetting2.Visible = true;
+ TbSetting2.Visible = true;
+
+ BtnTest.Text = Languages.SensorsMod_BtnTest_Wmi;
+ BtnTest.Visible = true;
+
+ CbApplyRounding.Visible = true;
+ if (CbApplyRounding.Checked)
+ {
+ NumRound.Visible = true;
+ LblDigits.Visible = true;
+ }
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'powershell command' type
+ ///
+ [SuppressMessage("ReSharper", "InvertIf")]
+ private void SetPowershellGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Powershell;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+
+ BtnTest.Text = Languages.SensorsMod_SensorsMod_BtnTest_Powershell;
+ BtnTest.Visible = true;
+
+ CbApplyRounding.Visible = true;
+ if (CbApplyRounding.Checked)
+ {
+ NumRound.Visible = true;
+ LblDigits.Visible = true;
+ }
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'performance counter' type
+ ///
+ [SuppressMessage("ReSharper", "InvertIf")]
+ private void SetPerformanceCounterGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Category;
+ LblSetting1.Visible = true;
+ TbSetting1.Text = string.Empty;
+ TbSetting1.Visible = true;
+
+ LblSetting2.Text = Languages.SensorsMod_LblSetting2_Counter;
+ LblSetting2.Visible = true;
+ TbSetting2.Text = string.Empty;
+ TbSetting2.Visible = true;
+
+ LblSetting3.Text = Languages.SensorsMod_LblSetting3_Instance;
+ LblSetting3.Visible = true;
+ TbSetting3.Text = string.Empty;
+ TbSetting3.Visible = true;
+
+ BtnTest.Text = Languages.SensorsMod_BtnTest_PerformanceCounter;
+ BtnTest.Visible = true;
+
+ CbApplyRounding.Visible = true;
+ if (CbApplyRounding.Checked)
+ {
+ NumRound.Visible = true;
+ LblDigits.Visible = true;
+ }
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'process active' type
+ ///
+ private void SetProcessGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Process;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'service state' type
+ ///
+ private void SetServiceStateGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Service;
+ LblSetting1.Visible = true;
+ TbSetting1.Visible = true;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'network' type
+ ///
+ private void SetNetworkGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_Network;
+ LblSetting1.Visible = true;
+
+ CbNetworkCard.Visible = true;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'internal sensor' type
+ ///
+ private void SetInternalSensorGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ LblSetting1.Text = Languages.SensorsMod_LblSetting1_InternalSensor;
+ LblSetting1.Visible = true;
+
+ CbNetworkCard.Visible = true;
+ }));
+ }
+
+ ///
+ /// Change the UI to a 'lastactive' type
+ ///
+ private void SetLastActiveGui()
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ SetEmptyGui();
+
+ CbApplyRounding.Text = Languages.SensorsMod_CbApplyRounding_LastActive;
+ LblDigits.Text = Languages.SensorsMod_LblSeconds;
+ NumRound.Text = LastActiveSensor.DefaultTimeWindow.ToString();
+ CbApplyRounding.Visible = true;
+ if (CbApplyRounding.Checked)
+ {
+ NumRound.Visible = true;
+ LblDigits.Visible = true;
+ }
+ }));
+ }
private void SetScreenshotGui()
{
@@ -554,264 +556,265 @@ private void SetScreenshotGui()
/// Change the UI to a general type
///
private void SetEmptyGui()
- {
- Invoke(new MethodInvoker(delegate
- {
- LblSetting1.Visible = false;
-
- CbNetworkCard.Visible = false;
-
- TbSetting1.Text = string.Empty;
- TbSetting1.Visible = false;
-
- LblSetting2.Visible = false;
- TbSetting2.Text = string.Empty;
- TbSetting2.Visible = false;
-
- LblSetting3.Visible = false;
- TbSetting3.Text = string.Empty;
- TbSetting3.Visible = false;
-
- CbApplyRounding.Text = Languages.SensorsMod_CbApplyRounding;
- CbApplyRounding.Checked = false;
- CbApplyRounding.Visible = false;
- NumRound.Visible = false;
- LblDigits.Text = Languages.SensorsMod_LblDigits;
- LblDigits.Visible = false;
-
- BtnTest.Visible = false;
- }));
- }
-
- private void LvSensors_SelectedIndexChanged(object sender, EventArgs e)
- {
- if (_loading) return;
-
- // set the ui to the selected type
- SetType();
-
- // set focus to the name field
- ActiveControl = TbName;
- if (!string.IsNullOrWhiteSpace(TbName.Text)) TbName.SelectionStart = TbName.Text.Length;
- }
-
- ///
- /// Prepare the sensor for processing
- ///
- ///
- ///
- private void BtnStore_Click(object sender, EventArgs e)
- {
- if (LvSensors.SelectedItems.Count == 0)
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- // get and check type
- var sensorId = int.Parse(LvSensors.SelectedItems[0].Text);
- var sensorCard = SensorsManager.SensorInfoCards.Where(card => card.Value.Key == sensorId)
- .Select(card => card.Value).FirstOrDefault();
-
- if (sensorCard == null)
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox2, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- // get and check name
- var name = TbName.Text.Trim();
- if (string.IsNullOrEmpty(name))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox3, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbName;
- return;
- }
-
- if (CompatHelper.HassVersionEqualOrOver("2023.8") && name.Contains(SharedHelperFunctions.GetSafeConfiguredDeviceName()))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_DeviceNameInSensorName, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning);
- }
-
- // get friendly name
- var friendlyName = string.IsNullOrEmpty(TbFriendlyName.Text.Trim()) ? name : TbFriendlyName.Text.Trim();
-
- // name contains illegal chars?
- var sanitized = SharedHelperFunctions.GetSafeValue(name);
- if (sanitized != name)
- {
- var confirmSanitize = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_MessageBox_Sanitize, sanitized), Variables.MessageBoxTitle, MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
- if (confirmSanitize != DialogResult.OK)
- {
- ActiveControl = TbName;
- return;
- }
-
- TbName.Text = sanitized;
- name = sanitized;
- }
-
- // name already used?
- if (!_serviceMode && Variables.SingleValueSensors.Any(x => string.Equals(x.EntityName, name, StringComparison.InvariantCultureIgnoreCase) && x.Id != Sensor.Id.ToString()))
- {
- var confirm = MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox4, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
- if (confirm != DialogResult.Yes)
- {
- ActiveControl = TbName;
- return;
- }
- }
-
- if (!_serviceMode && Variables.MultiValueSensors.Any(x => string.Equals(x.EntityName, name, StringComparison.InvariantCultureIgnoreCase) && x.Id != Sensor.Id.ToString()))
- {
- var confirm = MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox5, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
- if (confirm != DialogResult.Yes)
- {
- ActiveControl = TbName;
- return;
- }
- }
-
- // get and check update interval
- var interval = (int)NumInterval.Value;
- if (interval is < 1 or > 43200)
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox6, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = NumInterval;
- return;
- }
-
- // get and check round value
- var applyRounding = CbApplyRounding.Checked;
- int? round = null;
- if (applyRounding)
- {
- round = (int)NumRound.Value;
- if (round is < 0 or > 20)
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox12, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = NumRound;
- return;
- }
- }
-
- // check and set optional settings
- switch (sensorCard.SensorType)
- {
- case SensorType.NamedWindowSensor:
- var window = TbSetting1.Text.Trim();
- if (string.IsNullOrEmpty(window))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox7, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
- Sensor.WindowName = window;
- break;
-
- case SensorType.WmiQuerySensor:
- var query = TbSetting1.Text.Trim();
- var scope = TbSetting2.Text.Trim();
-
- // test the query
- if (string.IsNullOrEmpty(query))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox8, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
-
- // test the scope
- if (!string.IsNullOrEmpty(scope))
- {
- if (!HelperFunctions.CheckWmiScope(scope))
- {
- var scopeQ = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_WmiTestFailed, scope),
- Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
-
- if (scopeQ != DialogResult.Yes) return;
- }
- }
-
- Sensor.Query = query;
- Sensor.Scope = scope;
- break;
-
- case SensorType.PerformanceCounterSensor:
- var category = TbSetting1.Text.Trim();
- var counter = TbSetting2.Text.Trim();
- var instance = TbSetting3.Text.Trim();
- if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(counter))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox9, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
- Sensor.Category = category;
- Sensor.Counter = counter;
- Sensor.Instance = instance;
- break;
-
- case SensorType.ProcessActiveSensor:
- var process = TbSetting1.Text.Trim();
- if (string.IsNullOrEmpty(process))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox10, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
- Sensor.Query = process;
- break;
-
- case SensorType.ServiceStateSensor:
- var service = TbSetting1.Text.Trim();
- if (string.IsNullOrEmpty(service))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox11, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
- Sensor.Query = service;
- break;
-
- case SensorType.NetworkSensors:
- Sensor.Query = "*";
- if (CbNetworkCard.SelectedItem != null)
- {
- var item = (KeyValuePair)CbNetworkCard.SelectedItem;
- Sensor.Query = item.Key;
- }
- break;
-
- case SensorType.InternalDeviceSensor:
- if (CbNetworkCard.SelectedItem != null)
- {
- var item = (KeyValuePair)CbNetworkCard.SelectedItem;
- if (item.Value == Languages.SensorsMod_None)
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = CbNetworkCard;
- return;
- }
-
- Sensor.Query = item.Key;
- }
- break;
-
- case SensorType.PowershellSensor:
- Sensor.Query = TbSetting1.Text.Trim();
- break;
-
- case SensorType.WindowStateSensor:
- var windowprocess = TbSetting1.Text.Trim();
- if (string.IsNullOrEmpty(windowprocess))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox10, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
- ActiveControl = TbSetting1;
- return;
- }
- Sensor.Query = windowprocess;
- break;
+ {
+ Invoke(new MethodInvoker(delegate
+ {
+ LblSetting1.Visible = false;
+
+ CbNetworkCard.Visible = false;
+
+ TbSetting1.Text = string.Empty;
+ TbSetting1.Visible = false;
+
+ LblSetting2.Visible = false;
+ TbSetting2.Text = string.Empty;
+ TbSetting2.Visible = false;
+
+ LblSetting3.Visible = false;
+ TbSetting3.Text = string.Empty;
+ TbSetting3.Visible = false;
+
+ CbApplyRounding.Text = Languages.SensorsMod_CbApplyRounding;
+ CbApplyRounding.Checked = false;
+ CbApplyRounding.Visible = false;
+ NumRound.Visible = false;
+ LblDigits.Text = Languages.SensorsMod_LblDigits;
+ LblDigits.Visible = false;
+
+ BtnTest.Visible = false;
+ BtnAdvSettings.Visible = true;
+ }));
+ }
+
+ private void LvSensors_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (_loading) return;
+
+ // set the ui to the selected type
+ SetType();
+
+ // set focus to the name field
+ ActiveControl = TbName;
+ if (!string.IsNullOrWhiteSpace(TbName.Text)) TbName.SelectionStart = TbName.Text.Length;
+ }
+
+ ///
+ /// Prepare the sensor for processing
+ ///
+ ///
+ ///
+ private void BtnStore_Click(object sender, EventArgs e)
+ {
+ if (LvSensors.SelectedItems.Count == 0)
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ // get and check type
+ var sensorId = int.Parse(LvSensors.SelectedItems[0].Text);
+ var sensorCard = SensorsManager.SensorInfoCards.Where(card => card.Value.Key == sensorId)
+ .Select(card => card.Value).FirstOrDefault();
+
+ if (sensorCard == null)
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox2, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ // get and check name
+ var name = TbName.Text.Trim();
+ if (string.IsNullOrEmpty(name))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox3, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbName;
+ return;
+ }
+
+ if (CompatHelper.HassVersionEqualOrOver("2023.8") && name.Contains(SharedHelperFunctions.GetSafeConfiguredDeviceName()))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_DeviceNameInSensorName, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+
+ // get friendly name
+ var friendlyName = string.IsNullOrEmpty(TbFriendlyName.Text.Trim()) ? name : TbFriendlyName.Text.Trim();
+
+ // name contains illegal chars?
+ var sanitized = SharedHelperFunctions.GetSafeValue(name);
+ if (sanitized != name)
+ {
+ var confirmSanitize = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_MessageBox_Sanitize, sanitized), Variables.MessageBoxTitle, MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
+ if (confirmSanitize != DialogResult.OK)
+ {
+ ActiveControl = TbName;
+ return;
+ }
+
+ TbName.Text = sanitized;
+ name = sanitized;
+ }
+
+ // name already used?
+ if (!_serviceMode && Variables.SingleValueSensors.Any(x => string.Equals(x.EntityName, name, StringComparison.InvariantCultureIgnoreCase) && x.Id != Sensor.Id.ToString()))
+ {
+ var confirm = MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox4, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+ if (confirm != DialogResult.Yes)
+ {
+ ActiveControl = TbName;
+ return;
+ }
+ }
+
+ if (!_serviceMode && Variables.MultiValueSensors.Any(x => string.Equals(x.EntityName, name, StringComparison.InvariantCultureIgnoreCase) && x.Id != Sensor.Id.ToString()))
+ {
+ var confirm = MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox5, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+ if (confirm != DialogResult.Yes)
+ {
+ ActiveControl = TbName;
+ return;
+ }
+ }
+
+ // get and check update interval
+ var interval = (int)NumInterval.Value;
+ if (interval is < 1 or > 43200)
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox6, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = NumInterval;
+ return;
+ }
+
+ // get and check round value
+ var applyRounding = CbApplyRounding.Checked;
+ int? round = null;
+ if (applyRounding)
+ {
+ round = (int)NumRound.Value;
+ if (round is < 0 or > 20)
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox12, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = NumRound;
+ return;
+ }
+ }
+
+ // check and set optional settings
+ switch (sensorCard.SensorType)
+ {
+ case SensorType.NamedWindowSensor:
+ var window = TbSetting1.Text.Trim();
+ if (string.IsNullOrEmpty(window))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox7, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.WindowName = window;
+ break;
+
+ case SensorType.WmiQuerySensor:
+ var query = TbSetting1.Text.Trim();
+ var scope = TbSetting2.Text.Trim();
+
+ // test the query
+ if (string.IsNullOrEmpty(query))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox8, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+
+ // test the scope
+ if (!string.IsNullOrEmpty(scope))
+ {
+ if (!HelperFunctions.CheckWmiScope(scope))
+ {
+ var scopeQ = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_WmiTestFailed, scope),
+ Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
+
+ if (scopeQ != DialogResult.Yes) return;
+ }
+ }
+
+ Sensor.Query = query;
+ Sensor.Scope = scope;
+ break;
+
+ case SensorType.PerformanceCounterSensor:
+ var category = TbSetting1.Text.Trim();
+ var counter = TbSetting2.Text.Trim();
+ var instance = TbSetting3.Text.Trim();
+ if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(counter))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox9, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.Category = category;
+ Sensor.Counter = counter;
+ Sensor.Instance = instance;
+ break;
+
+ case SensorType.ProcessActiveSensor:
+ var process = TbSetting1.Text.Trim();
+ if (string.IsNullOrEmpty(process))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox10, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.Query = process;
+ break;
+
+ case SensorType.ServiceStateSensor:
+ var service = TbSetting1.Text.Trim();
+ if (string.IsNullOrEmpty(service))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox11, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.Query = service;
+ break;
+
+ case SensorType.NetworkSensors:
+ Sensor.Query = "*";
+ if (CbNetworkCard.SelectedItem != null)
+ {
+ var item = (KeyValuePair)CbNetworkCard.SelectedItem;
+ Sensor.Query = item.Key;
+ }
+ break;
+
+ case SensorType.InternalDeviceSensor:
+ if (CbNetworkCard.SelectedItem != null)
+ {
+ var item = (KeyValuePair)CbNetworkCard.SelectedItem;
+ if (item.Value == Languages.SensorsMod_None)
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = CbNetworkCard;
+ return;
+ }
+
+ Sensor.Query = item.Key;
+ }
+ break;
+
+ case SensorType.PowershellSensor:
+ Sensor.Query = TbSetting1.Text.Trim();
+ break;
+
+ case SensorType.WindowStateSensor:
+ var windowprocess = TbSetting1.Text.Trim();
+ if (string.IsNullOrEmpty(windowprocess))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_BtnStore_MessageBox10, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ ActiveControl = TbSetting1;
+ return;
+ }
+ Sensor.Query = windowprocess;
+ break;
case SensorType.ActiveDesktopSensor:
if (!VirtualDesktopManager.Initialized)
@@ -834,255 +837,272 @@ private void BtnStore_Click(object sender, EventArgs e)
break;
}
- // set values
- Sensor.Type = sensorCard.SensorType;
- Sensor.EntityName = name;
- Sensor.Name = friendlyName;
- Sensor.UpdateInterval = interval;
- Sensor.IgnoreAvailability = CbIgnoreAvailability.Checked;
- Sensor.ApplyRounding = applyRounding;
- Sensor.Round = round;
-
- // done
- DialogResult = DialogResult.OK;
- }
-
- private void TbDescription_LinkClicked(object sender, LinkClickedEventArgs e)
- {
- if (string.IsNullOrWhiteSpace(e.LinkText)) return;
- if (!e.LinkText.ToLower().StartsWith("http")) return;
-
- HelperFunctions.LaunchUrl(e.LinkText);
- }
-
- private void SensorsMod_ResizeEnd(object sender, EventArgs e)
- {
- if (Variables.ShuttingDown) return;
- if (!IsHandleCreated) return;
- if (IsDisposed) return;
-
- try
- {
- // hide the pesky horizontal scrollbar
- ListViewTheme.ShowScrollBar(LvSensors.Handle, ListViewTheme.SB_HORZ, false);
-
- Refresh();
- }
- catch
- {
- // best effort
- }
- }
-
- private void SensorsMod_KeyUp(object sender, KeyEventArgs e)
- {
- if (e.KeyCode != Keys.Escape) return;
- Close();
- }
-
- private void SensorsMod_Layout(object sender, LayoutEventArgs e)
- {
- // hide the pesky horizontal scrollbar
- ListViewTheme.ShowScrollBar(LvSensors.Handle, ListViewTheme.SB_HORZ, false);
- }
-
- ///
- /// Locks the interface if the selected entity can't be added to the current client
- ///
- private void LockWrongClient()
- {
- if (InvokeRequired)
- {
- Invoke(new MethodInvoker(LockWrongClient));
- return;
- }
-
- _interfaceLockedWrongType = true;
-
- var requiredClient = _serviceMode ? "hass.agent" : "service";
- LblSpecificClient.Text = string.Format(Languages.SensorsMod_SpecificClient, requiredClient);
-
- LblSpecificClient.Visible = true;
-
- TbName.Enabled = false;
- TbName.Text = string.Empty;
-
- TbFriendlyName.Enabled = false;
- TbFriendlyName.Text = string.Empty;
-
- SetEmptyGui();
-
- BtnStore.Enabled = false;
- }
-
- ///
- /// Unlocks the interface if the selected entity can be added to the current client
- ///
- private void UnlockWrongClient()
- {
- if (InvokeRequired)
- {
- Invoke(new MethodInvoker(UnlockWrongClient));
- return;
- }
-
- _interfaceLockedWrongType = false;
-
- LblSpecificClient.Visible = false;
-
- TbName.Enabled = true;
- TbFriendlyName.Enabled = true;
- BtnStore.Enabled = true;
- }
-
- private void BtnTest_Click(object sender, EventArgs e)
- {
- switch (_selectedSensorType)
- {
- case SensorType.WmiQuerySensor:
- TestWmi();
- break;
-
- case SensorType.PerformanceCounterSensor:
- TestPerformanceCounter();
- break;
-
- case SensorType.PowershellSensor:
- TestPowershell();
- break;
- }
- }
-
- private async void TestWmi()
- {
- // prepare values
- var query = TbSetting1.Text.Trim();
- var scope = TbSetting2.Text.Trim();
- var applyRounding = CbApplyRounding.Checked;
- var round = (int)NumRound.Value;
-
- if (string.IsNullOrEmpty(query))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_TestWmi_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
- ActiveControl = TbSetting1;
- return;
- }
-
- // test the scope
- if (!string.IsNullOrEmpty(scope))
- {
- if (!HelperFunctions.CheckWmiScope(scope))
- {
- var scopeQ = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_WmiTestFailed, scope),
- Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
-
- if (scopeQ != DialogResult.Yes) return;
- }
- }
-
- BtnTest.Enabled = false;
-
- // execute the test
- var result = await Task.Run(() => SensorTester.TestWmiQuery(query, scope, applyRounding, round));
-
- BtnTest.Enabled = true;
-
- if (result.Succesful)
- {
- MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestWmi_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
- return;
- }
-
- // failed
- var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestWmi_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
- if (q != DialogResult.Yes) return;
-
- // open logs
- HelperFunctions.OpenLocalFolder(Variables.LogPath);
- }
-
- private async void TestPerformanceCounter()
- {
- // prepare values
- var category = TbSetting1.Text.Trim();
- var counter = TbSetting2.Text.Trim();
- var instance = TbSetting3.Text.Trim();
- var applyRounding = CbApplyRounding.Checked;
- var round = (int)NumRound.Value;
-
- if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(counter))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_TestPerformanceCounter_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
- return;
- }
-
- BtnTest.Enabled = false;
-
- // execute the test
- var result = await Task.Run((() => SensorTester.TestPerformanceCounter(category, counter, instance, applyRounding, round)));
-
- BtnTest.Enabled = true;
-
- if (result.Succesful)
- {
- MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPerformanceCounter_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
- return;
- }
-
- // failed
- var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPerformanceCounter_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
- if (q != DialogResult.Yes) return;
-
- // open logs
- HelperFunctions.OpenLocalFolder(Variables.LogPath);
- }
-
- private async void TestPowershell()
- {
- // prepare values
- var command = TbSetting1.Text.Trim();
- var applyRounding = CbApplyRounding.Checked;
- var round = (int)NumRound.Value;
-
- if (string.IsNullOrEmpty(command))
- {
- MessageBoxAdv.Show(this, Languages.SensorsMod_TestPowershell_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
- return;
- }
-
- BtnTest.Enabled = false;
-
- // execute the test
- var result = await Task.Run((() => SensorTester.TestPowershell(command, applyRounding, round)));
-
- BtnTest.Enabled = true;
-
- if (result.Succesful)
- {
- MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPowershell_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
- return;
- }
-
- // failed
- var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPowershell_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
- if (q != DialogResult.Yes) return;
-
- // open logs
- HelperFunctions.OpenLocalFolder(Variables.LogPath);
- }
-
- private void CbRdValue_CheckedChanged(object sender, EventArgs e)
- {
- if (NumRound.Visible == true)
- {
- NumRound.Visible = false;
- LblDigits.Visible = false;
- }
- else
- {
- NumRound.Visible = true;
- LblDigits.Visible = true;
- }
- }
- }
+ // set values
+ Sensor.Type = sensorCard.SensorType;
+ Sensor.EntityName = name;
+ Sensor.Name = friendlyName;
+ Sensor.UpdateInterval = interval;
+ Sensor.IgnoreAvailability = CbIgnoreAvailability.Checked;
+ Sensor.ApplyRounding = applyRounding;
+ Sensor.Round = round;
+
+ // done
+ DialogResult = DialogResult.OK;
+ }
+
+ ///
+ /// Show advanced settings for sensor
+ ///
+ ///
+ ///
+ private void BtnAdvSettings_Click(object sender, EventArgs e)
+ {
+ using var advancedSensorSettingsDialog = new AdvancedSensorSettings(Sensor.AdvancedSettings);
+ advancedSensorSettingsDialog.Opacity = 0;
+
+ var ret = advancedSensorSettingsDialog.ShowDialog();
+ if (ret != DialogResult.OK)
+ return;
+
+ Sensor.AdvancedSettings = JsonConvert.SerializeObject(advancedSensorSettingsDialog.AdvancedSettings);
+ }
+
+ private void TbDescription_LinkClicked(object sender, LinkClickedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.LinkText)) return;
+ if (!e.LinkText.ToLower().StartsWith("http")) return;
+
+ HelperFunctions.LaunchUrl(e.LinkText);
+ }
+
+ private void SensorsMod_ResizeEnd(object sender, EventArgs e)
+ {
+ if (Variables.ShuttingDown) return;
+ if (!IsHandleCreated) return;
+ if (IsDisposed) return;
+
+ try
+ {
+ // hide the pesky horizontal scrollbar
+ ListViewTheme.ShowScrollBar(LvSensors.Handle, ListViewTheme.SB_HORZ, false);
+
+ Refresh();
+ }
+ catch
+ {
+ // best effort
+ }
+ }
+
+ private void SensorsMod_KeyUp(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode != Keys.Escape) return;
+ Close();
+ }
+
+ private void SensorsMod_Layout(object sender, LayoutEventArgs e)
+ {
+ // hide the pesky horizontal scrollbar
+ ListViewTheme.ShowScrollBar(LvSensors.Handle, ListViewTheme.SB_HORZ, false);
+ }
+
+ ///
+ /// Locks the interface if the selected entity can't be added to the current client
+ ///
+ private void LockWrongClient()
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new MethodInvoker(LockWrongClient));
+ return;
+ }
+
+ _interfaceLockedWrongType = true;
+
+ var requiredClient = _serviceMode ? "hass.agent" : "service";
+ LblSpecificClient.Text = string.Format(Languages.SensorsMod_SpecificClient, requiredClient);
+
+ LblSpecificClient.Visible = true;
+
+ TbName.Enabled = false;
+ TbName.Text = string.Empty;
+
+ TbFriendlyName.Enabled = false;
+ TbFriendlyName.Text = string.Empty;
+
+ SetEmptyGui();
+
+ BtnStore.Enabled = false;
+ }
+
+ ///
+ /// Unlocks the interface if the selected entity can be added to the current client
+ ///
+ private void UnlockWrongClient()
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new MethodInvoker(UnlockWrongClient));
+ return;
+ }
+
+ _interfaceLockedWrongType = false;
+
+ LblSpecificClient.Visible = false;
+
+ TbName.Enabled = true;
+ TbFriendlyName.Enabled = true;
+ BtnStore.Enabled = true;
+ }
+
+ private void BtnTest_Click(object sender, EventArgs e)
+ {
+ switch (_selectedSensorType)
+ {
+ case SensorType.WmiQuerySensor:
+ TestWmi();
+ break;
+
+ case SensorType.PerformanceCounterSensor:
+ TestPerformanceCounter();
+ break;
+
+ case SensorType.PowershellSensor:
+ TestPowershell();
+ break;
+ }
+ }
+
+ private async void TestWmi()
+ {
+ // prepare values
+ var query = TbSetting1.Text.Trim();
+ var scope = TbSetting2.Text.Trim();
+ var applyRounding = CbApplyRounding.Checked;
+ var round = (int)NumRound.Value;
+
+ if (string.IsNullOrEmpty(query))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_TestWmi_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ ActiveControl = TbSetting1;
+ return;
+ }
+
+ // test the scope
+ if (!string.IsNullOrEmpty(scope))
+ {
+ if (!HelperFunctions.CheckWmiScope(scope))
+ {
+ var scopeQ = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_WmiTestFailed, scope),
+ Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
+
+ if (scopeQ != DialogResult.Yes) return;
+ }
+ }
+
+ BtnTest.Enabled = false;
+
+ // execute the test
+ var result = await Task.Run(() => SensorTester.TestWmiQuery(query, scope, applyRounding, round));
+
+ BtnTest.Enabled = true;
+
+ if (result.Succesful)
+ {
+ MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestWmi_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ // failed
+ var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestWmi_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
+ if (q != DialogResult.Yes) return;
+
+ // open logs
+ HelperFunctions.OpenLocalFolder(Variables.LogPath);
+ }
+
+ private async void TestPerformanceCounter()
+ {
+ // prepare values
+ var category = TbSetting1.Text.Trim();
+ var counter = TbSetting2.Text.Trim();
+ var instance = TbSetting3.Text.Trim();
+ var applyRounding = CbApplyRounding.Checked;
+ var round = (int)NumRound.Value;
+
+ if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(counter))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_TestPerformanceCounter_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return;
+ }
+
+ BtnTest.Enabled = false;
+
+ // execute the test
+ var result = await Task.Run((() => SensorTester.TestPerformanceCounter(category, counter, instance, applyRounding, round)));
+
+ BtnTest.Enabled = true;
+
+ if (result.Succesful)
+ {
+ MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPerformanceCounter_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ // failed
+ var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPerformanceCounter_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
+ if (q != DialogResult.Yes) return;
+
+ // open logs
+ HelperFunctions.OpenLocalFolder(Variables.LogPath);
+ }
+
+ private async void TestPowershell()
+ {
+ // prepare values
+ var command = TbSetting1.Text.Trim();
+ var applyRounding = CbApplyRounding.Checked;
+ var round = (int)NumRound.Value;
+
+ if (string.IsNullOrEmpty(command))
+ {
+ MessageBoxAdv.Show(this, Languages.SensorsMod_TestPowershell_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return;
+ }
+
+ BtnTest.Enabled = false;
+
+ // execute the test
+ var result = await Task.Run((() => SensorTester.TestPowershell(command, applyRounding, round)));
+
+ BtnTest.Enabled = true;
+
+ if (result.Succesful)
+ {
+ MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPowershell_MessageBox2, result.ReturnValue), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ // failed
+ var q = MessageBoxAdv.Show(this, string.Format(Languages.SensorsMod_TestPowershell_MessageBox3, result.ErrorReason), Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error);
+ if (q != DialogResult.Yes) return;
+
+ // open logs
+ HelperFunctions.OpenLocalFolder(Variables.LogPath);
+ }
+
+ private void CbRdValue_CheckedChanged(object sender, EventArgs e)
+ {
+ if (NumRound.Visible == true)
+ {
+ NumRound.Visible = false;
+ LblDigits.Visible = false;
+ }
+ else
+ {
+ NumRound.Visible = true;
+ LblDigits.Visible = true;
+ }
+ }
+ }
}
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index fb9c0c56..4d4b5c16 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -128,6 +128,9 @@
+
+ Form
+
@@ -1613,6 +1616,24 @@
QuickActionsMod.resx
+
+ AdvancedSensorSettings.resx
+
+
+ AdvancedSensorSettings.resx
+
+
+ AdvancedSensorSettings.resx
+
+
+ AdvancedSensorSettings.resx
+
+
+ AdvancedSensorSettings.resx
+
+
+ AdvancedSensorSettings.resx
+
SensorsConfig.resx
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs
index a5b2c14a..8bc87fae 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs
@@ -26,7 +26,7 @@ public class ActiveDesktopSensor : AbstractSingleValueSensor
private string _desktopName = string.Empty;
private string _attributes = string.Empty;
- public ActiveDesktopSensor(int? updateInterval = null, string entityName = _defaultName, string name = _defaultName, string id = default) : base(entityName ?? _defaultName, name ?? null, updateInterval ?? 15, id)
+ public ActiveDesktopSensor(int? updateInterval = null, string entityName = _defaultName, string name = _defaultName, string id = default, string advancedSettings = default) : base(entityName ?? _defaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings)
{
UseAttributes = true;
}
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
index 99ecb2f1..de154e55 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
@@ -14,7 +14,7 @@ public class BluetoothDevicesSensor : AbstractSingleValueSensor
private const string DefaultName = "bluetoothdevices";
private string _attributes = "{}";
- public BluetoothDevicesSensor(int? updateInterval = 30, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public BluetoothDevicesSensor(int? updateInterval = 30, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
UseAttributes = true;
}
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
index 04ecf4d5..f63f17d5 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
@@ -14,7 +14,7 @@ public class BluetoothLeDevicesSensor : AbstractSingleValueSensor
private const string DefaultName = "bluetoothledevices";
private string _attributes = "{}";
- public BluetoothLeDevicesSensor(int? updateInterval = 30, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public BluetoothLeDevicesSensor(int? updateInterval = 30, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
UseAttributes = true;
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs
index 96138fd4..78bc6d83 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs
@@ -9,7 +9,7 @@ namespace HASS.Agent.HomeAssistant.Sensors.GeneralSensors.SingleValue
public class GeoLocationSensor : AbstractSingleValueSensor
{
private const string DefaultName = "geolocation";
- public GeoLocationSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id) { }
+ public GeoLocationSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
index 2c261ca7..a9197ac9 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
@@ -17,7 +17,7 @@ public class InternalDeviceSensor : AbstractSingleValueSensor
private readonly IInternalDeviceSensor _internalDeviceSensor;
- public InternalDeviceSensor(string sensorType, int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id)
+ public InternalDeviceSensor(string sensorType, int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings)
{
SensorType = Enum.Parse(sensorType);
_internalDeviceSensor = InternalDeviceSensorsManager.AvailableSensors.First(s => s.Type == SensorType);
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs
index d4564bb1..ae984599 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs
@@ -10,7 +10,7 @@ namespace HASS.Agent.HomeAssistant.Sensors.GeneralSensors.SingleValue
public class MonitorPowerStateSensor : AbstractSingleValueSensor
{
private const string DefaultName = "monitorpowerstate";
- public MonitorPowerStateSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id) { }
+ public MonitorPowerStateSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 6aa0e753..04fc72c3 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -163,6 +163,51 @@ internal static string About_Title {
}
}
+ ///
+ /// Looks up a localized string similar to Device class.
+ ///
+ internal static string AdvancedSensorConfig_LblDeviceClass {
+ get {
+ return ResourceManager.GetString("AdvancedSensorConfig_LblDeviceClass", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to State class.
+ ///
+ internal static string AdvancedSensorConfig_LblStateClass {
+ get {
+ return ResourceManager.GetString("AdvancedSensorConfig_LblStateClass", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unit of measurement.
+ ///
+ internal static string AdvancedSensorConfig_LblUnitOfMeasurement {
+ get {
+ return ResourceManager.GetString("AdvancedSensorConfig_LblUnitOfMeasurement", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Advanced Settings.
+ ///
+ internal static string AdvancedSensorConfig_Title {
+ get {
+ return ResourceManager.GetString("AdvancedSensorConfig_Title", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Warning, adding following settings (overrides) may break the sensor, use with caution!.
+ ///
+ internal static string AdvancedSensorConfig_Warning {
+ get {
+ return ResourceManager.GetString("AdvancedSensorConfig_Warning", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Button.
///
@@ -6251,6 +6296,15 @@ internal static string SensorsMod_All {
}
}
+ ///
+ /// Looks up a localized string similar to Ad&vanced Settings.
+ ///
+ internal static string SensorsMod_BtnAdvancedSettings {
+ get {
+ return ResourceManager.GetString("SensorsMod_BtnAdvancedSettings", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to &Store Sensor.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 667b8615..e8bbdff5 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3498,4 +3498,13 @@ Hinweis: Bei Verwendung im Satellitendienst werden keine Userspace-Anwendungen e
NFC
+
+ Erweiterte Einstellungen
+
+
+ Erwe&iterte Einstellungen
+
+
+ Achtung: Das Hinzufügen der folgenden Einstellungen (Überschreibungen) kann zur Beschädigung des Sensors führen. Gehen Sie daher vorsichtig vor!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 9ad44e64..23214f52 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3375,4 +3375,22 @@ Note: if used in the satellite service, it won't detect userspace applications.<
NFC
+
+ Ad&vanced Settings
+
+
+ Warning, adding following settings (overrides) may break the sensor, use with caution!
+
+
+ Device class
+
+
+ Unit of measurement
+
+
+ State class
+
+
+ Advanced Settings
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index f4714e88..d85d8775 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3374,4 +3374,13 @@ Nota: si se usa en el servicio satelital, no detectará aplicaciones del espacio
NFC
+
+ Ajustes avanzados
+
+
+ A&justes avanzados
+
+
+ Advertencia, agregar las siguientes configuraciones (anulaciones) puede dañar el sensor, ¡úselo con precaución!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index a3c2f2f1..ae38cacb 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3407,4 +3407,13 @@ Remarque : s'il est utilisé dans le service satellite, il ne détectera pas les
NFC
+
+ Réglages avancés
+
+
+ Réglages a&vancés
+
+
+ Attention, l'ajout des paramètres suivants (remplacements) peut casser le capteur, à utiliser avec prudence !
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index e87c832b..b67fab9a 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3395,4 +3395,13 @@ Opmerking: indien gebruikt in de satellietdienst, zal het geen gebruikersruimtet
NFC
+
+ Geavanceerde instellingen
+
+
+ Gea&vanceerde instellingen
+
+
+ Waarschuwing: het toevoegen van de volgende instellingen (overschrijvingen) kan de sensor kapot maken, wees voorzichtig!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index a209df91..6dd51e93 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3484,4 +3484,13 @@ Uwaga: jeśli jest używany w usłudze satelitarnej, nie wykryje aplikacji przes
NFC
+
+ Ustawienia zaawansowane
+
+
+ Ustaw&ienia zaawansowane
+
+
+ Uwaga, dodanie następujących ustawień (nadpisań) może spowodować uszkodzenie czujnika, zachowaj ostrożność!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 04f553a7..0d4fe746 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3420,4 +3420,13 @@ Nota: se usado no serviço de satélite, não detectará aplicações no espaço
NFC
+
+ Configurações avançadas
+
+
+ Configurações a&vançadas
+
+
+ Atenção, adicionar as seguintes configurações (substituições) pode quebrar o sensor, use com cuidado!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index a91866c5..c79a98a1 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3387,4 +3387,22 @@ Requires audio device name as a payload.
NFC
+
+ Ad&vanced Settings
+
+
+ Warning, adding following settings (overrides) may break the sensor, use with caution!
+
+
+ Device class
+
+
+ Unit of measurement
+
+
+ State class
+
+
+ Advanced Settings
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 2e7a6713..985e25a2 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3443,4 +3443,13 @@ Home Assistant.
NFC
+
+ Расширенные настройки
+
+
+ Рас&ширенные настройки
+
+
+ Внимание! Добавление следующих настроек (переопределений) может привести к поломке датчика, используйте с осторожностью!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 57350977..bb2dfbf6 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3523,4 +3523,13 @@ Opomba: če se uporablja v satelitski storitvi, ne bo zaznal aplikacij uporabni
NFC
+
+ Napredne nastavitve
+
+
+ Napredne nasta&vitve
+
+
+ Opozorilo, dodajanje naslednjih nastavitev (preglasitev) lahko pokvari senzor, uporabljajte previdno!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 21bd0270..4c44f726 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2990,4 +2990,13 @@ Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılam
NFC
+
+ Gelişmiş Ayarlar
+
+
+ Ge&lişmiş Ayarlar
+
+
+ Uyarı, aşağıdaki ayarların (geçersiz kılmaların) eklenmesi sensörü bozabilir, dikkatli kullanın!
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
index 8c908fd2..bbcd34dc 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
@@ -104,103 +104,103 @@ internal static AbstractSingleValueSensor ConvertConfiguredToAbstractSingleValue
switch (sensor.Type)
{
case SensorType.UserNotificationStateSensor:
- abstractSensor = new UserNotificationStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new UserNotificationStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.DummySensor:
- abstractSensor = new DummySensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new DummySensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.CurrentClockSpeedSensor:
- abstractSensor = new CurrentClockSpeedSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CurrentClockSpeedSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.CpuLoadSensor:
- abstractSensor = new CpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.MemoryUsageSensor:
- abstractSensor = new MemoryUsageSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MemoryUsageSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.ApplyRounding, sensor.Round, sensor.AdvancedSettings);
break;
case SensorType.ActiveWindowSensor:
- abstractSensor = new ActiveWindowSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ActiveWindowSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ActiveDesktopSensor:
- abstractSensor = new ActiveDesktopSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ActiveDesktopSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.NamedWindowSensor:
- abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString());
+ abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastActiveSensor:
- abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastSystemStateChangeSensor:
- abstractSensor = new LastSystemStateChangeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastSystemStateChangeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastBootSensor:
- abstractSensor = new LastBootSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LastBootSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WebcamActiveSensor:
- abstractSensor = new WebcamActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WebcamActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.MicrophoneActiveSensor:
- abstractSensor = new MicrophoneActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MicrophoneActiveSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.SessionStateSensor:
- abstractSensor = new SessionStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new SessionStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.CurrentVolumeSensor:
- abstractSensor = new CurrentVolumeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new CurrentVolumeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.GpuLoadSensor:
- abstractSensor = new GpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new GpuLoadSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.GpuTemperatureSensor:
- abstractSensor = new GpuTemperatureSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new GpuTemperatureSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WmiQuerySensor:
- abstractSensor = new WmiQuerySensor(sensor.Query, sensor.Scope, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WmiQuerySensor(sensor.Query, sensor.Scope, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.PerformanceCounterSensor:
- abstractSensor = new PerformanceCounterSensor(sensor.Category, sensor.Counter, sensor.Instance, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new PerformanceCounterSensor(sensor.Category, sensor.Counter, sensor.Instance, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ProcessActiveSensor:
- abstractSensor = new ProcessActiveSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ProcessActiveSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ServiceStateSensor:
- abstractSensor = new ServiceStateSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ServiceStateSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LoggedUsersSensor:
- abstractSensor = new LoggedUsersSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LoggedUsersSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LoggedUserSensor:
- abstractSensor = new LoggedUserSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new LoggedUserSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.GeoLocationSensor:
- abstractSensor = new GeoLocationSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new GeoLocationSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.MonitorPowerStateSensor:
- abstractSensor = new MonitorPowerStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MonitorPowerStateSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.PowershellSensor:
- abstractSensor = new PowershellSensor(sensor.Query, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new PowershellSensor(sensor.Query, sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WindowStateSensor:
- abstractSensor = new WindowStateSensor(sensor.Query, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString());
+ abstractSensor = new WindowStateSensor(sensor.Query, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.MicrophoneProcessSensor:
- abstractSensor = new MicrophoneProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new MicrophoneProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.WebcamProcessSensor:
- abstractSensor = new WebcamProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new WebcamProcessSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.BluetoothDevicesSensor:
- abstractSensor = new BluetoothDevicesSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new BluetoothDevicesSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.BluetoothLeDevicesSensor:
- abstractSensor = new BluetoothLeDevicesSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new BluetoothLeDevicesSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.InternalDeviceSensor:
- abstractSensor = new HomeAssistant.Sensors.GeneralSensors.SingleValue.InternalDeviceSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new HomeAssistant.Sensors.GeneralSensors.SingleValue.InternalDeviceSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.ScreenshotSensor:
- abstractSensor = new ScreenshotSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ abstractSensor = new ScreenshotSensor(sensor.Query, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
default:
Log.Error("[SETTINGS_SENSORS] [{name}] Unknown configured single-value sensor type: {type}", sensor.EntityName, sensor.Type.ToString());
@@ -277,7 +277,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Scope = wmiSensor.Scope,
Query = wmiSensor.Query,
ApplyRounding = wmiSensor.ApplyRounding,
- Round= wmiSensor.Round
+ Round= wmiSensor.Round,
+ AdvancedSettings = wmiSensor.AdvancedSettings
};
}
@@ -292,7 +293,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = namedWindowSensor.UpdateIntervalSeconds,
IgnoreAvailability = namedWindowSensor.IgnoreAvailability,
- WindowName = namedWindowSensor.WindowName
+ WindowName = namedWindowSensor.WindowName,
+ AdvancedSettings = namedWindowSensor.AdvancedSettings
};
}
@@ -311,7 +313,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Counter = performanceCounterSensor.CounterName,
Instance = performanceCounterSensor.InstanceName,
ApplyRounding = performanceCounterSensor.ApplyRounding,
- Round = performanceCounterSensor.Round
+ Round = performanceCounterSensor.Round,
+ AdvancedSettings = performanceCounterSensor.AdvancedSettings
};
}
@@ -326,7 +329,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = processActiveSensor.UpdateIntervalSeconds,
IgnoreAvailability = processActiveSensor.IgnoreAvailability,
- Query = processActiveSensor.ProcessName
+ Query = processActiveSensor.ProcessName,
+ AdvancedSettings = processActiveSensor.AdvancedSettings
};
}
@@ -341,7 +345,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = serviceStateSensor.UpdateIntervalSeconds,
IgnoreAvailability = serviceStateSensor.IgnoreAvailability,
- Query = serviceStateSensor.ServiceName
+ Query = serviceStateSensor.ServiceName,
+ AdvancedSettings = serviceStateSensor.AdvancedSettings
};
}
@@ -358,7 +363,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
IgnoreAvailability = powershellSensor.IgnoreAvailability,
Query = powershellSensor.Command,
ApplyRounding = powershellSensor.ApplyRounding,
- Round = powershellSensor.Round
+ Round = powershellSensor.Round,
+ AdvancedSettings = powershellSensor.AdvancedSettings
};
}
@@ -374,8 +380,9 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
UpdateInterval = lastActiveSensor.UpdateIntervalSeconds,
IgnoreAvailability = lastActiveSensor.IgnoreAvailability,
ApplyRounding = lastActiveSensor.ApplyRounding,
- Round = lastActiveSensor.Round
- };
+ Round = lastActiveSensor.Round,
+ AdvancedSettings = lastActiveSensor.AdvancedSettings
+ };
}
case WindowStateSensor windowStateSensor:
@@ -389,7 +396,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = windowStateSensor.UpdateIntervalSeconds,
IgnoreAvailability = windowStateSensor.IgnoreAvailability,
- Query = windowStateSensor.ProcessName
+ Query = windowStateSensor.ProcessName,
+ AdvancedSettings = windowStateSensor.AdvancedSettings
};
}
@@ -404,7 +412,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = internalDeviceSensor.UpdateIntervalSeconds,
IgnoreAvailability = internalDeviceSensor.IgnoreAvailability,
- Query = internalDeviceSensor.SensorType.ToString()
+ Query = internalDeviceSensor.SensorType.ToString(),
+ AdvancedSettings = internalDeviceSensor.AdvancedSettings
};
}
@@ -419,7 +428,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Type = type,
UpdateInterval = screenshotSensor.UpdateIntervalSeconds,
IgnoreAvailability = screenshotSensor.IgnoreAvailability,
- Query = screenshotSensor.ScreenIndex.ToString()
+ Query = screenshotSensor.ScreenIndex.ToString(),
+ AdvancedSettings = screenshotSensor.AdvancedSettings
};
}
@@ -433,7 +443,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = sensor.Name,
Type = type,
UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
+ IgnoreAvailability = sensor.IgnoreAvailability,
+ AdvancedSettings = sensor.AdvancedSettings
};
}
}
From 255d209cd2dc094417479cac9119186aab938ffa Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 3 May 2024 21:07:13 +0200
Subject: [PATCH 23/45] Fix: adjust sensor state_class/device_class (#82)
This PR adjusts state class and device class of various sensors.
Previously sensors like CPU load sensor would report their value as "string" - each update would be visible as a string, not number.
After this change, Home Assistant statistics and other features will be available for the corrected sensors.
---
.../GeneralSensors/MultiValue/AudioSensors.cs | 10 +++++-----
.../GeneralSensors/MultiValue/BatterySensors.cs | 6 +++---
.../MultiValue/DataTypes/DataTypeDoubleSensor.cs | 15 +++++++++++----
.../MultiValue/DataTypes/DataTypeIntSensor.cs | 15 +++++++++++----
.../GeneralSensors/MultiValue/DisplaySensors.cs | 2 +-
.../GeneralSensors/MultiValue/NetworkSensors.cs | 2 +-
.../GeneralSensors/MultiValue/StorageSensors.cs | 2 +-
.../MultiValue/WindowsUpdatesSensors.cs | 8 ++++----
.../SingleValue/CurrentVolumeSensor.cs | 1 +
.../GeneralSensors/SingleValue/DummySensor.cs | 1 +
.../GeneralSensors/SingleValue/GpuLoadSensor.cs | 3 ++-
.../SingleValue/GpuTemperatureSensor.cs | 1 +
.../SingleValue/MicrophoneProcessSensor.cs | 1 +
.../SingleValue/ProcessActiveSensor.cs | 1 +
.../SingleValue/WebcamProcessSensor.cs | 1 +
.../SingleValue/CpuLoadSensor.cs | 1 +
.../Sensors/PerformanceCounterSensor.cs | 1 +
.../SingleValue/CurrentClockSpeedSensor.cs | 2 ++
.../WmiSensors/SingleValue/MemoryUsageSensor.cs | 1 +
.../GeneralSensors/MultiValue/PrintersSensors.cs | 6 +++---
.../SingleValue/BluetoothDevicesSensor.cs | 1 +
.../SingleValue/BluetoothLeDevicesSensor.cs | 1 +
.../SingleValue/InternalDeviceSensor.cs | 7 +++++++
.../Managers/DeviceSensors/AccelerometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/ActivitySensor.cs | 5 +++++
.../Managers/DeviceSensors/AltimeterSensor.cs | 5 +++++
.../Managers/DeviceSensors/BarometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/CompassSensor.cs | 5 +++++
.../Managers/DeviceSensors/GyrometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/HingeAngleSensor.cs | 5 +++++
.../DeviceSensors/IInternalDeviceSensor.cs | 3 +++
.../Managers/DeviceSensors/InclinometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/LightSensor.cs | 5 +++++
.../Managers/DeviceSensors/MagnetometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/OrientationSensor.cs | 5 +++++
.../Managers/DeviceSensors/PedometerSensor.cs | 5 +++++
.../Managers/DeviceSensors/ProximitySensor.cs | 5 +++++
.../DeviceSensors/SimpleOrientationSensor.cs | 5 +++++
38 files changed, 135 insertions(+), 27 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
index 4739e906..5df24f5d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
@@ -76,7 +76,7 @@ private void HandleAudioOutputSensors(string parentSensorSafeName, string device
var masterVolume = Convert.ToInt32(Math.Round(audioDevice.AudioEndpointVolume?.MasterVolumeLevelScalar * 100 ?? 0, 0));
var defaultDeviceVolumeEntityName = $"{parentSensorSafeName}_default_device_volume";
var defaultDeviceVolumeId = $"{Id}_default_device_volume";
- var defaultDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultDeviceVolumeEntityName, $"Default Device Volume", defaultDeviceVolumeId, string.Empty, "mdi:speaker", string.Empty, EntityName);
+ var defaultDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultDeviceVolumeEntityName, $"Default Device Volume", defaultDeviceVolumeId, string.Empty, "measurement", "mdi:speaker", string.Empty, EntityName);
defaultDeviceVolumeSensor.SetState(masterVolume);
AddUpdateSensor(defaultDeviceVolumeId, defaultDeviceVolumeSensor);
@@ -98,7 +98,7 @@ private void HandleAudioOutputSensors(string parentSensorSafeName, string device
var sessionsEntityName = $"{parentSensorSafeName}_sessions";
var sessionsId = $"{Id}_sessions";
- var sessionsSensor = new DataTypeIntSensor(_updateInterval, sessionsEntityName, $"Audio Sessions", sessionsId, string.Empty, "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
+ var sessionsSensor = new DataTypeIntSensor(_updateInterval, sessionsEntityName, $"Audio Sessions", sessionsId, string.Empty, "measurement", "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
sessionsSensor.SetState(sessionInfos.Count);
sessionsSensor.SetAttributes(
JsonConvert.SerializeObject(new
@@ -111,7 +111,7 @@ private void HandleAudioOutputSensors(string parentSensorSafeName, string device
var audioOutputDevices = GetAudioOutputDevices();
var audioOutputDevicesEntityName = $"{parentSensorSafeName}_output_devices";
var audioOutputDevicesId = $"{Id}_output_devices";
- var audioOutputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioOutputDevicesEntityName, $"Audio Output Devices", audioOutputDevicesId, string.Empty, "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
+ var audioOutputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioOutputDevicesEntityName, $"Audio Output Devices", audioOutputDevicesId, string.Empty, "measurement", "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
audioOutputDevicesSensor.SetState(audioOutputDevices.Count);
audioOutputDevicesSensor.SetAttributes(
JsonConvert.SerializeObject(new
@@ -148,14 +148,14 @@ private void HandleAudioInputSensors(string parentSensorSafeName, string deviceN
var inputVolume = (int)GetDefaultInputDevicePeakVolume(inputDevice);
var defaultInputDeviceVolumeEntityName = $"{parentSensorSafeName}_default_input_device_volume";
var defaultInputDeviceVolumeId = $"{Id}_default_input_device_volume";
- var defaultInputDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultInputDeviceVolumeEntityName, $"Default Input Device Volume", defaultInputDeviceVolumeId, string.Empty, "mdi:microphone", string.Empty, EntityName);
+ var defaultInputDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultInputDeviceVolumeEntityName, $"Default Input Device Volume", defaultInputDeviceVolumeId, string.Empty, "measurement", "mdi:microphone", string.Empty, EntityName);
defaultInputDeviceVolumeSensor.SetState(inputVolume);
AddUpdateSensor(defaultInputDeviceVolumeId, defaultInputDeviceVolumeSensor);
var audioInputDevices = GetAudioInputDevices();
var audioInputDevicesEntityName = $"{parentSensorSafeName}_input_devices";
var audioInputDevicesId = $"{Id}_input_devices";
- var audioInputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioInputDevicesEntityName, $"Audio Input Devices", audioInputDevicesId, string.Empty, "mdi:microphone", string.Empty, EntityName, true);
+ var audioInputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioInputDevicesEntityName, $"Audio Input Devices", audioInputDevicesId, string.Empty, "measurement", "mdi:microphone", string.Empty, EntityName, true);
audioInputDevicesSensor.SetState(audioInputDevices.Count);
audioInputDevicesSensor.SetAttributes(
JsonConvert.SerializeObject(new
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/BatterySensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/BatterySensors.cs
index 23835adb..708e2dd2 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/BatterySensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/BatterySensors.cs
@@ -52,14 +52,14 @@ public sealed override void UpdateSensorValues()
var fullChargeLifetimeEntityName = $"{parentSensorSafeName}_full_charge_lifetime";
var fullChargeLifetimeId = $"{Id}_full_charge_lifetime";
- var fullChargeLifetimeSensor = new DataTypeIntSensor(_updateInterval, fullChargeLifetimeEntityName, "Full Charge Lifetime", fullChargeLifetimeId, string.Empty, "mdi:battery-high", string.Empty, EntityName);
+ var fullChargeLifetimeSensor = new DataTypeIntSensor(_updateInterval, fullChargeLifetimeEntityName, "Full Charge Lifetime", fullChargeLifetimeId, string.Empty, "measurement", "mdi:battery-high", string.Empty, EntityName);
fullChargeLifetimeSensor.SetState(fullChargeLifetimeMinutes);
AddUpdateSensor(fullChargeLifetimeId, fullChargeLifetimeSensor);
var chargeRemainingPercentage = Convert.ToInt32(powerStatus.BatteryLifePercent * 100);
var chargeRemainingPercentageEntityName = $"{parentSensorSafeName}_charge_remaining_percentage";
var chargeRemainingPercentageId = $"{Id}_charge_remaining_percentage";
- var chargeRemainingPercentageSensor = new DataTypeIntSensor(_updateInterval, chargeRemainingPercentageEntityName, "Charge Remaining Percentage", chargeRemainingPercentageId, string.Empty, "mdi:battery-high", "%", EntityName);
+ var chargeRemainingPercentageSensor = new DataTypeIntSensor(_updateInterval, chargeRemainingPercentageEntityName, "Charge Remaining Percentage", chargeRemainingPercentageId, string.Empty, "measurement", "mdi:battery-high", "%", EntityName);
chargeRemainingPercentageSensor.SetState(chargeRemainingPercentage);
AddUpdateSensor(chargeRemainingPercentageId, chargeRemainingPercentageSensor);
@@ -69,7 +69,7 @@ public sealed override void UpdateSensorValues()
var chargeRemainingMinutesEntityName = $"{parentSensorSafeName}_charge_remaining";
var chargeRemainingMinutesId = $"{Id}_charge_remaining";
- var chargeRemainingMinutesSensor = new DataTypeIntSensor(_updateInterval, chargeRemainingMinutesEntityName, "Charge Remaining", chargeRemainingMinutesId, string.Empty, "mdi:battery-high", string.Empty, EntityName);
+ var chargeRemainingMinutesSensor = new DataTypeIntSensor(_updateInterval, chargeRemainingMinutesEntityName, "Charge Remaining", chargeRemainingMinutesId, string.Empty, "measurement", "mdi:battery-high", string.Empty, EntityName);
chargeRemainingMinutesSensor.SetState(chargeRemainingMinutes);
AddUpdateSensor(chargeRemainingMinutesId, chargeRemainingMinutesSensor);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs
index fd80e103..b0fbc741 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs
@@ -10,17 +10,19 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.MultiValue.Data
public class DataTypeDoubleSensor : AbstractSingleValueSensor
{
private readonly string _deviceClass;
+ private readonly string _stateClass;
private readonly string _unitOfMeasurement;
private readonly string _icon;
private double _value = 0d;
private string _attributes = string.Empty;
- public DataTypeDoubleSensor(int? updateInterval, string entityName, string name, string id, string deviceClass, string icon, string unitOfMeasurement, string multiValueSensorName, bool useAttributes = false) : base(entityName, name, updateInterval ?? 30, id, useAttributes)
+ public DataTypeDoubleSensor(int? updateInterval, string entityName, string name, string id, string deviceClass, string stateClass, string icon, string unitOfMeasurement, string multiValueSensorName, bool useAttributes = false) : base(entityName, name, updateInterval ?? 30, id, useAttributes)
{
TopicName = multiValueSensorName;
_deviceClass = deviceClass;
+ _stateClass = stateClass;
_unitOfMeasurement = unitOfMeasurement;
_icon = icon;
@@ -60,9 +62,14 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
if (UseAttributes) model.Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{TopicName}/{ObjectId}/attributes";
- if (!string.IsNullOrWhiteSpace(_deviceClass)) model.Device_class = _deviceClass;
- if (!string.IsNullOrWhiteSpace(_unitOfMeasurement)) model.Unit_of_measurement = _unitOfMeasurement;
- if (!string.IsNullOrWhiteSpace(_icon)) model.Icon = _icon;
+ if (!string.IsNullOrWhiteSpace(_deviceClass))
+ model.Device_class = _deviceClass;
+ if (!string.IsNullOrWhiteSpace(_stateClass))
+ model.State_class = _stateClass;
+ if (!string.IsNullOrWhiteSpace(_unitOfMeasurement))
+ model.Unit_of_measurement = _unitOfMeasurement;
+ if (!string.IsNullOrWhiteSpace(_icon))
+ model.Icon = _icon;
return SetAutoDiscoveryConfigModel(model);
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs
index 59d08581..0a9ec4e6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs
@@ -9,17 +9,19 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.MultiValue.Data
public class DataTypeIntSensor : AbstractSingleValueSensor
{
private readonly string _deviceClass;
+ private readonly string _stateClass;
private readonly string _unitOfMeasurement;
private readonly string _icon;
private int _value = 0;
private string _attributes = string.Empty;
- public DataTypeIntSensor(int? updateInterval, string entityName, string name, string id, string deviceClass, string icon, string unitOfMeasurement, string multiValueSensorName, bool useAttributes = false) : base(entityName, name, updateInterval ?? 30, id, useAttributes)
+ public DataTypeIntSensor(int? updateInterval, string entityName, string name, string id, string deviceClass, string stateClass, string icon, string unitOfMeasurement, string multiValueSensorName, bool useAttributes = false) : base(entityName, name, updateInterval ?? 30, id, useAttributes)
{
TopicName = multiValueSensorName;
_deviceClass = deviceClass;
+ _stateClass = stateClass;
_unitOfMeasurement = unitOfMeasurement;
_icon = icon;
@@ -59,9 +61,14 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
if (UseAttributes) model.Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{TopicName}/{ObjectId}/attributes";
- if (!string.IsNullOrWhiteSpace(_deviceClass)) model.Device_class = _deviceClass;
- if (!string.IsNullOrWhiteSpace(_unitOfMeasurement)) model.Unit_of_measurement = _unitOfMeasurement;
- if (!string.IsNullOrWhiteSpace(_icon)) model.Icon = _icon;
+ if (!string.IsNullOrWhiteSpace(_deviceClass))
+ model.Device_class = _deviceClass;
+ if (!string.IsNullOrWhiteSpace(_stateClass))
+ model.State_class = _stateClass;
+ if (!string.IsNullOrWhiteSpace(_unitOfMeasurement))
+ model.Unit_of_measurement = _unitOfMeasurement;
+ if (!string.IsNullOrWhiteSpace(_icon))
+ model.Icon = _icon;
return SetAutoDiscoveryConfigModel(model);
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DisplaySensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DisplaySensors.cs
index d60c31bd..fd454cb9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DisplaySensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DisplaySensors.cs
@@ -48,7 +48,7 @@ public sealed override void UpdateSensorValues()
var displayCount = displays.Length;
var displayCountEntityName = $"{parentSensorSafeName}_display_count";
var displayCountId = $"{Id}_display_count";
- var displayCountSensor = new DataTypeIntSensor(_updateInterval, displayCountEntityName, "Display Count", displayCountId, string.Empty, "mdi:monitor", string.Empty, EntityName);
+ var displayCountSensor = new DataTypeIntSensor(_updateInterval, displayCountEntityName, "Display Count", displayCountId, string.Empty, "measurement", "mdi:monitor", string.Empty, EntityName);
displayCountSensor.SetState(displayCount);
AddUpdateSensor(displayCountId, displayCountSensor);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/NetworkSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/NetworkSensors.cs
index fdc091e1..e43755b6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/NetworkSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/NetworkSensors.cs
@@ -139,7 +139,7 @@ public override sealed void UpdateSensorValues()
var nicCountEntityName = $"{parentSensorSafeName}_total_network_card_count";
var nicCountId = $"{Id}_total_network_card_count";
- var nicCountSensor = new DataTypeIntSensor(_updateInterval, nicCountEntityName, "Network Card Count", nicCountId, string.Empty, "mdi:lan", string.Empty, EntityName);
+ var nicCountSensor = new DataTypeIntSensor(_updateInterval, nicCountEntityName, "Network Card Count", nicCountId, string.Empty, "measurement", "mdi:lan", string.Empty, EntityName);
nicCountSensor.SetState(nicCount);
AddUpdateSensor(nicCountId, nicCountSensor);
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/StorageSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/StorageSensors.cs
index 9d4c682d..dd65447c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/StorageSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/StorageSensors.cs
@@ -117,7 +117,7 @@ public override sealed void UpdateSensorValues()
var driveCountEntityName = $"{parentSensorSafeName}_total_disk_count";
var driveCountId = $"{Id}_total_disk_count";
- var driveCountSensor = new DataTypeIntSensor(_updateInterval, driveCountEntityName, "Total Disk Count", driveCountId, string.Empty, "mdi:harddisk", string.Empty, EntityName);
+ var driveCountSensor = new DataTypeIntSensor(_updateInterval, driveCountEntityName, "Total Disk Count", driveCountId, string.Empty, "measurement", "mdi:harddisk", string.Empty, EntityName);
driveCountSensor.SetState(driveCount);
AddUpdateSensor(driveCountId, driveCountSensor);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
index 3a3af5cb..5ac1797f 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
@@ -41,27 +41,27 @@ public sealed override void UpdateSensorValues()
var driverUpdateCount = driverUpdates.Count;
var driverUpdateCountId = $"{parentSensorSafeName}_driver_updates_pending";
- var driverUpdateCountSensor = new DataTypeIntSensor(_updateInterval, driverUpdateCountId, "Driver Updates Pending", driverUpdateCountId, string.Empty, "mdi:microsoft-windows", string.Empty, EntityName);
+ var driverUpdateCountSensor = new DataTypeIntSensor(_updateInterval, driverUpdateCountId, "Driver Updates Pending", driverUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
driverUpdateCountSensor.SetState(driverUpdateCount);
AddUpdateSensor(driverUpdateCountId, driverUpdateCountSensor);
var softwareUpdateCount = softwareUpdates.Count;
var softwareUpdateCountId = $"{parentSensorSafeName}_software_updates_pending";
- var softwareUpdateCountSensor = new DataTypeIntSensor(_updateInterval, softwareUpdateCountId, "Software Updates Pending", softwareUpdateCountId, string.Empty, "mdi:microsoft-windows", string.Empty, EntityName);
+ var softwareUpdateCountSensor = new DataTypeIntSensor(_updateInterval, softwareUpdateCountId, "Software Updates Pending", softwareUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
softwareUpdateCountSensor.SetState(softwareUpdateCount);
AddUpdateSensor(softwareUpdateCountId, softwareUpdateCountSensor);
var driverUpdatesList = new WindowsUpdateInfoCollection(driverUpdates);
var driverUpdatesStr = JsonConvert.SerializeObject(driverUpdatesList, Formatting.Indented);
var driverUpdatesId = $"{parentSensorSafeName}_driver_updates";
- var driverUpdatesSensor = new DataTypeIntSensor(_updateInterval, driverUpdatesId, "Available Driver Updates", driverUpdatesId, string.Empty, "mdi:microsoft-windows", string.Empty, EntityName, true);
+ var driverUpdatesSensor = new DataTypeIntSensor(_updateInterval, driverUpdatesId, "Available Driver Updates", driverUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
driverUpdatesSensor.SetState(driverUpdates.Count);
driverUpdatesSensor.SetAttributes(driverUpdatesStr);
AddUpdateSensor(driverUpdatesId, driverUpdatesSensor);
var softwareUpdatesStr = JsonConvert.SerializeObject(new WindowsUpdateInfoCollection(softwareUpdates), Formatting.Indented);
var softwareUpdatesId = $"{parentSensorSafeName}_software_updates";
- var softwareUpdatesSensor = new DataTypeIntSensor(_updateInterval, softwareUpdatesId, "Available Software Updates", softwareUpdatesId, string.Empty, "mdi:microsoft-windows", string.Empty, EntityName, true);
+ var softwareUpdatesSensor = new DataTypeIntSensor(_updateInterval, softwareUpdatesId, "Available Software Updates", softwareUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
softwareUpdatesSensor.SetState(softwareUpdates.Count);
softwareUpdatesSensor.SetAttributes(softwareUpdatesStr);
AddUpdateSensor(softwareUpdatesId, softwareUpdatesSensor);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
index b327c891..7ad33701 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
@@ -30,6 +30,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
Icon = "mdi:volume-medium",
Unit_of_measurement = "%",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
index 2e298eab..45afb306 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs
@@ -25,6 +25,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
index 16d1ce0b..e4b4e92a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs
@@ -40,7 +40,8 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
Unit_of_measurement = "%",
- Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
+ State_class = "measurement",
+ Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
index 96a209ea..8204c3d9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs
@@ -39,6 +39,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
Device_class = "temperature",
Unit_of_measurement = "°C",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
index 0b57d35c..c7b89615 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs
@@ -46,6 +46,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability",
Icon = "mdi:microphone",
Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes"
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
index 594e2c5d..09d51781 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs
@@ -36,6 +36,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Icon = "mdi:file-eye-outline",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability"
});
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
index c698f90c..8dbfbccd 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs
@@ -41,6 +41,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability",
Icon = "mdi:webcam"
};
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
index d55b4664..245c143e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs
@@ -27,6 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Icon = "mdi:chart-areaspline",
Unit_of_measurement = "%",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
index 2aa2f9d1..cde16828 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs
@@ -54,6 +54,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{EntityName}/state",
+ State_class = "measurement",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
});
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
index 82e34202..23770359 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs
@@ -34,6 +34,8 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
+ Device_class = "frequency",
Icon = "mdi:speedometer",
Unit_of_measurement = "MHz",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
index 77b61ba6..eaf79098 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs
@@ -27,6 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Icon = "mdi:memory",
Unit_of_measurement = "%",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability"
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
index 7e60bc70..44c6d085 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
@@ -51,7 +51,7 @@ public sealed override void UpdateSensorValues()
var printersCountEntityName = $"{parentSensorSafeName}_printers_count";
var printersCountId = $"{Id}_printers_count";
- var printersCountSensor = new DataTypeIntSensor(_updateInterval, printersCountEntityName, "Printers Count", printersCountId, string.Empty, "mdi:printer", string.Empty, EntityName);
+ var printersCountSensor = new DataTypeIntSensor(_updateInterval, printersCountEntityName, "Printers Count", printersCountId, string.Empty, "measurement", "mdi:printer", string.Empty, EntityName);
printersCountSensor.SetState(printerInfo.PrintQueues.Count);
AddUpdateSensor(printersCountId, printersCountSensor);
@@ -63,7 +63,7 @@ public sealed override void UpdateSensorValues()
var defaultQueueJobsEntityName = $"{parentSensorSafeName}_default_queue_jobs";
var defaultQueueJobsId = $"{Id}_default_queue_jobs";
- var defaultQueueJobsSensor = new DataTypeIntSensor(_updateInterval, defaultQueueJobsEntityName, "Default Queue Jobs", defaultQueueJobsId, string.Empty, "mdi:printer", string.Empty, EntityName);
+ var defaultQueueJobsSensor = new DataTypeIntSensor(_updateInterval, defaultQueueJobsEntityName, "Default Queue Jobs", defaultQueueJobsId, string.Empty, "measurement", "mdi:printer", string.Empty, EntityName);
defaultQueueJobsSensor.SetState(printerInfo.DefaultQueueJobs);
AddUpdateSensor(defaultQueueJobsId, defaultQueueJobsSensor);
@@ -72,7 +72,7 @@ public sealed override void UpdateSensorValues()
var printerQueueInfo = JsonConvert.SerializeObject(printer, Formatting.Indented);
var printerEntityName = $"{parentSensorSafeName}_{SharedHelperFunctions.GetSafeValue(printer.Name)}";
var printerId = $"{Id}_{SharedHelperFunctions.GetSafeValue(printer.Name)}";
- var printerSensor = new DataTypeIntSensor(_updateInterval, printerEntityName, $"{printer.Name}", printerId, string.Empty, "mdi:printer", string.Empty, EntityName, true);
+ var printerSensor = new DataTypeIntSensor(_updateInterval, printerEntityName, $"{printer.Name}", printerId, string.Empty, "measurement", "mdi:printer", string.Empty, EntityName, true);
printerSensor.SetState(printer.Jobs);
printerSensor.SetAttributes(printerQueueInfo);
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
index de154e55..62d75631 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs
@@ -33,6 +33,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Icon = "mdi:bluetooth",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability",
Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes"
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
index f63f17d5..c61a57b8 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs
@@ -36,6 +36,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
Unique_id = Id,
Device = deviceConfig,
State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ State_class = "measurement",
Icon = "mdi:bluetooth",
Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability",
Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes"
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
index a9197ac9..34fb6fd0 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs
@@ -48,6 +48,13 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
if (UseAttributes)
sensorDiscoveryConfigModel.Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes";
+ if (!string.IsNullOrWhiteSpace(_internalDeviceSensor.MeasurementType))
+ sensorDiscoveryConfigModel.Device_class = _internalDeviceSensor.MeasurementType;
+ if (!string.IsNullOrWhiteSpace(_internalDeviceSensor.UnitOfMeasurement))
+ sensorDiscoveryConfigModel.Unit_of_measurement = _internalDeviceSensor.UnitOfMeasurement;
+ if (_internalDeviceSensor.IsNumeric)
+ sensorDiscoveryConfigModel.State_class = "measurement";
+
return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(sensorDiscoveryConfigModel);
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
index 9faf68b8..eb7806c5 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
@@ -14,6 +14,9 @@ internal class AccelerometerSensor : IInternalDeviceSensor
public const string AttributeAccelerationZ = "AccelerationZ";
public const string AttributeLastShaken = "LastShaken";
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
private readonly Accelerometer _accelerometer;
public bool Available => _accelerometer != null;
@@ -42,6 +45,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
index 12145bdd..be2956d1 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
@@ -12,6 +12,9 @@ internal class ActivitySensor : IInternalDeviceSensor
public const string AttributeConfidence = "Confidence";
public const string AttributeTimestamp = "Timestamp";
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
private readonly Windows.Devices.Sensors.ActivitySensor _activitySensor;
public bool Available => _activitySensor != null;
@@ -31,6 +34,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = false;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
index 3e1afd59..958b9370 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
@@ -11,6 +11,9 @@ internal class AltimeterSensor : IInternalDeviceSensor
{
private readonly Altimeter _altimeter;
+ public string MeasurementType { get; } = "distance";
+ public string UnitOfMeasurement { get; } = "m";
+
public bool Available => _altimeter != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Altimeter;
public string Measurement
@@ -24,6 +27,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public AltimeterSensor(Altimeter altimeter)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
index 61a42c68..231261fd 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
@@ -11,6 +11,9 @@ internal class BarometerSensor : IInternalDeviceSensor
{
private readonly Barometer _barometer;
+ public string MeasurementType { get; } = "pressure";
+ public string UnitOfMeasurement { get; } = "hPa";
+
public bool Available => _barometer != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Barometer;
public string Measurement
@@ -24,6 +27,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public BarometerSensor(Barometer barometer)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
index 89433a79..ec1a5040 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
@@ -13,6 +13,9 @@ internal class CompassSensor : IInternalDeviceSensor
private readonly Compass _compass;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _compass != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Compass;
public string Measurement
@@ -28,6 +31,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
index ae2024ce..209c73c8 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
@@ -15,6 +15,9 @@ internal class GyrometerSensor : IInternalDeviceSensor
private readonly Gyrometer _gyrometer;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _gyrometer != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Gyrometer;
public string Measurement
@@ -41,6 +44,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
index b28382af..30c748b3 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
@@ -11,6 +11,9 @@ internal class HingeAngleSensor : IInternalDeviceSensor
{
private readonly Windows.Devices.Sensors.HingeAngleSensor _hingeAngelSensor;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _hingeAngelSensor != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.HingeAngleSensor;
public string Measurement
@@ -25,6 +28,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public HingeAngleSensor(Windows.Devices.Sensors.HingeAngleSensor hingeAngleSensor)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/IInternalDeviceSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/IInternalDeviceSensor.cs
index 485b9a99..5159a8f8 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/IInternalDeviceSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/IInternalDeviceSensor.cs
@@ -36,6 +36,9 @@ internal interface IInternalDeviceSensor
public bool Available { get; }
public InternalDeviceSensorType Type { get; }
public string Measurement { get; }
+ public string MeasurementType { get; }
+ public string UnitOfMeasurement { get; }
+ public bool IsNumeric { get; }
public Dictionary Attributes { get; }
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
index 6a0cf171..ec582c38 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
@@ -17,6 +17,9 @@ internal class InclinometerSensor : IInternalDeviceSensor
private readonly Inclinometer _inclinometer;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _inclinometer != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Inclinometer;
public string Measurement
@@ -37,6 +40,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = false;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
index a3ea817a..167d8cd7 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
@@ -11,6 +11,9 @@ internal class LightSensor : IInternalDeviceSensor
{
private readonly Windows.Devices.Sensors.LightSensor _lightSensor;
+ public string MeasurementType { get; } = "illuminance";
+ public string UnitOfMeasurement { get; } = "lx";
+
public bool Available => _lightSensor != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.LightSensor;
public string Measurement
@@ -24,6 +27,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public LightSensor(Windows.Devices.Sensors.LightSensor lightSensor)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
index b7d5802c..61b1f538 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
@@ -15,6 +15,9 @@ internal class MagnetometerSensor : IInternalDeviceSensor
private readonly Magnetometer _magnetometer;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _magnetometer != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Magnetometer;
public string Measurement
@@ -41,6 +44,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
index cc9c974b..d57b1446 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
@@ -15,6 +15,9 @@ internal class OrientationSensor : IInternalDeviceSensor
private readonly Windows.Devices.Sensors.OrientationSensor _orientationSensor;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _orientationSensor != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.OrientationSensor;
public string Measurement
@@ -32,6 +35,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = false;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
index d2d5084e..013b60c4 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
@@ -11,6 +11,9 @@ internal class PedometerSensor : IInternalDeviceSensor
{
private readonly Pedometer _pedometer;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _pedometer != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.Pedometer;
public string Measurement
@@ -37,6 +40,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
private readonly Dictionary _attributes = new();
public Dictionary Attributes => _attributes;
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
index 4ef5e466..de60c167 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
@@ -11,6 +11,9 @@ internal class ProximitySensor : IInternalDeviceSensor
{
private Windows.Devices.Sensors.ProximitySensor _proximitySensor;
+ public string MeasurementType { get; } = "distance";
+ public string UnitOfMeasurement { get; } = "mm";
+
public bool Available => _proximitySensor != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.ProximitySensor;
public string Measurement
@@ -24,6 +27,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = true;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public ProximitySensor(Windows.Devices.Sensors.ProximitySensor proximitySensor)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/SimpleOrientationSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/SimpleOrientationSensor.cs
index f5b5a80e..b4babdfb 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/SimpleOrientationSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/SimpleOrientationSensor.cs
@@ -11,6 +11,9 @@ internal class SimpleOrientationSensor : IInternalDeviceSensor
{
private readonly Windows.Devices.Sensors.SimpleOrientationSensor _simpleOrientationSensor;
+ public string MeasurementType { get; } = string.Empty;
+ public string UnitOfMeasurement { get; } = string.Empty;
+
public bool Available => _simpleOrientationSensor != null;
public InternalDeviceSensorType Type => InternalDeviceSensorType.SimpleOrientationSensor;
public string Measurement
@@ -24,6 +27,8 @@ public string Measurement
}
}
+ public bool IsNumeric { get; } = false;
+
public Dictionary Attributes => InternalDeviceSensor.NoAttributes;
public SimpleOrientationSensor(Windows.Devices.Sensors.SimpleOrientationSensor simpleOrientationSensor)
From 2ff369689bc72ae8e9be3615cf0ae75498597084 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 4 May 2024 11:48:45 +0200
Subject: [PATCH 24/45] Fix: screenshot sensor monitor scaling (#84)
This PR fixes screenshot sensor capturing only part of the screen when "non 100%" scaling.
Thanks for @ElDingo424 for reporting - #81
---
.../SingleValue/ScreenshotSensor.cs | 73 +++++++++++++++++--
1 file changed, 65 insertions(+), 8 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
index f3305b50..fa2dd172 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs
@@ -13,12 +13,18 @@
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
+using Windows.Graphics.Display;
namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue;
public class ScreenshotSensor : AbstractSingleValueSensor
{
private const string DefaultName = "screenshot";
+ private DEVMODE _devMode = new()
+ {
+ dmSize = (short)Marshal.SizeOf(typeof(DEVMODE))
+ };
+
public int ScreenIndex;
public ScreenshotSensor(string screenIndex = "0", int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings)
@@ -52,14 +58,14 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
public override string GetState()
{
- var screenCount = Screen.AllScreens.Length;
- if (ScreenIndex >= screenCount || ScreenIndex < 0)
+ if (ScreenIndex >= Screen.AllScreens.Length || ScreenIndex < 0)
{
Log.Warning("[SCREENSHOT] Wrong index '{index}' - returning image for screen 0", ScreenIndex);
ScreenIndex = 0;
}
var screenImage = CaptureScreen(ScreenIndex);
+
return Convert.ToBase64String(screenImage);
}
@@ -72,22 +78,73 @@ private byte[] CaptureScreen(int screenIndex)
catch (Exception ex)
{
Log.Error(ex, "[SCREENSHOT] Internal Error capturing screen {index}, {ex}", ex.Message);
+
return Array.Empty();
}
}
private byte[] CapturePngFile(Screen screen)
{
+ EnumDisplaySettings(screen.DeviceName, -1, ref _devMode);
+
+ var scalingFactor = Math.Round(decimal.Divide(_devMode.dmPelsWidth, screen.Bounds.Width), 2);
+
var captureRectangle = screen.Bounds;
- var captureBitmap = new Bitmap(captureRectangle.Width, captureRectangle.Height, PixelFormat.Format32bppArgb);
- var captureGraphics = Graphics.FromImage(captureBitmap);
- captureGraphics.CopyFromScreen(captureRectangle.Left, captureRectangle.Top, 0, 0, captureRectangle.Size);
+ var captureSize = new Size(
+ Convert.ToInt32(screen.Bounds.Width * scalingFactor),
+ Convert.ToInt32(screen.Bounds.Height * scalingFactor)
+ );
+ using var captureBitmap = new Bitmap(captureSize.Width, captureSize.Height, PixelFormat.Format32bppArgb);
+ using var captureGraphics = Graphics.FromImage(captureBitmap);
+ captureGraphics.CopyFromScreen(captureRectangle.Left, captureRectangle.Top, 0, 0, captureSize);
- using var ms = new MemoryStream();
- captureBitmap.Save(ms, ImageFormat.Png);
+ using var memoryStream = new MemoryStream();
+ captureBitmap.Save(memoryStream, ImageFormat.Png);
- return ms.ToArray();
+ return memoryStream.ToArray();
}
public override string GetAttributes() => string.Empty;
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct DEVMODE
+ {
+ private const int CCHDEVICENAME = 0x20;
+ private const int CCHFORMNAME = 0x20;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string dmDeviceName;
+ public short dmSpecVersion;
+ public short dmDriverVersion;
+ public short dmSize;
+ public short dmDriverExtra;
+ public int dmFields;
+ public int dmPositionX;
+ public int dmPositionY;
+ public ScreenOrientation dmDisplayOrientation;
+ public int dmDisplayFixedOutput;
+ public short dmColor;
+ public short dmDuplex;
+ public short dmYResolution;
+ public short dmTTOption;
+ public short dmCollate;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string dmFormName;
+ public short dmLogPixels;
+ public int dmBitsPerPel;
+ public int dmPelsWidth;
+ public int dmPelsHeight;
+ public int dmDisplayFlags;
+ public int dmDisplayFrequency;
+ public int dmICMMethod;
+ public int dmICMIntent;
+ public int dmMediaType;
+ public int dmDitherType;
+ public int dmReserved1;
+ public int dmReserved2;
+ public int dmPanningWidth;
+ public int dmPanningHeight;
+ }
+
+ [DllImport("user32.dll")]
+ private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
}
From eec64dec6e006432b37e724a076c571d0ddd8c3f Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 4 May 2024 12:22:14 +0200
Subject: [PATCH 25/45] Fix: CoreAudio library memory leak / redesign (#85)
This PR should fix issues with the CoreAudio library that were either due to our improper use of it or some internal unmanaged resources leak - #64
After testing multiple audio libraries I've settled for:
CSCore for general audio sensors
DIY solution for activating specific device (making it default)
In addition to that, whole audio management was redesigned to work on an event basis rather than constantly pooling the OS for information.
---
.../HASS.Agent.Shared.csproj | 3 +-
.../SetApplicationVolumeCommand.cs | 83 +++---
.../InternalCommands/SetAudioInputCommand.cs | 30 +-
.../InternalCommands/SetAudioOutputCommand.cs | 30 +-
.../InternalCommands/SetVolumeCommand.cs | 34 +--
.../GeneralSensors/MultiValue/AudioSensors.cs | 261 ++---------------
.../SingleValue/CurrentVolumeSensor.cs | 14 +-
.../Managers/Audio/AudioDevice.cs | 19 ++
.../Managers/Audio/AudioManager.cs | 270 ++++++++++++++++++
.../Managers/Audio/AudioSession.cs | 17 ++
.../Managers/Audio/DeviceRole.cs | 13 +
.../Managers/Audio/DeviceType.cs | 12 +
.../Audio/Exceptions/AudioSessionException.cs | 21 ++
.../Internal/CPolicyConfigVistaClient.cs | 43 +++
.../Audio/Internal/IPolicyConfigVista.cs | 50 ++++
.../Audio/Internal/InternalAudioDevice.cs | 49 ++++
.../Audio/Internal/InternalAudioSession.cs | 85 ++++++
.../Internal/InternalAudioSessionManager.cs | 69 +++++
.../Internal/_CPolicyConfigVistaClient.cs | 17 ++
.../Models/Internal/AudioSessionInfo.cs | 19 --
src/HASS.Agent/HASS.Agent.Shared/Variables.cs | 2 -
src/HASS.Agent/HASS.Agent/Forms/Main.cs | 11 +
.../MultiValue/PrintersSensors.cs | 1 -
.../HASS.Agent/Media/MediaManager.cs | 4 +-
.../HASS.Agent/Media/MediaManagerCommands.cs | 19 +-
.../HASS.Agent/Media/MediaManagerRequests.cs | 31 +-
.../Resources/Localization/Languages.de.resx | 10 +-
.../Resources/Localization/Languages.en.resx | 29 +-
.../Resources/Localization/Languages.es.resx | 10 +-
.../Resources/Localization/Languages.fr.resx | 10 +-
.../Resources/Localization/Languages.nl.resx | 10 +-
.../Resources/Localization/Languages.pl.resx | 10 +-
.../Localization/Languages.pt-br.resx | 10 +-
.../Resources/Localization/Languages.resx | 10 +-
.../Resources/Localization/Languages.ru.resx | 10 +-
.../Resources/Localization/Languages.sl.resx | 10 +-
.../Resources/Localization/Languages.tr.resx | 10 +-
src/HASS.Agent/HASS.Agent/Variables.cs | 2 -
38 files changed, 945 insertions(+), 393 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioDevice.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioSession.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceRole.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceType.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Exceptions/AudioSessionException.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/_CPolicyConfigVistaClient.cs
delete mode 100644 src/HASS.Agent/HASS.Agent.Shared/Models/Internal/AudioSessionInfo.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index aa05de02..d142df54 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -33,7 +33,7 @@
-
+
@@ -44,6 +44,7 @@
+
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
index f0646eb3..0d0e1084 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
@@ -1,5 +1,7 @@
-using CoreAudio;
-using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
+using HidSharp;
using Newtonsoft.Json;
using Serilog;
using System;
@@ -13,7 +15,6 @@ namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands
public class SetApplicationVolumeCommand : InternalCommand
{
private const string DefaultName = "setappvolume";
- private static readonly Dictionary ApplicationNames = new Dictionary();
public SetApplicationVolumeCommand(string entityName = DefaultName, string name = DefaultName, string commandConfig = "", CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(entityName ?? DefaultName, name ?? null, commandConfig, entityType, id)
{
@@ -33,28 +34,14 @@ public override void TurnOn()
TurnOnWithAction(CommandConfig);
}
- private MMDevice GetAudioDeviceOrDefault(string playbackDeviceName)
+ private AudioDevice GetDeviceOrDefault(string deviceName)
{
- var devices = Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
- var playbackDevice = devices.Where(d => d.DeviceFriendlyName == playbackDeviceName).FirstOrDefault();
+ var device = AudioManager.GetDevices().Where(d => d.FriendlyName == deviceName).FirstOrDefault();
+ if (device != null)
+ return device;
- return playbackDevice ?? Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- }
-
- private string GetSessionDisplayName(AudioSessionControl2 session)
- {
- var procId = (int)session.ProcessID;
-
- if (procId <= 0)
- return session.DisplayName;
-
- if (ApplicationNames.ContainsKey(procId))
- return ApplicationNames[procId];
-
- using var p = Process.GetProcessById(procId);
- ApplicationNames.Add(procId, p.ProcessName);
-
- return p.ProcessName;
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
+ return AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
}
public override void TurnOnWithAction(string action)
@@ -72,29 +59,44 @@ public override void TurnOnWithAction(string action)
return;
}
- using var audioDevice = GetAudioDeviceOrDefault(actionData.PlaybackDevice);
- using var session = audioDevice.AudioSessionManager2?.Sessions?.Where(s =>
- s != null &&
- actionData.ApplicationName == GetSessionDisplayName(s)
- ).FirstOrDefault();
-
- if (session == null)
- {
- Log.Error("[SETAPPVOLUME] Error, no session of application {app} can be found", actionData.ApplicationName);
-
+ var audioDevice = GetDeviceOrDefault(actionData.PlaybackDevice);
+ if (audioDevice == null)
return;
- }
- session.SimpleAudioVolume.Mute = actionData.Mute;
+ var applicationAudioSessions = audioDevice.Sessions.Where(s =>
+ s.Application == actionData.ApplicationName
+ );
+
if (actionData.Volume == -1)
- {
Log.Debug("[SETAPPVOLUME] No volume value provided, only mute has been set for {app}", actionData.ApplicationName);
- return;
- }
- var volume = Math.Clamp(actionData.Volume, 0, 100) / 100.0f;
- session.SimpleAudioVolume.MasterVolume = volume;
+ if (string.IsNullOrWhiteSpace(actionData.SessionId))
+ {
+ foreach (var session in applicationAudioSessions)
+ {
+ AudioManager.SetMute(session, actionData.Mute);
+ if (actionData.Volume == -1)
+ return;
+
+ AudioManager.SetVolume(session, actionData.Volume);
+ }
+ }
+ else
+ {
+ var session = applicationAudioSessions.Where(s => s.Id == actionData.SessionId).FirstOrDefault();
+ if (session == null)
+ {
+ Log.Debug("[SETAPPVOLUME] No session {actionData.SessionId} found for device {device}", actionData.ApplicationName, audioDevice.FriendlyName);
+ return;
+ }
+
+ AudioManager.SetMute(session, actionData.Mute);
+ if (actionData.Volume == -1)
+ return;
+
+ AudioManager.SetVolume(session, actionData.Volume);
+ }
}
catch (Exception ex)
{
@@ -112,6 +114,7 @@ private class ApplicationVolumeAction
public bool Mute { get; set; } = false;
public string ApplicationName { get; set; } = string.Empty;
public string PlaybackDevice { get; set; } = string.Empty;
+ public string SessionId { get; set; } = string.Empty;
}
}
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
index 292b09ba..2d7de409 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
@@ -1,5 +1,6 @@
-using CoreAudio;
-using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using Newtonsoft.Json;
using Serilog;
using System;
@@ -35,25 +36,28 @@ public override void TurnOn()
TurnOnWithAction(InputDevice);
}
- private MMDevice GetAudioDeviceOrDefault(string playbackDeviceName)
- {
- var devices = Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);
- var playbackDevice = devices.Where(d => d.DeviceFriendlyName == playbackDeviceName).FirstOrDefault();
-
- return playbackDevice ?? Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
- }
-
public override void TurnOnWithAction(string action)
{
State = "ON";
try
{
- var outputDevice = GetAudioDeviceOrDefault(action);
- if (outputDevice == Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications))
+ var audioDevices = AudioManager.GetDevices();
+ var inputDevice = audioDevices
+ .Where(d => d.Type == DeviceType.Input)
+ .Where(d => d.FriendlyName == action)
+ .FirstOrDefault();
+
+ if (inputDevice == null)
+ {
+ Log.Warning("[SETAUDIOIN] No input device {device} found", action);
+ return;
+ }
+
+ if(inputDevice.Default)
return;
- outputDevice.Selected = true;
+ AudioManager.Activate(inputDevice);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
index 4893eea6..1cefb69a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
@@ -1,5 +1,6 @@
-using CoreAudio;
-using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using Newtonsoft.Json;
using Serilog;
using System;
@@ -35,25 +36,28 @@ public override void TurnOn()
TurnOnWithAction(OutputDevice);
}
- private MMDevice GetAudioDeviceOrDefault(string playbackDeviceName)
- {
- var devices = Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
- var playbackDevice = devices.Where(d => d.DeviceFriendlyName == playbackDeviceName).FirstOrDefault();
-
- return playbackDevice ?? Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- }
-
public override void TurnOnWithAction(string action)
{
State = "ON";
try
{
- var outputDevice = GetAudioDeviceOrDefault(action);
- if (outputDevice == Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia))
+ var audioDevices = AudioManager.GetDevices();
+ var outputDevice = audioDevices
+ .Where(d => d.Type == DeviceType.Output)
+ .Where(d => d.FriendlyName == action)
+ .FirstOrDefault();
+
+ if (outputDevice == null)
+ {
+ Log.Warning("[SETAUDIOOUT] No input device {device} found", action);
+ return;
+ }
+
+ if (outputDevice.Default)
return;
- outputDevice.Selected = true;
+ AudioManager.Activate(outputDevice);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
index 746d70c7..96ffda7e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
@@ -3,9 +3,11 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using CoreAudio;
+using System.Linq;
using HASS.Agent.Shared.Enums;
using HASS.Agent.Shared.Functions;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using Serilog;
namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands
@@ -18,7 +20,7 @@ namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands
public class SetVolumeCommand : InternalCommand
{
private const string DefaultName = "setvolume";
- private readonly float _volume = -1f;
+ private readonly int _volume = -1;
public SetVolumeCommand(string entityName = DefaultName, string name = DefaultName, string volume = "", CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(entityName ?? DefaultName, name ?? null, volume, entityType, id)
{
@@ -28,10 +30,10 @@ public SetVolumeCommand(string entityName = DefaultName, string name = DefaultNa
if (!parsed)
{
Log.Error("[SETVOLUME] [{name}] Unable to parse configured volume level, not an int: {val}", EntityName, volume);
- _volume = -1f;
+ _volume = -1;
}
- _volume = volumeInt / 100.0f;
+ _volume = volumeInt;
}
State = "OFF";
@@ -50,16 +52,12 @@ public override void TurnOn()
return;
}
- // get the current default endpoint
- using var audioDevice = Variables.AudioDeviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- if (audioDevice?.AudioEndpointVolume == null)
- {
- Log.Warning("[SETVOLUME] [{name}] Unable to trigger command, no default audio endpoint found", EntityName);
-
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if(audioDevice == null)
return;
- }
- audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar = _volume;
+ AudioManager.SetVolume(audioDevice, _volume);
}
catch (Exception ex)
{
@@ -85,16 +83,12 @@ public override void TurnOnWithAction(string action)
return;
}
- // get the current default endpoint
- using var audioDevice = Variables.AudioDeviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- if (audioDevice?.AudioEndpointVolume == null)
- {
- Log.Warning("[SETVOLUME] [{name}] Unable to trigger action for command, no default audio endpoint found", EntityName);
-
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if (audioDevice == null)
return;
- }
- audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar = volumeInt / 100.0f; ;
+ AudioManager.SetVolume(audioDevice, volumeInt);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
index 5df24f5d..4c6ecb97 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs
@@ -3,9 +3,10 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq;
-using CoreAudio;
using HASS.Agent.Shared.Functions;
using HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.MultiValue.DataTypes;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using HASS.Agent.Shared.Models.HomeAssistant;
using HASS.Agent.Shared.Models.Internal;
using HidSharp;
@@ -20,7 +21,6 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.MultiValue;
public class AudioSensors : AbstractMultiValueSensor
{
private const string DefaultName = "audio";
- private static readonly Dictionary ApplicationNames = new();
private bool _errorPrinted = false;
private readonly int _updateInterval;
@@ -42,73 +42,55 @@ private void AddUpdateSensor(string sensorId, AbstractSingleValueSensor sensor)
Sensors[sensorId] = sensor;
}
- private List GetAudioDevices(DataFlow type)
+ private void HandleAudioOutputSensors(IEnumerable outputDevices, string parentSensorSafeName)
{
- var audioDevices = new List();
- foreach (var device in Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(type, DeviceState.Active))
- {
- audioDevices.Add(device.DeviceFriendlyName);
- device.Dispose();
- }
-
- return audioDevices;
- }
-
- private List GetAudioOutputDevices() => GetAudioDevices(DataFlow.Render);
- private List GetAudioInputDevices() => GetAudioDevices(DataFlow.Capture);
-
- private void HandleAudioOutputSensors(string parentSensorSafeName, string deviceName)
- {
- using var audioDevice = Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
+ var outputDevice = outputDevices.Where(d => d.Default).FirstOrDefault();
+ if (outputDevice == null)
+ return;
var defaultDeviceEntityName = $"{parentSensorSafeName}_default_device";
var defaultDeviceId = $"{Id}_default_device";
var defaultDeviceSensor = new DataTypeStringSensor(_updateInterval, defaultDeviceEntityName, $"Default Device", defaultDeviceId, string.Empty, "mdi:speaker", string.Empty, EntityName);
- defaultDeviceSensor.SetState(audioDevice.DeviceFriendlyName);
+ defaultDeviceSensor.SetState(outputDevice.FriendlyName);
AddUpdateSensor(defaultDeviceId, defaultDeviceSensor);
var defaultDeviceStateEntityName = $"{parentSensorSafeName}_default_device_state";
var defaultDeviceStateId = $"{Id}_default_device_state";
var defaultDeviceStateSensor = new DataTypeStringSensor(_updateInterval, defaultDeviceStateEntityName, $"Default Device State", defaultDeviceStateId, string.Empty, "mdi:speaker", string.Empty, EntityName);
- defaultDeviceStateSensor.SetState(GetReadableState(audioDevice.State));
+ defaultDeviceStateSensor.SetState(outputDevice.State);
AddUpdateSensor(defaultDeviceStateId, defaultDeviceStateSensor);
- var masterVolume = Convert.ToInt32(Math.Round(audioDevice.AudioEndpointVolume?.MasterVolumeLevelScalar * 100 ?? 0, 0));
var defaultDeviceVolumeEntityName = $"{parentSensorSafeName}_default_device_volume";
var defaultDeviceVolumeId = $"{Id}_default_device_volume";
var defaultDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultDeviceVolumeEntityName, $"Default Device Volume", defaultDeviceVolumeId, string.Empty, "measurement", "mdi:speaker", string.Empty, EntityName);
- defaultDeviceVolumeSensor.SetState(masterVolume);
+ defaultDeviceVolumeSensor.SetState(outputDevice.Volume);
AddUpdateSensor(defaultDeviceVolumeId, defaultDeviceVolumeSensor);
- var defaultDeviceIsMuted = audioDevice.AudioEndpointVolume?.Mute ?? false;
var defaultDeviceIsMutedEntityName = $"{parentSensorSafeName}_default_device_muted";
var defaultDeviceIsMutedId = $"{Id}_default_device_muted";
var defaultDeviceIsMutedSensor = new DataTypeBoolSensor(_updateInterval, defaultDeviceIsMutedEntityName, $"Default Device Muted", defaultDeviceIsMutedId, string.Empty, "mdi:speaker", EntityName);
- defaultDeviceIsMutedSensor.SetState(defaultDeviceIsMuted);
+ defaultDeviceIsMutedSensor.SetState(outputDevice.Muted);
AddUpdateSensor(defaultDeviceIsMutedId, defaultDeviceIsMutedSensor);
- // get session and volume info
- var sessionInfos = GetSessions(out var peakVolume);
-
var peakVolumeEntityName = $"{parentSensorSafeName}_peak_volume";
var peakVolumeId = $"{Id}_peak_volume";
- var peakVolumeSensor = new DataTypeStringSensor(_updateInterval, peakVolumeEntityName, $"Peak Volume", peakVolumeId, string.Empty, "mdi:volume-high", string.Empty, EntityName);
- peakVolumeSensor.SetState(peakVolume.ToString(CultureInfo.CurrentCulture));
+ var peakVolumeSensor = new DataTypeDoubleSensor(_updateInterval, peakVolumeEntityName, $"Peak Volume", peakVolumeId, string.Empty, "measurement", "mdi:volume-high", string.Empty, EntityName);
+ peakVolumeSensor.SetState(outputDevice.PeakVolume);
AddUpdateSensor(peakVolumeId, peakVolumeSensor);
var sessionsEntityName = $"{parentSensorSafeName}_sessions";
var sessionsId = $"{Id}_sessions";
var sessionsSensor = new DataTypeIntSensor(_updateInterval, sessionsEntityName, $"Audio Sessions", sessionsId, string.Empty, "measurement", "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
- sessionsSensor.SetState(sessionInfos.Count);
+ sessionsSensor.SetState(outputDevice.Sessions.Count);
sessionsSensor.SetAttributes(
JsonConvert.SerializeObject(new
{
- AudioSessions = sessionInfos
+ AudioSessions = outputDevice.Sessions
}, Formatting.Indented)
);
AddUpdateSensor(sessionsId, sessionsSensor);
- var audioOutputDevices = GetAudioOutputDevices();
+ var audioOutputDevices = outputDevices.Select(d => d.FriendlyName).ToList();
var audioOutputDevicesEntityName = $"{parentSensorSafeName}_output_devices";
var audioOutputDevicesId = $"{Id}_output_devices";
var audioOutputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioOutputDevicesEntityName, $"Audio Output Devices", audioOutputDevicesId, string.Empty, "measurement", "mdi:music-box-multiple-outline", string.Empty, EntityName, true);
@@ -122,37 +104,37 @@ private void HandleAudioOutputSensors(string parentSensorSafeName, string device
AddUpdateSensor(audioOutputDevicesId, audioOutputDevicesSensor);
}
- private void HandleAudioInputSensors(string parentSensorSafeName, string deviceName)
+ private void HandleAudioInputSensors(IEnumerable inputDevices, string parentSensorSafeName)
{
- using var inputDevice = Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
+ var inputDevice = inputDevices.Where(d => d.Default).FirstOrDefault();
+ if (inputDevice == null)
+ return;
var defaultInputDeviceEntityName = $"{parentSensorSafeName}_default_input_device";
var defaultInputDeviceId = $"{Id}_default_input_device";
var defaultInputDeviceSensor = new DataTypeStringSensor(_updateInterval, defaultInputDeviceEntityName, $"Default Input Device", defaultInputDeviceId, string.Empty, "mdi:microphone", string.Empty, EntityName);
- defaultInputDeviceSensor.SetState(inputDevice.DeviceFriendlyName);
+ defaultInputDeviceSensor.SetState(inputDevice.FriendlyName);
AddUpdateSensor(defaultInputDeviceId, defaultInputDeviceSensor);
var defaultInputDeviceStateEntityName = $"{parentSensorSafeName}_default_input_device_state";
var defaultInputDeviceStateId = $"{Id}_default_input_device_state";
var defaultInputDeviceStateSensor = new DataTypeStringSensor(_updateInterval, defaultInputDeviceStateEntityName, $"Default Input Device State", defaultInputDeviceStateId, string.Empty, "mdi:microphone", string.Empty, EntityName);
- defaultInputDeviceStateSensor.SetState(GetReadableState(inputDevice.State));
+ defaultInputDeviceStateSensor.SetState(inputDevice.State);
AddUpdateSensor(defaultInputDeviceStateId, defaultInputDeviceStateSensor);
- var defaultInputDeviceIsMuted = inputDevice.AudioEndpointVolume?.Mute ?? false;
var defaultInputDeviceIsMutedEntityName = $"{parentSensorSafeName}_default_input_device_muted";
var defaultInputDeviceIsMutedId = $"{Id}_default_input_device_muted";
var defaultInputDeviceIsMutedSensor = new DataTypeBoolSensor(_updateInterval, defaultInputDeviceIsMutedEntityName, $"Default Input Device Muted", defaultInputDeviceIsMutedId, string.Empty, "mdi:microphone", EntityName);
- defaultInputDeviceIsMutedSensor.SetState(defaultInputDeviceIsMuted);
+ defaultInputDeviceIsMutedSensor.SetState(inputDevice.Muted);
AddUpdateSensor(defaultInputDeviceIsMutedId, defaultInputDeviceIsMutedSensor);
- var inputVolume = (int)GetDefaultInputDevicePeakVolume(inputDevice);
var defaultInputDeviceVolumeEntityName = $"{parentSensorSafeName}_default_input_device_volume";
var defaultInputDeviceVolumeId = $"{Id}_default_input_device_volume";
var defaultInputDeviceVolumeSensor = new DataTypeIntSensor(_updateInterval, defaultInputDeviceVolumeEntityName, $"Default Input Device Volume", defaultInputDeviceVolumeId, string.Empty, "measurement", "mdi:microphone", string.Empty, EntityName);
- defaultInputDeviceVolumeSensor.SetState(inputVolume);
+ defaultInputDeviceVolumeSensor.SetState(Convert.ToInt32(inputDevice.PeakVolume));
AddUpdateSensor(defaultInputDeviceVolumeId, defaultInputDeviceVolumeSensor);
- var audioInputDevices = GetAudioInputDevices();
+ var audioInputDevices = inputDevices.Select(d => d.FriendlyName).ToList();
var audioInputDevicesEntityName = $"{parentSensorSafeName}_input_devices";
var audioInputDevicesId = $"{Id}_input_devices";
var audioInputDevicesSensor = new DataTypeIntSensor(_updateInterval, audioInputDevicesEntityName, $"Audio Input Devices", audioInputDevicesId, string.Empty, "measurement", "mdi:microphone", string.Empty, EntityName, true);
@@ -170,11 +152,14 @@ public override sealed void UpdateSensorValues()
{
try
{
+ var audioDevices = AudioManager.GetDevices();
+ var outputDevices = audioDevices.Where(d => d.Type == DeviceType.Output);
+ var inputDevices = audioDevices.Where(d => d.Type == DeviceType.Input);
+
var parentSensorSafeName = SharedHelperFunctions.GetSafeValue(EntityName);
- var deviceSafeName = SharedHelperFunctions.GetSafeDeviceName();
- HandleAudioOutputSensors(parentSensorSafeName, deviceSafeName);
- HandleAudioInputSensors(parentSensorSafeName, deviceSafeName);
+ HandleAudioOutputSensors(outputDevices, parentSensorSafeName);
+ HandleAudioInputSensors(inputDevices, parentSensorSafeName);
if (_errorPrinted)
_errorPrinted = false;
@@ -190,191 +175,5 @@ public override sealed void UpdateSensorValues()
}
}
- private string GetSessionDisplayName(AudioSessionControl2 session)
- {
- var procId = (int)session.ProcessID;
-
- if (procId <= 0)
- return session.DisplayName;
-
- if (ApplicationNames.ContainsKey(procId))
- return ApplicationNames[procId];
-
- // we don't know this app yet, get process info
- using var p = Process.GetProcessById(procId);
- ApplicationNames.Add(procId, p.ProcessName);
-
- return p.ProcessName;
- }
-
- private List GetSessions(out float peakVolume)
- {
- var sessionInfos = new List();
- peakVolume = 0f;
-
- try
- {
- var errors = false;
-
- foreach (var device in Variables.AudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active))
- {
- using (device)
- {
- foreach (var session in device.AudioSessionManager2?.Sessions.Where(x => x != null))
- {
- if (session.ProcessID == 0)
- continue;
-
- try
- {
- var displayName = GetSessionDisplayName(session);
-
- if (displayName == "audiodg")
- continue;
-
- if (displayName.Length > 30)
- displayName = $"{displayName[..30]}..";
-
- var sessionInfo = new AudioSessionInfo
- {
- Application = displayName,
- PlaybackDevice = device.DeviceFriendlyName,
- Muted = session.SimpleAudioVolume?.Mute ?? false,
- Active = session.State == AudioSessionState.AudioSessionStateActive,
- MasterVolume = session.SimpleAudioVolume?.MasterVolume * 100 ?? 0f,
- PeakVolume = session.AudioMeterInformation?.MasterPeakValue * 100 ?? 0f
- };
-
- // new max?
- if (sessionInfo.PeakVolume > peakVolume)
- peakVolume = sessionInfo.PeakVolume;
-
- sessionInfos.Add(sessionInfo);
- }
- catch (Exception ex)
- {
- if (!_errorPrinted)
- Log.Fatal(ex, "[AUDIO] [{name}] [{app}] Exception while retrieving info: {err}", EntityName, session.DisplayName, ex.Message);
-
- errors = true;
- }
- finally
- {
- session?.Dispose();
- }
- }
- }
- }
-
- // only print errors once
- if (errors && !_errorPrinted)
- {
- _errorPrinted = true;
-
- return sessionInfos;
- }
-
- // optionally reset error flag
- if (_errorPrinted)
- _errorPrinted = false;
- }
- catch (Exception ex)
- {
- // something went wrong, only print once
- if (_errorPrinted)
- return sessionInfos;
-
- _errorPrinted = true;
-
- Log.Fatal(ex, "[AUDIO] [{name}] Fatal exception while getting sessions: {err}", EntityName, ex.Message);
- }
-
- return sessionInfos;
- }
-
- private float GetDefaultInputDevicePeakVolume(MMDevice inputDevice)
- {
- if (inputDevice == null)
- return 0f;
-
- var peakVolume = 0f;
-
- try
- {
- var errors = false;
-
- // process sessions (and get peak volume)
- foreach (var session in inputDevice.AudioSessionManager2?.Sessions?.Where(x => x != null)!)
- {
- try
- {
- // filter inactive sessions
- if (session.State != AudioSessionState.AudioSessionStateActive)
- continue;
-
- // set peak volume
- var sessionPeakVolume = session.AudioMeterInformation?.MasterPeakValue * 100 ?? 0f;
-
- // new max?
- if (sessionPeakVolume > peakVolume)
- peakVolume = sessionPeakVolume;
- }
- catch (Exception ex)
- {
- if (!_errorPrinted)
- Log.Fatal(ex, "[AUDIO] [{name}] [{app}] Exception while retrieving input info: {err}", EntityName, session.DisplayName, ex.Message);
-
- errors = true;
- }
- finally
- {
- session?.Dispose();
- }
- }
-
- // only print errors once
- if (errors && !_errorPrinted)
- {
- _errorPrinted = true;
-
- return peakVolume;
- }
-
- // optionally reset error flag
- if (_errorPrinted)
- _errorPrinted = false;
- }
- catch (Exception ex)
- {
- // something went wrong, only print once
- if (_errorPrinted)
- return peakVolume;
-
- _errorPrinted = true;
-
- Log.Fatal(ex, "[AUDIO] [{name}] Fatal exception while getting input info: {err}", EntityName, ex.Message);
- }
-
- return peakVolume;
- }
-
- ///
- /// Converts the audio device's state to a better readable form
- ///
- ///
- ///
- private static string GetReadableState(DeviceState state)
- {
- return state switch
- {
- DeviceState.Active => "ACTIVE",
- DeviceState.Disabled => "DISABLED",
- DeviceState.NotPresent => "NOT PRESENT",
- DeviceState.Unplugged => "UNPLUGGED",
- DeviceState.MaskAll => "STATEMASK_ALL",
- _ => "UNKNOWN"
- };
- }
-
public override DiscoveryConfigModel GetAutoDiscoveryConfig() => null;
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
index 7ad33701..e1a03ce9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
@@ -1,6 +1,8 @@
using System;
using System.Globalization;
-using CoreAudio;
+using System.Linq;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using HASS.Agent.Shared.Models.HomeAssistant;
namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue
@@ -37,13 +39,13 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
public override string GetState()
{
- using var audioDevice = Variables.AudioDeviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- // check for null & mute
- if (audioDevice?.AudioEndpointVolume == null) return "0";
- if (audioDevice.AudioEndpointVolume.Mute) return "0";
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if (audioDevice == null)
+ return "0";
// return as percentage
- return Math.Round(audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0).ToString(CultureInfo.InvariantCulture);
+ return audioDevice.Volume.ToString(CultureInfo.InvariantCulture);
}
public override string GetAttributes() => string.Empty;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioDevice.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioDevice.cs
new file mode 100644
index 00000000..7db9b95c
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioDevice.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio;
+public class AudioDevice
+{
+ public string State { get; set; } = string.Empty;
+ public DeviceType Type { get; set; }
+ public string Id { get; set; } = string.Empty;
+ public string FriendlyName { get; set; } = string.Empty;
+ public int Volume { get; set; }
+ public double PeakVolume { get; set; }
+ public bool Muted { get; set; }
+ public List Sessions { get; set; } = new();
+ public bool Default { get; set; }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
new file mode 100644
index 00000000..8f0cea0d
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+using HASS.Agent.Shared.Managers.Audio.Exceptions;
+using HASS.Agent.Shared.Managers.Audio.Internal;
+using Serilog;
+
+namespace HASS.Agent.Shared.Managers.Audio;
+public static class AudioManager
+{
+ private static bool _initialized = false;
+
+ private static readonly MMDeviceEnumerator _enumerator = new();
+
+ private static readonly ConcurrentDictionary _devices = new();
+ private static readonly ConcurrentQueue _devicesToBeRemoved = new();
+ private static readonly ConcurrentQueue _devicesToBeAdded = new();
+
+ private static readonly Dictionary _applicationNameCache = new();
+
+ public static void Initialize()
+ {
+ Log.Debug("[AUDIOMGR] Audio Manager initializing");
+
+ foreach (var device in _enumerator.EnumAudioEndpoints(DataFlow.All, DeviceState.Active))
+ _devices[device.DeviceID] = new InternalAudioDevice(device);
+
+ var nc = new MMNotificationClient(_enumerator);
+ nc.DeviceAdded += (sender, e) =>
+ {
+ if (_devices.ContainsKey(e.DeviceId))
+ return;
+
+ _devicesToBeAdded.Enqueue(e.DeviceId);
+ };
+
+ nc.DeviceRemoved += (sender, e) =>
+ {
+ _devicesToBeRemoved.Enqueue(e.DeviceId);
+ };
+
+ _initialized = true;
+
+ nc.DeviceStateChanged += (sender, e) =>
+ {
+ switch (e.DeviceState)
+ {
+ case DeviceState.Active:
+ if (_devices.ContainsKey(e.DeviceId))
+ return;
+
+ _devicesToBeAdded.Enqueue(e.DeviceId);
+ break;
+
+ case DeviceState.NotPresent:
+ case DeviceState.UnPlugged:
+ _devicesToBeRemoved.Enqueue(e.DeviceId);
+ break;
+
+ default:
+ break;
+ }
+ };
+
+ Log.Information("[AUDIOMGR] Audio Manager initialized");
+ }
+
+ private static void CheckInitialization()
+ {
+ if (!_initialized)
+ throw new InvalidOperationException("AudioManager is not initialized");
+
+ while (_devicesToBeRemoved.TryDequeue(out var deviceId))
+ {
+ if (_devices.Remove(deviceId, out var device))
+ device.Dispose();
+ }
+
+ while (_devicesToBeAdded.TryDequeue(out var deviceId))
+ {
+ var device = _enumerator.GetDevice(deviceId);
+ _devices[deviceId] = new InternalAudioDevice(device);
+ }
+ }
+
+ private static string GetSessionDisplayName(InternalAudioSession session)
+ {
+ var procId = session.ProcessId;
+
+ if (procId <= 0)
+ return session.DisplayName;
+
+ if (_applicationNameCache.TryGetValue(procId, out var cachedName))
+ return cachedName;
+
+ using var process = Process.GetProcessById(procId);
+ _applicationNameCache[procId] = process.ProcessName;
+
+ return process.ProcessName;
+ }
+
+ private static List GetDeviceSessions(InternalAudioDevice internalAudioDevice)
+ {
+ var audioSessions = new List();
+
+ internalAudioDevice.Manager.RemoveDisconnectedSessions();
+ foreach (var (sessionId, session) in internalAudioDevice.Manager.Sessions)
+ {
+ try
+ {
+ var displayName = string.IsNullOrWhiteSpace(session.DisplayName) ? GetSessionDisplayName(session) : session.DisplayName;
+ if (displayName == "audiodg")
+ continue;
+
+ if (displayName.Length > 30)
+ displayName = $"{displayName[..30]}..";
+
+ var audioSession = new AudioSession
+ {
+ Id = sessionId,
+ Application = displayName,
+ PlaybackDevice = internalAudioDevice.FriendlyName,
+ Muted = session.Volume.IsMuted,
+ Active = session.Control.SessionState == AudioSessionState.AudioSessionStateActive,
+ MasterVolume = Convert.ToInt32(session.Volume.MasterVolume * 100),
+ PeakVolume = Convert.ToDouble(session.MeterInformation.PeakValue * 100)
+ };
+
+ audioSessions.Add(audioSession);
+ }
+ catch (Exception ex)
+ {
+ throw new AudioSessionException($"error retrieving session information for {internalAudioDevice.FriendlyName}", ex);
+ }
+ }
+
+ return audioSessions;
+ }
+
+ private static string GetReadableState(DeviceState state)
+ {
+ return state switch
+ {
+ DeviceState.Active => "ACTIVE",
+ DeviceState.Disabled => "DISABLED",
+ DeviceState.NotPresent => "NOT PRESENT",
+ DeviceState.UnPlugged => "UNPLUGGED",
+ DeviceState.All => "STATEMASK_ALL",
+ _ => "UNKNOWN"
+ };
+ }
+
+ public static List GetDevices()
+ {
+ CheckInitialization();
+
+ var audioDevices = new List();
+
+ using var defaultInputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
+ using var defaultOutputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
+
+ var defaultInputDeviceId = defaultInputDevice.DeviceID;
+ var defaultOutputDeviceId = defaultOutputDevice.DeviceID;
+
+ foreach (var device in _devices.Values.Where(d => d.MMDevice.DeviceState == DeviceState.Active))
+ {
+
+ var audioSessions = GetDeviceSessions(device);
+ var loudestSession = audioSessions.MaxBy(s => s.PeakVolume);
+
+ var audioDevice = new AudioDevice
+ {
+ State = GetReadableState(device.MMDevice.DeviceState),
+ Type = device.MMDevice.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output,
+ Id = device.DeviceId,
+ FriendlyName = device.FriendlyName,
+ Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.GetMasterVolumeLevelScalar() * 100, 0)),
+ PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume,
+ Sessions = audioSessions,
+ Default = device.DeviceId == defaultInputDeviceId || device.DeviceId == defaultOutputDeviceId
+ };
+
+ audioDevices.Add(audioDevice);
+ }
+
+ return audioDevices;
+ }
+
+ public static string GetDefaultDeviceId(DeviceType deviceType, DeviceRole deviceRole)
+ {
+ var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
+ var role = (Role)deviceRole;
+
+ using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
+
+ return defaultDevice.DeviceID;
+ }
+
+ public static void Activate(AudioDevice audioDevice)
+ {
+ if (!_devices.TryGetValue(audioDevice.Id, out var device))
+ return;
+
+ device.Activate();
+ }
+
+ public static void SetVolume(AudioDevice audioDevice, int volume)
+ {
+ if (!_devices.TryGetValue(audioDevice.Id, out var device))
+ return;
+
+ var volumeScalar = volume / 100f;
+ device.AudioEndpointVolume.SetMasterVolumeLevelScalar(volumeScalar, Guid.Empty);
+ }
+
+ public static void SetVolume(AudioSession audioSession, int volume)
+ {
+ var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
+ if (device == null)
+ return;
+
+ if (!device.Manager.Sessions.TryGetValue(audioSession.Id, out var session))
+ return;
+
+ var volumeScalar = volume / 100f;
+ session.Volume.MasterVolume = volumeScalar;
+ }
+
+ public static void SetMute(AudioDevice audioDevice, bool mute)
+ {
+ if (!_devices.TryGetValue(audioDevice.Id, out var device))
+ return;
+
+ device.AudioEndpointVolume.SetMute(mute, Guid.Empty);
+ }
+
+ public static void SetMute(AudioSession audioSession, bool mute)
+ {
+ var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
+ if (device == null)
+ return;
+
+ if (!device.Manager.Sessions.TryGetValue(audioSession.Id, out var session))
+ return;
+
+ session.Volume.IsMuted = mute;
+ }
+
+ public static void Shutdown()
+ {
+ Log.Debug("[AUDIOMGR] Audio Manager shutting down");
+ try
+ {
+ foreach(var device in _devices.Values)
+ device.Dispose();
+
+ _enumerator.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "[AUDIOMGR] Audio Manager shutdown fatal error: {ex}", ex.Message);
+ }
+ Log.Debug("[AUDIOMGR] Audio Manager shutdown completed");
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioSession.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioSession.cs
new file mode 100644
index 00000000..26849437
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioSession.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio;
+public class AudioSession
+{
+ public string Id { get; set; }
+ public string Application { get; set; } = string.Empty;
+ public string PlaybackDevice { get; set; } = string.Empty;
+ public bool Muted { get; set; }
+ public bool Active { get; set; }
+ public int MasterVolume { get; set; }
+ public double PeakVolume { get; set; }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceRole.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceRole.cs
new file mode 100644
index 00000000..1c0a34c2
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceRole.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio;
+public enum DeviceRole
+{
+ Console,
+ Multimedia,
+ Communications
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceType.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceType.cs
new file mode 100644
index 00000000..3de925f6
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/DeviceType.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio;
+public enum DeviceType
+{
+ Input,
+ Output
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Exceptions/AudioSessionException.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Exceptions/AudioSessionException.cs
new file mode 100644
index 00000000..246e53ea
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Exceptions/AudioSessionException.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio.Exceptions;
+public class AudioSessionException : Exception
+{
+ public AudioSessionException()
+ {
+ }
+
+ public AudioSessionException(string message) : base(message)
+ {
+ }
+
+ public AudioSessionException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
new file mode 100644
index 00000000..73db3317
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+
+// https://github.com/morphx666/CoreAudio
+// https://github.com/File-New-Project/EarTrumpet
+
+internal class CPolicyConfigVistaClient : IDisposable
+{
+ private IPolicyConfigVista policyConfigVistaClient;
+
+ public CPolicyConfigVistaClient()
+ {
+ policyConfigVistaClient = (IPolicyConfigVista)new _CPolicyConfigVistaClient();
+ }
+
+ public void SetDefaultDevice(string deviceID)
+ {
+
+ policyConfigVistaClient.SetDefaultEndpoint(deviceID, Role.Console);
+ policyConfigVistaClient.SetDefaultEndpoint(deviceID, Role.Multimedia);
+ policyConfigVistaClient.SetDefaultEndpoint(deviceID, Role.Communications);
+ }
+
+ public void Dispose()
+ {
+ if (policyConfigVistaClient != null && Marshal.IsComObject(policyConfigVistaClient))
+ Marshal.FinalReleaseComObject(policyConfigVistaClient);
+
+ GC.SuppressFinalize(this);
+ }
+
+ ~CPolicyConfigVistaClient()
+ {
+ Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
new file mode 100644
index 00000000..e430e102
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+
+// https://github.com/morphx666/CoreAudio
+// https://github.com/File-New-Project/EarTrumpet
+
+[Guid("568b9108-44bf-40b4-9006-86afe5b5a620")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+internal interface IPolicyConfigVista
+{
+ [PreserveSig]
+ int GetMixFormat();
+
+ [PreserveSig]
+ int GetDeviceFormat();
+
+ [PreserveSig]
+ int SetDeviceFormat();
+
+ [PreserveSig]
+ int GetProcessingPeriod();
+
+ [PreserveSig]
+ int SetProcessingPeriod();
+
+ [PreserveSig]
+ int GetShareMode();
+
+ [PreserveSig]
+ int SetShareMode();
+
+ [PreserveSig]
+ int GetPropertyValue();
+
+ [PreserveSig]
+ int SetPropertyValue();
+
+ [PreserveSig]
+ int SetDefaultEndpoint([MarshalAs(UnmanagedType.LPWStr)] string wszDeviceId, Role eRole);
+
+ [PreserveSig]
+ int SetEndpointVisibility();
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
new file mode 100644
index 00000000..59721484
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+internal class InternalAudioDevice : IDisposable
+{
+ public MMDevice MMDevice { get; private set; }
+ public AudioEndpointVolume AudioEndpointVolume { get; private set; }
+ public InternalAudioSessionManager Manager { get; private set; }
+
+ public string DeviceId { get; private set; }
+ public string FriendlyName { get; private set; }
+ public bool Reinitialized { get; private set; }
+
+ public InternalAudioDevice(MMDevice device)
+ {
+ MMDevice = device;
+ var sessionManager2 = AudioSessionManager2.FromMMDevice(device);
+ Manager = new InternalAudioSessionManager(sessionManager2);
+ AudioEndpointVolume = AudioEndpointVolume.FromDevice(device);
+
+ DeviceId = device.DeviceID;
+ FriendlyName = device.FriendlyName;
+ }
+
+ public void Activate()
+ {
+ using var configClient = new CPolicyConfigVistaClient();
+ configClient.SetDefaultDevice(MMDevice.DeviceID);
+ }
+
+ public void Dispose()
+ {
+ AudioEndpointVolume?.Dispose();
+ Manager?.Dispose();
+ MMDevice?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ ~InternalAudioDevice()
+ {
+ Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
new file mode 100644
index 00000000..480b40c4
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+internal class InternalAudioSession : IDisposable, IAudioSessionEvents
+{
+ public AudioSessionControl Control { get; private set; }
+ public AudioSessionControl2 Control2 { get; private set; }
+ public SimpleAudioVolume Volume { get; private set; }
+ public AudioMeterInformation MeterInformation { get; private set; }
+
+ public string DisplayName { get; private set; }
+ public int ProcessId { get; private set; }
+ public bool Expired { get; private set; } = false;
+
+ public InternalAudioSession(AudioSessionControl audioSessionControl)
+ {
+ Control = audioSessionControl;
+ Control2 = audioSessionControl.QueryInterface();
+ Volume = audioSessionControl.QueryInterface();
+ MeterInformation = audioSessionControl.QueryInterface();
+
+ DisplayName = Control2.DisplayName;
+ ProcessId = Control2.ProcessID;
+
+ Control.RegisterAudioSessionNotification(this);
+ }
+
+ public void Dispose()
+ {
+ Control.UnregisterAudioSessionNotification(this);
+
+ MeterInformation?.Dispose();
+ Volume?.Dispose();
+ Control2?.Dispose();
+ Control?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ ~InternalAudioSession()
+ {
+ Dispose();
+ }
+
+ public void OnDisplayNameChanged(string newDisplayName, ref Guid eventContext)
+ {
+
+ }
+
+ public void OnIconPathChanged(string newIconPath, ref Guid eventContext)
+ {
+
+ }
+
+ public void OnSimpleVolumeChanged(float newVolume, bool newMute, ref Guid eventContext)
+ {
+
+ }
+
+ public void OnChannelVolumeChanged(int channelCount, float[] newChannelVolumeArray, int changedChannel, ref Guid eventContext)
+ {
+
+ }
+
+ public void OnGroupingParamChanged(ref Guid newGroupingParam, ref Guid eventContext)
+ {
+
+ }
+
+ public void OnStateChanged(AudioSessionState newState)
+ {
+ if (newState == AudioSessionState.AudioSessionStateExpired)
+ Expired = true;
+ }
+
+ public void OnSessionDisconnected(AudioSessionDisconnectReason disconnectReason)
+ {
+
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
new file mode 100644
index 00000000..0ebab737
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CSCore.CoreAudioAPI;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+internal class InternalAudioSessionManager : IDisposable
+{
+ public AudioSessionManager2 Manager { get; private set; }
+ public ConcurrentDictionary Sessions { get; private set; } = new();
+ public InternalAudioSessionManager(AudioSessionManager2 sessionManager2)
+ {
+ Manager = sessionManager2;
+
+ using var sessionEnumerator = Manager.GetSessionEnumerator();
+ foreach (var session in sessionEnumerator)
+ {
+ var internalSession = new InternalAudioSession(session);
+ Sessions[internalSession.Control2.SessionInstanceIdentifier] = internalSession;
+ }
+
+ Manager.SessionCreated += Manager_SessionCreated;
+ }
+
+ private void Manager_SessionCreated(object? sender, SessionCreatedEventArgs e)
+ {
+ if (e.NewSession != null)
+ {
+ var internalSession = new InternalAudioSession(e.NewSession);
+ Sessions[internalSession.Control2.SessionInstanceIdentifier] = internalSession;
+ }
+ }
+
+ public void RemoveDisconnectedSessions()
+ {
+ var expiredSessionsId = Sessions.Values.Where(s => s.Expired).Select(s => s.Control2.SessionInstanceIdentifier);
+ if (!expiredSessionsId.Any())
+ return;
+
+ foreach (var expiredSessionId in expiredSessionsId)
+ {
+ Sessions.Remove(expiredSessionId, out var expiredSession);
+ expiredSession?.Dispose();
+ }
+ }
+
+ public void Dispose()
+ {
+ if (Manager != null)
+ Manager.SessionCreated -= Manager_SessionCreated;
+
+ foreach (var session in Sessions.Values)
+ {
+ session?.Dispose();
+ }
+
+ Manager?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ ~InternalAudioSessionManager()
+ {
+ Dispose();
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/_CPolicyConfigVistaClient.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/_CPolicyConfigVistaClient.cs
new file mode 100644
index 00000000..eea62999
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/_CPolicyConfigVistaClient.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+
+// https://github.com/morphx666/CoreAudio
+// https://github.com/File-New-Project/EarTrumpet
+
+[ComImport]
+[Guid("294935CE-F637-4E7C-A41B-AB255460B862")]
+internal class _CPolicyConfigVistaClient
+{
+}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/AudioSessionInfo.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/AudioSessionInfo.cs
deleted file mode 100644
index c884e3ed..00000000
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/Internal/AudioSessionInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Collections.Generic;
-
-namespace HASS.Agent.Shared.Models.Internal
-{
- public class AudioSessionInfo
- {
- public AudioSessionInfo()
- {
- //
- }
-
- public string Application { get; set; } = string.Empty;
- public string PlaybackDevice { get; set; } = string.Empty;
- public bool Muted { get; set; }
- public bool Active { get; set; }
- public float MasterVolume { get; set; } = 0f;
- public float PeakVolume { get; set; } = 0f;
- }
-}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Variables.cs b/src/HASS.Agent/HASS.Agent.Shared/Variables.cs
index 1c73153d..bcd3ff7a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Variables.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Variables.cs
@@ -1,5 +1,4 @@
using System;
-using CoreAudio;
using HASS.Agent.Shared.Mqtt;
namespace HASS.Agent.Shared
@@ -14,7 +13,6 @@ internal class Variables
///
/// public references
///
- internal static MMDeviceEnumerator AudioDeviceEnumerator { get; } = new MMDeviceEnumerator(Guid.NewGuid());
internal static Random Rnd { get; } = new Random();
///
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.cs b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
index 583c9197..e09e8933 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Main.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
@@ -20,6 +20,7 @@
using HASS.Agent.Shared.Extensions;
using HASS.Agent.Shared.Functions;
using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using Serilog;
using Syncfusion.Windows.Forms;
using WindowsDesktop;
@@ -91,6 +92,7 @@ private async void Main_Load(object sender, EventArgs e)
await InternalDeviceSensorsManager.Initialize();
InitializeHardwareManager();
InitializeVirtualDesktopManager();
+ await Task.Run(InitializeAudioManager);
// load entities
var loaded = await SettingsManager.LoadEntitiesAsync();
@@ -163,6 +165,7 @@ await Task.WhenAll(hassApiTask, sensorTask, commandsTask, serviceTask,
private void OnProcessExit(object sender, EventArgs e)
{
+ AudioManager.Shutdown();
HardwareManager.Shutdown();
NotificationManager.Exit();
}
@@ -334,6 +337,14 @@ private void InitializeHardwareManager()
HardwareManager.Initialize();
}
+ ///
+ /// Initialized the Audio Manager
+ ///
+ private void InitializeAudioManager()
+ {
+ AudioManager.Initialize();
+ }
+
///
/// Hide if not shutting down, close otherwise
///
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
index 44c6d085..a06e73e3 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs
@@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Printing;
-using CoreAudio;
using HASS.Agent.Models.Internal;
using HASS.Agent.Shared.Functions;
using HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.MultiValue.DataTypes;
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
index 76ed14f6..08b97f7a 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
@@ -2,7 +2,6 @@
using System.Text.Json;
using Windows.Media.Control;
using Windows.Media.Playback;
-using CoreAudio;
using HASS.Agent.Enums;
using HASS.Agent.Extensions;
using HASS.Agent.Managers;
@@ -49,8 +48,7 @@ internal static async Task InitializeAsync()
// todo: optional, but add an OS check - not all OSs support this
try
{
- // create the objects
- Variables.AudioDeviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
+ // create the object
Variables.MediaPlayer = new MediaPlayer();
_sessionManager = await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
index 1d4b2646..47089ca0 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
@@ -1,5 +1,4 @@
-using CoreAudio;
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -12,6 +11,8 @@
using static HASS.Agent.Shared.Functions.Inputs;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using HASS.Agent.Shared.Functions;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
namespace HASS.Agent.Media
{
@@ -70,18 +71,12 @@ internal static void SetVolume(int volume)
if (volume < 0) volume = 0;
if (volume > 100) volume = 100;
- var fVolume = volume / 100.0f;
-
- // get the current default endpoint
- using var audioDevice = Variables.AudioDeviceEnumerator?.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- if (audioDevice?.AudioEndpointVolume == null)
- {
- Log.Warning("[MEDIA] Unable to set volume, no default audio endpoint found");
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if (audioDevice == null)
return;
- }
- // all good, set the volume
- audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar = fVolume;
+ AudioManager.SetVolume(audioDevice, volume);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
index 9317ad4b..bdf93fca 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
@@ -3,7 +3,8 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CoreAudio;
+using HASS.Agent.Shared.Managers;
+using HASS.Agent.Shared.Managers.Audio;
using Serilog;
namespace HASS.Agent.Media
@@ -18,16 +19,12 @@ internal static int GetVolume()
{
try
{
- // get the default audio device
- using var audioDevice = Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if (audioDevice == null)
+ return 0;
- // get default device volume
- var volume = Convert.ToInt32(Math.Round(audioDevice.AudioEndpointVolume?.MasterVolumeLevelScalar * 100 ?? 0, 0));
-
- // Log.Debug("[MEDIA] Current volume: {vol}", volume);
-
- // return it
- return volume;
+ return audioDevice.Volume;
}
catch (Exception ex)
{
@@ -44,16 +41,12 @@ internal static bool GetMuteState()
{
try
{
- // get the default audio device
- using var audioDevice = Variables.AudioDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
-
- // get mute state
- var muted = audioDevice.AudioEndpointVolume?.Mute ?? false;
-
- // Log.Debug("[MEDIA] Muted: {mute}", muted);
+ var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia);
+ var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
+ if (audioDevice == null)
+ return false;
- // return it
- return muted;
+ return audioDevice.Muted;
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index e8bbdff5..08861f67 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3416,7 +3416,15 @@ Befehl/Nutzlast muss im JSON-Format vorliegen. Beispiel aller möglichen Optione
Wenn kein „playbackDevice“ angegeben ist, verwendet HASS.Agent das Standardgerät.
Wenn keine „volume“ angegeben ist, stellt HASS.Agent nur den Stummschaltungsstatus ein.
-Wenn keine „mute“ bereitgestellt wird, hebt HASS.Agent die Stummschaltung der bereitgestellten Anwendung auf.
+Wenn keine „mute“ bereitgestellt wird, hebt HASS.Agent die Stummschaltung der bereitgestellten Anwendung auf.
+
+Erweiterte Option: Zusätzliche „sessionId“ kann bereitgestellt werden, um die Lautstärke nur für eine bestimmte Sitzung der Anwendung festzulegen. Die Sitzungs-ID kann vom Sensor „Audio Sessions“ abgerufen werden:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Bitte geben Sie einen gültigen JSON-String ein!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 23214f52..a96a6a15 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3312,7 +3312,34 @@ Command / payload needs to be in JSON format. Example of all possible options:
If no "playbackDevice" is provided, HASS.Agent will use the default one.
If no "volume" is provided, HASS.Agent will set only mute status.
-If no "mute" is provided, HASS.Agent will unmute the provided application.
+If no "mute" is provided, HASS.Agent will unmute the provided application.
+
+Advanced option: additional "sessionId" can be provided to set volume only for specific session of the application, session id can be obtained from "Audio Sessions" sensor:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}Sets the volume and mute status of the provided application on provided audio device to the specified level.
+Command / payload needs to be in JSON format. Example of all possible options:
+{
+ "playbackDevice": "Speakers (THX Spatial Audio)",
+ "applicationName": "Discord",
+ "volume": 90,
+ "mute": true
+}
+
+If no "playbackDevice" is provided, HASS.Agent will use the default one.
+If no "volume" is provided, HASS.Agent will set only mute status.
+If no "mute" is provided, HASS.Agent will unmute the provided application.
+
+Advanced option: additional "sessionId" can be provided to set volume only for specific session of the application, session id can be obtained from "Audio Sessions" sensor:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Please enter a valid JSON string!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index d85d8775..11bc91fd 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3292,7 +3292,15 @@ El comando/carga útil debe estar en formato JSON. Ejemplo de todas las opciones
Si no se proporciona ningún "playbackDevice", HASS.Agent utilizará el predeterminado.
Si no se proporciona ningún "volume", HASS.Agent establecerá sólo el estado de silencio.
-Si no se proporciona ningún "mute", HASS.Agent reactivará el silencio de la aplicación proporcionada.
+Si no se proporciona ningún "mute", HASS.Agent reactivará el silencio de la aplicación proporcionada.
+
+Opción avanzada: se puede proporcionar un "sessionId" adicional para configurar el volumen solo para una sesión específica de la aplicación; el id de sesión se puede obtener del sensor "Sesiones de audio":
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
¡Ingrese una cadena JSON válida!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index ae38cacb..036c40c4 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3325,7 +3325,15 @@ La commande/charge utile doit être au format JSON. Exemple de toutes les option
Si aucun "playbackDevice" n'est fourni, HASS.Agent utilisera celui par défaut.
Si aucun "volume" n'est fourni, HASS.Agent définira uniquement le statut muet.
-Si aucun "mute" n'est fourni, HASS.Agent réactivera le son de l'application fournie.
+Si aucun "mute" n'est fourni, HASS.Agent réactivera le son de l'application fournie.
+
+Option avancée : un "sessionId" supplémentaire peut être fourni pour définir le volume uniquement pour une session spécifique de l'application, l'identifiant de session peut être obtenu à partir du capteur "Sessions audio" :
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Veuillez saisir une chaîne JSON valide !
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index b67fab9a..7b6f0a41 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3313,7 +3313,15 @@ Commando/payload moet in JSON-indeling zijn. Voorbeeld van alle mogelijke opties
}
Als er geen "playbackDevice" is opgegeven, zal HASS.Agent het standaardapparaat gebruiken.
Als er geen "volume" is opgegeven, zal HASS.Agent alleen de mute-status instellen.
-Als er geen "mute" is opgegeven, zal HASS.Agent het dempen van de opgegeven applicatie opheffen.
+Als er geen "mute" is opgegeven, zal HASS.Agent het dempen van de opgegeven applicatie opheffen.
+
+Geavanceerde optie: er kan een extra "sessionId" worden opgegeven om het volume alleen in te stellen voor een specifieke sessie van de applicatie. De sessie-ID kan worden verkregen via de sensor "Audio Sessions":
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Voer een geldige JSON-string in!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 6dd51e93..684d9e02 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3402,7 +3402,15 @@ Polecenie/ładunek musi być w formacie JSON. Przykład wszystkich możliwych op
Jeśli nie podano „playbackDevice”, HASS.Agent użyje urządzenia domyślnego.
Jeśli nie zostanie podany „volume”, HASS.Agent ustawi jedynie stan wyciszenia.
-Jeśli nie podano opcji „mute”, HASS.Agent wyłączy wyciszenie dostarczonej aplikacji.
+Jeśli nie podano opcji „mute”, HASS.Agent wyłączy wyciszenie dostarczonej aplikacji.
+
+Opcja zaawansowana: można udostępnić dodatkowy parametr „sessionId”, aby ustawić głośność tylko dla konkretnej sesji aplikacji, identyfikator sesji można uzyskać z czujnika „Audio Sessions”:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Proszę wprowadzić prawidłowy ciąg JSON!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 0d4fe746..bdebb522 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3338,7 +3338,15 @@ O comando/carga precisa estar no formato JSON. Exemplo de todas as opções poss
Se nenhum "playbackDevice" for fornecido, o HASS.Agent usará o padrão.
Se nenhum "volume" for fornecido, o HASS.Agent definirá apenas o status mudo.
-Se nenhum "mute" for fornecido, o HASS.Agent ativará o som do aplicativo fornecido.
+Se nenhum "mute" for fornecido, o HASS.Agent ativará o som do aplicativo fornecido.
+
+Opção avançada: "sessionId" adicional pode ser fornecido para definir o volume apenas para uma sessão específica do aplicativo, o ID da sessão pode ser obtido no sensor "Sessões de Áudio":
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Insira uma string JSON válida!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index c79a98a1..8c25f36e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3267,7 +3267,15 @@ Command / payload needs to be in JSON format. Example of all possible options:
If no "playbackDevice" is provided, HASS.Agent will use the default one.
If no "volume" is provided, HASS.Agent will set only mute status.
-If no "mute" is provided, HASS.Agent will unmute the provided application.
+If no "mute" is provided, HASS.Agent will unmute the provided application.
+
+Advanced option: additional "sessionId" can be provided to set volume only for specific session of the application, session id can be obtained from "Audio Sessions" sensor:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Please enter a valid JSON string!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 985e25a2..b34d9222 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3361,7 +3361,15 @@ Home Assistant.
Если «playbackDevice» не указано, HASS.Agent будет использовать устройство по умолчанию.
Если «громкость» не указана, HASS.Agent установит только статус отключения звука.
-Если «отключение звука» не указано, HASS.Agent включит звук предоставленного приложения.
+Если «отключение звука» не указано, HASS.Agent включит звук предоставленного приложения.
+
+Расширенный вариант: можно указать дополнительный «sessionId» для установки громкости только для определенного сеанса приложения, идентификатор сеанса можно получить из датчика «Аудио сеансы»:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Пожалуйста, введите действительную строку JSON!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index bb2dfbf6..e8554e97 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3441,7 +3441,15 @@ Ukaz/tovor mora biti v formatu JSON. Primer vseh možnih možnosti:
Če ni na voljo "playbackDevice", bo HASS.Agent uporabil privzeto.
Če ni na voljo nobena "volume", bo HASS.Agent nastavil samo stanje utišanja.
-Če ni na voljo možnost "mute", bo HASS.Agent znova vključil zvok navedene aplikacije.
+Če ni na voljo možnost "mute", bo HASS.Agent znova vključil zvok navedene aplikacije.
+
+Napredna možnost: dodatni "sessionId" je mogoče zagotoviti za nastavitev glasnosti samo za določeno sejo aplikacije, ID seje je mogoče pridobiti iz senzorja "Audio Sessions":
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Vnesite veljaven niz JSON!
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index 4c44f726..edc26869 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -2908,7 +2908,15 @@ Komut/yükün JSON formatında olması gerekir. Tüm olası seçeneklere örnek:
Hiçbir "playbackDevice" sağlanmazsa, HASS.Agent varsayılan olanı kullanacaktır.
Hiçbir "volume" sağlanmazsa, HASS.Agent yalnızca sessiz durumunu ayarlayacaktır.
-Eğer "mute" sağlanmadıysa, HASS.Agent sağlanan uygulamanın sesini açacaktır.
+Eğer "mute" sağlanmadıysa, HASS.Agent sağlanan uygulamanın sesini açacaktır.
+
+Gelişmiş seçenek: yalnızca uygulamanın belirli bir oturumu için ses seviyesini ayarlamak üzere ek "sessionId" sağlanabilir, oturum kimliği "Sesli Oturumlar" sensöründen alınabilir:
+{
+ "playbackDevice":"Speakers (THX SpatialAudio)",
+ "applicationName":"Discord",
+ "volume":50,
+ "sessionid":"<LONG SESSION ID FROM SENSOR>"
+}
Lütfen geçerli bir JSON dizesi girin!
diff --git a/src/HASS.Agent/HASS.Agent/Variables.cs b/src/HASS.Agent/HASS.Agent/Variables.cs
index 5fe6cca1..c8f67547 100644
--- a/src/HASS.Agent/HASS.Agent/Variables.cs
+++ b/src/HASS.Agent/HASS.Agent/Variables.cs
@@ -5,7 +5,6 @@
using System.Net.Http;
using System.Reflection;
using Windows.Media.Playback;
-using CoreAudio;
using Grapevine;
using HASS.Agent.Forms;
using HASS.Agent.Functions;
@@ -123,7 +122,6 @@ public static class Variables
///
/// Media
///
- internal static MMDeviceEnumerator AudioDeviceEnumerator { get; set; }
internal static MediaPlayer MediaPlayer { get; set; }
///
From efa9a323bbe5bbfc160aedf71b55f87d83d8846a Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Tue, 7 May 2024 15:13:48 +0200
Subject: [PATCH 26/45] Fix: name being used instead of friendly name while
creating/editing commands (#88)
This PR fixed commands friendly name not being properly saved when creating/editing sensors - entity name was used instead.
---
src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
index 983b41b8..1bdbd978 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
@@ -265,7 +265,7 @@ private void BtnStore_Click(object sender, EventArgs e)
MessageBoxAdv.Show(this, Languages.CommandsMod_BtnStore_DeviceNameInSensorName, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
- var friendlyName = string.IsNullOrEmpty(TbName.Text.Trim()) ? name : TbName.Text.Trim();
+ var friendlyName = string.IsNullOrEmpty(TbFriendlyName.Text.Trim()) ? name : TbFriendlyName.Text.Trim();
var sanitized = SharedHelperFunctions.GetSafeValue(name);
if (sanitized != name)
From dafd930ed37675012a70d72e6047cc326fdfa520 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Wed, 8 May 2024 21:33:43 +0200
Subject: [PATCH 27/45] Fix: windows updates sensor missing uniqueid (#90)
* separated sensor id from sensor entity name in image of audio sensors
* cleanup
---
.../MultiValue/WindowsUpdatesSensors.cs | 45 +++++++++++--------
1 file changed, 26 insertions(+), 19 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
index 5ac1797f..949d7933 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/WindowsUpdatesSensors.cs
@@ -16,7 +16,7 @@ public class WindowsUpdatesSensors : AbstractMultiValueSensor
private const string DefaultName = "windowsupdates";
private readonly int _updateInterval;
- public sealed override Dictionary Sensors { get; protected set; } = new Dictionary();
+ public override sealed Dictionary Sensors { get; protected set; } = new Dictionary();
public WindowsUpdatesSensors(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 900, id)
{
@@ -33,37 +33,44 @@ private void AddUpdateSensor(string sensorId, AbstractSingleValueSensor sensor)
Sensors[sensorId] = sensor;
}
- public sealed override void UpdateSensorValues()
+ public override sealed void UpdateSensorValues()
{
var parentSensorSafeName = SharedHelperFunctions.GetSafeValue(EntityName);
var (driverUpdates, softwareUpdates) = WindowsUpdatesManager.GetAvailableUpdates();
- var driverUpdateCount = driverUpdates.Count;
- var driverUpdateCountId = $"{parentSensorSafeName}_driver_updates_pending";
- var driverUpdateCountSensor = new DataTypeIntSensor(_updateInterval, driverUpdateCountId, "Driver Updates Pending", driverUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
- driverUpdateCountSensor.SetState(driverUpdateCount);
+ var driverUpdateCountEntityName = $"{parentSensorSafeName}_driver_updates_pending";
+ var driverUpdateCountId = $"{Id}_driver_updates_pending";
+ var driverUpdateCountSensor = new DataTypeIntSensor(_updateInterval, driverUpdateCountEntityName, "Driver Updates Pending", driverUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
+ driverUpdateCountSensor.SetState(driverUpdates.Count);
AddUpdateSensor(driverUpdateCountId, driverUpdateCountSensor);
- var softwareUpdateCount = softwareUpdates.Count;
- var softwareUpdateCountId = $"{parentSensorSafeName}_software_updates_pending";
- var softwareUpdateCountSensor = new DataTypeIntSensor(_updateInterval, softwareUpdateCountId, "Software Updates Pending", softwareUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
- softwareUpdateCountSensor.SetState(softwareUpdateCount);
+ var softwareUpdateCountEntityName = $"{parentSensorSafeName}_software_updates_pending";
+ var softwareUpdateCountId = $"{Id}_software_updates_pending";
+ var softwareUpdateCountSensor = new DataTypeIntSensor(_updateInterval, softwareUpdateCountEntityName, "Software Updates Pending", softwareUpdateCountId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName);
+ softwareUpdateCountSensor.SetState(softwareUpdates.Count);
AddUpdateSensor(softwareUpdateCountId, softwareUpdateCountSensor);
- var driverUpdatesList = new WindowsUpdateInfoCollection(driverUpdates);
- var driverUpdatesStr = JsonConvert.SerializeObject(driverUpdatesList, Formatting.Indented);
- var driverUpdatesId = $"{parentSensorSafeName}_driver_updates";
- var driverUpdatesSensor = new DataTypeIntSensor(_updateInterval, driverUpdatesId, "Available Driver Updates", driverUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
+ var driverUpdatesEntityName = $"{parentSensorSafeName}_driver_updates";
+ var driverUpdatesId = $"{Id}_driver_updates";
+ var driverUpdatesSensor = new DataTypeIntSensor(_updateInterval, driverUpdatesEntityName, "Available Driver Updates", driverUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
driverUpdatesSensor.SetState(driverUpdates.Count);
- driverUpdatesSensor.SetAttributes(driverUpdatesStr);
+ driverUpdatesSensor.SetAttributes(
+ JsonConvert.SerializeObject(
+ new WindowsUpdateInfoCollection(driverUpdates)
+ , Formatting.Indented)
+ );
AddUpdateSensor(driverUpdatesId, driverUpdatesSensor);
- var softwareUpdatesStr = JsonConvert.SerializeObject(new WindowsUpdateInfoCollection(softwareUpdates), Formatting.Indented);
- var softwareUpdatesId = $"{parentSensorSafeName}_software_updates";
- var softwareUpdatesSensor = new DataTypeIntSensor(_updateInterval, softwareUpdatesId, "Available Software Updates", softwareUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
+ var softwareUpdatesEntityName = $"{parentSensorSafeName}_software_updates";
+ var softwareUpdatesId = $"{Id}_software_updates";
+ var softwareUpdatesSensor = new DataTypeIntSensor(_updateInterval, softwareUpdatesEntityName, "Available Software Updates", softwareUpdatesId, string.Empty, "measurement", "mdi:microsoft-windows", string.Empty, EntityName, true);
softwareUpdatesSensor.SetState(softwareUpdates.Count);
- softwareUpdatesSensor.SetAttributes(softwareUpdatesStr);
+ softwareUpdatesSensor.SetAttributes(
+ JsonConvert.SerializeObject(
+ new WindowsUpdateInfoCollection(softwareUpdates)
+ , Formatting.Indented)
+ );
AddUpdateSensor(softwareUpdatesId, softwareUpdatesSensor);
}
From c8cb353b6541bea5fb9d9a5e76f71f8db2f518fc Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 16 May 2024 22:03:36 +0200
Subject: [PATCH 28/45] Fix: added lost useAttributes parameter (#92)
---
.../Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
index 849a1a28..46b689e3 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs
@@ -16,7 +16,7 @@ public class ActiveWindowSensor : AbstractSingleValueSensor
private string _processName = string.Empty;
- public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, advancedSettings: advancedSettings) { }
+ public ActiveWindowSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 15, id, useAttributes: true, advancedSettings: advancedSettings) { }
public override DiscoveryConfigModel GetAutoDiscoveryConfig()
{
From 8051a8bbbe24be84f2dae1734ed4b858efccde74 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sun, 2 Jun 2024 17:25:17 +0300
Subject: [PATCH 29/45] Feature: "MultipleKeys" command support for action
parameter (#98)
This PR adds functionality requested via #97 - action parameter support for "MultipleKeys" command.
As of now, the "Key" command has been left unchanged.
---
.../Commands/MultipleKeysCommand.cs | 63 ++++++++++++++++++-
.../HASS.Agent/Commands/CommandsManager.cs | 2 +-
.../HASS.Agent/Forms/Commands/CommandsMod.cs | 14 ++++-
.../HASS.Agent/Functions/HelperFunctions.cs | 5 +-
4 files changed, 75 insertions(+), 9 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs
index 485c80ad..3d56a286 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using HASS.Agent.Shared.Enums;
using HASS.Agent.Shared.Models.HomeAssistant;
+using HASS.Agent.Shared.Resources.Localization;
using Serilog;
namespace HASS.Agent.Shared.HomeAssistant.Commands
@@ -50,7 +53,7 @@ public override void TurnOff()
//
}
- public override async void TurnOn()
+ public async override void TurnOn()
{
try
{
@@ -73,9 +76,63 @@ public override async void TurnOn()
}
}
- public override void TurnOnWithAction(string action)
+ public async override void TurnOnWithAction(string action)
{
- //
+ var keys = ParseMultipleKeys(action);
+ if (keys.Count == 0)
+ return;
+
+ foreach (var key in keys)
+ {
+ SendKeys.SendWait(key);
+ SendKeys.Flush();
+ await Task.Delay(50);
+ }
+ }
+
+ private List ParseMultipleKeys(string keyString)
+ {
+ var keys = new List();
+
+ try
+ {
+ if (string.IsNullOrWhiteSpace(keyString))
+ return keys;
+
+
+ if (!keyString.Contains('[') || !keyString.Contains(']'))
+ return keys;
+
+ // replace all escaped brackets
+ // todo: ugly, let regex do that
+ keyString = keyString.Replace(@"\[", "left_bracket");
+ keyString = keyString.Replace(@"\]", "right_bracket");
+
+ // lets see if the brackets corresponds
+ var leftBrackets = keyString.Count(x => x == '[');
+ var rightBrackets = keyString.Count(x => x == ']');
+
+ if (leftBrackets != rightBrackets)
+ return keys;
+
+ // ok, try parsen
+ var pattern = @"\[(.*?)\]";
+ var matches = Regex.Matches(keyString, pattern);
+ keys.AddRange(from Match m in matches select m.Groups[1].ToString());
+
+ // restore escaped brackets
+ for (var i = 0; i < keys.Count; i++)
+ {
+ if (keys[i] == "left_bracket") keys[i] = "[";
+ if (keys[i] == "right_bracket") keys[i] = "]";
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error("[MULTIPLEKEYS] [{name}] Error parsing multiple keys: {msg}", EntityName, ex.Message);
+ }
+
+ return keys;
}
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
index 0d4dbc69..f3646e59 100644
--- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
@@ -423,7 +423,7 @@ internal static void LoadCommandInfo()
commandInfoCard = new CommandInfoCard(CommandType.MultipleKeysCommand,
Languages.CommandsManager_MultipleKeysCommandDescription,
- true, false, false);
+ true, false, true);
CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
index 1bdbd978..f494dbb9 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs
@@ -353,7 +353,19 @@ private void BtnStore_Click(object sender, EventArgs e)
return;
}
- Command.Keys = keys;
+
+ if (keys.Count == 0)
+ {
+ var q = MessageBoxAdv.Show(this, Languages.CommandsMod_BtnStore_MessageBox4, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+ if (q != DialogResult.Yes)
+ {
+ ActiveControl = TbSetting;
+
+ return;
+ }
+ }
+
+ Command.Keys = keys;
break;
case CommandType.LaunchUrlCommand:
diff --git a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
index 877b4e57..db1138ef 100644
--- a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
+++ b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
@@ -236,10 +236,7 @@ internal static bool ParseMultipleKeys(string keyString, out List keys,
{
// any value?
if (string.IsNullOrWhiteSpace(keyString))
- {
- errorMsg = Languages.HelperFunctions_ParseMultipleKeys_ErrorMsg1;
- return false;
- }
+ return true;
// any brackets?
if (!keyString.Contains('[') || !keyString.Contains(']'))
From 8e6727272fb1dc8c75602164dce7ad2ed5d21d31 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sun, 16 Jun 2024 19:25:56 +0200
Subject: [PATCH 30/45] Fix: audio library/sleep management/playback (#101)
This PR:
changes the audio library to NAudio
fixed issue with media playback using deprecated method that occasionally resulted in access memory violation
adds additional logic to handle audio management after system has been put to sleep
---
.../HASS.Agent.Shared.csproj | 2 +-
.../Managers/Audio/AudioManager.cs | 227 ++++++++++++------
.../Internal/CPolicyConfigVistaClient.cs | 2 +-
.../Audio/Internal/DeviceChangeEventArgs.cs | 50 ++++
.../Audio/Internal/IPolicyConfigVista.cs | 2 +-
.../Audio/Internal/InternalAudioDevice.cs | 11 +-
.../Audio/Internal/InternalAudioSession.cs | 47 ++--
.../Internal/InternalAudioSessionManager.cs | 29 ++-
.../Audio/Internal/MMNotificationClient.cs | 43 ++++
.../HASS.Agent/Managers/SystemStateManager.cs | 7 +
.../HASS.Agent/Media/MediaManager.cs | 3 +-
11 files changed, 298 insertions(+), 125 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/DeviceChangeEventArgs.cs
create mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/MMNotificationClient.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index d142df54..6b665622 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -33,11 +33,11 @@
-
+
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
index 8f0cea0d..ce7e246d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
@@ -5,17 +5,19 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
using HASS.Agent.Shared.Managers.Audio.Exceptions;
using HASS.Agent.Shared.Managers.Audio.Internal;
using Serilog;
+using NAudio.CoreAudioApi.Interfaces;
namespace HASS.Agent.Shared.Managers.Audio;
public static class AudioManager
{
private static bool _initialized = false;
- private static readonly MMDeviceEnumerator _enumerator = new();
+ private static MMDeviceEnumerator _enumerator = null;
+ private static MMNotificationClient _notificationClient = null;
private static readonly ConcurrentDictionary _devices = new();
private static readonly ConcurrentQueue _devicesToBeRemoved = new();
@@ -23,57 +25,65 @@ public static class AudioManager
private static readonly Dictionary _applicationNameCache = new();
- public static void Initialize()
+ private static void InitializeDevices()
{
- Log.Debug("[AUDIOMGR] Audio Manager initializing");
+ _enumerator = new MMDeviceEnumerator();
- foreach (var device in _enumerator.EnumAudioEndpoints(DataFlow.All, DeviceState.Active))
- _devices[device.DeviceID] = new InternalAudioDevice(device);
+ foreach (var device in _enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active))
+ _devices[device.ID] = new InternalAudioDevice(device);
- var nc = new MMNotificationClient(_enumerator);
- nc.DeviceAdded += (sender, e) =>
- {
- if (_devices.ContainsKey(e.DeviceId))
- return;
+ _notificationClient = new MMNotificationClient();
+ _notificationClient.DeviceAdded += DeviceAdded;
+ _notificationClient.DeviceRemoved += DeviceRemoved;
+ _notificationClient.DeviceStateChanged += DeviceStateChanged;
+ _enumerator.RegisterEndpointNotificationCallback(_notificationClient);
- _devicesToBeAdded.Enqueue(e.DeviceId);
- };
+ _initialized = true;
+ }
- nc.DeviceRemoved += (sender, e) =>
+ private static void DeviceStateChanged(object sender, DeviceStateChangedEventArgs e)
+ {
+ switch (e.DeviceState)
{
- _devicesToBeRemoved.Enqueue(e.DeviceId);
- };
+ case DeviceState.Active:
+ if (_devices.ContainsKey(e.DeviceId))
+ return;
- _initialized = true;
+ _devicesToBeAdded.Enqueue(e.DeviceId);
+ break;
- nc.DeviceStateChanged += (sender, e) =>
- {
- switch (e.DeviceState)
- {
- case DeviceState.Active:
- if (_devices.ContainsKey(e.DeviceId))
- return;
+ case DeviceState.NotPresent:
+ case DeviceState.Unplugged:
+ case DeviceState.Disabled:
+ _devicesToBeRemoved.Enqueue(e.DeviceId);
+ break;
- _devicesToBeAdded.Enqueue(e.DeviceId);
- break;
+ default:
+ break;
+ }
+ }
- case DeviceState.NotPresent:
- case DeviceState.UnPlugged:
- _devicesToBeRemoved.Enqueue(e.DeviceId);
- break;
+ private static void DeviceRemoved(object sender, DeviceNotificationEventArgs e)
+ {
+ _devicesToBeRemoved.Enqueue(e.DeviceId);
+ }
- default:
- break;
- }
- };
+ private static void DeviceAdded(object sender, DeviceNotificationEventArgs e)
+ {
+ if (_devices.ContainsKey(e.DeviceId))
+ return;
- Log.Information("[AUDIOMGR] Audio Manager initialized");
+ _devicesToBeAdded.Enqueue(e.DeviceId);
}
- private static void CheckInitialization()
+ private static bool CheckInitialization()
{
if (!_initialized)
- throw new InvalidOperationException("AudioManager is not initialized");
+ {
+ Log.Warning("[AUDIOMGR] not yet initialized!");
+
+ return false;
+ }
while (_devicesToBeRemoved.TryDequeue(out var deviceId))
{
@@ -86,6 +96,8 @@ private static void CheckInitialization()
var device = _enumerator.GetDevice(deviceId);
_devices[deviceId] = new InternalAudioDevice(device);
}
+
+ return true;
}
private static string GetSessionDisplayName(InternalAudioSession session)
@@ -125,10 +137,10 @@ private static List GetDeviceSessions(InternalAudioDevice internal
Id = sessionId,
Application = displayName,
PlaybackDevice = internalAudioDevice.FriendlyName,
- Muted = session.Volume.IsMuted,
- Active = session.Control.SessionState == AudioSessionState.AudioSessionStateActive,
- MasterVolume = Convert.ToInt32(session.Volume.MasterVolume * 100),
- PeakVolume = Convert.ToDouble(session.MeterInformation.PeakValue * 100)
+ Muted = session.Volume.Mute,
+ Active = session.Control.State == AudioSessionState.AudioSessionStateActive,
+ MasterVolume = Convert.ToInt32(session.Volume.Volume * 100),
+ PeakVolume = Convert.ToDouble(session.MeterInformation.MasterPeakValue * 100)
};
audioSessions.Add(audioSession);
@@ -149,43 +161,91 @@ private static string GetReadableState(DeviceState state)
DeviceState.Active => "ACTIVE",
DeviceState.Disabled => "DISABLED",
DeviceState.NotPresent => "NOT PRESENT",
- DeviceState.UnPlugged => "UNPLUGGED",
+ DeviceState.Unplugged => "UNPLUGGED",
DeviceState.All => "STATEMASK_ALL",
_ => "UNKNOWN"
};
}
- public static List GetDevices()
+ public static void Initialize()
{
- CheckInitialization();
+ Log.Debug("[AUDIOMGR] initializing");
- var audioDevices = new List();
+ InitializeDevices();
+
+ Log.Information("[AUDIOMGR] initialized");
+ }
+
+ public static void ReInitialize()
+ {
+ if (_initialized)
+ CleanupDevices();
+
+ Log.Debug("[AUDIOMGR] re-initializing");
+
+ InitializeDevices();
+
+ Log.Information("[AUDIOMGR] re-initialized");
+ }
- using var defaultInputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
- using var defaultOutputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
+ public static void CleanupDevices()
+ {
+ Log.Debug("[AUDIOMGR] starting cleanup");
+ _initialized = false;
+
+ foreach (var device in _devices.Values)
+ device.Dispose();
+
+ _notificationClient.DeviceAdded -= DeviceAdded;
+ _notificationClient.DeviceRemoved -= DeviceRemoved;
+ _notificationClient.DeviceStateChanged -= DeviceStateChanged;
+ _enumerator.UnregisterEndpointNotificationCallback(_notificationClient);
- var defaultInputDeviceId = defaultInputDevice.DeviceID;
- var defaultOutputDeviceId = defaultOutputDevice.DeviceID;
+ _enumerator.Dispose();
+
+ _devices.Clear();
+ Log.Debug("[AUDIOMGR] cleanup completed");
+ }
+
+ public static List GetDevices()
+ {
+ var audioDevices = new List();
- foreach (var device in _devices.Values.Where(d => d.MMDevice.DeviceState == DeviceState.Active))
+ if (!CheckInitialization())
+ return audioDevices;
+
+ try
{
+ using var defaultInputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia);
+ using var defaultOutputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
- var audioSessions = GetDeviceSessions(device);
- var loudestSession = audioSessions.MaxBy(s => s.PeakVolume);
+ var defaultInputDeviceId = defaultInputDevice.ID;
+ var defaultOutputDeviceId = defaultOutputDevice.ID;
- var audioDevice = new AudioDevice
+ foreach (var device in _devices.Values.Where(d => d.MMDevice.State == DeviceState.Active))
{
- State = GetReadableState(device.MMDevice.DeviceState),
- Type = device.MMDevice.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output,
- Id = device.DeviceId,
- FriendlyName = device.FriendlyName,
- Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.GetMasterVolumeLevelScalar() * 100, 0)),
- PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume,
- Sessions = audioSessions,
- Default = device.DeviceId == defaultInputDeviceId || device.DeviceId == defaultOutputDeviceId
- };
-
- audioDevices.Add(audioDevice);
+
+ var audioSessions = GetDeviceSessions(device);
+ var loudestSession = audioSessions.MaxBy(s => s.PeakVolume);
+
+ var audioDevice = new AudioDevice
+ {
+ State = GetReadableState(device.MMDevice.State),
+ Type = device.MMDevice.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output,
+ Id = device.DeviceId,
+ FriendlyName = device.FriendlyName,
+ Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0)),
+ PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume,
+ Sessions = audioSessions,
+ Default = device.DeviceId == defaultInputDeviceId || device.DeviceId == defaultOutputDeviceId
+ };
+
+ audioDevices.Add(audioDevice);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Debug(ex, "[AUDIOMGR] Failed to retrieve devices: {msg}", ex.Message);
}
return audioDevices;
@@ -193,16 +253,22 @@ public static List GetDevices()
public static string GetDefaultDeviceId(DeviceType deviceType, DeviceRole deviceRole)
{
+ if (!CheckInitialization())
+ return string.Empty;
+
var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
var role = (Role)deviceRole;
using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
- return defaultDevice.DeviceID;
+ return defaultDevice.ID;
}
public static void Activate(AudioDevice audioDevice)
{
+ if (!CheckInitialization())
+ return;
+
if (!_devices.TryGetValue(audioDevice.Id, out var device))
return;
@@ -211,15 +277,21 @@ public static void Activate(AudioDevice audioDevice)
public static void SetVolume(AudioDevice audioDevice, int volume)
{
+ if (!CheckInitialization())
+ return;
+
if (!_devices.TryGetValue(audioDevice.Id, out var device))
return;
var volumeScalar = volume / 100f;
- device.AudioEndpointVolume.SetMasterVolumeLevelScalar(volumeScalar, Guid.Empty);
+ device.AudioEndpointVolume.MasterVolumeLevelScalar = volumeScalar;
}
public static void SetVolume(AudioSession audioSession, int volume)
{
+ if (!CheckInitialization())
+ return;
+
var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
if (device == null)
return;
@@ -228,19 +300,25 @@ public static void SetVolume(AudioSession audioSession, int volume)
return;
var volumeScalar = volume / 100f;
- session.Volume.MasterVolume = volumeScalar;
+ session.Volume.Volume = volumeScalar;
}
public static void SetMute(AudioDevice audioDevice, bool mute)
{
+ if (!CheckInitialization())
+ return;
+
if (!_devices.TryGetValue(audioDevice.Id, out var device))
return;
- device.AudioEndpointVolume.SetMute(mute, Guid.Empty);
+ device.AudioEndpointVolume.Mute = mute;
}
public static void SetMute(AudioSession audioSession, bool mute)
{
+ if (!CheckInitialization())
+ return;
+
var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
if (device == null)
return;
@@ -248,23 +326,20 @@ public static void SetMute(AudioSession audioSession, bool mute)
if (!device.Manager.Sessions.TryGetValue(audioSession.Id, out var session))
return;
- session.Volume.IsMuted = mute;
+ session.Volume.Mute = mute;
}
public static void Shutdown()
{
- Log.Debug("[AUDIOMGR] Audio Manager shutting down");
+ Log.Debug("[AUDIOMGR] shutting down");
try
{
- foreach(var device in _devices.Values)
- device.Dispose();
-
- _enumerator.Dispose();
+ CleanupDevices();
}
catch (Exception ex)
{
- Log.Fatal(ex, "[AUDIOMGR] Audio Manager shutdown fatal error: {ex}", ex.Message);
+ Log.Fatal(ex, "[AUDIOMGR] shutdown fatal error: {ex}", ex.Message);
}
- Log.Debug("[AUDIOMGR] Audio Manager shutdown completed");
+ Log.Debug("[AUDIOMGR] shutdown completed");
}
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
index 73db3317..9eed7840 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/CPolicyConfigVistaClient.cs
@@ -4,7 +4,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
namespace HASS.Agent.Shared.Managers.Audio.Internal;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/DeviceChangeEventArgs.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/DeviceChangeEventArgs.cs
new file mode 100644
index 00000000..177cf4e3
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/DeviceChangeEventArgs.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NAudio.CoreAudioApi;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+
+internal class DeviceNotificationEventArgs : EventArgs
+{
+ public string DeviceId { get; private set; }
+
+ public DeviceNotificationEventArgs(string deviceId)
+ {
+ DeviceId = deviceId;
+ }
+}
+
+internal class DefaultDeviceChangedEventArgs : DeviceNotificationEventArgs
+{
+ public DataFlow DataFlow { get; private set; }
+ public Role Role { get; private set; }
+
+ public DefaultDeviceChangedEventArgs(string deviceId, DataFlow dataFlow, Role role) : base(deviceId)
+ {
+ DataFlow = dataFlow;
+ Role = role;
+ }
+}
+
+internal class DeviceStateChangedEventArgs : DeviceNotificationEventArgs
+{
+ public DeviceState DeviceState { get; private set; }
+
+ public DeviceStateChangedEventArgs(string deviceId, DeviceState deviceState) : base(deviceId)
+ {
+ DeviceState = deviceState;
+ }
+}
+
+internal class DevicePropertyChangedEventArgs : DeviceNotificationEventArgs
+{
+ public PropertyKey PropertyKey { get; private set; }
+
+ public DevicePropertyChangedEventArgs(string deviceId, PropertyKey propertyKey) : base(deviceId)
+ {
+ PropertyKey = propertyKey;
+ }
+}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
index e430e102..b6d91b92 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/IPolicyConfigVista.cs
@@ -4,7 +4,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
namespace HASS.Agent.Shared.Managers.Audio.Internal;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
index 59721484..b477c2d9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
@@ -3,7 +3,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
namespace HASS.Agent.Shared.Managers.Audio.Internal;
internal class InternalAudioDevice : IDisposable
@@ -19,18 +19,17 @@ internal class InternalAudioDevice : IDisposable
public InternalAudioDevice(MMDevice device)
{
MMDevice = device;
- var sessionManager2 = AudioSessionManager2.FromMMDevice(device);
- Manager = new InternalAudioSessionManager(sessionManager2);
- AudioEndpointVolume = AudioEndpointVolume.FromDevice(device);
+ Manager = new InternalAudioSessionManager(device.AudioSessionManager);
+ AudioEndpointVolume = device.AudioEndpointVolume;
- DeviceId = device.DeviceID;
+ DeviceId = device.ID;
FriendlyName = device.FriendlyName;
}
public void Activate()
{
using var configClient = new CPolicyConfigVistaClient();
- configClient.SetDefaultDevice(MMDevice.DeviceID);
+ configClient.SetDefaultDevice(MMDevice.ID);
}
public void Dispose()
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
index 480b40c4..9e9c210d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSession.cs
@@ -3,78 +3,71 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
+using NAudio.CoreAudioApi.Interfaces;
namespace HASS.Agent.Shared.Managers.Audio.Internal;
-internal class InternalAudioSession : IDisposable, IAudioSessionEvents
+internal class InternalAudioSession : IDisposable, IAudioSessionEventsHandler
{
public AudioSessionControl Control { get; private set; }
- public AudioSessionControl2 Control2 { get; private set; }
public SimpleAudioVolume Volume { get; private set; }
public AudioMeterInformation MeterInformation { get; private set; }
public string DisplayName { get; private set; }
public int ProcessId { get; private set; }
+
public bool Expired { get; private set; } = false;
public InternalAudioSession(AudioSessionControl audioSessionControl)
{
Control = audioSessionControl;
- Control2 = audioSessionControl.QueryInterface();
- Volume = audioSessionControl.QueryInterface();
- MeterInformation = audioSessionControl.QueryInterface();
+ Volume = audioSessionControl.SimpleAudioVolume;
+ MeterInformation = audioSessionControl.AudioMeterInformation;
- DisplayName = Control2.DisplayName;
- ProcessId = Control2.ProcessID;
+ DisplayName = Control.DisplayName;
+ ProcessId = Convert.ToInt32(Control.GetProcessID);
- Control.RegisterAudioSessionNotification(this);
+ Control.RegisterEventClient(this);
}
public void Dispose()
{
- Control.UnregisterAudioSessionNotification(this);
+ Control.UnRegisterEventClient(this);
- MeterInformation?.Dispose();
Volume?.Dispose();
- Control2?.Dispose();
Control?.Dispose();
GC.SuppressFinalize(this);
}
- ~InternalAudioSession()
- {
- Dispose();
- }
-
- public void OnDisplayNameChanged(string newDisplayName, ref Guid eventContext)
+ public void OnVolumeChanged(float volume, bool isMuted)
{
}
- public void OnIconPathChanged(string newIconPath, ref Guid eventContext)
+ public void OnDisplayNameChanged(string displayName)
{
}
- public void OnSimpleVolumeChanged(float newVolume, bool newMute, ref Guid eventContext)
+ public void OnIconPathChanged(string iconPath)
{
}
- public void OnChannelVolumeChanged(int channelCount, float[] newChannelVolumeArray, int changedChannel, ref Guid eventContext)
+ public void OnChannelVolumeChanged(uint channelCount, nint newVolumes, uint channelIndex)
{
}
- public void OnGroupingParamChanged(ref Guid newGroupingParam, ref Guid eventContext)
+ public void OnGroupingParamChanged(ref Guid groupingId)
{
}
- public void OnStateChanged(AudioSessionState newState)
+ public void OnStateChanged(AudioSessionState state)
{
- if (newState == AudioSessionState.AudioSessionStateExpired)
+ if (state == AudioSessionState.AudioSessionStateExpired)
Expired = true;
}
@@ -82,4 +75,10 @@ public void OnSessionDisconnected(AudioSessionDisconnectReason disconnectReason)
{
}
+
+ ~InternalAudioSession()
+ {
+ Dispose();
+ }
+
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
index 0ebab737..c201b427 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioSessionManager.cs
@@ -4,39 +4,40 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CSCore.CoreAudioAPI;
+using NAudio.CoreAudioApi;
namespace HASS.Agent.Shared.Managers.Audio.Internal;
internal class InternalAudioSessionManager : IDisposable
{
- public AudioSessionManager2 Manager { get; private set; }
+ public AudioSessionManager Manager { get; private set; }
public ConcurrentDictionary Sessions { get; private set; } = new();
- public InternalAudioSessionManager(AudioSessionManager2 sessionManager2)
+ public InternalAudioSessionManager(AudioSessionManager sessionManager2)
{
Manager = sessionManager2;
- using var sessionEnumerator = Manager.GetSessionEnumerator();
- foreach (var session in sessionEnumerator)
+ var sessions = Manager.Sessions;
+ for (var i = 0; i < sessions.Count; i++)
{
+ var session = sessions[i];
var internalSession = new InternalAudioSession(session);
- Sessions[internalSession.Control2.SessionInstanceIdentifier] = internalSession;
+ Sessions[internalSession.Control.GetSessionInstanceIdentifier] = internalSession;
}
- Manager.SessionCreated += Manager_SessionCreated;
+ Manager.OnSessionCreated += Manager_OnSessionCreated;
}
- private void Manager_SessionCreated(object? sender, SessionCreatedEventArgs e)
+ private void Manager_OnSessionCreated(object sender, NAudio.CoreAudioApi.Interfaces.IAudioSessionControl newSession)
{
- if (e.NewSession != null)
+ if (newSession != null)
{
- var internalSession = new InternalAudioSession(e.NewSession);
- Sessions[internalSession.Control2.SessionInstanceIdentifier] = internalSession;
+ var internalSession = new InternalAudioSession(new AudioSessionControl(newSession));
+ Sessions[internalSession.Control.GetSessionInstanceIdentifier] = internalSession;
}
}
public void RemoveDisconnectedSessions()
{
- var expiredSessionsId = Sessions.Values.Where(s => s.Expired).Select(s => s.Control2.SessionInstanceIdentifier);
+ var expiredSessionsId = Sessions.Values.Where(s => s.Expired).Select(s => s.Control.GetSessionInstanceIdentifier);
if (!expiredSessionsId.Any())
return;
@@ -50,12 +51,10 @@ public void RemoveDisconnectedSessions()
public void Dispose()
{
if (Manager != null)
- Manager.SessionCreated -= Manager_SessionCreated;
+ Manager.OnSessionCreated -= Manager_OnSessionCreated;
foreach (var session in Sessions.Values)
- {
session?.Dispose();
- }
Manager?.Dispose();
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/MMNotificationClient.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/MMNotificationClient.cs
new file mode 100644
index 00000000..acc3c66c
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/MMNotificationClient.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NAudio.CoreAudioApi.Interfaces;
+using NAudio.CoreAudioApi;
+
+namespace HASS.Agent.Shared.Managers.Audio.Internal;
+
+internal class MMNotificationClient : IMMNotificationClient
+{
+ public event EventHandler DeviceStateChanged;
+ public event EventHandler DeviceAdded;
+ public event EventHandler DeviceRemoved;
+ public event EventHandler DefaultDeviceChanged;
+ public event EventHandler DevicePropertyChanged;
+
+ void IMMNotificationClient.OnDeviceStateChanged(string deviceId, DeviceState deviceState)
+ {
+ DeviceStateChanged?.Invoke(this, new DeviceStateChangedEventArgs(deviceId, deviceState));
+ }
+
+ void IMMNotificationClient.OnDeviceAdded(string deviceId)
+ {
+ DeviceAdded?.Invoke(this, new DeviceNotificationEventArgs(deviceId));
+ }
+
+ void IMMNotificationClient.OnDeviceRemoved(string deviceId)
+ {
+ DeviceRemoved?.Invoke(this, new DeviceNotificationEventArgs(deviceId));
+ }
+
+ void IMMNotificationClient.OnDefaultDeviceChanged(DataFlow dataFlow, Role role, string deviceId)
+ {
+ DefaultDeviceChanged?.Invoke(this, new DefaultDeviceChangedEventArgs(deviceId, dataFlow, role));
+ }
+
+ void IMMNotificationClient.OnPropertyValueChanged(string deviceId, PropertyKey key)
+ {
+ DevicePropertyChanged?.Invoke(this, new DevicePropertyChangedEventArgs(deviceId, key));
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
index 22fbfb1e..a8794708 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
@@ -1,6 +1,7 @@
using System.Runtime.InteropServices;
using HASS.Agent.Functions;
using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.Managers.Audio;
using Microsoft.Win32;
using Serilog;
@@ -200,6 +201,9 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang
Log.Information("[SYSTEMSTATE] Session resuming");
Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync());
LastSystemStateEvent = SystemStateEvent.Resume;
+
+ AudioManager.ReInitialize();
+
break;
case PowerModes.Suspend:
@@ -209,6 +213,9 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang
Log.Information("[SYSTEMSTATE] Session halting: system suspending");
Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync(true));
LastSystemStateEvent = SystemStateEvent.Suspend;
+
+ AudioManager.CleanupDevices();
+
break;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
index 08b97f7a..fd15f99d 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
@@ -12,6 +12,7 @@
using Serilog;
using MediaPlayerState = HASS.Agent.Enums.MediaPlayerState;
using Octokit;
+using Windows.Media.Core;
namespace HASS.Agent.Media
{
@@ -406,7 +407,7 @@ internal static async void ProcessMedia(string mediaUri)
if (Variables.MediaPlayer.CurrentState == Windows.Media.Playback.MediaPlayerState.Playing) Variables.MediaPlayer.Pause();
// set the uri source
- Variables.MediaPlayer.SetUriSource(new Uri(localFile));
+ Variables.MediaPlayer.Source = MediaSource.CreateFromUri(new Uri(localFile));
if (Variables.ExtendedLogging) Log.Information("[MEDIA] Playing: {file}", Path.GetFileName(localFile));
From bd03bf5413f65d527bb942d6f17b6a94538fa70c Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sun, 16 Jun 2024 22:54:37 +0200
Subject: [PATCH 31/45] added missing audio device mute attribute assignment;
added missing media player mute assignment (#102)
This PR fixes:
audio devices returned from AudioManager not having "Mute" attribute assigned properly
media player not reporting mute status
---
.../Managers/Audio/AudioManager.cs | 1 +
.../HASS.Agent/Media/MediaManager.cs | 22 +++++++++++--------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
index ce7e246d..b6358d45 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
@@ -235,6 +235,7 @@ public static List GetDevices()
Id = device.DeviceId,
FriendlyName = device.FriendlyName,
Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0)),
+ Muted = device.MMDevice.AudioEndpointVolume.Mute,
PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume,
Sessions = audioSessions,
Default = device.DeviceId == defaultInputDeviceId || device.DeviceId == defaultOutputDeviceId
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
index fd15f99d..9dae42ec 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManager.cs
@@ -13,6 +13,7 @@
using MediaPlayerState = HASS.Agent.Enums.MediaPlayerState;
using Octokit;
using Windows.Media.Core;
+using HASS.Agent.Shared.Managers.Audio;
namespace HASS.Agent.Media
{
@@ -38,7 +39,7 @@ internal static async Task InitializeAsync()
Log.Information("[MEDIA] Disabled");
return;
}
-
+
if (!Variables.AppSettings.LocalApiEnabled && !Variables.AppSettings.MqttEnabled)
{
Log.Warning("[MEDIA] Both local API and MQTT are disabled, unable to receive media requests");
@@ -70,7 +71,7 @@ internal static async Task InitializeAsync()
}
catch (Exception ex)
{
- Log.Fatal(ex, "[MEDIA] Unable to initialize: {err}" , ex.Message);
+ Log.Fatal(ex, "[MEDIA] Unable to initialize: {err}", ex.Message);
Variables.AppSettings.MediaPlayerEnabled = false;
Log.Warning("[MEDIA] Failed, disabled");
@@ -79,7 +80,7 @@ internal static async Task InitializeAsync()
// start monitoring playing media
_ = Task.Run(MediaMonitor);
-
+
if (!Variables.AppSettings.MqttEnabled) Log.Warning("[MEDIA] MQTT is disabled, only basic media functionality will work");
else
{
@@ -191,7 +192,8 @@ private static async void MediaMonitor()
message.AlbumArtist = mediaProperties.AlbumArtist;
message.AlbumTitle = mediaProperties.AlbumTitle;
message.Volume = MediaManagerRequests.GetVolume();
-
+ message.Muted = AudioManager.GetDevices().FirstOrDefault(d => d.Type == DeviceType.Output && d.Default)?.Muted == true;
+
// get timeline info
var timeline = session.GetTimelineProperties();
if (timeline != null)
@@ -243,9 +245,9 @@ private static GlobalSystemMediaTransportControlsSession GetCurrentMediaSession(
// if none are playing: pick the first
if (sessions.Count == 1) return sessions[0];
-
- return sessions.Any(x => x.GetPlaybackInfo().PlaybackStatus == GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing)
- ? sessions.First(x => x.GetPlaybackInfo().PlaybackStatus == GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing)
+
+ return sessions.Any(x => x.GetPlaybackInfo().PlaybackStatus == GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing)
+ ? sessions.First(x => x.GetPlaybackInfo().PlaybackStatus == GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing)
: sessions[0];
}
@@ -331,7 +333,8 @@ internal static void ProcessCommand(MediaPlayerCommand command)
case MediaPlayerCommand.Play:
if (Variables.ExtendedLogging) Log.Information("[MEDIA] Command received: Play");
- if (State == MediaPlayerState.Playing) {
+ if (State == MediaPlayerState.Playing)
+ {
if (Variables.ExtendedLogging) Log.Warning("[MEDIA] Media already playing");
break;
}
@@ -340,7 +343,8 @@ internal static void ProcessCommand(MediaPlayerCommand command)
case MediaPlayerCommand.Pause:
if (Variables.ExtendedLogging) Log.Information("[MEDIA] Command received: Pause");
- if (State == MediaPlayerState.Paused) {
+ if (State == MediaPlayerState.Paused)
+ {
if (Variables.ExtendedLogging) Log.Warning("[MEDIA] Media already paused");
break;
}
From c63e485edb24e3765d0acec4dc9849756ba0f733 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sun, 16 Jun 2024 23:40:52 +0200
Subject: [PATCH 32/45] Feature: separate installer for client and satellite
(#103)
This PR separates the installer into two different ones (embedded) that allows for non-privileged user installation.
---
.github/workflows/build.yml | 7 +-
.../AfterInstallNotice-Service.rtf | 222 ++++++++++++++++++
.../BeforeInstallNotice-Service.rtf | 218 +++++++++++++++++
.../InstallerScript-Service.iss | 82 +++++++
src/HASS.Agent.Installer/InstallerScript.iss | 54 ++---
.../HASS.Agent.Satellite.Service.csproj | 2 +-
.../HASS.Agent.Shared.csproj | 2 +-
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 2 +-
8 files changed, 555 insertions(+), 34 deletions(-)
create mode 100644 src/HASS.Agent.Installer/AfterInstallNotice-Service.rtf
create mode 100644 src/HASS.Agent.Installer/BeforeInstallNotice-Service.rtf
create mode 100644 src/HASS.Agent.Installer/InstallerScript-Service.iss
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 329c2c9c..53104d3b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -120,7 +120,12 @@ jobs:
working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent\\HASS.Agent.Satellite.Service"
run: "dotnet publish -c Release -f net6.0-windows10.0.19041.0 -o bin\\Publish-x64\\Release\\ --no-self-contained -r win-x64 -p:Platform=x64"
- - name: Compile InnoSetup Installer
+ - name: Compile InnoSetup Installer - Satellite Service
+ working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer"
+ run: |
+ & "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript-Service.iss
+
+ - name: Compile InnoSetup Installer - Client
working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer"
run: |
& "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript.iss
diff --git a/src/HASS.Agent.Installer/AfterInstallNotice-Service.rtf b/src/HASS.Agent.Installer/AfterInstallNotice-Service.rtf
new file mode 100644
index 00000000..e0517548
--- /dev/null
+++ b/src/HASS.Agent.Installer/AfterInstallNotice-Service.rtf
@@ -0,0 +1,222 @@
+{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang2057\deflangfe2057\themelang1045\themelangfe0\themelangcs1025{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 00000000000000000000}Tahoma;}
+{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \fswiss\fcharset238\fprq2{\*\panose 020b0604020202020204}Arial;}{\f46\fbidi \froman\fcharset0\fprq2 Times New Roman;}
+{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f386\fbidi \froman\fcharset0\fprq2 Cambria Math;}
+{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
+{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f476\fbidi \fswiss\fcharset0\fprq2 Tahoma;}{\f475\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f477\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}
+{\f478\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;}{\f479\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f480\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f481\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}
+{\f482\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f483\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;}
+{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}
+{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}
+{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;}
+{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
+{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
+{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}
+{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}
+{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}
+{\fbiminor\f31580\fbidi \fswiss\fcharset0\fprq2 Arial;}{\fbiminor\f31579\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\fbiminor\f31581\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\fbiminor\f31582\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}
+{\fbiminor\f31583\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\fbiminor\f31584\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\fbiminor\f31585\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}
+{\fbiminor\f31586\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
+\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;
+\chyperlink\ctint255\cshade255\red5\green99\blue193;\red96\green94\blue92;\red225\green223\blue221;}{\*\defchp \f31506\fs22\lang2057\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025
+\ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
+\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
+\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid11156637 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21 \sbasedon10 \ssemihidden \sunhideused \styrsid11156637 Unresolved Mention;}{
+\s17\ql \li720\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\contextualspace \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033
+\sbasedon0 \snext17 \sqformat \spriority34 \styrsid5989241 List Paragraph;}}{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
+\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
+\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
+\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid11156637 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21 \sbasedon10 \ssemihidden \sunhideused \styrsid11156637 Unresolved Mention;}{
+\s17\ql \li720\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\contextualspace \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033
+\sbasedon0 \snext17 \sqformat \spriority34 \styrsid5989241 List Paragraph;}}}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid8367\rsid4147124\rsid5989241\rsid7820185\rsid7821067\rsid8347995\rsid8915765\rsid9249300\rsid11156637\rsid11431173
+\rsid12408085\rsid15815836\rsid15995208\rsid16254539\rsid16263522\rsid16538847}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Amadeo}{\operator Amadeo}
+{\creatim\yr2023\mo11\dy27\hr16\min48}{\revtim\yr2024\mo5\dy5\hr20\min11}{\version13}{\edmins110}{\nofpages1}{\nofwords69}{\nofchars394}{\nofcharsws462}{\vern81}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}
+\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect
+\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1
+\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1
+\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
+\asianbrkrule\rsidroot16254539\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0
+{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}
+{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}
+{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9
+\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid16254539 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
+\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid11431173 \line }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16254539\charrsid8347995 Thank you for choosing HASS.Agent!
+\par We hope you'll have a seamless and enjoyable experience with our application. If you ever need assistance or have feedback, feel free to reach out to the HASS.Agent team on}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid5989241\charrsid8347995 }
+{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid8367 HYPERLINK "https://discord.com/invite/JfZj98xqJr"}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid8367\charrsid8347995 {\*\datafield
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b64000000680074007400700073003a002f002f0064006900730063006f00720064002e0063006f006d002f0069006e0076006900740065002f004a0066005a006a0039003800780071004a0072000000795881f43b1d
+7f48af2c825dc485276300000000a5ab000345}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid5989241\charrsid8347995 Discord}}}\sectd \ltrsect
+\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid5989241\charrsid8347995 .
+\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16254539\charrsid8347995 Make sure to read through the documentation }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid7820185 HYPERLINK "https://www.hass-agent.io/latest/"}{
+\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid7820185\charrsid8347995 {\*\datafield
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5c000000680074007400700073003a002f002f007700770077002e0068006100730073002d006100670065006e0074002e0069006f002f006c00610074006500730074002f000000795881f43b1d7f48af2c825dc485
+276300000000a5ab0003000000}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid16254539\charrsid8347995 here}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {
+\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16254539\charrsid8347995 to get started configuring HASS.Agent}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16538847 Satellite Service}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16254539
+\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid8347995\charrsid8347995
+\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16254539\charrsid8347995 Happy exploring!
+\par The HASS.Agent Team}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid12408085\charrsid8347995
+\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
+9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
+5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
+b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
+0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
+a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
+c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
+0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
+a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
+6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
+4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
+4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210041daf6e5a6070000e6200000160000007468656d652f7468656d652f
+7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f222b524cfda33f660c90e7bac914add65557789aed28cc56208de532e81c026e49085
+bded21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c9f3810326cc0886eed2efbdfad57bafde7baabaffd9ab983a1738e584253db77eafe63a3899b13949
+c29efb7c3aae745c870b94cc116509eeb91bccddcf1efcfa57f7d19188708c1d904ff811eab99110aba36a95cf6018f17b6c8513f86ec1d21809784dc3ea3c45
+97a037a6d546add6aac68824ae93a018d49e32b1b974566849b0583b4f170b32c3ee83ed24230a332582cb81194d27720a9c4b9e29195d64beac4b20dff080a6
+ce05a23d17a69db3cb297e255c87222ee08b9e5b537f6ef5c1fd2a3aca85a83820abc98dd55f2e970bcc970d35671a9e17937a9eefb5fa857e05a0621f376a8f
+5aa356a14f01d06c060bceb8983adb8dc0cbb11a287bb4e81eb687cdba81d7f437f738f77df931f00a94e9f7f6f0e371005634f00a94e1fd3dbc3fe80e86a67e
+05caf0ad3d7cbbd61f7a6d43bf02459424cb3d74cd6f3583ed6a0bc882d1632bbceb7be37623575ea2201a8a2093532c5822ae08b918bd64e91870124f912089
+23362bbc403388ed0051729e12e7848411c4df0a258cc370ad511bd79af05f7e3cf5a41c8b8e30d2a4253d20c4f786242d87cf52b2123df711687535c8bb9f7f
+7efbe6c7b76f7e7afbd5576fdffc3d9f5ba932e48e5112ea72bf7cf787ff7cf35be7df3f7cfbcbd77fcca6dec5731dfffe6fbf7bff8f7f7e483dacb834c5bb3f
+7dfffec7efdffdf9f7fffaebd716edfd149debf0298931779ee04be7198b618116fef83cbd99c434424497e82721470992b358f48f4464a09f6c104516dc009b
+767c9142c6b1011fae5f1a842751ba16c4a2f171141bc053c6e880a5562b3c967369669eae93d03e79bad671cf10bab0cd1da0c4f0f268bd828c4b6c2a83081b
+34cf284a040a71828523bf634b8c2dabfb8210c3aea7649632ce16c2f982380344ac26999273239a4aa16312835f363682e06fc336a72f9c01a3b6550ff18589
+84bd81a885fc1453c38c0fd15aa0d8a6728a62aa1bfc0489c84672b249673a6ec405783ac49439a339e6dc26f33485f56a4e7f0c69c6eef653ba894d642ac8d2
+a6f30431a623876c1944285ed9b01392443af673be841045ce191336f8293377887c073fa0e4a0bb5f402fa04f707536780e1956972803447eb34e2dbe7c8899
+11bf930d5d206c4b35fd3436526c3f25d6e818ac4323b44f30a6e812cd31769e7f6e6130602bc3e625e947116495636c0bac47c88c55f99e608e1dd5e3ece7c9
+13c28d909de0901de073bad9493c1b94c4283da4f909785db7f9084a5d6c0b80a774b6d4814f087486102f56a33ce5a0430bee835acf22641430f9ceedf1ba49
+0dff5d678fc1be7c69d0b8c6be04197c631948ecbacc076d3345d498a00c9829822ec3966e41c4707f29228bab125b5be516e6a62ddd004d92d1f4c424b9b203
+dae97dfcff5def031dc6bbbf7c63d96c1fa7dfb12b3692d50d3b9d43c9e478a7bf3984dbed6a0296cec9a7dfd40cd13a39c35047f633d65d4f73d7d3b8fff73d
+cda1fd7cd7c91cea37ee3a19173a8cbb4e263f5cf9389d4cd9bc405f230f3cb2f31e75fa135f75f8b320944ec486e213aece7f38fcac998f61508aabe3505c9c
+09ae227894d50ee63170618a948c9332f11b22a249845670485477a59290e7aa43eeac1887b323356cd52df1741d9fb27976f459afcb63ceacc07224caf19a5f
+8cc37995c8d0ad76799c57a8576c4375faba2520656f42429bcc24d1b490686f07a591d4592f18cd4242adeca3b0e85a5874a4faadabf65800b5c22bf0bbdb81
+5feb3dd7f7400484e0580e7af4b9f453e6eaad7795333fa6a70f19d38800e8b3b711507aba2bb91e5c9e5c5d166ad7f0b441420b379384b28ceaf37804bf86f3
+e894a3d7a171535f774b971af4a429d47c105a258d76e7432c6eeb6b90dbcd0d34d133054d9ccb9edb6afa103233b4eab90b383b86c77805b1c3e54f2f4443b8
+96998934dbf0b7c92cab948b21e25166709574b26c101381538792b8e7cae5176ea089ca218a5bbd0109e19325d785b4f2a99103a79b4ec68b059e09ddedda88
+b474f60a193ecb15d66f95f8edc15292adc1dd93687ee99cd375fa0c4188f9edba34e09c70b842a867d69c13b81a2b1259197f3b85294fbbfadd948aa16c1cd1
+5584f28aa227f30cae52794147bd1536d0def23583413593e485f03c94055637aa514d8baa9171385875af169296d3926659338dac22aba63d8b19336ccbc08e
+2d6f57e435565b13434ed32b7c96ba77536e779beb76fa84a24a80c10bfb59aaee350a8246ad9ccca02619efa76199b3f351b3766c177805b5eb14092debb7b6
+6a77ec56d408eb743078abca0f72bb510b438b6d7ba92cadaed4f5db6e76fe1292c7109add35155cb9122eb153040dd144f52459da802df24ae45b039e9c754a
+7aee9735bfef050d3fa8d43afea8e235bd5aa5e3f79b95beef37eb23bf5e1b0e1aafa1b08828aefbd975fe18ee31e826bfd457e37b17fbf1f6aae6de8cc555a6
+aeeaab8ab8bad8af378c8bfdec2adf99ca1b7bd7219074be6c35c6dd6677d0aa749bfd71c51b0e3a956ed01a5486ada03d1c0f03bfd31dbf769d0b05f6facdc0
+6b8d3a95563d082a5eab26e977ba95b6d768f4bd76bf33f2faaff33606569ea58fdc16605ec5ebc17f010000ffff0300504b0304140006000800000021000dd1
+909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277
+086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b
+64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996
+509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff
+0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7
+c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000
+001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210041
+daf6e5a6070000e62000001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600080000
+0021000dd1909fb60000001b0100002700000000000000000000000000b00a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ab0b00000000}
+{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
+617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
+6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
+656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
+{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
+\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;
+\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
+\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
+\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
+\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
+\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
+\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
+\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
+\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
+\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
+\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
+\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
+\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
+\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
+\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
+\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
+\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
+\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
+\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
+\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
+\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
+\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
+\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
+\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
+\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
+\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
+\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
+\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
+\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
+\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
+\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000
+4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000
+d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e50000000000000000000000000098
+809f179fda01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000105000000000000}}
\ No newline at end of file
diff --git a/src/HASS.Agent.Installer/BeforeInstallNotice-Service.rtf b/src/HASS.Agent.Installer/BeforeInstallNotice-Service.rtf
new file mode 100644
index 00000000..b9ab19df
--- /dev/null
+++ b/src/HASS.Agent.Installer/BeforeInstallNotice-Service.rtf
@@ -0,0 +1,218 @@
+{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang2057\deflangfe2057\themelang1045\themelangfe0\themelangcs1025{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 020b0604030504040204}Tahoma;}
+{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
+{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \fswiss\fcharset238\fprq2{\*\panose 020b0604020202020204}Arial;}{\f46\fbidi \froman\fcharset0\fprq2 Times New Roman;}
+{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f386\fbidi \froman\fcharset0\fprq2 Cambria Math;}
+{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}
+{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f476\fbidi \fswiss\fcharset0\fprq2 Tahoma;}{\f475\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f477\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}
+{\f478\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;}{\f479\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f480\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f481\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}
+{\f482\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f483\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;}
+{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}
+{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}
+{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
+{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
+{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
+{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
+{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
+{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;}
+{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
+{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
+{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}
+{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}
+{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}
+{\fbiminor\f31580\fbidi \fswiss\fcharset0\fprq2 Arial;}{\fbiminor\f31579\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\fbiminor\f31581\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\fbiminor\f31582\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}
+{\fbiminor\f31583\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\fbiminor\f31584\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\fbiminor\f31585\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}
+{\fbiminor\f31586\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
+\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;
+\chyperlink\ctint255\cshade255\red5\green99\blue193;\red96\green94\blue92;\red225\green223\blue221;}{\*\defchp \f31506\fs22\lang2057\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025
+\ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
+\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
+\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid490208 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21 \sbasedon10 \ssemihidden \sunhideused \styrsid490208 Unresolved Mention;}}
+{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033
+\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
+\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
+\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
+\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid490208 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21 \sbasedon10 \ssemihidden \sunhideused \styrsid490208 Unresolved Mention;}}}}
+{\*\rsidtbl \rsid227266\rsid490208\rsid1248923\rsid2324250\rsid3874449\rsid4072052\rsid5209844\rsid6709929\rsid7356386\rsid7820567\rsid12408085\rsid16263522}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1
+\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Amadeo}{\operator Amadeo}{\creatim\yr2023\mo11\dy27\hr16\min53}{\revtim\yr2024\mo5\dy5\hr20\min10}{\version9}{\edmins14}{\nofpages1}{\nofwords76}{\nofchars435}{\nofcharsws510}{\vern81}}
+{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect
+\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1
+\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1
+\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
+\asianbrkrule\rsidroot490208\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0
+{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}
+{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}
+{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9
+\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid490208 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
+\f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid227266 \line }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 Welcome to HASS.Agent}{\rtlch\fcs1 \af43 \ltrch\fcs0
+\f43\fs16\insrsid1248923 Satellite Service}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 Installer!}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208
+\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 This version is maintained by the }{\field{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid2324250 HYPERLINK "https://github.com/hass-agent/" }{\rtlch\fcs1 \af43
+\ltrch\fcs0 \f43\fs16\insrsid2324250 {\*\datafield
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b56000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f0068006100730073002d006100670065006e0074002f000000795881f43b1d7f48af2c825dc485276300000000
+a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid2324250 HASS.Agent Team}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\rtlch\fcs1
+\af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 and is a fork of the }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 HYPERLINK "https://github.com/LAB02-Research/HASS.Agent" }{\rtlch\fcs1
+\af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 {\*\datafield
+00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b72000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f004c0041004200300032002d00520065007300650061007200630068002f0048004100530053002e0041006700
+65006e0074000000795881f43b1d7f48af2c825dc485276300000000a5ab00030064d96621af0000}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid4072052 original}}}\sectd \ltrsect
+\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 version by LAB02-Research.
+\par The intention of this version is to provide bug-fix and feature updated until the development of original version is resumed.
+\par We've tried our best to maintain compatibility with the original version but some breaking changes are unavoidable.}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid12408085\charrsid4072052
+\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
+9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
+5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
+b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
+0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
+a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
+c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
+0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
+a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
+6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
+4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
+4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210041daf6e5a6070000e6200000160000007468656d652f7468656d652f
+7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f222b524cfda33f660c90e7bac914add65557789aed28cc56208de532e81c026e49085
+bded21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c9f3810326cc0886eed2efbdfad57bafde7baabaffd9ab983a1738e584253db77eafe63a3899b13949
+c29efb7c3aae745c870b94cc116509eeb91bccddcf1efcfa57f7d19188708c1d904ff811eab99110aba36a95cf6018f17b6c8513f86ec1d21809784dc3ea3c45
+97a037a6d546add6aac68824ae93a018d49e32b1b974566849b0583b4f170b32c3ee83ed24230a332582cb81194d27720a9c4b9e29195d64beac4b20dff080a6
+ce05a23d17a69db3cb297e255c87222ee08b9e5b537f6ef5c1fd2a3aca85a83820abc98dd55f2e970bcc970d35671a9e17937a9eefb5fa857e05a0621f376a8f
+5aa356a14f01d06c060bceb8983adb8dc0cbb11a287bb4e81eb687cdba81d7f437f738f77df931f00a94e9f7f6f0e371005634f00a94e1fd3dbc3fe80e86a67e
+05caf0ad3d7cbbd61f7a6d43bf02459424cb3d74cd6f3583ed6a0bc882d1632bbceb7be37623575ea2201a8a2093532c5822ae08b918bd64e91870124f912089
+23362bbc403388ed0051729e12e7848411c4df0a258cc370ad511bd79af05f7e3cf5a41c8b8e30d2a4253d20c4f786242d87cf52b2123df711687535c8bb9f7f
+7efbe6c7b76f7e7afbd5576fdffc3d9f5ba932e48e5112ea72bf7cf787ff7cf35be7df3f7cfbcbd77fcca6dec5731dfffe6fbf7bff8f7f7e483dacb834c5bb3f
+7dfffec7efdffdf9f7fffaebd716edfd149debf0298931779ee04be7198b618116fef83cbd99c434424497e82721470992b358f48f4464a09f6c104516dc009b
+767c9142c6b1011fae5f1a842751ba16c4a2f171141bc053c6e880a5562b3c967369669eae93d03e79bad671cf10bab0cd1da0c4f0f268bd828c4b6c2a83081b
+34cf284a040a71828523bf634b8c2dabfb8210c3aea7649632ce16c2f982380344ac26999273239a4aa16312835f363682e06fc336a72f9c01a3b6550ff18589
+84bd81a885fc1453c38c0fd15aa0d8a6728a62aa1bfc0489c84672b249673a6ec405783ac49439a339e6dc26f33485f56a4e7f0c69c6eef653ba894d642ac8d2
+a6f30431a623876c1944285ed9b01392443af673be841045ce191336f8293377887c073fa0e4a0bb5f402fa04f707536780e1956972803447eb34e2dbe7c8899
+11bf930d5d206c4b35fd3436526c3f25d6e818ac4323b44f30a6e812cd31769e7f6e6130602bc3e625e947116495636c0bac47c88c55f99e608e1dd5e3ece7c9
+13c28d909de0901de073bad9493c1b94c4283da4f909785db7f9084a5d6c0b80a774b6d4814f087486102f56a33ce5a0430bee835acf22641430f9ceedf1ba49
+0dff5d678fc1be7c69d0b8c6be04197c631948ecbacc076d3345d498a00c9829822ec3966e41c4707f29228bab125b5be516e6a62ddd004d92d1f4c424b9b203
+dae97dfcff5def031dc6bbbf7c63d96c1fa7dfb12b3692d50d3b9d43c9e478a7bf3984dbed6a0296cec9a7dfd40cd13a39c35047f633d65d4f73d7d3b8fff73d
+cda1fd7cd7c91cea37ee3a19173a8cbb4e263f5cf9389d4cd9bc405f230f3cb2f31e75fa135f75f8b320944ec486e213aece7f38fcac998f61508aabe3505c9c
+09ae227894d50ee63170618a948c9332f11b22a249845670485477a59290e7aa43eeac1887b323356cd52df1741d9fb27976f459afcb63ceacc07224caf19a5f
+8cc37995c8d0ad76799c57a8576c4375faba2520656f42429bcc24d1b490686f07a591d4592f18cd4242adeca3b0e85a5874a4faadabf65800b5c22bf0bbdb81
+5feb3dd7f7400484e0580e7af4b9f453e6eaad7795333fa6a70f19d38800e8b3b711507aba2bb91e5c9e5c5d166ad7f0b441420b379384b28ceaf37804bf86f3
+e894a3d7a171535f774b971af4a429d47c105a258d76e7432c6eeb6b90dbcd0d34d133054d9ccb9edb6afa103233b4eab90b383b86c77805b1c3e54f2f4443b8
+96998934dbf0b7c92cab948b21e25166709574b26c101381538792b8e7cae5176ea089ca218a5bbd0109e19325d785b4f2a99103a79b4ec68b059e09ddedda88
+b474f60a193ecb15d66f95f8edc15292adc1dd93687ee99cd375fa0c4188f9edba34e09c70b842a867d69c13b81a2b1259197f3b85294fbbfadd948aa16c1cd1
+5584f28aa227f30cae52794147bd1536d0def23583413593e485f03c94055637aa514d8baa9171385875af169296d3926659338dac22aba63d8b19336ccbc08e
+2d6f57e435565b13434ed32b7c96ba77536e779beb76fa84a24a80c10bfb59aaee350a8246ad9ccca02619efa76199b3f351b3766c177805b5eb14092debb7b6
+6a77ec56d408eb743078abca0f72bb510b438b6d7ba92cadaed4f5db6e76fe1292c7109add35155cb9122eb153040dd144f52459da802df24ae45b039e9c754a
+7aee9735bfef050d3fa8d43afea8e235bd5aa5e3f79b95beef37eb23bf5e1b0e1aafa1b08828aefbd975fe18ee31e826bfd457e37b17fbf1f6aae6de8cc555a6
+aeeaab8ab8bad8af378c8bfdec2adf99ca1b7bd7219074be6c35c6dd6677d0aa749bfd71c51b0e3a956ed01a5486ada03d1c0f03bfd31dbf769d0b05f6facdc0
+6b8d3a95563d082a5eab26e977ba95b6d768f4bd76bf33f2faaff33606569ea58fdc16605ec5ebc17f010000ffff0300504b0304140006000800000021000dd1
+909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277
+086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b
+64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996
+509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff
+0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7
+c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000
+001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210041
+daf6e5a6070000e62000001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600080000
+0021000dd1909fb60000001b0100002700000000000000000000000000b00a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ab0b00000000}
+{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
+617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
+6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
+656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
+{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
+\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
+\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;
+\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
+\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
+\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
+\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
+\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
+\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
+\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
+\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
+\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
+\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
+\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
+\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
+\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
+\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
+\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
+\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
+\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
+\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
+\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
+\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
+\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
+\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
+\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
+\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
+\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
+\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
+\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
+\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
+\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
+\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
+\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
+\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
+\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
+\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
+\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000
+4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000
+d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000803c
+d68e179fda01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000105000000000000}}
\ No newline at end of file
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
new file mode 100644
index 00000000..694e19ca
--- /dev/null
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -0,0 +1,82 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+; Modified to suit HASS.Agent requirements :)
+
+; InnoDependencyInstaller
+; Thanks to https://github.com/DomGries/InnoDependencyInstaller for the amazing work!
+#define public Dependency_Path_NetCoreCheck "dependencies\"
+#include "CodeDependencies.iss"
+
+; Standard installation constants
+#define MyAppName "HASS.Agent Satellite Service"
+#define MyAppVersion "2.1.0-beta2"
+#define MyAppPublisher "HASS.Agent Team"
+#define MyAppURL "https://hass-agent.io"
+#define MyAppExeName "HASS.Agent.Satellite.Service.exe"
+#define ServiceName "hass.agent.svc"
+#define ServiceDisplayName "HASS.Agent - Satellite Service"
+#define ServiceDescription "Satellite service for HASS.Agent: a Windows based Home Assistant client. This service processes commands and sensors without the requirement of a logged-in user."
+
+[Setup]
+ArchitecturesInstallIn64BitMode=x64
+;SetupMutex=Global\HASS.Agent.Setup.Satellite.Mutex,HASS.Agent.Satellite.Setup.Mutex
+AppMutex=Global\\HASS.Agent.Service.Mutex
+AppId={{4004588E-F411-41C2-ABD8-A898B0A14B93}
+AppName={#MyAppName}
+AppVersion={#MyAppVersion}
+;AppVerName={#MyAppName} {#MyAppVersion}
+AppPublisher={#MyAppPublisher}
+AppPublisherURL={#MyAppURL}
+AppSupportURL={#MyAppURL}
+AppUpdatesURL={#MyAppURL}
+DefaultDirName={commonpf64}\{#MyAppName}\Service
+DisableProgramGroupPage=yes
+LicenseFile=..\..\LICENSE.md
+InfoBeforeFile=.\BeforeInstallNotice-Service.rtf
+InfoAfterFile=.\AfterInstallNotice-Service.rtf
+PrivilegesRequired=admin
+OutputDir=.\bin
+OutputBaseFilename=HASS.Agent.Service.Installer
+SetupIconFile=..\HASS.Agent\HASS.Agent.Shared\hassagent.ico
+Compression=lzma
+SolidCompression=yes
+WizardStyle=modern
+CloseApplications=force
+CloseApplicationsFilter=*.*
+UninstallDisplayIcon={app}\{#MyAppExeName}
+UninstallDisplayName={#MyAppName} {#MyAppVersion}
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+; NOTE: Don't use "Flags: ignoreversion" on any shared system files
+[Files]
+; Service files
+Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs
+
+[Run]
+Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser
+
+[Registry]
+Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{commonpf64}\{#MyAppName}\Service"; Flags: createvalueifdoesntexist uninsdeletevalue
+
+; Service registration and removal
+[Run]
+Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""{commonpf64}\{#MyAppName}\Service\{#MyAppExeName}"""; Flags: runhidden
+Filename: "{sys}\sc.exe"; Parameters: "failure {#ServiceName} reset= 86400 actions= restart/60000/restart/60000//1000"; Flags: runhidden
+Filename: "{sys}\sc.exe"; Parameters: "description {#ServiceName} ""{#ServiceDescription}"""; Flags: runhidden
+Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} DisplayName= ""{#ServiceDisplayName}"""; Flags: runhidden
+Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} start= auto"; Flags: runhidden
+[UninstallRun]
+Filename: "{sys}\sc.exe"; Parameters: "stop {#ServiceName}"; RunOnceId: StopService; Flags: runhidden
+Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay1; Flags:runhidden
+Filename: "{sys}\sc.exe"; Parameters: "delete {#ServiceName}" ; RunOnceId: DeleteService; Flags: runhidden
+Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay2; Flags:runhidden
+
+[Code]
+function InitializeSetup: Boolean;
+begin
+ Dependency_ForceX86 := False;
+ Dependency_AddDotNet60Desktop;
+ Result := True;
+end;
\ No newline at end of file
diff --git a/src/HASS.Agent.Installer/InstallerScript.iss b/src/HASS.Agent.Installer/InstallerScript.iss
index 8e7190f5..d1425a74 100644
--- a/src/HASS.Agent.Installer/InstallerScript.iss
+++ b/src/HASS.Agent.Installer/InstallerScript.iss
@@ -9,19 +9,15 @@
; Standard installation constants
#define MyAppName "HASS.Agent"
-#define MyAppVersion "2.1.0-beta1"
+#define MyAppVersion "2.1.0-beta2"
#define MyAppPublisher "HASS.Agent Team"
#define MyAppURL "https://hass-agent.io"
#define MyAppExeName "HASS.Agent.exe"
-#define MyAppServiceExeName "HASS.Agent.Satellite.Service.exe"
-#define ServiceName "hass.agent.svc"
-#define ServiceDisplayName "HASS.Agent - Satellite Service"
-#define ServiceDescription "Satellite service for HASS.Agent: a Windows based Home Assistant client. This service processes commands and sensors without the requirement of a logged-in user."
[Setup]
ArchitecturesInstallIn64BitMode=x64
SetupMutex=Global\HASS.Agent.Setup.Mutex,HASS.Agent.Setup.Mutex
-AppMutex=Global\HASS.Agent.App.Mutex,HASS.Agent.App.Mutex
+AppMutex=HASS.Agent.App.Mutex
AppId={{7BBED458-609B-4D13-AD9E-4FF219DF8644}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
@@ -36,7 +32,7 @@ LicenseFile=..\..\LICENSE.md
InfoBeforeFile=.\BeforeInstallNotice.rtf
InfoAfterFile=.\AfterInstallNotice.rtf
; Uncomment the following line to run in non administrative install mode (install for current user only.)
-;PrivilegesRequired=lowest
+PrivilegesRequired=lowest
OutputDir=.\bin
OutputBaseFilename=HASS.Agent.Installer
SetupIconFile=..\HASS.Agent\HASS.Agent.Shared\hassagent.ico
@@ -57,38 +53,19 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Files]
; Client files
-;Source: "..\HASS.Agent\HASS.Agent\bin\Publish-x64\Release\*"; Excludes: "*.pdb"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
-; Service files
-;Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; Excludes: "*.pdb"; DestDir: "{commonpf64}\{#MyAppName}\Service"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\HASS.Agent\HASS.Agent\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
-; Service files
-Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{commonpf64}\{#MyAppName}\Service"; Flags: ignoreversion recursesubdirs createallsubdirs
+; Service installer
+Source: ".\bin\HASS.Agent.Service.Installer.exe"; DestDir: "{tmp}"; Flags: ignoreversion
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
-Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration"; Flags: postinstall skipifsilent runascurrentuser unchecked
-Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser
+Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration (administrative permissions required)"; Flags: postinstall skipifsilent runascurrentuser unchecked
+Filename: "{tmp}\HASS.Agent.Service.Installer.exe"; Description: "Install Satellite Service (administrative permissions required)"; Flags: postinstall runascurrentuser
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall skipifsilent nowait
-[Registry]
-Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{commonpf64}\{#MyAppName}\Service"; Flags: createvalueifdoesntexist uninsdeletevalue
-
-; Service registration and removal
-[Run]
-Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""{commonpf64}\{#MyAppName}\Service\{#MyAppServiceExeName}"""; Flags: runhidden
-Filename: "{sys}\sc.exe"; Parameters: "failure {#ServiceName} reset= 86400 actions= restart/60000/restart/60000//1000"; Flags: runhidden
-Filename: "{sys}\sc.exe"; Parameters: "description {#ServiceName} ""{#ServiceDescription}"""; Flags: runhidden
-Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} DisplayName= ""{#ServiceDisplayName}"""; Flags: runhidden
-Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} start= auto"; Flags: runhidden
-[UninstallRun]
-Filename: "{sys}\sc.exe"; Parameters: "stop {#ServiceName}"; RunOnceId: StopService; Flags: runhidden
-Filename: "{sys}\sc.exe"; Parameters: "delete {#ServiceName}" ; RunOnceId: DeleteService; Flags: runhidden
-; Additional delay for the service to be uninstalled
-Filename: "{sys}\timeout.exe"; Parameters: "5"; Flags:runhidden
-
[Code]
function InitializeSetup: Boolean;
begin
@@ -118,3 +95,20 @@ begin
Result := True;
end
end;
+
+procedure CurUninstallStepChanged (CurUninstallStep: TUninstallStep);
+var
+ mres : integer;
+ serviceUninstallerPath : String;
+ ResultCode : integer;
+begin
+ case CurUninstallStep of
+ usPostUninstall:
+ begin
+ mres := MsgBox('Do you want to uninstall the Satellite Service? (administrative permissions required)', mbConfirmation, MB_YESNO or MB_DEFBUTTON2)
+ if mres = IDYES then
+ RegQueryStringValue(HKLM, 'SOFTWARE\HASSAgent\SatelliteService', 'InstallPath', serviceUninstallerPath);
+ Exec(serviceUninstallerPath + '\unins000.exe', '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
+ end;
+ end;
+end;
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index e9e2d70f..243ebf47 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -8,7 +8,7 @@
dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154
x64
x64;x86
- 2.1.0-beta1
+ 2.1.0-beta2
HASS.Agent Team
HASS.Agent Satellite Service
HASS.Agent.Satellite.Service
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index 6b665622..bd41bf7b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -12,7 +12,7 @@
https://github.com/hass-agent/HASS.Agent
2.1.0
2.1.0
- 2.1.0-beta1
+ 2.1.0-beta2
logo_128.png
True
hassagent.ico
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index 4d4b5c16..b4d922eb 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -12,7 +12,7 @@
x64
x64;x86
full
- 2.1.0-beta1
+ 2.1.0-beta2
HASS.Agent Team
HASS.Agent Team
Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more.
From 0bd19040d3808c535d4ef09a766b5bbc394e4a25 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 22 Jun 2024 14:35:30 +0200
Subject: [PATCH 33/45] Fix: advanced settings not being saved for satellite
sensors (#104)
---
.../Extensions/RpcExtensions.cs | 6 ++++--
.../RPC/Protos/hassagentsatellite.proto | 1 +
src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs | 6 ++++--
.../HASS.Agent/Service/Protos/hassagentsatellite.proto | 1 +
4 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
index 9275740a..53f3f88b 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
@@ -108,7 +108,8 @@ public static ConfiguredSensor ConvertToConfiguredSensor(this RpcConfiguredServe
Counter = rpcConfiguredSensor.Counter,
Instance = rpcConfiguredSensor.Instance,
EntityName = rpcConfiguredSensor.EntityName,
- Name = rpcConfiguredSensor.Name
+ Name = rpcConfiguredSensor.Name,
+ AdvancedSettings = rpcConfiguredSensor.AdvancedSettings,
};
return configuredSensor;
@@ -149,7 +150,8 @@ public static RpcConfiguredServerSensor ConvertToRpcConfiguredSensor(this Config
Counter = configuredSensor.Counter ?? string.Empty,
Instance = configuredSensor.Instance ?? string.Empty,
Name = configuredSensor.Name ?? string.Empty,
- EntityName = configuredSensor?.EntityName ?? string.Empty
+ EntityName = configuredSensor?.EntityName ?? string.Empty,
+ AdvancedSettings = configuredSensor?.AdvancedSettings ?? string.Empty,
};
return configuredRpcSensor;
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
index 169b6263..ad2d5d68 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
@@ -134,6 +134,7 @@ message RpcConfiguredServerSensor {
string instance = 9;
string name = 10;
string entityName = 11;
+ string advancedSettings = 12;
}
message RpcConfiguredServerCommand {
diff --git a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
index 0ca8550c..3a7acc82 100644
--- a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
@@ -109,7 +109,8 @@ public static ConfiguredSensor ConvertToConfiguredSensor(this RpcConfiguredServe
Counter = rpcConfiguredSensor.Counter,
Instance = rpcConfiguredSensor.Instance,
EntityName = rpcConfiguredSensor.EntityName,
- Name = rpcConfiguredSensor.Name
+ Name = rpcConfiguredSensor.Name,
+ AdvancedSettings = rpcConfiguredSensor.AdvancedSettings,
};
return configuredSensor;
@@ -150,7 +151,8 @@ public static RpcConfiguredServerSensor ConvertToRpcConfiguredSensor(this Config
Counter = configuredSensor.Counter ?? string.Empty,
Instance = configuredSensor.Instance ?? string.Empty,
Name = configuredSensor.Name ?? string.Empty,
- EntityName = configuredSensor.EntityName ?? string.Empty
+ EntityName = configuredSensor.EntityName ?? string.Empty,
+ AdvancedSettings = configuredSensor?.AdvancedSettings ?? string.Empty,
};
return configuredRpcSensor;
diff --git a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
index 169b6263..ad2d5d68 100644
--- a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
+++ b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
@@ -134,6 +134,7 @@ message RpcConfiguredServerSensor {
string instance = 9;
string name = 10;
string entityName = 11;
+ string advancedSettings = 12;
}
message RpcConfiguredServerCommand {
From be6a48c1b832c6a03a7452d8a9a443eea5222bc9 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 24 Jun 2024 19:57:16 +0200
Subject: [PATCH 34/45] fixed advanced settings not being saved properly (#105)
---
.../HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs | 3 ---
.../Models/HomeAssistant/AbstractSingleValueSensor.cs | 1 +
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
index 14bf596d..1b0b5b5b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs
@@ -19,8 +19,6 @@ public class WmiQuerySensor : AbstractSingleValueSensor
public bool ApplyRounding { get; private set; }
public int? Round { get; private set; }
- public string AdvancedSettings { get; private set; }
-
protected readonly ObjectQuery ObjectQuery;
protected readonly ManagementObjectSearcher Searcher;
@@ -30,7 +28,6 @@ public WmiQuerySensor(string query, string scope = "", bool applyRounding = fals
Scope = scope;
ApplyRounding = applyRounding;
Round = round;
- AdvancedSettings = advancedSettings;
// prepare query
ObjectQuery = new ObjectQuery(Query);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
index eb7ee64c..e3d3f267 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs
@@ -33,6 +33,7 @@ protected AbstractSingleValueSensor(string entityName, string name, int updateIn
if (!string.IsNullOrWhiteSpace(advancedSettings))
{
+ AdvancedSettings = advancedSettings;
_advancedInfo = JsonConvert.DeserializeObject(advancedSettings);
}
}
From abec36f28a4c5c20664b4b95f93016d77f73a450 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Mon, 24 Jun 2024 20:06:58 +0200
Subject: [PATCH 35/45] Fix: added ignoreversion parameter (#107)
---
src/HASS.Agent.Installer/InstallerScript-Service.iss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
index 694e19ca..eb685ee3 100644
--- a/src/HASS.Agent.Installer/InstallerScript-Service.iss
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -52,7 +52,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Files]
; Service files
-Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs
+Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Run]
Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser
From b577482178b8fc75f0ba0a1f5499c0da52009d8d Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Wed, 3 Jul 2024 21:39:37 +0200
Subject: [PATCH 36/45] Fix: satellite service installer not respecting user
selected path (#113)
---
src/HASS.Agent.Installer/InstallerScript-Service.iss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
index eb685ee3..f90e087d 100644
--- a/src/HASS.Agent.Installer/InstallerScript-Service.iss
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -58,11 +58,11 @@ Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*";
Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser
[Registry]
-Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{commonpf64}\{#MyAppName}\Service"; Flags: createvalueifdoesntexist uninsdeletevalue
+Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: createvalueifdoesntexist uninsdeletevalue
; Service registration and removal
[Run]
-Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""{commonpf64}\{#MyAppName}\Service\{#MyAppExeName}"""; Flags: runhidden
+Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""{app}\{#MyAppExeName}"""; Flags: runhidden
Filename: "{sys}\sc.exe"; Parameters: "failure {#ServiceName} reset= 86400 actions= restart/60000/restart/60000//1000"; Flags: runhidden
Filename: "{sys}\sc.exe"; Parameters: "description {#ServiceName} ""{#ServiceDescription}"""; Flags: runhidden
Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} DisplayName= ""{#ServiceDisplayName}"""; Flags: runhidden
From f30f24aa021189af9c65bf5e0ef4a64e871a5607 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 12 Jul 2024 23:14:56 +0200
Subject: [PATCH 37/45] Fix: audio manager com-thread safety (#122)
Devices are no longer cached but rather their IDs - required devices are instantiated as needed. This has a small performance hit but should resolve random issues with audio sensors and commands.
---
.../SetApplicationVolumeCommand.cs | 49 +--
.../InternalCommands/SetAudioInputCommand.cs | 17 +-
.../InternalCommands/SetAudioOutputCommand.cs | 17 +-
.../InternalCommands/SetVolumeCommand.cs | 18 +-
.../SingleValue/CurrentVolumeSensor.cs | 7 +-
.../Managers/Audio/AudioManager.cs | 322 ++++++++++++------
.../Audio/Internal/InternalAudioDevice.cs | 48 ---
.../HASS.Agent/Managers/SystemStateManager.cs | 4 -
.../HASS.Agent/Media/MediaManagerCommands.cs | 7 +-
.../HASS.Agent/Media/MediaManagerRequests.cs | 14 +-
10 files changed, 238 insertions(+), 265 deletions(-)
delete mode 100644 src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
index 0d0e1084..cdb39cdc 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs
@@ -34,16 +34,6 @@ public override void TurnOn()
TurnOnWithAction(CommandConfig);
}
- private AudioDevice GetDeviceOrDefault(string deviceName)
- {
- var device = AudioManager.GetDevices().Where(d => d.FriendlyName == deviceName).FirstOrDefault();
- if (device != null)
- return device;
-
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
- return AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- }
-
public override void TurnOnWithAction(string action)
{
State = "ON";
@@ -59,44 +49,7 @@ public override void TurnOnWithAction(string action)
return;
}
- var audioDevice = GetDeviceOrDefault(actionData.PlaybackDevice);
- if (audioDevice == null)
- return;
-
- var applicationAudioSessions = audioDevice.Sessions.Where(s =>
- s.Application == actionData.ApplicationName
- );
-
- if (actionData.Volume == -1)
- Log.Debug("[SETAPPVOLUME] No volume value provided, only mute has been set for {app}", actionData.ApplicationName);
-
-
- if (string.IsNullOrWhiteSpace(actionData.SessionId))
- {
- foreach (var session in applicationAudioSessions)
- {
- AudioManager.SetMute(session, actionData.Mute);
- if (actionData.Volume == -1)
- return;
-
- AudioManager.SetVolume(session, actionData.Volume);
- }
- }
- else
- {
- var session = applicationAudioSessions.Where(s => s.Id == actionData.SessionId).FirstOrDefault();
- if (session == null)
- {
- Log.Debug("[SETAPPVOLUME] No session {actionData.SessionId} found for device {device}", actionData.ApplicationName, audioDevice.FriendlyName);
- return;
- }
-
- AudioManager.SetMute(session, actionData.Mute);
- if (actionData.Volume == -1)
- return;
-
- AudioManager.SetVolume(session, actionData.Volume);
- }
+ AudioManager.SetApplicationProperties(actionData.PlaybackDevice, actionData.ApplicationName, actionData.SessionId, actionData.Volume, actionData.Mute);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
index 2d7de409..1b6b1a35 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs
@@ -42,22 +42,7 @@ public override void TurnOnWithAction(string action)
try
{
- var audioDevices = AudioManager.GetDevices();
- var inputDevice = audioDevices
- .Where(d => d.Type == DeviceType.Input)
- .Where(d => d.FriendlyName == action)
- .FirstOrDefault();
-
- if (inputDevice == null)
- {
- Log.Warning("[SETAUDIOIN] No input device {device} found", action);
- return;
- }
-
- if(inputDevice.Default)
- return;
-
- AudioManager.Activate(inputDevice);
+ AudioManager.ActivateDevice(action);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
index 1cefb69a..51769946 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs
@@ -42,22 +42,7 @@ public override void TurnOnWithAction(string action)
try
{
- var audioDevices = AudioManager.GetDevices();
- var outputDevice = audioDevices
- .Where(d => d.Type == DeviceType.Output)
- .Where(d => d.FriendlyName == action)
- .FirstOrDefault();
-
- if (outputDevice == null)
- {
- Log.Warning("[SETAUDIOOUT] No input device {device} found", action);
- return;
- }
-
- if (outputDevice.Default)
- return;
-
- AudioManager.Activate(outputDevice);
+ AudioManager.ActivateDevice(action);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
index 96ffda7e..dccacc8e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetVolumeCommand.cs
@@ -48,16 +48,11 @@ public override void TurnOn()
if (_volume == -1f)
{
Log.Warning("[SETVOLUME] [{name}] Unable to trigger command, it's configured as action-only", EntityName);
-
- return;
- }
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if(audioDevice == null)
return;
+ }
- AudioManager.SetVolume(audioDevice, _volume);
+ AudioManager.SetDefaultDeviceProperties(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console, _volume, null);
}
catch (Exception ex)
{
@@ -79,16 +74,11 @@ public override void TurnOnWithAction(string action)
if (!parsed)
{
Log.Error("[SETVOLUME] [{name}] Unable to trigger command, the provided action value can't be parsed: {val}", EntityName, action);
-
- return;
- }
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if (audioDevice == null)
return;
+ }
- AudioManager.SetVolume(audioDevice, volumeInt);
+ AudioManager.SetDefaultDeviceProperties(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console, _volume, null);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
index e1a03ce9..659db848 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs
@@ -39,13 +39,10 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
public override string GetState()
{
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if (audioDevice == null)
- return "0";
+ var volume = AudioManager.GetDefaultDeviceVolume(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
// return as percentage
- return audioDevice.Volume.ToString(CultureInfo.InvariantCulture);
+ return volume.ToString(CultureInfo.InvariantCulture);
}
public override string GetAttributes() => string.Empty;
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
index b6358d45..7f1be72b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs
@@ -10,6 +10,8 @@
using HASS.Agent.Shared.Managers.Audio.Internal;
using Serilog;
using NAudio.CoreAudioApi.Interfaces;
+using HidSharp;
+using Microsoft.VisualBasic.ApplicationServices;
namespace HASS.Agent.Shared.Managers.Audio;
public static class AudioManager
@@ -19,9 +21,7 @@ public static class AudioManager
private static MMDeviceEnumerator _enumerator = null;
private static MMNotificationClient _notificationClient = null;
- private static readonly ConcurrentDictionary _devices = new();
- private static readonly ConcurrentQueue _devicesToBeRemoved = new();
- private static readonly ConcurrentQueue _devicesToBeAdded = new();
+ private static readonly ConcurrentDictionary _devices = new();
private static readonly Dictionary _applicationNameCache = new();
@@ -30,7 +30,7 @@ private static void InitializeDevices()
_enumerator = new MMDeviceEnumerator();
foreach (var device in _enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active))
- _devices[device.ID] = new InternalAudioDevice(device);
+ _devices[device.ID] = device.FriendlyName;
_notificationClient = new MMNotificationClient();
_notificationClient.DeviceAdded += DeviceAdded;
@@ -41,21 +41,58 @@ private static void InitializeDevices()
_initialized = true;
}
+ private static void AddDevice(string deviceId)
+ {
+ if (_devices.ContainsKey(deviceId))
+ return;
+
+ using var device = _enumerator.GetDevice(deviceId);
+ _devices[deviceId] = device.FriendlyName;
+ Log.Debug($"[AUDIOMGR] added device: {_devices[deviceId]}");
+ }
+
+ private static void RemoveDevice(string deviceId)
+ {
+ _devices.Remove(deviceId, out var removedDeviceName);
+ if (!string.IsNullOrWhiteSpace(removedDeviceName))
+ Log.Debug($"[AUDIOMGR] removed device: {removedDeviceName}");
+ }
+
+ private static void DeviceRemoved(object sender, DeviceNotificationEventArgs e)
+ {
+ try
+ {
+ RemoveDevice(e.DeviceId);
+ }
+ catch
+ {
+ Log.Error($"[AUDIOMGR] failed to remove device: {e.DeviceId}");
+ }
+ }
+ private static void DeviceAdded(object sender, DeviceNotificationEventArgs e)
+ {
+ try
+ {
+ AddDevice(e.DeviceId);
+ }
+ catch
+ {
+ Log.Error($"[AUDIOMGR] failed to add device: {e.DeviceId}");
+ }
+ }
+
private static void DeviceStateChanged(object sender, DeviceStateChangedEventArgs e)
{
switch (e.DeviceState)
{
case DeviceState.Active:
- if (_devices.ContainsKey(e.DeviceId))
- return;
-
- _devicesToBeAdded.Enqueue(e.DeviceId);
+ AddDevice(e.DeviceId);
break;
case DeviceState.NotPresent:
case DeviceState.Unplugged:
case DeviceState.Disabled:
- _devicesToBeRemoved.Enqueue(e.DeviceId);
+ RemoveDevice(e.DeviceId);
break;
default:
@@ -63,40 +100,14 @@ private static void DeviceStateChanged(object sender, DeviceStateChangedEventArg
}
}
- private static void DeviceRemoved(object sender, DeviceNotificationEventArgs e)
- {
- _devicesToBeRemoved.Enqueue(e.DeviceId);
- }
-
- private static void DeviceAdded(object sender, DeviceNotificationEventArgs e)
- {
- if (_devices.ContainsKey(e.DeviceId))
- return;
-
- _devicesToBeAdded.Enqueue(e.DeviceId);
- }
-
private static bool CheckInitialization()
{
if (!_initialized)
{
Log.Warning("[AUDIOMGR] not yet initialized!");
-
return false;
}
- while (_devicesToBeRemoved.TryDequeue(out var deviceId))
- {
- if (_devices.Remove(deviceId, out var device))
- device.Dispose();
- }
-
- while (_devicesToBeAdded.TryDequeue(out var deviceId))
- {
- var device = _enumerator.GetDevice(deviceId);
- _devices[deviceId] = new InternalAudioDevice(device);
- }
-
return true;
}
@@ -116,12 +127,17 @@ private static string GetSessionDisplayName(InternalAudioSession session)
return process.ProcessName;
}
- private static List GetDeviceSessions(InternalAudioDevice internalAudioDevice)
+ private static List GetDeviceSessions(MMDevice mmDevice)
+ {
+ using var internalAudioSessionManager = new InternalAudioSessionManager(mmDevice.AudioSessionManager);
+ return GetDeviceSessions(_devices[mmDevice.ID], internalAudioSessionManager);
+ }
+
+ private static List GetDeviceSessions(string deviceName, InternalAudioSessionManager internalAudioSessionManager)
{
var audioSessions = new List();
- internalAudioDevice.Manager.RemoveDisconnectedSessions();
- foreach (var (sessionId, session) in internalAudioDevice.Manager.Sessions)
+ foreach (var (sessionId, session) in internalAudioSessionManager.Sessions)
{
try
{
@@ -136,7 +152,7 @@ private static List GetDeviceSessions(InternalAudioDevice internal
{
Id = sessionId,
Application = displayName,
- PlaybackDevice = internalAudioDevice.FriendlyName,
+ PlaybackDevice = deviceName,
Muted = session.Volume.Mute,
Active = session.Control.State == AudioSessionState.AudioSessionStateActive,
MasterVolume = Convert.ToInt32(session.Volume.Volume * 100),
@@ -147,7 +163,7 @@ private static List GetDeviceSessions(InternalAudioDevice internal
}
catch (Exception ex)
{
- throw new AudioSessionException($"error retrieving session information for {internalAudioDevice.FriendlyName}", ex);
+ throw new AudioSessionException($"error retrieving session information for {deviceName}", ex);
}
}
@@ -176,26 +192,11 @@ public static void Initialize()
Log.Information("[AUDIOMGR] initialized");
}
- public static void ReInitialize()
- {
- if (_initialized)
- CleanupDevices();
-
- Log.Debug("[AUDIOMGR] re-initializing");
-
- InitializeDevices();
-
- Log.Information("[AUDIOMGR] re-initialized");
- }
-
public static void CleanupDevices()
{
Log.Debug("[AUDIOMGR] starting cleanup");
_initialized = false;
- foreach (var device in _devices.Values)
- device.Dispose();
-
_notificationClient.DeviceAdded -= DeviceAdded;
_notificationClient.DeviceRemoved -= DeviceRemoved;
_notificationClient.DeviceStateChanged -= DeviceStateChanged;
@@ -222,23 +223,24 @@ public static List GetDevices()
var defaultInputDeviceId = defaultInputDevice.ID;
var defaultOutputDeviceId = defaultOutputDevice.ID;
- foreach (var device in _devices.Values.Where(d => d.MMDevice.State == DeviceState.Active))
+ foreach (var (deviceId, deviceName) in _devices)
{
+ using var device = _enumerator.GetDevice(deviceId);
var audioSessions = GetDeviceSessions(device);
var loudestSession = audioSessions.MaxBy(s => s.PeakVolume);
var audioDevice = new AudioDevice
{
- State = GetReadableState(device.MMDevice.State),
- Type = device.MMDevice.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output,
- Id = device.DeviceId,
- FriendlyName = device.FriendlyName,
+ State = GetReadableState(device.State),
+ Type = device.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output,
+ Id = device.ID,
+ FriendlyName = deviceName,
Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0)),
- Muted = device.MMDevice.AudioEndpointVolume.Mute,
+ Muted = device.AudioEndpointVolume.Mute,
PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume,
Sessions = audioSessions,
- Default = device.DeviceId == defaultInputDeviceId || device.DeviceId == defaultOutputDeviceId
+ Default = device.ID == defaultInputDeviceId || device.ID == defaultOutputDeviceId
};
audioDevices.Add(audioDevice);
@@ -246,7 +248,7 @@ public static List GetDevices()
}
catch (Exception ex)
{
- Log.Debug(ex, "[AUDIOMGR] Failed to retrieve devices: {msg}", ex.Message);
+ Log.Error(ex, "[AUDIOMGR] failed to retrieve devices: {msg}", ex.Message);
}
return audioDevices;
@@ -257,77 +259,205 @@ public static string GetDefaultDeviceId(DeviceType deviceType, DeviceRole device
if (!CheckInitialization())
return string.Empty;
- var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
- var role = (Role)deviceRole;
+ var deviceId = string.Empty;
+
+ try
+ {
+ var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
+ var role = (Role)deviceRole;
+
+ using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
- using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
+ deviceId = defaultDevice.ID;
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to retrieve default device id: {msg}", ex.Message);
+ }
- return defaultDevice.ID;
+ return deviceId;
}
- public static void Activate(AudioDevice audioDevice)
+ public static int GetDefaultDeviceVolume(DeviceType deviceType, DeviceRole deviceRole)
{
if (!CheckInitialization())
- return;
+ return 0;
- if (!_devices.TryGetValue(audioDevice.Id, out var device))
- return;
+ var volume = 0;
+
+ try
+ {
+ var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
+ var role = (Role)deviceRole;
- device.Activate();
+ using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
+
+ volume = Convert.ToInt32(Math.Round(defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0));
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to retrieve default device volume: {msg}", ex.Message);
+ }
+
+ return volume;
}
- public static void SetVolume(AudioDevice audioDevice, int volume)
+ public static bool GetDefaultDeviceMute(DeviceType deviceType, DeviceRole deviceRole)
{
if (!CheckInitialization())
- return;
+ return false;
- if (!_devices.TryGetValue(audioDevice.Id, out var device))
- return;
+ var mute = false;
- var volumeScalar = volume / 100f;
- device.AudioEndpointVolume.MasterVolumeLevelScalar = volumeScalar;
+ try
+ {
+ var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render;
+ var role = (Role)deviceRole;
+
+ using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role);
+
+ mute = defaultDevice.AudioEndpointVolume.Mute;
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to retrieve default device mute: {msg}", ex.Message);
+ }
+
+ return mute;
}
- public static void SetVolume(AudioSession audioSession, int volume)
+ public static void ActivateDevice(string deviceName)
{
if (!CheckInitialization())
return;
- var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
- if (device == null)
- return;
+ try
+ {
+ var deviceId = _devices.FirstOrDefault(v => v.Value == deviceName).Key;
+ if (string.IsNullOrWhiteSpace(deviceId))
+ return;
- if (!device.Manager.Sessions.TryGetValue(audioSession.Id, out var session))
- return;
+ using var configClient = new CPolicyConfigVistaClient();
+ configClient.SetDefaultDevice(deviceId);
- var volumeScalar = volume / 100f;
- session.Volume.Volume = volumeScalar;
+ Log.Debug($"[AUDIOMGR] device '{deviceName}' activated");
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to activate device '{deviceName}': {msg}", deviceName, ex.Message);
+ }
}
- public static void SetMute(AudioDevice audioDevice, bool mute)
+ public static void SetDeviceProperties(string deviceName, int volume, bool? mute)
{
if (!CheckInitialization())
return;
- if (!_devices.TryGetValue(audioDevice.Id, out var device))
- return;
+ try
+ {
+ var deviceId = _devices.FirstOrDefault(v => v.Value == deviceName).Key;
+ if (string.IsNullOrWhiteSpace(deviceId))
+ return;
+
+ using var device = _enumerator.GetDevice(deviceId);
+ SetDeviceProperties(device, volume, mute);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to set device properties '{deviceName}': {msg}", deviceName, ex.Message);
+ }
+ }
+
+ private static void SetDeviceProperties(MMDevice device, int volume, bool? mute)
+ {
+ if (mute != null)
+ {
+ device.AudioEndpointVolume.Mute = (bool)mute;
+ Log.Debug($"[AUDIOMGR] mute for '{_devices[device.ID]}' set to '{mute}'");
+ }
+
+ if (volume != -1)
+ {
+ var volumeScalar = volume / 100f;
+ device.AudioEndpointVolume.MasterVolumeLevelScalar = volumeScalar;
+ Log.Debug($"[AUDIOMGR] volume for '{_devices[device.ID]}' set to '{volume}'");
+ }
+ }
- device.AudioEndpointVolume.Mute = mute;
+ public static void SetDefaultDeviceProperties(DeviceType type, DeviceRole deviceRole, int volume, bool? mute)
+ {
+ try
+ {
+ var flow = type == DeviceType.Output ? DataFlow.Render : DataFlow.Capture;
+ var role = (Role)deviceRole;
+ using var device = _enumerator.GetDefaultAudioEndpoint(flow, role);
+ SetDeviceProperties(device, volume, mute);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to set default device properties: {msg}", ex.Message);
+ }
}
- public static void SetMute(AudioSession audioSession, bool mute)
+ public static void SetApplicationProperties(string deviceName, string applicationName, string sessionId, int volume, bool mute)
{
if (!CheckInitialization())
return;
- var device = _devices.Values.Where(d => d.FriendlyName == audioSession.PlaybackDevice).FirstOrDefault();
- if (device == null)
- return;
+ try
+ {
+ var deviceId = _devices.FirstOrDefault(v => v.Value == deviceName).Key;
+ if (string.IsNullOrWhiteSpace(deviceId))
+ return;
+
+ using var device = _enumerator.GetDevice(deviceId);
+ using var sessionManager = new InternalAudioSessionManager(device.AudioSessionManager);
+ var sessions = GetDeviceSessions(deviceName, sessionManager);
+
+ var applicationAudioSessions = sessions.Where(s =>
+ s.Application == applicationName
+ );
+
+ if (string.IsNullOrWhiteSpace(sessionId))
+ {
+ foreach (var session in applicationAudioSessions)
+ {
+ if (!sessionManager.Sessions.TryGetValue(session.Id, out var internalSession))
+ return;
+
+ SetSessionProperties(internalSession, volume, mute);
+ }
+ }
+ else
+ {
+ if (!sessionManager.Sessions.TryGetValue(sessionId, out var internalSession))
+ {
+ Log.Debug("[AUDIOMGR] no session '{sessionId}' found for device '{device}'", sessionId, deviceName);
+ return;
+ }
+
+ SetSessionProperties(internalSession, volume, mute);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "[AUDIOMGR] failed to set application properties '{appName}': {msg}", applicationName, ex.Message);
+ }
+ }
- if (!device.Manager.Sessions.TryGetValue(audioSession.Id, out var session))
+ private static void SetSessionProperties(InternalAudioSession internalAudioSession, int volume, bool mute)
+ {
+ var displayName = string.IsNullOrWhiteSpace(internalAudioSession.DisplayName) ? GetSessionDisplayName(internalAudioSession) : internalAudioSession.DisplayName;
+
+ internalAudioSession.Volume.Mute = mute;
+ Log.Debug("[AUDIOMGR] mute for '{sessionName}' ({sessionId}) set to '{mute}'", displayName, internalAudioSession.Control.GetSessionInstanceIdentifier, mute);
+
+ if (volume == -1)
return;
- session.Volume.Mute = mute;
+ var volumeScalar = volume / 100f;
+ internalAudioSession.Volume.Volume = volumeScalar;
+ Log.Debug("[AUDIOMGR] volume for '{sessionName}' ({sessionId}) set to '{vol}'", displayName, internalAudioSession.Control.GetSessionInstanceIdentifier, volume);
}
public static void Shutdown()
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
deleted file mode 100644
index b477c2d9..00000000
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using NAudio.CoreAudioApi;
-
-namespace HASS.Agent.Shared.Managers.Audio.Internal;
-internal class InternalAudioDevice : IDisposable
-{
- public MMDevice MMDevice { get; private set; }
- public AudioEndpointVolume AudioEndpointVolume { get; private set; }
- public InternalAudioSessionManager Manager { get; private set; }
-
- public string DeviceId { get; private set; }
- public string FriendlyName { get; private set; }
- public bool Reinitialized { get; private set; }
-
- public InternalAudioDevice(MMDevice device)
- {
- MMDevice = device;
- Manager = new InternalAudioSessionManager(device.AudioSessionManager);
- AudioEndpointVolume = device.AudioEndpointVolume;
-
- DeviceId = device.ID;
- FriendlyName = device.FriendlyName;
- }
-
- public void Activate()
- {
- using var configClient = new CPolicyConfigVistaClient();
- configClient.SetDefaultDevice(MMDevice.ID);
- }
-
- public void Dispose()
- {
- AudioEndpointVolume?.Dispose();
- Manager?.Dispose();
- MMDevice?.Dispose();
-
- GC.SuppressFinalize(this);
- }
-
- ~InternalAudioDevice()
- {
- Dispose();
- }
-}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
index a8794708..3354bda2 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs
@@ -202,8 +202,6 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang
Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync());
LastSystemStateEvent = SystemStateEvent.Resume;
- AudioManager.ReInitialize();
-
break;
case PowerModes.Suspend:
@@ -214,8 +212,6 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang
Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync(true));
LastSystemStateEvent = SystemStateEvent.Suspend;
- AudioManager.CleanupDevices();
-
break;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
index 47089ca0..11b6665e 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManagerCommands.cs
@@ -71,12 +71,7 @@ internal static void SetVolume(int volume)
if (volume < 0) volume = 0;
if (volume > 100) volume = 100;
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if (audioDevice == null)
- return;
-
- AudioManager.SetVolume(audioDevice, volume);
+ AudioManager.SetDefaultDeviceProperties(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console, volume, null);
}
catch (Exception ex)
{
diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
index bdf93fca..ceff8721 100644
--- a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
+++ b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs
@@ -19,12 +19,7 @@ internal static int GetVolume()
{
try
{
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if (audioDevice == null)
- return 0;
-
- return audioDevice.Volume;
+ return AudioManager.GetDefaultDeviceVolume(DeviceType.Output, DeviceRole.Multimedia);
}
catch (Exception ex)
{
@@ -41,12 +36,7 @@ internal static bool GetMuteState()
{
try
{
- var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia);
- var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault();
- if (audioDevice == null)
- return false;
-
- return audioDevice.Muted;
+ return AudioManager.GetDefaultDeviceMute(DeviceType.Output, DeviceRole.Multimedia);
}
catch (Exception ex)
{
From 1027b1f14b86a3ba6ff25335785b8725da43d3d1 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 12 Jul 2024 23:20:24 +0200
Subject: [PATCH 38/45] Fix: satellite service sensors/commands not being
properly saved (#123)
This PR fixes satellite sensors/commands:
not saving advanced settings
not saving round configuration
not saving "ignore availability" option
---
.../InstallerScript-Service.iss | 36 +-
.../Extensions/RpcExtensions.cs | 6 +
.../RPC/Protos/hassagentsatellite.proto | 3 +
.../Settings/StoredSensors.cs | 11 +
.../HASS.Agent/Extensions/RpcExtensions.cs | 6 +
.../Service/Protos/hassagentsatellite.proto | 3 +
.../HASS.Agent/Settings/StoredSensors.cs | 713 +++++++++---------
7 files changed, 421 insertions(+), 357 deletions(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
index f90e087d..fba2c845 100644
--- a/src/HASS.Agent.Installer/InstallerScript-Service.iss
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -79,4 +79,38 @@ begin
Dependency_ForceX86 := False;
Dependency_AddDotNet60Desktop;
Result := True;
-end;
\ No newline at end of file
+end;
+
+//procedure CurStepChanged(CurStep: TSetupStep);
+//var
+// ProgressPage: TOutputProgressWizardPage;
+// I, Step, Wait, ResultCode: Integer;
+//begin
+// if CurStep = ssInstall then
+// begin
+// //MsgBox(ExpandConstant('{sys}') + '\sc.exe', mbInformation, MB_OK);
+// //MsgBox('stop ' + ExpandConstant('{#ServiceName}'), mbInformation, MB_OK);
+// Exec(ExpandConstant('{sys}') + '\sc.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
+//
+// //thanks to https://stackoverflow.com/a/39827761
+// Wait := 5000;
+// Step := 100;
+// ProgressPage :=
+// CreateOutputProgressPage(
+// WizardForm.PageNameLabel.Caption,
+// WizardForm.PageDescriptionLabel.Caption);
+// ProgressPage.SetText('Making sure the satellite service is stopped...', '');
+// ProgressPage.SetProgress(0, Wait);
+// ProgressPage.Show;
+// try
+// for I := 0 to Wait div Step do
+// begin
+// ProgressPage.SetProgress(I * Step, Wait);
+// Sleep(Step);
+// end;
+// finally
+// ProgressPage.Hide;
+// ProgressPage.Free;
+// end;
+// end;
+//end;
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
index 53f3f88b..a57b6c07 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs
@@ -110,6 +110,9 @@ public static ConfiguredSensor ConvertToConfiguredSensor(this RpcConfiguredServe
EntityName = rpcConfiguredSensor.EntityName,
Name = rpcConfiguredSensor.Name,
AdvancedSettings = rpcConfiguredSensor.AdvancedSettings,
+ IgnoreAvailability = rpcConfiguredSensor.IgnoreAvailability,
+ ApplyRounding = rpcConfiguredSensor.ApplyRounding,
+ Round = rpcConfiguredSensor.RoundValue,
};
return configuredSensor;
@@ -152,6 +155,9 @@ public static RpcConfiguredServerSensor ConvertToRpcConfiguredSensor(this Config
Name = configuredSensor.Name ?? string.Empty,
EntityName = configuredSensor?.EntityName ?? string.Empty,
AdvancedSettings = configuredSensor?.AdvancedSettings ?? string.Empty,
+ IgnoreAvailability = configuredSensor?.IgnoreAvailability ?? false,
+ ApplyRounding = configuredSensor?.ApplyRounding ?? false,
+ RoundValue = configuredSensor?.Round ?? 0,
};
return configuredRpcSensor;
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
index ad2d5d68..a21c9157 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto
@@ -135,6 +135,9 @@ message RpcConfiguredServerSensor {
string name = 10;
string entityName = 11;
string advancedSettings = 12;
+ bool applyRounding = 13;
+ int32 roundValue = 14;
+ bool ignoreAvailability = 15;
}
message RpcConfiguredServerCommand {
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
index 5dfdb27a..6ff98745 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs
@@ -172,6 +172,9 @@ await Task.Run(delegate
break;
}
+ if (abstractSensor != null)
+ abstractSensor.IgnoreAvailability = sensor.IgnoreAvailability;
+
return abstractSensor;
}
@@ -231,6 +234,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = wmiSensor.Name,
Type = type,
UpdateInterval = wmiSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = wmiSensor.IgnoreAvailability,
Scope = wmiSensor.Scope,
Query = wmiSensor.Query,
ApplyRounding = wmiSensor.ApplyRounding,
@@ -249,6 +253,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = namedWindowSensor.EntityName,
Type = type,
UpdateInterval = namedWindowSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = namedWindowSensor.IgnoreAvailability,
WindowName = namedWindowSensor.WindowName,
AdvancedSettings = namedWindowSensor.AdvancedSettings
};
@@ -264,6 +269,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = performanceCounterSensor.EntityName,
Type = type,
UpdateInterval = performanceCounterSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = performanceCounterSensor.IgnoreAvailability,
Category = performanceCounterSensor.CategoryName,
Counter = performanceCounterSensor.CounterName,
Instance = performanceCounterSensor.InstanceName,
@@ -283,6 +289,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = processActiveSensor.EntityName,
Type = type,
UpdateInterval = processActiveSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = processActiveSensor.IgnoreAvailability,
Query = processActiveSensor.ProcessName,
AdvancedSettings = processActiveSensor.AdvancedSettings
};
@@ -298,6 +305,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = serviceStateSensor.EntityName,
Type = type,
UpdateInterval = serviceStateSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = serviceStateSensor.IgnoreAvailability,
Query = serviceStateSensor.ServiceName,
AdvancedSettings = serviceStateSensor.AdvancedSettings
};
@@ -313,6 +321,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = powershellSensor.EntityName,
Type = type,
UpdateInterval = powershellSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = powershellSensor.IgnoreAvailability,
Query = powershellSensor.Command,
ApplyRounding = powershellSensor.ApplyRounding,
Round = powershellSensor.Round,
@@ -330,6 +339,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = windowStateSensor.EntityName,
Type = type,
UpdateInterval = windowStateSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = windowStateSensor.IgnoreAvailability,
Query = windowStateSensor.ProcessName,
AdvancedSettings = windowStateSensor.AdvancedSettings
};
@@ -345,6 +355,7 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = sensor.EntityName,
Type = type,
UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability,
AdvancedSettings = sensor.AdvancedSettings
};
}
diff --git a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
index 3a7acc82..d36c4879 100644
--- a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs
@@ -111,6 +111,9 @@ public static ConfiguredSensor ConvertToConfiguredSensor(this RpcConfiguredServe
EntityName = rpcConfiguredSensor.EntityName,
Name = rpcConfiguredSensor.Name,
AdvancedSettings = rpcConfiguredSensor.AdvancedSettings,
+ IgnoreAvailability = rpcConfiguredSensor.IgnoreAvailability,
+ ApplyRounding = rpcConfiguredSensor.ApplyRounding,
+ Round = rpcConfiguredSensor.RoundValue,
};
return configuredSensor;
@@ -153,6 +156,9 @@ public static RpcConfiguredServerSensor ConvertToRpcConfiguredSensor(this Config
Name = configuredSensor.Name ?? string.Empty,
EntityName = configuredSensor.EntityName ?? string.Empty,
AdvancedSettings = configuredSensor?.AdvancedSettings ?? string.Empty,
+ IgnoreAvailability = configuredSensor?.IgnoreAvailability ?? false,
+ ApplyRounding = configuredSensor?.ApplyRounding ?? false,
+ RoundValue = configuredSensor?.Round ?? 0,
};
return configuredRpcSensor;
diff --git a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
index ad2d5d68..a21c9157 100644
--- a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
+++ b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto
@@ -135,6 +135,9 @@ message RpcConfiguredServerSensor {
string name = 10;
string entityName = 11;
string advancedSettings = 12;
+ bool applyRounding = 13;
+ int32 roundValue = 14;
+ bool ignoreAvailability = 15;
}
message RpcConfiguredServerCommand {
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
index bbcd34dc..57c52ed9 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs
@@ -21,71 +21,71 @@
namespace HASS.Agent.Settings
{
- ///
- /// Handles loading and storing sensors
- ///
- internal static class StoredSensors
- {
- ///
- /// Load all stored sensors
- ///
- ///
- internal static async Task LoadAsync()
- {
- try
- {
- // set empty lists
- Variables.SingleValueSensors = new List();
- Variables.MultiValueSensors = new List();
-
- // check for existing file
- if (!File.Exists(Variables.SensorsFile))
- {
- // none yet
- Log.Information("[SETTINGS_SENSORS] Config not found, no entities loaded");
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Stopped);
- return true;
- }
-
- // read the content
- var sensorsRaw = await File.ReadAllTextAsync(Variables.SensorsFile);
- if (string.IsNullOrWhiteSpace(sensorsRaw))
- {
- Log.Information("[SETTINGS_SENSORS] Config is empty, no entities loaded");
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Stopped);
- return true;
- }
-
- // deserialize
- var configuredSensors = JsonConvert.DeserializeObject>(sensorsRaw);
-
- // null-check
- if (configuredSensors == null)
- {
- Log.Error("[SETTINGS_SENSORS] Error loading entities: returned null object");
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Failed);
- return false;
- }
-
- // convert to abstract sensors
- await Task.Run(delegate
- {
- foreach (var sensor in configuredSensors)
- {
- if (sensor.IsSingleValue()) Variables.SingleValueSensors.Add(ConvertConfiguredToAbstractSingleValue(sensor));
- else Variables.MultiValueSensors.Add(ConvertConfiguredToAbstractMultiValue(sensor));
- }
- });
-
- // all good
- Log.Information("[SETTINGS_SENSORS] Loaded {count} entities", (Variables.SingleValueSensors.Count + Variables.MultiValueSensors.Count));
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Ok);
- return true;
- }
- catch (Exception ex)
- {
- Log.Fatal(ex, "[SETTINGS_SENSORS] Error loading entities: {err}", ex.Message);
- Variables.MainForm?.ShowMessageBox(string.Format(Languages.StoredSensors_Load_MessageBox1, ex.Message), true);
+ ///
+ /// Handles loading and storing sensors
+ ///
+ internal static class StoredSensors
+ {
+ ///
+ /// Load all stored sensors
+ ///
+ ///
+ internal static async Task LoadAsync()
+ {
+ try
+ {
+ // set empty lists
+ Variables.SingleValueSensors = new List();
+ Variables.MultiValueSensors = new List();
+
+ // check for existing file
+ if (!File.Exists(Variables.SensorsFile))
+ {
+ // none yet
+ Log.Information("[SETTINGS_SENSORS] Config not found, no entities loaded");
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Stopped);
+ return true;
+ }
+
+ // read the content
+ var sensorsRaw = await File.ReadAllTextAsync(Variables.SensorsFile);
+ if (string.IsNullOrWhiteSpace(sensorsRaw))
+ {
+ Log.Information("[SETTINGS_SENSORS] Config is empty, no entities loaded");
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Stopped);
+ return true;
+ }
+
+ // deserialize
+ var configuredSensors = JsonConvert.DeserializeObject>(sensorsRaw);
+
+ // null-check
+ if (configuredSensors == null)
+ {
+ Log.Error("[SETTINGS_SENSORS] Error loading entities: returned null object");
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Failed);
+ return false;
+ }
+
+ // convert to abstract sensors
+ await Task.Run(delegate
+ {
+ foreach (var sensor in configuredSensors)
+ {
+ if (sensor.IsSingleValue()) Variables.SingleValueSensors.Add(ConvertConfiguredToAbstractSingleValue(sensor));
+ else Variables.MultiValueSensors.Add(ConvertConfiguredToAbstractMultiValue(sensor));
+ }
+ });
+
+ // all good
+ Log.Information("[SETTINGS_SENSORS] Loaded {count} entities", (Variables.SingleValueSensors.Count + Variables.MultiValueSensors.Count));
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Ok);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "[SETTINGS_SENSORS] Error loading entities: {err}", ex.Message);
+ Variables.MainForm?.ShowMessageBox(string.Format(Languages.StoredSensors_Load_MessageBox1, ex.Message), true);
Variables.MainForm?.SetSensorsStatus(ComponentStatus.Failed);
return false;
@@ -127,10 +127,10 @@ internal static AbstractSingleValueSensor ConvertConfiguredToAbstractSingleValue
case SensorType.NamedWindowSensor:
abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
- case SensorType.LastActiveSensor:
- abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
- break;
- case SensorType.LastSystemStateChangeSensor:
+ case SensorType.LastActiveSensor:
+ abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
+ break;
+ case SensorType.LastSystemStateChangeSensor:
abstractSensor = new LastSystemStateChangeSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings);
break;
case SensorType.LastBootSensor:
@@ -207,47 +207,48 @@ internal static AbstractSingleValueSensor ConvertConfiguredToAbstractSingleValue
break;
}
- abstractSensor.IgnoreAvailability = sensor.IgnoreAvailability;
+ if (abstractSensor != null)
+ abstractSensor.IgnoreAvailability = sensor.IgnoreAvailability;
return abstractSensor;
}
- ///
- /// Convert a multi-value 'ConfiguredSensor' (local storage, UI) to an 'AbstractSensor' (MQTT)
- ///
- ///
- ///
- internal static AbstractMultiValueSensor ConvertConfiguredToAbstractMultiValue(ConfiguredSensor sensor)
- {
- AbstractMultiValueSensor abstractSensor = null;
-
- switch (sensor.Type)
- {
- case SensorType.StorageSensors:
- abstractSensor = new StorageSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- case SensorType.NetworkSensors:
- abstractSensor = new NetworkSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Query, sensor.Id.ToString());
- break;
- case SensorType.WindowsUpdatesSensors:
- abstractSensor = new WindowsUpdatesSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- case SensorType.BatterySensors:
- abstractSensor = new BatterySensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- case SensorType.DisplaySensors:
- abstractSensor = new DisplaySensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- case SensorType.AudioSensors:
- abstractSensor = new AudioSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- case SensorType.PrintersSensors:
- abstractSensor = new PrintersSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
- break;
- default:
- Log.Error("[SETTINGS_SENSORS] [{name}] Unknown configured multi-value sensor type: {type}", sensor.EntityName, sensor.Type.ToString());
- break;
- }
+ ///
+ /// Convert a multi-value 'ConfiguredSensor' (local storage, UI) to an 'AbstractSensor' (MQTT)
+ ///
+ ///
+ ///
+ internal static AbstractMultiValueSensor ConvertConfiguredToAbstractMultiValue(ConfiguredSensor sensor)
+ {
+ AbstractMultiValueSensor abstractSensor = null;
+
+ switch (sensor.Type)
+ {
+ case SensorType.StorageSensors:
+ abstractSensor = new StorageSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.NetworkSensors:
+ abstractSensor = new NetworkSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Query, sensor.Id.ToString());
+ break;
+ case SensorType.WindowsUpdatesSensors:
+ abstractSensor = new WindowsUpdatesSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.BatterySensors:
+ abstractSensor = new BatterySensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.DisplaySensors:
+ abstractSensor = new DisplaySensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.AudioSensors:
+ abstractSensor = new AudioSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ case SensorType.PrintersSensors:
+ abstractSensor = new PrintersSensors(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString());
+ break;
+ default:
+ Log.Error("[SETTINGS_SENSORS] [{name}] Unknown configured multi-value sensor type: {type}", sensor.EntityName, sensor.Type.ToString());
+ break;
+ }
abstractSensor.IgnoreAvailability = sensor.IgnoreAvailability;
@@ -264,142 +265,142 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
switch (sensor)
{
case WmiQuerySensor wmiSensor:
- {
- _ = Enum.TryParse(wmiSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(wmiSensor.Id),
- EntityName = wmiSensor.EntityName,
- Name = wmiSensor.Name,
- Type = type,
- UpdateInterval = wmiSensor.UpdateIntervalSeconds,
- IgnoreAvailability = wmiSensor.IgnoreAvailability,
- Scope = wmiSensor.Scope,
- Query = wmiSensor.Query,
- ApplyRounding = wmiSensor.ApplyRounding,
- Round= wmiSensor.Round,
- AdvancedSettings = wmiSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(wmiSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(wmiSensor.Id),
+ EntityName = wmiSensor.EntityName,
+ Name = wmiSensor.Name,
+ Type = type,
+ UpdateInterval = wmiSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = wmiSensor.IgnoreAvailability,
+ Scope = wmiSensor.Scope,
+ Query = wmiSensor.Query,
+ ApplyRounding = wmiSensor.ApplyRounding,
+ Round = wmiSensor.Round,
+ AdvancedSettings = wmiSensor.AdvancedSettings
+ };
+ }
case NamedWindowSensor namedWindowSensor:
- {
- _ = Enum.TryParse(namedWindowSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(namedWindowSensor.Id),
- EntityName = namedWindowSensor.EntityName,
- Name = namedWindowSensor.Name,
- Type = type,
- UpdateInterval = namedWindowSensor.UpdateIntervalSeconds,
- IgnoreAvailability = namedWindowSensor.IgnoreAvailability,
- WindowName = namedWindowSensor.WindowName,
- AdvancedSettings = namedWindowSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(namedWindowSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(namedWindowSensor.Id),
+ EntityName = namedWindowSensor.EntityName,
+ Name = namedWindowSensor.Name,
+ Type = type,
+ UpdateInterval = namedWindowSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = namedWindowSensor.IgnoreAvailability,
+ WindowName = namedWindowSensor.WindowName,
+ AdvancedSettings = namedWindowSensor.AdvancedSettings
+ };
+ }
case PerformanceCounterSensor performanceCounterSensor:
- {
- _ = Enum.TryParse(performanceCounterSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(performanceCounterSensor.Id),
- EntityName = performanceCounterSensor.EntityName,
- Name = performanceCounterSensor.Name,
- Type = type,
- UpdateInterval = performanceCounterSensor.UpdateIntervalSeconds,
- IgnoreAvailability = performanceCounterSensor.IgnoreAvailability,
- Category = performanceCounterSensor.CategoryName,
- Counter = performanceCounterSensor.CounterName,
- Instance = performanceCounterSensor.InstanceName,
- ApplyRounding = performanceCounterSensor.ApplyRounding,
- Round = performanceCounterSensor.Round,
- AdvancedSettings = performanceCounterSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(performanceCounterSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(performanceCounterSensor.Id),
+ EntityName = performanceCounterSensor.EntityName,
+ Name = performanceCounterSensor.Name,
+ Type = type,
+ UpdateInterval = performanceCounterSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = performanceCounterSensor.IgnoreAvailability,
+ Category = performanceCounterSensor.CategoryName,
+ Counter = performanceCounterSensor.CounterName,
+ Instance = performanceCounterSensor.InstanceName,
+ ApplyRounding = performanceCounterSensor.ApplyRounding,
+ Round = performanceCounterSensor.Round,
+ AdvancedSettings = performanceCounterSensor.AdvancedSettings
+ };
+ }
case ProcessActiveSensor processActiveSensor:
- {
- _ = Enum.TryParse(processActiveSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(processActiveSensor.Id),
- EntityName = processActiveSensor.EntityName,
- Name = processActiveSensor.Name,
- Type = type,
- UpdateInterval = processActiveSensor.UpdateIntervalSeconds,
- IgnoreAvailability = processActiveSensor.IgnoreAvailability,
- Query = processActiveSensor.ProcessName,
- AdvancedSettings = processActiveSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(processActiveSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(processActiveSensor.Id),
+ EntityName = processActiveSensor.EntityName,
+ Name = processActiveSensor.Name,
+ Type = type,
+ UpdateInterval = processActiveSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = processActiveSensor.IgnoreAvailability,
+ Query = processActiveSensor.ProcessName,
+ AdvancedSettings = processActiveSensor.AdvancedSettings
+ };
+ }
case ServiceStateSensor serviceStateSensor:
- {
- _ = Enum.TryParse(serviceStateSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(serviceStateSensor.Id),
- EntityName = serviceStateSensor.EntityName,
- Name = serviceStateSensor.Name,
- Type = type,
- UpdateInterval = serviceStateSensor.UpdateIntervalSeconds,
- IgnoreAvailability = serviceStateSensor.IgnoreAvailability,
- Query = serviceStateSensor.ServiceName,
- AdvancedSettings = serviceStateSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(serviceStateSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(serviceStateSensor.Id),
+ EntityName = serviceStateSensor.EntityName,
+ Name = serviceStateSensor.Name,
+ Type = type,
+ UpdateInterval = serviceStateSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = serviceStateSensor.IgnoreAvailability,
+ Query = serviceStateSensor.ServiceName,
+ AdvancedSettings = serviceStateSensor.AdvancedSettings
+ };
+ }
case PowershellSensor powershellSensor:
- {
- _ = Enum.TryParse(powershellSensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(powershellSensor.Id),
- EntityName = powershellSensor.EntityName,
- Name = powershellSensor.Name,
- Type = type,
- UpdateInterval = powershellSensor.UpdateIntervalSeconds,
- IgnoreAvailability = powershellSensor.IgnoreAvailability,
- Query = powershellSensor.Command,
- ApplyRounding = powershellSensor.ApplyRounding,
- Round = powershellSensor.Round,
- AdvancedSettings = powershellSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(powershellSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(powershellSensor.Id),
+ EntityName = powershellSensor.EntityName,
+ Name = powershellSensor.Name,
+ Type = type,
+ UpdateInterval = powershellSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = powershellSensor.IgnoreAvailability,
+ Query = powershellSensor.Command,
+ ApplyRounding = powershellSensor.ApplyRounding,
+ Round = powershellSensor.Round,
+ AdvancedSettings = powershellSensor.AdvancedSettings
+ };
+ }
- case LastActiveSensor lastActiveSensor:
- {
- _ = Enum.TryParse(lastActiveSensor.GetType().Name, out var type);
- return new ConfiguredSensor
- {
- Id = Guid.Parse(lastActiveSensor.Id),
- EntityName = lastActiveSensor.EntityName,
- Name = lastActiveSensor.Name,
- Type = type,
- UpdateInterval = lastActiveSensor.UpdateIntervalSeconds,
- IgnoreAvailability = lastActiveSensor.IgnoreAvailability,
- ApplyRounding = lastActiveSensor.ApplyRounding,
- Round = lastActiveSensor.Round,
+ case LastActiveSensor lastActiveSensor:
+ {
+ _ = Enum.TryParse(lastActiveSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(lastActiveSensor.Id),
+ EntityName = lastActiveSensor.EntityName,
+ Name = lastActiveSensor.Name,
+ Type = type,
+ UpdateInterval = lastActiveSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = lastActiveSensor.IgnoreAvailability,
+ ApplyRounding = lastActiveSensor.ApplyRounding,
+ Round = lastActiveSensor.Round,
AdvancedSettings = lastActiveSensor.AdvancedSettings
};
- }
+ }
- case WindowStateSensor windowStateSensor:
- {
- _ = Enum.TryParse(windowStateSensor.GetType().Name, out var type);
- return new ConfiguredSensor
+ case WindowStateSensor windowStateSensor:
{
- Id = Guid.Parse(windowStateSensor.Id),
- EntityName = windowStateSensor.EntityName,
- Name = windowStateSensor.Name,
- Type = type,
- UpdateInterval = windowStateSensor.UpdateIntervalSeconds,
- IgnoreAvailability = windowStateSensor.IgnoreAvailability,
- Query = windowStateSensor.ProcessName,
- AdvancedSettings = windowStateSensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(windowStateSensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(windowStateSensor.Id),
+ EntityName = windowStateSensor.EntityName,
+ Name = windowStateSensor.Name,
+ Type = type,
+ UpdateInterval = windowStateSensor.UpdateIntervalSeconds,
+ IgnoreAvailability = windowStateSensor.IgnoreAvailability,
+ Query = windowStateSensor.ProcessName,
+ AdvancedSettings = windowStateSensor.AdvancedSettings
+ };
+ }
case HomeAssistant.Sensors.GeneralSensors.SingleValue.InternalDeviceSensor internalDeviceSensor:
{
@@ -411,8 +412,8 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
Name = internalDeviceSensor.Name,
Type = type,
UpdateInterval = internalDeviceSensor.UpdateIntervalSeconds,
- IgnoreAvailability = internalDeviceSensor.IgnoreAvailability,
- Query = internalDeviceSensor.SensorType.ToString(),
+ IgnoreAvailability = internalDeviceSensor.IgnoreAvailability,
+ Query = internalDeviceSensor.SensorType.ToString(),
AdvancedSettings = internalDeviceSensor.AdvancedSettings
};
}
@@ -434,19 +435,19 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract
}
default:
- {
- _ = Enum.TryParse(sensor.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.Name,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability,
- AdvancedSettings = sensor.AdvancedSettings
- };
- }
+ _ = Enum.TryParse(sensor.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.Name,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability,
+ AdvancedSettings = sensor.AdvancedSettings
+ };
+ }
}
}
@@ -460,147 +461,147 @@ internal static ConfiguredSensor ConvertAbstractMultiValueToConfigured(AbstractM
switch (sensor)
{
case StorageSensors storageSensors:
- {
- _ = Enum.TryParse(storageSensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(storageSensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case NetworkSensors networkSensors:
- {
- _ = Enum.TryParse(networkSensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Query = networkSensors.NetworkCard,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(networkSensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Query = networkSensors.NetworkCard,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case WindowsUpdatesSensors windowsUpdatesSensors:
- {
- _ = Enum.TryParse(windowsUpdatesSensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(windowsUpdatesSensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case BatterySensors batterySensors:
- {
- _ = Enum.TryParse(batterySensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(batterySensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case DisplaySensors displaySensors:
- {
- _ = Enum.TryParse(displaySensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(displaySensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case AudioSensors audioSensors:
- {
- _ = Enum.TryParse(audioSensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
- }
+ _ = Enum.TryParse(audioSensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
case PrintersSensors printersSensors:
- {
- _ = Enum.TryParse(printersSensors.GetType().Name, out var type);
- return new ConfiguredSensor
{
- Id = Guid.Parse(sensor.Id),
- EntityName = sensor.EntityName,
- Name = sensor.EntityName,
- Type = type,
- UpdateInterval = sensor.UpdateIntervalSeconds,
- IgnoreAvailability = sensor.IgnoreAvailability
- };
+ _ = Enum.TryParse(printersSensors.GetType().Name, out var type);
+ return new ConfiguredSensor
+ {
+ Id = Guid.Parse(sensor.Id),
+ EntityName = sensor.EntityName,
+ Name = sensor.EntityName,
+ Type = type,
+ UpdateInterval = sensor.UpdateIntervalSeconds,
+ IgnoreAvailability = sensor.IgnoreAvailability
+ };
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Store all current sensors
+ ///
+ ///
+ internal static bool Store()
+ {
+ try
+ {
+ // check config dir
+ if (!Directory.Exists(Variables.ConfigPath))
+ {
+ // create
+ Directory.CreateDirectory(Variables.ConfigPath);
}
+
+ // convert single-value sensors
+ var configuredSensors = Variables.SingleValueSensors.Select(ConvertAbstractSingleValueToConfigured).Where(configuredSensor => configuredSensor != null).ToList();
+
+ // convert multi-value sensors
+ var configuredMultiValueSensors = Variables.MultiValueSensors.Select(ConvertAbstractMultiValueToConfigured).Where(configuredSensor => configuredSensor != null).ToList();
+ configuredSensors = configuredSensors.Concat(configuredMultiValueSensors).ToList();
+
+ // serialize to file
+ var sensors = JsonConvert.SerializeObject(configuredSensors, Formatting.Indented);
+ File.WriteAllText(Variables.SensorsFile, sensors);
+
+ // done
+ Log.Information("[SETTINGS_SENSORS] Stored {count} entities", (Variables.SingleValueSensors.Count + Variables.MultiValueSensors.Count));
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Ok);
+ return true;
}
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "[SETTINGS_SENSORS] Error storing entities: {err}", ex.Message);
+ Variables.MainForm?.ShowMessageBox(string.Format(Languages.StoredSensors_Store_MessageBox1, ex.Message), true);
- return null;
- }
-
- ///
- /// Store all current sensors
- ///
- ///
- internal static bool Store()
- {
- try
- {
- // check config dir
- if (!Directory.Exists(Variables.ConfigPath))
- {
- // create
- Directory.CreateDirectory(Variables.ConfigPath);
- }
-
- // convert single-value sensors
- var configuredSensors = Variables.SingleValueSensors.Select(ConvertAbstractSingleValueToConfigured).Where(configuredSensor => configuredSensor != null).ToList();
-
- // convert multi-value sensors
- var configuredMultiValueSensors = Variables.MultiValueSensors.Select(ConvertAbstractMultiValueToConfigured).Where(configuredSensor => configuredSensor != null).ToList();
- configuredSensors = configuredSensors.Concat(configuredMultiValueSensors).ToList();
-
- // serialize to file
- var sensors = JsonConvert.SerializeObject(configuredSensors, Formatting.Indented);
- File.WriteAllText(Variables.SensorsFile, sensors);
-
- // done
- Log.Information("[SETTINGS_SENSORS] Stored {count} entities", (Variables.SingleValueSensors.Count + Variables.MultiValueSensors.Count));
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Ok);
- return true;
- }
- catch (Exception ex)
- {
- Log.Fatal(ex, "[SETTINGS_SENSORS] Error storing entities: {err}", ex.Message);
- Variables.MainForm?.ShowMessageBox(string.Format(Languages.StoredSensors_Store_MessageBox1, ex.Message), true);
-
- Variables.MainForm?.SetSensorsStatus(ComponentStatus.Failed);
- return false;
- }
- }
- }
+ Variables.MainForm?.SetSensorsStatus(ComponentStatus.Failed);
+ return false;
+ }
+ }
+ }
}
From 5161f6f5ef59c2005027c3b7a3d0de5dbc6358e4 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Fri, 12 Jul 2024 23:23:16 +0200
Subject: [PATCH 39/45] Feature: tray webview command (#124)
This PR adds feature requested via #116 - a command (best working as switch) that allows to show/hide the configured tray web view.
---
.../HASS.Agent.Shared/Enums/CommandType.cs | 4 ++
.../Localization/Languages.Designer.cs | 9 +++
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3 +
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
.../HASS.Agent/Commands/CommandsManager.cs | 8 +++
src/HASS.Agent/HASS.Agent/Forms/Main.cs | 11 +---
src/HASS.Agent/HASS.Agent/Forms/WebView.cs | 3 +-
.../HASS.Agent/Functions/HelperFunctions.cs | 29 ++++++++--
.../InternalCommands/TrayWebViewCommand.cs | 58 +++++++++++++++++++
.../Localization/Languages.Designer.cs | 10 ++++
.../Resources/Localization/Languages.de.resx | 4 ++
.../Resources/Localization/Languages.en.resx | 4 ++
.../Resources/Localization/Languages.es.resx | 4 ++
.../Resources/Localization/Languages.fr.resx | 4 ++
.../Resources/Localization/Languages.nl.resx | 4 ++
.../Resources/Localization/Languages.pl.resx | 4 ++
.../Localization/Languages.pt-br.resx | 4 ++
.../Resources/Localization/Languages.resx | 4 ++
.../Resources/Localization/Languages.ru.resx | 4 ++
.../Resources/Localization/Languages.sl.resx | 4 ++
.../Resources/Localization/Languages.tr.resx | 4 ++
.../HASS.Agent/Settings/StoredCommands.cs | 3 +
31 files changed, 196 insertions(+), 16 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/TrayWebViewCommand.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
index e92db473..a25ca5b1 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs
@@ -121,6 +121,10 @@ public enum CommandType
[EnumMember(Value = "WebViewCommand")]
WebViewCommand,
+ [LocalizedDescription("CommandType_TrayWebViewCommand", typeof(Languages))]
+ [EnumMember(Value = "TrayWebViewCommand")]
+ TrayWebViewCommand,
+
[LocalizedDescription("CommandType_RadioCommand", typeof(Languages))]
[EnumMember(Value = "RadioCommand")]
RadioCommand
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index be913c63..dac0f1dc 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -1338,6 +1338,15 @@ internal static string CommandType_SwitchDesktopCommand {
}
}
+ ///
+ /// Looks up a localized string similar to TrayWebView.
+ ///
+ internal static string CommandType_TrayWebViewCommand {
+ get {
+ return ResourceManager.GetString("CommandType_TrayWebViewCommand", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to WebView.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index 736f2957..09bbf764 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3364,4 +3364,7 @@ Willst Du den Runtime Installer herunterladen?
Legen Sie den Audioeingabebefehl fest
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index decc96d4..997c345c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3240,4 +3240,7 @@ Do you want to download the runtime installer?
SetAudioInputCommand
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 58a31529..86f336a6 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3240,4 +3240,7 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Establecer comando de entrada de audio
+
+ Vista web de la bandeja
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index 32f1ca61..39449077 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3273,4 +3273,7 @@ Do you want to download the runtime installer?
Définir la commande d'entrée audio
+
+ Vue Web de la barre d'état
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 664534da..31addb36 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3262,4 +3262,7 @@ Wil je de runtime installatie downloaden?
Stel het audio-invoercommando in
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index c6ffb694..3da9e587 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3350,4 +3350,7 @@ Czy chcesz pobrać plik instalacyjny?
Ustaw wejście audio
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index 76c7b10d..dae75e54 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3287,4 +3287,7 @@ Deseja baixar o Microsoft WebView2 runtime?
Definir comando de entrada de áudio
+
+ BandejaWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index 94dad3f7..fef61f2c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3223,4 +3223,7 @@ Do you want to download the runtime installer?
SetAudioInputCommand
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index 758aa8bd..bbeaa1ad 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3308,4 +3308,7 @@ Home Assistant.
Установить команду аудиовхода
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index 77cf6153..5f3406dc 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3389,4 +3389,7 @@ Ali želite prenesti runtime installer?
Nastavite ukaz za zvočni vhod
+
+ TrayWebView
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index e2c8d79d..243bb449 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2847,4 +2847,7 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
Ses Giriş Komutunu Ayarla
+
+ TepsiWebGörünümü
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
index f3646e59..811b574d 100644
--- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
+++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs
@@ -525,6 +525,14 @@ internal static void LoadCommandInfo()
// =================================
+ commandInfoCard = new CommandInfoCard(CommandType.TrayWebViewCommand,
+ Languages.CommandsManager_TrayWebViewCommandDescription,
+ true, false, false);
+
+ CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard);
+
+ // =================================
+
commandInfoCard = new CommandInfoCard(CommandType.RadioCommand,
Languages.CommandsManager_RadioCommandDescription,
true, false, true);
diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.cs b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
index e09e8933..0f8467ff 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/Main.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/Main.cs
@@ -961,16 +961,7 @@ private void NotifyIcon_MouseClick(object sender, MouseEventArgs e)
return;
}
- // prepare the webview
- var webView = new WebViewInfo
- {
- Url = Variables.AppSettings.TrayIconWebViewUrl,
- Height = Variables.AppSettings.TrayIconWebViewHeight,
- Width = Variables.AppSettings.TrayIconWebViewWidth,
- };
-
- // show it
- HelperFunctions.LaunchTrayIconWebView(webView);
+ HelperFunctions.LaunchTrayIconWebView();
}
private async void PbDonate_Click(object sender, EventArgs e)
diff --git a/src/HASS.Agent/HASS.Agent/Forms/WebView.cs b/src/HASS.Agent/HASS.Agent/Forms/WebView.cs
index b5983909..90444df0 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/WebView.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/WebView.cs
@@ -274,10 +274,11 @@ private void WebView_FormClosing(object sender, FormClosingEventArgs e)
return;
}
+ Opacity = 0;
+
// do we need to stay open?
if (_isTrayIcon)
{
- Opacity = 0;
e.Cancel = true;
return;
}
diff --git a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
index db1138ef..ab6d7cb9 100644
--- a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
+++ b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs
@@ -447,7 +447,7 @@ internal static void PrepareTrayIconWebView()
Variables.MainForm.Invoke(new MethodInvoker(delegate
{
// optionally close an existing one
- if (Variables.TrayIconWebView != null) Variables.TrayIconWebView.ForceClose();
+ Variables.TrayIconWebView?.ForceClose();
// bind the new one
Variables.TrayIconWebView = new WebView(webViewInfo);
@@ -456,6 +456,22 @@ internal static void PrepareTrayIconWebView()
}));
}
+ ///
+ /// Shows the user-configured webview form near the tray icon
+ ///
+ ///
+ internal static void LaunchTrayIconWebView()
+ {
+ var webView = new WebViewInfo
+ {
+ Url = Variables.AppSettings.TrayIconWebViewUrl,
+ Height = Variables.AppSettings.TrayIconWebViewHeight,
+ Width = Variables.AppSettings.TrayIconWebViewWidth,
+ };
+
+ LaunchTrayIconWebView(webView);
+ }
+
///
/// Shows a new webview form near the tray icon
///
@@ -491,7 +507,8 @@ private static void LaunchTrayIconBackgroundLoadedWebView()
Variables.MainForm.Invoke(new MethodInvoker(delegate
{
// make sure it's ready
- if (Variables.TrayIconWebView == null || Variables.TrayIconWebView.IsDisposed) PrepareTrayIconWebView();
+ if (Variables.TrayIconWebView == null || Variables.TrayIconWebView.IsDisposed)
+ PrepareTrayIconWebView();
// show it
Variables.TrayIconWebView?.MakeVisible();
@@ -514,6 +531,8 @@ private static void LaunchTrayIconCustomWebView(WebViewInfo webViewInfo)
var webView = new WebView(webViewInfo);
webView.Opacity = 0;
+
+ Variables.TrayIconWebView = webView;
webView.Show();
}));
}
@@ -545,12 +564,12 @@ internal static bool InputLanguageCheckDiffers(out bool knownToCollide, out stri
// check for known OK languages
if (KnownOkInputLanguage.ContainsKey(inputLanguage))
return false;
-
+
// check for known NOT OK languages
var germanLayoutDetected = false;
- foreach(InputLanguage language in InputLanguage.InstalledInputLanguages)
+ foreach (InputLanguage language in InputLanguage.InstalledInputLanguages)
{
- if(language.Culture.TwoLetterISOLanguageName == "de")
+ if (language.Culture.TwoLetterISOLanguageName == "de")
{
germanLayoutDetected = true;
break;
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/TrayWebViewCommand.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/TrayWebViewCommand.cs
new file mode 100644
index 00000000..46dbd99d
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/TrayWebViewCommand.cs
@@ -0,0 +1,58 @@
+using HASS.Agent.Functions;
+using HASS.Agent.Models.Internal;
+using HASS.Agent.Resources.Localization;
+using HASS.Agent.Shared.Enums;
+using HASS.Agent.Shared.HomeAssistant.Commands;
+using Newtonsoft.Json;
+using Serilog;
+using Syncfusion.Windows.Forms;
+
+namespace HASS.Agent.HomeAssistant.Commands.InternalCommands
+{
+ internal class TrayWebViewCommand : InternalCommand
+ {
+ private const string DefaultName = "traywebview";
+
+ internal TrayWebViewCommand(string entityName = DefaultName, string name = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(entityName ?? DefaultName, name ?? null, string.Empty, entityType, id)
+ {
+ State = "OFF";
+ }
+
+ public override void TurnOn()
+ {
+ if (string.IsNullOrEmpty(Variables.AppSettings.TrayIconWebViewUrl))
+ {
+ Log.Warning("[TRAYWEBVIEW] [{name}] No webview URL is configured in 'Configuration -> Tray Icon'", EntityName);
+ return;
+ }
+
+ HelperFunctions.LaunchTrayIconWebView();
+ }
+
+ public override void TurnOff()
+ {
+ Variables.MainForm.Invoke(() =>
+ {
+ if(Variables.TrayIconWebView == null)
+ return;
+
+ Variables.TrayIconWebView.Opacity = 0;
+
+ if (!Variables.AppSettings.TrayIconWebViewBackgroundLoading) {
+ Variables.TrayIconWebView.ForceClose();
+ Variables.TrayIconWebView.Dispose();
+ }
+ });
+ }
+
+ public override string GetState()
+ {
+ return Variables.TrayIconWebView != null && Variables.TrayIconWebView.Opacity != 0 ? "ON" : "OFF";
+ }
+
+ public override void TurnOnWithAction(string action)
+ {
+ Log.Warning("[TRAYWEBVIEW] [{name}] Launching with action is not supported", EntityName);
+ }
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 04fc72c3..c895acce 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -715,6 +715,16 @@ internal static string CommandsManager_SwitchDesktopCommandDescription {
}
}
+ ///
+ /// Looks up a localized string similar to Shows / hides the Tray Icon Web View.
+ ///Requires it to be configured in "Configuration -> Tray Icon".
+ ///
+ internal static string CommandsManager_TrayWebViewCommandDescription {
+ get {
+ return ResourceManager.GetString("CommandsManager_TrayWebViewCommandDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Shows a window with the provided URL.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 08861f67..38cecbfb 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3515,4 +3515,8 @@ Hinweis: Bei Verwendung im Satellitendienst werden keine Userspace-Anwendungen e
Achtung: Das Hinzufügen der folgenden Einstellungen (Überschreibungen) kann zur Beschädigung des Sensors führen. Gehen Sie daher vorsichtig vor!
+
+ Zeigt/verbirgt das Tray-Icon-Web-View.
+Muss unter „Konfiguration -> Tray-Icon“ konfiguriert werden.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index a96a6a15..382a168f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3420,4 +3420,8 @@ Note: if used in the satellite service, it won't detect userspace applications.<
Advanced Settings
+
+ Shows / hides the Tray Icon Web View.
+Requires it to be configured in "Configuration -> Tray Icon"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 11bc91fd..d72f64e8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3391,4 +3391,8 @@ Nota: si se usa en el servicio satelital, no detectará aplicaciones del espacio
Advertencia, agregar las siguientes configuraciones (anulaciones) puede dañar el sensor, ¡úselo con precaución!
+
+ Muestra u oculta la vista web del icono de la bandeja.
+Requiere que se configure en "Configuración -> Icono de la bandeja"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 036c40c4..5280d3c2 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3424,4 +3424,8 @@ Remarque : s'il est utilisé dans le service satellite, il ne détectera pas les
Attention, l'ajout des paramètres suivants (remplacements) peut casser le capteur, à utiliser avec prudence !
+
+ Affiche/masque l'affichage Web de l'icône de la barre d'état système.
+Nécessite sa configuration dans « Configuration -> Icône de la barre d'état système »
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 7b6f0a41..acb7c723 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3412,4 +3412,8 @@ Opmerking: indien gebruikt in de satellietdienst, zal het geen gebruikersruimtet
Waarschuwing: het toevoegen van de volgende instellingen (overschrijvingen) kan de sensor kapot maken, wees voorzichtig!
+
+ Toont/verbergt het Tray Icon Web View.
+Vereist dat het geconfigureerd is in "Configuratie -> Tray Icon"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 684d9e02..e3ad5c06 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3501,4 +3501,8 @@ Uwaga: jeśli jest używany w usłudze satelitarnej, nie wykryje aplikacji przes
Uwaga, dodanie następujących ustawień (nadpisań) może spowodować uszkodzenie czujnika, zachowaj ostrożność!
+
+ Pokazuje/ukrywa Tray Icon Web View.
+Wymaga konfiguracji w „Configuration -> Tray Icon”
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index bdebb522..a94fa6ad 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3437,4 +3437,8 @@ Nota: se usado no serviço de satélite, não detectará aplicações no espaço
Atenção, adicionar as seguintes configurações (substituições) pode quebrar o sensor, use com cuidado!
+
+ Mostra/oculta o Tray Icon Web View.
+Requer que seja configurado em "Configuration -> Tray Icon"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 8c25f36e..c7f9e248 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3413,4 +3413,8 @@ Requires audio device name as a payload.
Advanced Settings
+
+ Shows / hides the Tray Icon Web View.
+Requires it to be configured in "Configuration -> Tray Icon"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index b34d9222..6baf2b09 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3460,4 +3460,8 @@ Home Assistant.
Внимание! Добавление следующих настроек (переопределений) может привести к поломке датчика, используйте с осторожностью!
+
+ Показывает/скрывает веб-представление значка в трее.
+Требует настройки в разделе «Конфигурация -> Значок в трее»
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index e8554e97..43005ddb 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3540,4 +3540,8 @@ Opomba: če se uporablja v satelitski storitvi, ne bo zaznal aplikacij uporabni
Opozorilo, dodajanje naslednjih nastavitev (preglasitev) lahko pokvari senzor, uporabljajte previdno!
+
+ Prikaže/skrije spletni pogled ikone pladnja.
+Zahteva, da je konfiguriran v "Konfiguracija -> Ikona pladnja"
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index edc26869..e5dda1e4 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -3007,4 +3007,8 @@ Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılam
Uyarı, aşağıdaki ayarların (geçersiz kılmaların) eklenmesi sensörü bozabilir, dikkatli kullanın!
+
+ Tepsi Simgesi Web Görünümünü gösterir/gizler.
+"Yapılandırma -> Tepsi Simgesi"nde yapılandırılması gerekir
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
index 9ddb228a..15305e16 100644
--- a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
+++ b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs
@@ -160,6 +160,9 @@ internal static AbstractCommand ConvertConfiguredToAbstract(ConfiguredCommand co
case CommandType.WebViewCommand:
abstractCommand = new WebViewCommand(command.EntityName, command.Name, command.Command, command.EntityType, command.Id.ToString());
break;
+ case CommandType.TrayWebViewCommand:
+ abstractCommand = new TrayWebViewCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString());
+ break;
case CommandType.MonitorSleepCommand:
abstractCommand = new MonitorSleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString());
break;
From 6b474c2a6602c4605b61529fbb7f0a03d0dc4943 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 13 Jul 2024 12:09:47 +0200
Subject: [PATCH 40/45] Feature: quick action automation trigger (#125)
https://github.com/hass-agent/HASS.Agent/pull/125
---
src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs | 7 ++++++-
.../Resources/Localization/Languages.Designer.cs | 9 +++++++++
.../Resources/Localization/Languages.de.resx | 3 +++
.../Resources/Localization/Languages.en.resx | 3 +++
.../Resources/Localization/Languages.es.resx | 3 +++
.../Resources/Localization/Languages.fr.resx | 3 +++
.../Resources/Localization/Languages.nl.resx | 3 +++
.../Resources/Localization/Languages.pl.resx | 3 +++
.../Resources/Localization/Languages.pt-br.resx | 3 +++
.../Resources/Localization/Languages.resx | 3 +++
.../Resources/Localization/Languages.ru.resx | 3 +++
.../Resources/Localization/Languages.sl.resx | 3 +++
.../Resources/Localization/Languages.tr.resx | 3 +++
.../Resources/Localization/Languages.Designer.cs | 9 +++++++++
.../HASS.Agent/Resources/Localization/Languages.de.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.en.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.es.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.fr.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.nl.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.pl.resx | 3 +++
.../Resources/Localization/Languages.pt-br.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.ru.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.sl.resx | 3 +++
.../HASS.Agent/Resources/Localization/Languages.tr.resx | 3 +++
25 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
index ad01d554..cf0f6b0a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
@@ -45,6 +45,11 @@ public enum HassAction
[LocalizedDescription("HassAction_Toggle", typeof(Languages))]
[Category("toggle")]
[EnumMember(Value = "Toggle")]
- Toggle
+ Toggle,
+
+ [LocalizedDescription("HassAction_Trigger", typeof(Languages))]
+ [Category("trigger")]
+ [EnumMember(Value = "Trigger")]
+ Trigger
}
}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index dac0f1dc..4faf9516 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -3380,6 +3380,15 @@ internal static string HassAction_Toggle {
}
}
+ ///
+ /// Looks up a localized string similar to Trigger.
+ ///
+ internal static string HassAction_Trigger {
+ get {
+ return ResourceManager.GetString("HassAction_Trigger", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Client certificate file not found..
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index 09bbf764..b38b81b2 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3367,4 +3367,7 @@ Willst Du den Runtime Installer herunterladen?
TrayWebView
+
+ Auslösen
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index 997c345c..7e73bd49 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3243,4 +3243,7 @@ Do you want to download the runtime installer?
TrayWebView
+
+ Trigger
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 86f336a6..0ae04d4f 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3243,4 +3243,7 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Vista web de la bandeja
+
+ Desencadenar
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index 39449077..a4b1bac5 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3276,4 +3276,7 @@ Do you want to download the runtime installer?
Vue Web de la barre d'état
+
+ Déclencher
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 31addb36..787d2fe9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3265,4 +3265,7 @@ Wil je de runtime installatie downloaden?
TrayWebView
+
+ Activering
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index 3da9e587..08a6a802 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3353,4 +3353,7 @@ Czy chcesz pobrać plik instalacyjny?
TrayWebView
+
+ Wyzwól
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index dae75e54..c2a18493 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3290,4 +3290,7 @@ Deseja baixar o Microsoft WebView2 runtime?
BandejaWebView
+
+ Acionar
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index fef61f2c..eb13eb78 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3226,4 +3226,7 @@ Do you want to download the runtime installer?
TrayWebView
+
+ Trigger
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index bbeaa1ad..f731ff08 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3311,4 +3311,7 @@ Home Assistant.
TrayWebView
+
+ Вызывать
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index 5f3406dc..7de61d8e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3392,4 +3392,7 @@ Ali želite prenesti runtime installer?
TrayWebView
+
+ Sprožilec
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index 243bb449..703db654 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2850,4 +2850,7 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
TepsiWebGörünümü
+
+ Tetiklemek
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index c895acce..ea0b00cd 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -3689,6 +3689,15 @@ internal static string HassAction_Toggle {
}
}
+ ///
+ /// Looks up a localized string similar to Trigger.
+ ///
+ internal static string HassAction_Trigger {
+ get {
+ return ResourceManager.GetString("HassAction_Trigger", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Client certificate file not found..
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 38cecbfb..e1737ea8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3519,4 +3519,7 @@ Hinweis: Bei Verwendung im Satellitendienst werden keine Userspace-Anwendungen e
Zeigt/verbirgt das Tray-Icon-Web-View.
Muss unter „Konfiguration -> Tray-Icon“ konfiguriert werden.
+
+ Auslösen
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 382a168f..615a5750 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3424,4 +3424,7 @@ Note: if used in the satellite service, it won't detect userspace applications.<
Shows / hides the Tray Icon Web View.
Requires it to be configured in "Configuration -> Tray Icon"
+
+ Trigger
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index d72f64e8..7a8fa344 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3395,4 +3395,7 @@ Nota: si se usa en el servicio satelital, no detectará aplicaciones del espacio
Muestra u oculta la vista web del icono de la bandeja.
Requiere que se configure en "Configuración -> Icono de la bandeja"
+
+ Desencadenar
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 5280d3c2..bbf2e98c 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3428,4 +3428,7 @@ Remarque : s'il est utilisé dans le service satellite, il ne détectera pas les
Affiche/masque l'affichage Web de l'icône de la barre d'état système.
Nécessite sa configuration dans « Configuration -> Icône de la barre d'état système »
+
+ Déclencher
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index acb7c723..8d767c18 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3416,4 +3416,7 @@ Opmerking: indien gebruikt in de satellietdienst, zal het geen gebruikersruimtet
Toont/verbergt het Tray Icon Web View.
Vereist dat het geconfigureerd is in "Configuratie -> Tray Icon"
+
+ Activering
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index e3ad5c06..4326d1e4 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3505,4 +3505,7 @@ Uwaga: jeśli jest używany w usłudze satelitarnej, nie wykryje aplikacji przes
Pokazuje/ukrywa Tray Icon Web View.
Wymaga konfiguracji w „Configuration -> Tray Icon”
+
+ Wyzwól
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index a94fa6ad..535978b8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3441,4 +3441,7 @@ Nota: se usado no serviço de satélite, não detectará aplicações no espaço
Mostra/oculta o Tray Icon Web View.
Requer que seja configurado em "Configuration -> Tray Icon"
+
+ Acionar
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index c7f9e248..16c55408 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3417,4 +3417,7 @@ Requires audio device name as a payload.
Shows / hides the Tray Icon Web View.
Requires it to be configured in "Configuration -> Tray Icon"
+
+ Trigger
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 6baf2b09..20e97fb9 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3464,4 +3464,7 @@ Home Assistant.
Показывает/скрывает веб-представление значка в трее.
Требует настройки в разделе «Конфигурация -> Значок в трее»
+
+ Вызывать
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 43005ddb..5057bb6e 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3544,4 +3544,7 @@ Opomba: če se uporablja v satelitski storitvi, ne bo zaznal aplikacij uporabni
Prikaže/skrije spletni pogled ikone pladnja.
Zahteva, da je konfiguriran v "Konfiguracija -> Ikona pladnja"
+
+ Sprožilec
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index e5dda1e4..f2da35f2 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -3011,4 +3011,7 @@ Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılam
Tepsi Simgesi Web Görünümünü gösterir/gizler.
"Yapılandırma -> Tepsi Simgesi"nde yapılandırılması gerekir
+
+ Tetiklemek
+
\ No newline at end of file
From 5082f67435def0730f51a4e600482beb322ea991 Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 13 Jul 2024 12:38:48 +0200
Subject: [PATCH 41/45] Feature: quick action button press (#126)
https://github.com/hass-agent/HASS.Agent/pull/126
---
.../HASS.Agent.Shared/Enums/HassAction.cs | 7 ++++++-
.../HASS.Agent.Shared/Enums/HassDomain.cs | 7 ++++++-
.../Localization/Languages.Designer.cs | 18 ++++++++++++++++++
.../Resources/Localization/Languages.de.resx | 6 ++++++
.../Resources/Localization/Languages.en.resx | 6 ++++++
.../Resources/Localization/Languages.es.resx | 6 ++++++
.../Resources/Localization/Languages.fr.resx | 6 ++++++
.../Resources/Localization/Languages.nl.resx | 6 ++++++
.../Resources/Localization/Languages.pl.resx | 6 ++++++
.../Localization/Languages.pt-br.resx | 6 ++++++
.../Resources/Localization/Languages.resx | 6 ++++++
.../Resources/Localization/Languages.ru.resx | 6 ++++++
.../Resources/Localization/Languages.sl.resx | 6 ++++++
.../Resources/Localization/Languages.tr.resx | 6 ++++++
.../Forms/QuickActions/QuickActionsMod.cs | 8 ++++++++
.../HASS.Agent/HomeAssistant/HassApiManager.cs | 3 +++
.../Localization/Languages.Designer.cs | 18 ++++++++++++++++++
.../Resources/Localization/Languages.de.resx | 6 ++++++
.../Resources/Localization/Languages.en.resx | 6 ++++++
.../Resources/Localization/Languages.es.resx | 6 ++++++
.../Resources/Localization/Languages.fr.resx | 6 ++++++
.../Resources/Localization/Languages.nl.resx | 6 ++++++
.../Resources/Localization/Languages.pl.resx | 6 ++++++
.../Localization/Languages.pt-br.resx | 6 ++++++
.../Resources/Localization/Languages.resx | 6 ++++++
.../Resources/Localization/Languages.ru.resx | 6 ++++++
.../Resources/Localization/Languages.sl.resx | 6 ++++++
.../Resources/Localization/Languages.tr.resx | 6 ++++++
28 files changed, 191 insertions(+), 2 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
index cf0f6b0a..6bead640 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassAction.cs
@@ -50,6 +50,11 @@ public enum HassAction
[LocalizedDescription("HassAction_Trigger", typeof(Languages))]
[Category("trigger")]
[EnumMember(Value = "Trigger")]
- Trigger
+ Trigger,
+
+ [LocalizedDescription("HassAction_Press", typeof(Languages))]
+ [Category("press")]
+ [EnumMember(Value = "Press")]
+ Press
}
}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs
index 2d153255..4b453b34 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs
@@ -57,6 +57,11 @@ public enum HassDomain
[LocalizedDescription("HassDomain_Switch", typeof(Languages))]
[Category("switch")]
[EnumMember(Value = "Switch")]
- Switch
+ Switch,
+
+ [LocalizedDescription("HassDomain_Button", typeof(Languages))]
+ [Category("button")]
+ [EnumMember(Value = "Button")]
+ Button
}
}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index 4faf9516..931858fa 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -3362,6 +3362,15 @@ internal static string HassAction_Play {
}
}
+ ///
+ /// Looks up a localized string similar to Press.
+ ///
+ internal static string HassAction_Press {
+ get {
+ return ResourceManager.GetString("HassAction_Press", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Stop.
///
@@ -3479,6 +3488,15 @@ internal static string HassDomain_Automation {
}
}
+ ///
+ /// Looks up a localized string similar to Button.
+ ///
+ internal static string HassDomain_Button {
+ get {
+ return ResourceManager.GetString("HassDomain_Button", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Climate.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index b38b81b2..c5179acd 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3370,4 +3370,10 @@ Willst Du den Runtime Installer herunterladen?
Auslösen
+
+ Knopf
+
+
+ Presse
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index 7e73bd49..f881bf80 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3246,4 +3246,10 @@ Do you want to download the runtime installer?
Trigger
+
+ Press
+
+
+ Button
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 0ae04d4f..2ea0d91a 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3246,4 +3246,10 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Desencadenar
+
+ Botón
+
+
+ Prensa
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index a4b1bac5..27c3ec38 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3279,4 +3279,10 @@ Do you want to download the runtime installer?
Déclencher
+
+ Knop
+
+
+ Presse
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 787d2fe9..3a9871c4 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3268,4 +3268,10 @@ Wil je de runtime installatie downloaden?
Activering
+
+ Knop
+
+
+ Pers
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index 08a6a802..6947c51d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3356,4 +3356,10 @@ Czy chcesz pobrać plik instalacyjny?
Wyzwól
+
+ Przycisk
+
+
+ Naciśnij
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index c2a18493..a7b67ecb 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3293,4 +3293,10 @@ Deseja baixar o Microsoft WebView2 runtime?
Acionar
+
+ Botão
+
+
+ Imprensa
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index eb13eb78..8fc95014 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -3229,4 +3229,10 @@ Do you want to download the runtime installer?
Trigger
+
+ Press
+
+
+ Button
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index f731ff08..6f5d7c6e 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3314,4 +3314,10 @@ Home Assistant.
Вызывать
+
+ Button
+
+
+ Нажимать
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index 7de61d8e..1edd421c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3395,4 +3395,10 @@ Ali želite prenesti runtime installer?
Sprožilec
+
+ Gumb
+
+
+ Pritisnite
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index 703db654..28956a1c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2853,4 +2853,10 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
Tetiklemek
+
+ Buton
+
+
+ Basmak
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs
index 827c5c06..dcb776fb 100644
--- a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs
+++ b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs
@@ -388,6 +388,14 @@ private void LoadEntityList()
CbEntity.Items.Add(item.EntityName);
}
break;
+
+ case HassDomain.Button:
+ foreach (var item in HassApiManager.ButtonList)
+ {
+ CbEntity.AutoCompleteCustomSource.Add(item);
+ CbEntity.Items.Add(item);
+ }
+ break;
}
}
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs
index 524e6a4d..36a49af8 100644
--- a/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs
@@ -43,6 +43,7 @@ internal static class HassApiManager
internal static List CoverList = new();
internal static List ClimateList = new();
internal static List MediaPlayerList = new();
+ internal static List ButtonList = new();
private static readonly string[] OnStates = { "on", "playing", "open", "opening" };
private static readonly string[] OffStates = { "off", "idle", "paused", "stopped", "closed", "closing" };
@@ -389,6 +390,7 @@ private static async Task LoadEntitiesAsync(bool clearCurrent = false)
CoverList.Clear();
ClimateList.Clear();
MediaPlayerList.Clear();
+ ButtonList.Clear();
}
try
@@ -402,6 +404,7 @@ private static async Task LoadEntitiesAsync(bool clearCurrent = false)
await LoadDomain("cover", CoverList);
await LoadDomain("climate", ClimateList);
await LoadDomain("media_player", MediaPlayerList);
+ await LoadDomain("button", ButtonList);
if (ManagerStatus != HassManagerStatus.Failed)
return;
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index ea0b00cd..269df973 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -3671,6 +3671,15 @@ internal static string HassAction_Play {
}
}
+ ///
+ /// Looks up a localized string similar to Press.
+ ///
+ internal static string HassAction_Press {
+ get {
+ return ResourceManager.GetString("HassAction_Press", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Stop.
///
@@ -3788,6 +3797,15 @@ internal static string HassDomain_Automation {
}
}
+ ///
+ /// Looks up a localized string similar to Button.
+ ///
+ internal static string HassDomain_Button {
+ get {
+ return ResourceManager.GetString("HassDomain_Button", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Climate.
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index e1737ea8..63a460db 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3522,4 +3522,10 @@ Muss unter „Konfiguration -> Tray-Icon“ konfiguriert werden.
Auslösen
+
+ Knopf
+
+
+ Presse
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 615a5750..8dc6f1b7 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3427,4 +3427,10 @@ Requires it to be configured in "Configuration -> Tray Icon"
Trigger
+
+ Button
+
+
+ Press
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 7a8fa344..16b9e7d7 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3398,4 +3398,10 @@ Requiere que se configure en "Configuración -> Icono de la bandeja"
Desencadenar
+
+ Botón
+
+
+ Prensa
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index bbf2e98c..1bfdf5df 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3431,4 +3431,10 @@ Nécessite sa configuration dans « Configuration -> Icône de la barre d'ét
Déclencher
+
+ Knop
+
+
+ Presse
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index 8d767c18..ea161750 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3419,4 +3419,10 @@ Vereist dat het geconfigureerd is in "Configuratie -> Tray Icon"
Activering
+
+ Knop
+
+
+ Pers
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index 4326d1e4..b8a5706d 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3508,4 +3508,10 @@ Wymaga konfiguracji w „Configuration -> Tray Icon”
Wyzwól
+
+ Przycisk
+
+
+ Naciśnij
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index 535978b8..f171b0c2 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3444,4 +3444,10 @@ Requer que seja configurado em "Configuration -> Tray Icon"
Acionar
+
+ Botão
+
+
+ Imprensa
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index 16c55408..da531004 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -3420,4 +3420,10 @@ Requires it to be configured in "Configuration -> Tray Icon"
Trigger
+
+ Press
+
+
+ Button
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 20e97fb9..24b2bb4f 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3467,4 +3467,10 @@ Home Assistant.
Вызывать
+
+ Button
+
+
+ Нажимать
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 5057bb6e..0c0728a7 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3547,4 +3547,10 @@ Zahteva, da je konfiguriran v "Konfiguracija -> Ikona pladnja"
Sprožilec
+
+ Gumb
+
+
+ Pritisnite
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index f2da35f2..e1d3413b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -3014,4 +3014,10 @@ Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılam
Tetiklemek
+
+ Buton
+
+
+ Basmak
+
\ No newline at end of file
From 3fd5fda1e53ceebfd5df8bc3489301e71ffd322a Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Thu, 1 Aug 2024 21:27:37 +0200
Subject: [PATCH 42/45] Fix: readded service stop logic (#133)
---
.../InstallerScript-Service.iss | 64 +++++++++----------
1 file changed, 31 insertions(+), 33 deletions(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
index fba2c845..96c5c501 100644
--- a/src/HASS.Agent.Installer/InstallerScript-Service.iss
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -81,36 +81,34 @@ begin
Result := True;
end;
-//procedure CurStepChanged(CurStep: TSetupStep);
-//var
-// ProgressPage: TOutputProgressWizardPage;
-// I, Step, Wait, ResultCode: Integer;
-//begin
-// if CurStep = ssInstall then
-// begin
-// //MsgBox(ExpandConstant('{sys}') + '\sc.exe', mbInformation, MB_OK);
-// //MsgBox('stop ' + ExpandConstant('{#ServiceName}'), mbInformation, MB_OK);
-// Exec(ExpandConstant('{sys}') + '\sc.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
-//
-// //thanks to https://stackoverflow.com/a/39827761
-// Wait := 5000;
-// Step := 100;
-// ProgressPage :=
-// CreateOutputProgressPage(
-// WizardForm.PageNameLabel.Caption,
-// WizardForm.PageDescriptionLabel.Caption);
-// ProgressPage.SetText('Making sure the satellite service is stopped...', '');
-// ProgressPage.SetProgress(0, Wait);
-// ProgressPage.Show;
-// try
-// for I := 0 to Wait div Step do
-// begin
-// ProgressPage.SetProgress(I * Step, Wait);
-// Sleep(Step);
-// end;
-// finally
-// ProgressPage.Hide;
-// ProgressPage.Free;
-// end;
-// end;
-//end;
\ No newline at end of file
+procedure CurStepChanged(CurStep: TSetupStep);
+var
+ ProgressPage: TOutputProgressWizardPage;
+ I, Step, Wait, ResultCode: Integer;
+begin
+ if CurStep = ssInstall then
+ begin
+ Exec(ExpandConstant('{sys}') + '\sc.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
+
+ //thanks to https://stackoverflow.com/a/39827761
+ Wait := 5000;
+ Step := 100;
+ ProgressPage :=
+ CreateOutputProgressPage(
+ WizardForm.PageNameLabel.Caption,
+ WizardForm.PageDescriptionLabel.Caption);
+ ProgressPage.SetText('Making sure the satellite service is stopped...', '');
+ ProgressPage.SetProgress(0, Wait);
+ ProgressPage.Show;
+ try
+ for I := 0 to Wait div Step do
+ begin
+ ProgressPage.SetProgress(I * Step, Wait);
+ Sleep(Step);
+ end;
+ finally
+ ProgressPage.Hide;
+ ProgressPage.Free;
+ end;
+ end;
+end;
\ No newline at end of file
From ee6c38fae65a04814ee0676781efefa9e5be79ac Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sat, 3 Aug 2024 22:05:40 +0200
Subject: [PATCH 43/45] Fix: added additional null check for malfunctioning
sensors (#134)
---
.../Managers/DeviceSensors/AccelerometerSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs | 6 +++++-
.../HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs | 6 +++++-
.../HASS.Agent/Managers/DeviceSensors/CompassSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs | 7 +++++--
.../Managers/DeviceSensors/InclinometerSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/LightSensor.cs | 6 +++++-
.../Managers/DeviceSensors/MagnetometerSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs | 3 +++
.../HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs | 5 ++++-
.../HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs | 6 +++++-
13 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
index eb7806c5..a8e3013b 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AccelerometerSensor.cs
@@ -29,6 +29,9 @@ public string Measurement
return null;
var sensorReading = _accelerometer.GetCurrentReading();
+ if(sensorReading == null)
+ return null;
+
var accX = Math.Round((decimal)sensorReading.AccelerationX, 2);
var accY = Math.Round((decimal)sensorReading.AccelerationX, 2);
var accZ = Math.Round((decimal)sensorReading.AccelerationX, 2);
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
index be2956d1..c02d0a7b 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ActivitySensor.cs
@@ -27,6 +27,9 @@ public string Measurement
return null;
var sensorReading = _activitySensor.GetCurrentReadingAsync().AsTask().Result;
+ if (sensorReading == null)
+ return null;
+
_attributes[AttributeConfidence] = sensorReading.Confidence.ToString();
_attributes[AttributeTimestamp] = sensorReading.Timestamp.ToLocalTime().ToString();
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
index 958b9370..2662e6ae 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/AltimeterSensor.cs
@@ -23,7 +23,11 @@ public string Measurement
if (!Available)
return null;
- return _altimeter.GetCurrentReading().AltitudeChangeInMeters.ToString();
+ var sensorReading = _altimeter.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
+ return sensorReading.AltitudeChangeInMeters.ToString();
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
index 231261fd..189883fc 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/BarometerSensor.cs
@@ -23,7 +23,11 @@ public string Measurement
if (!Available)
return null;
- return _barometer.GetCurrentReading().StationPressureInHectopascals.ToString();
+ var sensorReading = _barometer.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
+ return sensorReading.StationPressureInHectopascals.ToString();
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
index ec1a5040..2f94d5b6 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/CompassSensor.cs
@@ -26,6 +26,9 @@ public string Measurement
return null;
var sensorReading = _compass.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
_attributes[AttributeMagneticNorth] = Math.Round((decimal)sensorReading.HeadingMagneticNorth, 2).ToString();
return Math.Round((decimal)sensorReading.HeadingTrueNorth, 2).ToString();
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
index 209c73c8..39b4144b 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/GyrometerSensor.cs
@@ -28,6 +28,9 @@ public string Measurement
return null;
var sensorReading = _gyrometer.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
var angVelX = Math.Round((decimal)sensorReading.AngularVelocityX, 2);
var angVelY = Math.Round((decimal)sensorReading.AngularVelocityY, 2);
var angVelZ = Math.Round((decimal)sensorReading.AngularVelocityZ, 2);
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
index 30c748b3..4844c0a9 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HingeAngleSensor.cs
@@ -23,8 +23,11 @@ public string Measurement
if (!Available)
return null;
- var sensorReading = _hingeAngelSensor.GetCurrentReadingAsync().AsTask().Result.AngleInDegrees;
- return Math.Round((decimal)sensorReading, 2).ToString();
+ var sensorReading = _hingeAngelSensor.GetCurrentReadingAsync().AsTask().Result;
+ if (sensorReading == null)
+ return null;
+
+ return Math.Round((decimal)sensorReading.AngleInDegrees, 2).ToString();
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
index ec582c38..a3d1e497 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InclinometerSensor.cs
@@ -30,6 +30,9 @@ public string Measurement
return null;
var sensorReading = _inclinometer.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
_attributes[AttributePitchDegrees] = Math.Round((decimal)sensorReading.PitchDegrees, 2).ToString();
_attributes[AttributeYawDegrees] = Math.Round((decimal)sensorReading.YawDegrees, 2).ToString();
_attributes[AttributeRollDegrees] = Math.Round((decimal)sensorReading.RollDegrees, 2).ToString();
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
index 167d8cd7..74733911 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/LightSensor.cs
@@ -22,8 +22,12 @@ public string Measurement
{
if (!Available)
return null;
+
+ var sensorReading = _lightSensor.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
- return Math.Round((decimal)_lightSensor.GetCurrentReading().IlluminanceInLux, 2).ToString();
+ return Math.Round((decimal)sensorReading.IlluminanceInLux, 2).ToString();
}
}
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
index 61b1f538..d85a5bfe 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/MagnetometerSensor.cs
@@ -28,6 +28,9 @@ public string Measurement
return null;
var sensorReading = _magnetometer.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
var magFieldX = Math.Round((decimal)sensorReading.MagneticFieldX,2);
var magFieldY = Math.Round((decimal)sensorReading.MagneticFieldY, 2);
var magFieldZ = Math.Round((decimal)sensorReading.MagneticFieldZ, 2);
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
index d57b1446..f8b46f17 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/OrientationSensor.cs
@@ -28,6 +28,9 @@ public string Measurement
return null;
var sensorReading = _orientationSensor.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
_attributes[AttributeRotationMatrix] = JsonConvert.SerializeObject(sensorReading.RotationMatrix);
_attributes[AttributeYawAccuracy] = sensorReading.YawAccuracy.ToString();
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
index 013b60c4..f4011458 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/PedometerSensor.cs
@@ -26,7 +26,10 @@ public string Measurement
var totalStepCount = 0;
var sensorReadings = _pedometer.GetCurrentReadings();
- foreach(var sensorReading in sensorReadings)
+ if (sensorReadings == null)
+ return null;
+
+ foreach (var sensorReading in sensorReadings)
{
var attributeCumulativeSteps = $"{sensorReading.Key}CumulativeSteps";
_attributes[attributeCumulativeSteps] = sensorReading.Value.CumulativeSteps.ToString();
diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
index de60c167..3d46f893 100644
--- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
+++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/ProximitySensor.cs
@@ -23,7 +23,11 @@ public string Measurement
if (!Available)
return null;
- return _proximitySensor.GetCurrentReading().DistanceInMillimeters.ToString();
+ var sensorReading = _proximitySensor.GetCurrentReading();
+ if (sensorReading == null)
+ return null;
+
+ return sensorReading.DistanceInMillimeters.ToString();
}
}
From cdb26c69903f54465e46636992626ddecc07201f Mon Sep 17 00:00:00 2001
From: amadeo-alex <68441479+amadeo-alex@users.noreply.github.com>
Date: Sun, 4 Aug 2024 13:17:50 +0200
Subject: [PATCH 44/45] Release: 2.1.0-beta3 (#135)
* changed version to beta3
* changed version to beta3
* added note to the migration task to be used only once
---
src/HASS.Agent.Installer/InstallerScript-Service.iss | 2 +-
src/HASS.Agent.Installer/InstallerScript.iss | 4 ++--
.../HASS.Agent.Satellite.Service.csproj | 2 +-
src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj | 2 +-
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss
index 96c5c501..f630602d 100644
--- a/src/HASS.Agent.Installer/InstallerScript-Service.iss
+++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss
@@ -9,7 +9,7 @@
; Standard installation constants
#define MyAppName "HASS.Agent Satellite Service"
-#define MyAppVersion "2.1.0-beta2"
+#define MyAppVersion "2.1.0-beta3"
#define MyAppPublisher "HASS.Agent Team"
#define MyAppURL "https://hass-agent.io"
#define MyAppExeName "HASS.Agent.Satellite.Service.exe"
diff --git a/src/HASS.Agent.Installer/InstallerScript.iss b/src/HASS.Agent.Installer/InstallerScript.iss
index d1425a74..52f1b638 100644
--- a/src/HASS.Agent.Installer/InstallerScript.iss
+++ b/src/HASS.Agent.Installer/InstallerScript.iss
@@ -9,7 +9,7 @@
; Standard installation constants
#define MyAppName "HASS.Agent"
-#define MyAppVersion "2.1.0-beta2"
+#define MyAppVersion "2.1.0-beta3"
#define MyAppPublisher "HASS.Agent Team"
#define MyAppURL "https://hass-agent.io"
#define MyAppExeName "HASS.Agent.exe"
@@ -62,7 +62,7 @@ Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
-Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration (administrative permissions required)"; Flags: postinstall skipifsilent runascurrentuser unchecked
+Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration - use only once (administrative permissions required)"; Flags: postinstall skipifsilent runascurrentuser unchecked
Filename: "{tmp}\HASS.Agent.Service.Installer.exe"; Description: "Install Satellite Service (administrative permissions required)"; Flags: postinstall runascurrentuser
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall skipifsilent nowait
diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
index 243ebf47..007702cc 100644
--- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
+++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj
@@ -8,7 +8,7 @@
dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154
x64
x64;x86
- 2.1.0-beta2
+ 2.1.0-beta3
HASS.Agent Team
HASS.Agent Satellite Service
HASS.Agent.Satellite.Service
diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
index bd41bf7b..968e8ba9 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
+++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj
@@ -12,7 +12,7 @@
https://github.com/hass-agent/HASS.Agent
2.1.0
2.1.0
- 2.1.0-beta2
+ 2.1.0-beta3
logo_128.png
True
hassagent.ico
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index b4d922eb..1612eadf 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -12,7 +12,7 @@
x64
x64;x86
full
- 2.1.0-beta2
+ 2.1.0-beta3
HASS.Agent Team
HASS.Agent Team
Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more.
From ec6cf762347113e3af4e1bab2d68e9e672260e6b Mon Sep 17 00:00:00 2001
From: Denis
Date: Tue, 17 Sep 2024 16:01:21 +0200
Subject: [PATCH 45/45] Add Device Location Track Sensor, and minor code
cleanups
---
.../HASS.Agent.Shared/Enums/SensorType.cs | 4 +
.../Extensions/CommandExtensions.cs | 25 +-
.../Extensions/SensorExtensions.cs | 3 +-
.../Managers/PowershellManager.cs | 5 +-
.../Localization/Languages.Designer.cs | 9 +
.../Resources/Localization/Languages.de.resx | 3 +
.../Resources/Localization/Languages.en.resx | 3 +
.../Resources/Localization/Languages.es.resx | 3 +
.../Resources/Localization/Languages.fr.resx | 3 +
.../Resources/Localization/Languages.nl.resx | 3 +
.../Resources/Localization/Languages.pl.resx | 3 +
.../Localization/Languages.pt-br.resx | 3 +
.../Resources/Localization/Languages.resx | 3717 +++++++++--------
.../Resources/Localization/Languages.ru.resx | 3 +
.../Resources/Localization/Languages.sl.resx | 3 +
.../Resources/Localization/Languages.tr.resx | 3 +
src/HASS.Agent/HASS.Agent/HASS.Agent.csproj | 40 +-
.../SingleValue/DeviceTrackerSensor.cs | 104 +
.../Models/Internal/GeolocationInfo.cs | 37 +
.../Localization/Languages.Designer.cs | 13 +
.../Resources/Localization/Languages.de.resx | 6 +
.../Resources/Localization/Languages.en.resx | 7 +
.../Resources/Localization/Languages.es.resx | 7 +
.../Resources/Localization/Languages.fr.resx | 7 +
.../Resources/Localization/Languages.nl.resx | 7 +
.../Resources/Localization/Languages.pl.resx | 7 +
.../Localization/Languages.pt-br.resx | 7 +
.../Resources/Localization/Languages.resx | 7 +
.../Resources/Localization/Languages.ru.resx | 7 +
.../Resources/Localization/Languages.sl.resx | 7 +
.../Resources/Localization/Languages.tr.resx | 2011 ++++-----
.../HASS.Agent/Sensors/SensorsManager.cs | 7 +
.../HASS.Agent/Settings/StoredSensors.cs | 3 +
33 files changed, 3192 insertions(+), 2885 deletions(-)
create mode 100644 src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/DeviceTrackerSensor.cs
create mode 100644 src/HASS.Agent/HASS.Agent/Models/Internal/GeolocationInfo.cs
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
index 65348afa..4e2bbb53 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs
@@ -58,6 +58,10 @@ public enum SensorType
[LocalizedDescription("SensorType_GeoLocationSensor", typeof(Languages))]
[EnumMember(Value = "GeoLocationSensor")]
GeoLocationSensor,
+
+ [LocalizedDescription("SensorType_DeviceTrackerSensor", typeof(Languages))]
+ [EnumMember(Value = "DeviceTrackerSensor")]
+ DeviceTrackerSensor,
[LocalizedDescription("SensorType_GpuLoadSensor", typeof(Languages))]
[EnumMember(Value = "GpuLoadSensor")]
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Extensions/CommandExtensions.cs b/src/HASS.Agent/HASS.Agent.Shared/Extensions/CommandExtensions.cs
index 0038d6d3..c7983404 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Extensions/CommandExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Extensions/CommandExtensions.cs
@@ -21,18 +21,19 @@ public static string GetCommandName(this CommandType commandType)
//TODO: remove after tests
- ///
- /// Returns the name of the commandtype, based on the provided devicename
- ///
- ///
- ///
- ///
- /* public static string GetCommandName(this CommandType commandType, string deviceName)
- {
- var (_, name) = commandType.GetLocalizedDescriptionAndKey();
- var commandName = name.ToLower();
+ /*
+ ///
+ /// Returns the name of the commandtype, based on the provided devicename
+ ///
+ ///
+ ///
+ ///
+ public static string GetCommandName(this CommandType commandType, string deviceName)
+ {
+ var (_, name) = commandType.GetLocalizedDescriptionAndKey();
+ var commandName = name.ToLower();
- return $"{SharedHelperFunctions.GetSafeValue(deviceName)}_{commandName}";
- }*/
+ return $"{SharedHelperFunctions.GetSafeValue(deviceName)}_{commandName}";
+ }*/
}
}
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Extensions/SensorExtensions.cs b/src/HASS.Agent/HASS.Agent.Shared/Extensions/SensorExtensions.cs
index ef91a026..2dd5617c 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Extensions/SensorExtensions.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Extensions/SensorExtensions.cs
@@ -21,13 +21,14 @@ public static string GetSensorName(this SensorType sensorType)
//TODO: remove after tests
+ /*
///
/// Returns the name of the sensortype, based on the provided devicename
///
///
///
///
-/* public static string GetSensorName(this SensorType sensorType, string deviceName)
+ public static string GetSensorName(this SensorType sensorType, string deviceName)
{
var (_, name) = sensorType.GetLocalizedDescriptionAndKey();
var sensorName = name.ToLower();
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
index f28df5d7..37e13b4d 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/PowershellManager.cs
@@ -2,10 +2,8 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
-using System.Linq;
using System.Text;
-using System.Text.RegularExpressions;
-using CliWrap;
+
using Newtonsoft.Json;
using Serilog;
@@ -105,6 +103,7 @@ private static bool ExecuteHeadless(string command, string parameters, bool isSc
/// Executes a Powershell script, logs the output if it fails
///
///
+ ///
///
///
public static bool ExecuteScript(string script, string parameters, TimeSpan timeout) => Execute(script, parameters, true, timeout);
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
index 931858fa..0dd73ba8 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs
@@ -6591,6 +6591,15 @@ internal static string SensorType_CurrentVolumeSensor {
}
}
+ ///
+ /// Looks up a localized string similar to DeviceLocationTracker.
+ ///
+ internal static string SensorType_DeviceTrackerSensor {
+ get {
+ return ResourceManager.GetString("SensorType_DeviceTrackerSensor", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Display.
///
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
index c5179acd..e1fa5ece 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx
@@ -3376,4 +3376,7 @@ Willst Du den Runtime Installer herunterladen?
Presse
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
index f881bf80..ef59f0ef 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx
@@ -3252,4 +3252,7 @@ Do you want to download the runtime installer?
Button
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
index 2ea0d91a..30e9d133 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx
@@ -3252,4 +3252,7 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida.
Prensa
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
index 27c3ec38..b1fdcaaa 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx
@@ -3285,4 +3285,7 @@ Do you want to download the runtime installer?
Presse
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
index 3a9871c4..c43f0a20 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx
@@ -3274,4 +3274,7 @@ Wil je de runtime installatie downloaden?
Pers
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
index 6947c51d..64ffb845 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx
@@ -3362,4 +3362,7 @@ Czy chcesz pobrać plik instalacyjny?
Naciśnij
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
index a7b67ecb..e6e688a5 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx
@@ -3299,4 +3299,7 @@ Deseja baixar o Microsoft WebView2 runtime?
Imprensa
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
index 8fc95014..cdf119f3 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx
@@ -1,76 +1,96 @@
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ text/microsoft-resx
+ 2.0
+ System.Resources.ResXResourceReader, System.Windows.Forms, ...
+ System.Resources.ResXResourceWriter, System.Windows.Forms, ...
+ this is my long stringthis is a comment
+ Blue
+
+ [base64 mime encoded serialized .NET Framework object]
+
+
+ [base64 mime encoded string representing a byte array form of the .NET Framework object]
+ This is a comment
+
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
@@ -89,296 +109,296 @@
text/microsoft-resx
- 1.3
+ 2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- This page allows you to configure bindings with external tools.
-
+ This page allows you to configure bindings with external tools.
+
- Clear Image Cache
-
+ Clear Image Cache
+
- Open Folder
-
+ Open Folder
+
- Image Cache Location
-
+ Image Cache Location
+
- days
-
+ days
+
- Port
-
+ Port
+
- Show Test Notification
-
+ Show Test Notification
+
- Execute Port Reservation
-
+ Execute Port Reservation
+
- Note: 5115 is the default port, only change it if you changed it in Home Assistant.
-
+ Note: 5115 is the default port, only change it if you changed it in Home Assistant.
+
- Yes, accept notifications on port
-
+ Yes, accept notifications on port
+
- HASS.Agent can receive notifications from Home Assistant, using text and/or images.
+ HASS.Agent can receive notifications from Home Assistant, using text and/or images.
Do you want to enable this function?
-
+
- HASS.Agent-Notifier GitHub Page
-
+ HASS.Agent-Notifier GitHub Page
+
- Make sure you follow these steps:
+ Make sure you follow these steps:
- Install HASS.Agent-Notifier integration
- Restart Home Assistant
- Configure a notifier entity
- Restart Home Assistant
-
+
- To use notifications, you need to install and configure the HASS.Agent-notifier integration in
+ To use notifications, you need to install and configure the HASS.Agent-notifier integration in
Home Assistant.
This is very easy using HACS but may also be installed manually, visit the link below for more
information.
-
+
- Name
-
+ Name
+
- Type
-
+ Type
+
- Apply
-
+ Apply
+
- Stored!
-
+ Stored!
+
- Name
-
+ Name
+
- Type
-
+ Type
+
- HASS.Agent Port Reservation
-
+ HASS.Agent Port Reservation
+
- HASS.Agent Post Update
-
+ HASS.Agent Post Update
+
- HASS.Agent Restarter
-
+ HASS.Agent Restarter
+
- Name
-
+ Name
+
- Type
-
+ Type
+
- Type
-
+ Type
+
- Domain
-
+ Domain
+
- Entity
-
+ Entity
+
- Action
-
+ Action
+
- Hotkey
-
+ Hotkey
+
- Description
-
+ Description
+
- Domain
-
+ Domain
+
- enable hotkey
-
+ enable hotkey
+
- Name
-
+ Name
+
- Type
-
+ Type
+
- Sensors Configuration
-
+ Sensors Configuration
+
- setting 1
-
+ setting 1
+
- Setting 2
-
+ Setting 2
+
- Setting 3
-
+ Setting 3
+
- Type
-
+ Type
+
- General
-
+ General
+
- MQTT
-
+ MQTT
+
- Commands
-
+ Commands
+
- Sensors
-
+ Sensors
+
- General
-
+ General
+
- External Tools
-
+ External Tools
+
- Home Assistant API
-
+ Home Assistant API
+
- Hotkey
-
+ Hotkey
+
- Local Storage
-
+ Local Storage
+
- Logging
-
+ Logging
+
- MQTT
-
+ MQTT
+
- Notifications
-
+ Notifications
+
- Satellite Service
-
+ Satellite Service
+
- Startup
-
+ Startup
+
- Updates
-
+ Updates
+
- Browse HASS.Agent documentation and usage examples.
-
+ Browse HASS.Agent documentation and usage examples.
+
- Show HASS.Agent
-
+ Show HASS.Agent
+
- Show Quick Actions
-
+ Show Quick Actions
+
- Configuration
-
+ Configuration
+
- Manage Quick Actions
-
+ Manage Quick Actions
+
- Manage Local Sensors
-
+ Manage Local Sensors
+
- Manage Commands
-
+ Manage Commands
+
- Check for Updates
-
+ Check for Updates
+
- Donate
-
+ Donate
+
- Help && Contact
-
+ Help && Contact
+
- About
-
+ About
+
- Exit HASS.Agent
-
+ Exit HASS.Agent
+
- Loading..
-
+ Loading..
+
- Loading..
-
+ Loading..
+
- notification api:
-
+ notification api:
+
- HASS.Agent Onboarding
-
+ HASS.Agent Onboarding
+
- Fetching info, please wait..
-
+ Fetching info, please wait..
+
- Execute a custom command.
+ Execute a custom command.
These commands run without special elevation. To run elevated, create a Scheduled Task, and use 'schtasks /Run /TN "TaskName"' as the command to execute your task.
Or enable 'run as low integrity' for even stricter execution.
-
+
- Executes the command through the configured custom executor (in Configuration -> External Tools).
+ Executes the command through the configured custom executor (in Configuration -> External Tools).
Your command is provided as an argument 'as is', so you have to supply your own quotes etc. if necessary.
-
+
- Sets the machine in hibernation.
-
+ Sets the machine in hibernation.
+
- Simulates a single keypress.
+ Simulates a single keypress.
Click on the 'keycode' textbox and press the key you want simulated. The corresponding keycode will be entered for you.
If you need more keys and/or modifiers like CTRL, use the MultipleKeys command.
-
+
- Launches the provided URL, by default in your default browser.
+ Launches the provided URL, by default in your default browser.
To use 'incognito', provide a specific browser in Configuration -> External Tools.
If you just want a window with a specific URL (not an entire browser), use a 'WebView' command.
-
+
- Locks the current session.
-
+ Locks the current session.
+
- Logs off the current session.
-
+ Logs off the current session.
+
- Simulates 'Mute' key.
-
+ Simulates 'Mute' key.
+
- Simulates 'Media Next' key.
-
+ Simulates 'Media Next' key.
+
- Simulates 'Media Pause/Play' key.
-
+ Simulates 'Media Pause/Play' key.
+
- Simulates 'Media Previous' key.
-
+ Simulates 'Media Previous' key.
+
- Simulates 'Volume Down' key.
-
+ Simulates 'Volume Down' key.
+
- Simulates 'Volume Up' key.
-
+ Simulates 'Volume Up' key.
+
- Simulates pressing mulitple keys.
+ Simulates pressing mulitple keys.
You need to put [ ] between every key, otherwise HASS.Agent can't tell them apart. So say you want to press X TAB Y SHIFT-Z, it'd be [X] [{TAB}] [Y] [+Z].
@@ -393,803 +413,803 @@ There are a few tricks you can use:
- For multiple presses, use {z 15}, which means Z will get pressed 15 times.
More info: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.sendkeys
-
+
- Execute a Powershell command or script.
+ Execute a Powershell command or script.
You can either provide the location of a script (*.ps1), or a single-line command.
This will run without special elevation.
-
+
- Resets all sensor checks, forcing all sensors to process and send their value.
+ Resets all sensor checks, forcing all sensors to process and send their value.
Useful for example if you want to force HASS.Agent to update all your sensors after a HA reboot.
-
+
- Restarts the machine after one minute.
+ Restarts the machine after one minute.
Tip: Accidentally triggered? Run 'shutdown /a' to abort shutdown.
-
+
- Shuts down the machine after one minute.
+ Shuts down the machine after one minute.
Tip: Accidentally triggered? Run 'shutdown /a' to abort shutdown.
-
+
- Puts the machine to sleep.
+ Puts the machine to sleep.
Note: due to a limitation in Windows, this only works if hibernation is disabled, otherwise it will just hibernate.
You can use something like NirCmd (http://www.nirsoft.net/utils/nircmd.html) to circumvent this.
-
+
- Please enter the location of your browser's binary! (.exe file)
-
+ Please enter the location of your browser's binary! (.exe file)
+
- The browser binary provided could not be found, please ensure the path is correct and try again.
-
+ The browser binary provided could not be found, please ensure the path is correct and try again.
+
- No incognito arguments were provided so the browser will likely launch normally.
+ No incognito arguments were provided so the browser will likely launch normally.
Do you want to continue?
-
+
- Something went wrong while launching your browser in incognito mode!
+ Something went wrong while launching your browser in incognito mode!
Please check the logs for more information.
-
+
- Please enter a valid API key!
-
+ Please enter a valid API key!
+
- Please enter a value for your Home Assistant's URI.
-
+ Please enter a value for your Home Assistant's URI.
+
- Unable to connect, the following error was returned:
+ Unable to connect, the following error was returned:
{0}
-
+
- Connection OK!
+ Connection OK!
Home Assistant version: {0}
-
+
- Image cache has been cleared!
-
+ Image cache has been cleared!
+
- Cleaning..
-
+ Cleaning..
+
- Notifications are currently disabled, please enable them and restart the HASS.Agent, then try again.
-
+ Notifications are currently disabled, please enable them and restart the HASS.Agent, then try again.
+
- The test notification should have appeared, if you did not receive it please check the logs or consult the documentation for troubleshooting tips.
+ The test notification should have appeared, if you did not receive it please check the logs or consult the documentation for troubleshooting tips.
Note: This only tests locally whether notifications can be shown!
-
+
- This is a test notification!
-
+ This is a test notification!
+
- Executing, please wait..
-
+ Executing, please wait..
+
- Something went wrong whilst reserving the port!
+ Something went wrong whilst reserving the port!
Manual execution is required and a command has been copied to your clipboard, please open an elevated terminal and paste the command.
Additionally, remember to change your Firewall Rules port!
-
+
- Not Installed
-
+ Not Installed
+
- Disabled
-
+ Disabled
+
- Running
-
+ Running
+
- Stopped
-
+ Stopped
+
- Failed
-
+ Failed
+
- Something went wrong whilst stopping the service, did you allow the UAC prompt?
+ Something went wrong whilst stopping the service, did you allow the UAC prompt?
Check the HASS.Agent (not the service) logs for more information.
-
+
- The service is set to 'disabled', so it cannot be started.
+ The service is set to 'disabled', so it cannot be started.
Please enable the service first and try again.
-
+
- Something went wrong whilst starting the service, did you allow the UAC prompt?
+ Something went wrong whilst starting the service, did you allow the UAC prompt?
Check the HASS.Agent (not the service) logs for more information.
-
+
- Something went wrong whilst disabling the service, did you allow the UAC prompt?
+ Something went wrong whilst disabling the service, did you allow the UAC prompt?
Check the HASS.Agent (not the service) logs for more information.
-
+
- Something went wrong whilst enabling the service, did you allow the UAC prompt?
+ Something went wrong whilst enabling the service, did you allow the UAC prompt?
Check the HASS.Agent (not the service) logs for more information.
-
+
- Something went wrong whilst reinstalling the service, did you allow the UAC prompt?
+ Something went wrong whilst reinstalling the service, did you allow the UAC prompt?
Check the HASS.Agent (not the service) logs for more information.
-
+
- Something went wrong whilst disabling Start-on-Login, please check the logs for more information.
-
+ Something went wrong whilst disabling Start-on-Login, please check the logs for more information.
+
- Something went wrong whilst disabling Start-on-Login, please check the logs for more information.
-
+ Something went wrong whilst disabling Start-on-Login, please check the logs for more information.
+
- Enabled
-
+ Enabled
+
- Disable Start-on-Login
-
+ Disable Start-on-Login
+
- Disabled
-
+ Disabled
+
- Enable Start-on-Login
-
+ Enable Start-on-Login
+
- Start-on-Login has been activated!
-
+ Start-on-Login has been activated!
+
- Do you want to enable Start-on-Login now?
-
+ Do you want to enable Start-on-Login now?
+
- Start-on-Login is already activated, all set!
-
+ Start-on-Login is already activated, all set!
+
- Activating Start-on-Login..
-
+ Activating Start-on-Login..
+
- Something went wrong. You can try again, or skip to the next page and retry after HASS.Agent's restart.
-
+ Something went wrong. You can try again, or skip to the next page and retry after HASS.Agent's restart.
+
- Enable Start-on-Login
-
+ Enable Start-on-Login
+
- Please provide a valid API key.
-
+ Please provide a valid API key.
+
- Please enter your Home Assistant's URI.
-
+ Please enter your Home Assistant's URI.
+
- Unable to connect, the following error was returned:
+ Unable to connect, the following error was returned:
{0}
-
+
- Connection OK!
+ Connection OK!
Home Assistant version: {0}
-
+
- Testing..
-
+ Testing..
+
- An error occurred whilst saving your commands, please check the logs for more information.
-
+ An error occurred whilst saving your commands, please check the logs for more information.
+
- Storing and registering, please wait..
-
+ Storing and registering, please wait..
+
- Connecting with satellite service, please wait..
-
+ Connecting with satellite service, please wait..
+
- Connecting to the service has failed!
-
+ Connecting to the service has failed!
+
- The service hasn't been found! You can install and manage it from the configuration panel.
+ The service hasn't been found! You can install and manage it from the configuration panel.
When it's up and running, come back here to configure the commands and sensors.
-
+
- Communicating with the service has failed!
-
+ Communicating with the service has failed!
+
- Unable to communicate with the service. Check the logs for more info.
+ Unable to communicate with the service. Check the logs for more info.
You can open the logs and manage the service from the configuration panel.
-
+
- Unauthorized
-
+ Unauthorized
+
- You are not authorized to contact the service.
+ You are not authorized to contact the service.
If you have the correct auth ID, you can set it now and try again.
-
+
- Fetching settings failed!
-
+ Fetching settings failed!
+
- The service returned an error while requesting its settings. Check the logs for more info.
+ The service returned an error while requesting its settings. Check the logs for more info.
You can open the logs and manage the service from the configuration panel.
-
+
- Fetching MQTT settings failed!
-
+ Fetching MQTT settings failed!
+
- The service returned an error while requesting its MQTT settings. Check the logs for more info.
+ The service returned an error while requesting its MQTT settings. Check the logs for more info.
You can open the logs and manage the service from the configuration panel.
-
+
- Fetching configured commands failed!
-
+ Fetching configured commands failed!
+
- The service returned an error while requesting its configured commands. Check the logs for more info.
+ The service returned an error while requesting its configured commands. Check the logs for more info.
You can open the logs and manage the service from the configuration panel.
-
+
- Fetching configured sensors failed!
-
+ Fetching configured sensors failed!
+
- The service returned an error while requesting its configured sensors. Check the logs for more info.
+ The service returned an error while requesting its configured sensors. Check the logs for more info.
You can open the logs and manage the service from the configuration panel.
-
+
- Storing an empty auth ID will allow all HASS.Agent to access the service.
+ Storing an empty auth ID will allow all HASS.Agent to access the service.
Are you sure you want this?
-
+
- An error occurred whilst saving, check the logs for more information.
-
+ An error occurred whilst saving, check the logs for more information.
+
- Please provide a device name!
-
+ Please provide a device name!
+
- Please select an executor first. (Tip: Double click to Browse)
-
+ Please select an executor first. (Tip: Double click to Browse)
+
- The selected executor could not be found, please ensure the path provided is correct and try again.
-
+ The selected executor could not be found, please ensure the path provided is correct and try again.
+
- Set an auth ID if you don't want every instance of HASS.Agent on this PC to connect with the satellite service.
+ Set an auth ID if you don't want every instance of HASS.Agent on this PC to connect with the satellite service.
Only the instances that have the correct ID, can connect.
Leave empty to allow all to connect.
-
+
- This is the name with which the satellite service registers itself on Home Assistant.
+ This is the name with which the satellite service registers itself on Home Assistant.
By default, it's your PC's name plus '-satellite'.
-
+
- The amount of time the satellite service will wait before reporting a lost connection to the MQTT broker.
-
+ The amount of time the satellite service will wait before reporting a lost connection to the MQTT broker.
+
- Error fetching status, please check logs for information.
-
+ Error fetching status, please check logs for information.
+
- An error occurred whilst saving the configuration, please check the logs for more information.
-
+ An error occurred whilst saving the configuration, please check the logs for more information.
+
- Storing and registering, please wait..
-
+ Storing and registering, please wait..
+
- An error occurred whilst saving the sensors, please check the logs for more information.
-
+ An error occurred whilst saving the sensors, please check the logs for more information.
+
- Storing and registering, please wait..
-
+ Storing and registering, please wait..
+
- Not all steps completed succesfully. Please consult the logs for more information.
-
+ Not all steps completed succesfully. Please consult the logs for more information.
+
- Not all steps completed succesfully. Please consult the logs for more information.
-
+ Not all steps completed succesfully. Please consult the logs for more information.
+
- HASS.Agent is still active after {0} seconds. Please close all instances and restart manually.
+ HASS.Agent is still active after {0} seconds. Please close all instances and restart manually.
Check the logs for more info, and optionally inform the developers.
-
+
- Not all steps completed successfully, please check the logs for more information.
-
+ Not all steps completed successfully, please check the logs for more information.
+
- Enable Satellite Service
-
+ Enable Satellite Service
+
- Disable Satellite Service
-
+ Disable Satellite Service
+
- Start Satellite Service
-
+ Start Satellite Service
+
- Stop Satellite Service
-
+ Stop Satellite Service
+
- Something went wrong while processing the desired service state.
+ Something went wrong while processing the desired service state.
Please consult the logs for more information.
-
+
- Topic copied to clipboard!
-
+ Topic copied to clipboard!
+
- Storing and registering, please wait..
-
+ Storing and registering, please wait..
+
- An error occurred whilst saving commands, please check the logs for more information.
-
+ An error occurred whilst saving commands, please check the logs for more information.
+
- New Command
-
+ New Command
+
- Mod Command
-
+ Mod Command
+
- Please select a command type!
-
+ Please select a command type!
+
- Please select a valid command type!
-
+ Please select a valid command type!
+
- Select a valid entity type first.
-
+ Select a valid entity type first.
+
- Please provide a name!
-
+ Please provide a name!
+
- A command with that name already exists, are you sure you want to continue?
-
+ A command with that name already exists, are you sure you want to continue?
+
- If a command is not provided, you may only use this entity with an 'action' value via Home Assistant, running it as-is will have no action.
+ If a command is not provided, you may only use this entity with an 'action' value via Home Assistant, running it as-is will have no action.
Are you sure you want to proceed?
-
+
- If you don't enter a command or script, you can only use this entity with an 'action' value through Home Assistant. Running it as-is won't do anything.
+ If you don't enter a command or script, you can only use this entity with an 'action' value through Home Assistant. Running it as-is won't do anything.
Are you sure you want this?
-
+
- Please enter a key code!
-
+ Please enter a key code!
+
- Checking keys failed: {0}
-
+ Checking keys failed: {0}
+
- If a URL is not provided, you may only use this entity with an 'action' value via Home Assistant, running it as-is will have no action.
+ If a URL is not provided, you may only use this entity with an 'action' value via Home Assistant, running it as-is will have no action.
Are you sure you want to proceed?
-
+
- Command
-
+ Command
+
- Command or Script
-
+ Command or Script
+
- Keycode
-
+ Keycode
+
- Keycodes
-
+ Keycodes
+
- Launch in Incognito Mode
-
+ Launch in Incognito Mode
+
- Browser: Default
+ Browser: Default
Please configure a custom browser to enable incognito mode.
-
+
- URL
-
+ URL
+
- Browser: {0}
-
+ Browser: {0}
+
- Executor: None
+ Executor: None
Please configure an executor or your command will not run.
-
+
- Executor: {0}
-
+ Executor: {0}
+
- Low integrity means your command will be executed with restricted privileges.
-
+ Low integrity means your command will be executed with restricted privileges.
+
- This means it will only be able to save and modify files in certain locations,
-
+ This means it will only be able to save and modify files in certain locations,
+
- such as the '%USERPROFILE%\AppData\LocalLow' folder or
-
+ such as the '%USERPROFILE%\AppData\LocalLow' folder or
+
- the 'HKEY_CURRENT_USER\Software\AppDataLow' registry key.
-
+ the 'HKEY_CURRENT_USER\Software\AppDataLow' registry key.
+
- You should test your command to make sure it's not influenced by this!
-
+ You should test your command to make sure it's not influenced by this!
+
- {0} only!
-
+ {0} only!
+
- The MQTT manager hasn't been configured properly, or hasn't yet completed its startup.
-
+ The MQTT manager hasn't been configured properly, or hasn't yet completed its startup.
+
- Unable to fetch your entities because of missing config, please enter the required values in the config screen.
-
+ Unable to fetch your entities because of missing config, please enter the required values in the config screen.
+
- There was an error trying to fetch your entities!
-
+ There was an error trying to fetch your entities!
+
- New Quick Action
-
+ New Quick Action
+
- Mod Quick Action
-
+ Mod Quick Action
+
- Unable to fetch your entities because of missing config, please enter the required values in the config screen.
-
+ Unable to fetch your entities because of missing config, please enter the required values in the config screen.
+
- There was an error trying to fetch your entities.
-
+ There was an error trying to fetch your entities.
+
- Please select an entity!
-
+ Please select an entity!
+
- Unknown action, please select a valid one.
-
+ Unknown action, please select a valid one.
+
- Storing and registering, please wait..
-
+ Storing and registering, please wait..
+
- An error occurred whilst saving the sensors, please check the logs for more information.
-
+ An error occurred whilst saving the sensors, please check the logs for more information.
+
- New Sensor
-
+ New Sensor
+
- Mod Sensor
-
+ Mod Sensor
+
- Window Name
-
+ Window Name
+
- WMI Query
-
+ WMI Query
+
- WMI Scope (optional)
-
+ WMI Scope (optional)
+
- Category
-
+ Category
+
- Counter
-
+ Counter
+
- Instance (optional)
-
+ Instance (optional)
+
- Process
-
+ Process
+
- Service
-
+ Service
+
- Please select a sensor type!
-
+ Please select a sensor type!
+
- Please select a valid sensor type!
-
+ Please select a valid sensor type!
+
- Please provide a name!
-
+ Please provide a name!
+
- A single-value sensor already exists with that name, are you sure you want to proceed?
-
+ A single-value sensor already exists with that name, are you sure you want to proceed?
+
- A multi-value sensor already exists with that name, are you sure you want to proceed?
-
+ A multi-value sensor already exists with that name, are you sure you want to proceed?
+
- Please provide an interval between 1 and 43200 (12 hours)!
-
+ Please provide an interval between 1 and 43200 (12 hours)!
+
- Please enter a window name!
-
+ Please enter a window name!
+
- Please enter a query!
-
+ Please enter a query!
+
- Please enter a category and instance!
-
+ Please enter a category and instance!
+
- Please enter the name of a process!
-
+ Please enter the name of a process!
+
- Please enter the name of a service!
-
+ Please enter the name of a service!
+
- {0} only!
-
+ {0} only!
+
- You've changed your device's name.
+ You've changed your device's name.
All your sensors and commands will now be unpublished, and HASS.Agent will restart afterwards to republish them.
Don't worry, they'll keep their current names, so your automations or scripts will keep working.
Note: the name will get 'sanitized', which means everything except letters, digits and whitespace get replaced by an underscore. This is required by HA.
-
+
- You've changed the local API's port. This new port needs to be reserved.
+ You've changed the local API's port. This new port needs to be reserved.
You'll get an UAC request to do so, please approve.
-
+
- Something went wrong!
+ Something went wrong!
Please manually execute the required command. It has been copied onto your clipboard, you just need to paste it into an elevated command prompt.
Remember to change your firewall rule's port as well.
-
+
- The port has succesfully been reserved!
+ The port has succesfully been reserved!
HASS.Agent will now restart to activate the new configuration.
-
+
- Something went wrong while preparing to restart.
+ Something went wrong while preparing to restart.
Please restart manually.
-
+
- Your configuration has been saved. Most changes require HASS.Agent to restart before they take effect.
+ Your configuration has been saved. Most changes require HASS.Agent to restart before they take effect.
Do you want to restart now?
-
+
- Something went wrong while loading your settings.
+ Something went wrong while loading your settings.
Check appsettings.json in the 'config' subfolder, or just delete it to start fresh.
-
+
- There was an error launching HASS.Agent.
+ There was an error launching HASS.Agent.
Please check the logs and make a bug report on GitHub.
-
+
- &Sensors
-
+ &Sensors
+
- &Commands
-
+ &Commands
+
- Checking..
-
+ Checking..
+
- You're running the latest version: {0}{1}
-
+ You're running the latest version: {0}{1}
+
- There's a new BETA release available:
-
+ There's a new BETA release available:
+
- HASS.Agent BETA Update
-
+ HASS.Agent BETA Update
+
- Do you want to &download and launch the installer?
-
+ Do you want to &download and launch the installer?
+
- Do you want to &navigate to the release page?
-
+ Do you want to &navigate to the release page?
+
- Install Update
-
+ Install Update
+
- Install Beta Release
-
+ Install Beta Release
+
- Open Release Page
-
+ Open Release Page
+
- Open Beta Release Page
-
+ Open Beta Release Page
+
- Processing request, please wait..
-
+ Processing request, please wait..
+
- Processing..
-
+ Processing..
+
- HASS.Agent Onboarding: Start [{0}/{1}]
-
+ HASS.Agent Onboarding: Start [{0}/{1}]
+
- HASS.Agent Onboarding: Startup [{0}/{1}]
-
+ HASS.Agent Onboarding: Startup [{0}/{1}]
+
- HASS.Agent Onboarding: Notifications [{0}/{1}]
-
+ HASS.Agent Onboarding: Notifications [{0}/{1}]
+
- HASS.Agent Onboarding: Integration [{0}/{1}]
-
+ HASS.Agent Onboarding: Integration [{0}/{1}]
+
- HASS.Agent Onboarding: API [{0}/{1}]
-
+ HASS.Agent Onboarding: API [{0}/{1}]
+
- HASS.Agent Onboarding: MQTT [{0}/{1}]
-
+ HASS.Agent Onboarding: MQTT [{0}/{1}]
+
- HASS.Agent Onboarding: HotKey [{0}/{1}]
-
+ HASS.Agent Onboarding: HotKey [{0}/{1}]
+
- HASS.Agent Onboarding: Updates [{0}/{1}]
-
+ HASS.Agent Onboarding: Updates [{0}/{1}]
+
- HASS.Agent Onboarding: Completed [{0}/{1}]
-
+ HASS.Agent Onboarding: Completed [{0}/{1}]
+
- Are you sure you want to abort the onboarding process?
+ Are you sure you want to abort the onboarding process?
Your progress will not be saved, and it will not be shown again on next launch.
-
+
- Error fetching info, please check logs for more information.
-
+ Error fetching info, please check logs for more information.
+
- Unable to prepare downloading the update, check the logs for more info.
+ Unable to prepare downloading the update, check the logs for more info.
The release page will now open instead.
-
+
- Unable to download the update, check the logs for more info.
+ Unable to download the update, check the logs for more info.
The release page will now open instead.
-
+
- The downloaded file FAILED the certificate check.
+ The downloaded file FAILED the certificate check.
This could be a technical error, but also a tampered file!
Please check the logs, and post a ticket with the findings.
-
+
- Unable to launch the installer (did you approve the UAC prompt?), check the logs for more info.
+ Unable to launch the installer (did you approve the UAC prompt?), check the logs for more info.
The release page will now open instead.
-
+
- HASS API: Connection setup failed.
-
+ HASS API: Connection setup failed.
+
- HASS API: Initial connection failed.
-
+ HASS API: Initial connection failed.
+
- HASS API: Connection failed.
-
+ HASS API: Connection failed.
+
- Client certificate file not found.
-
+ Client certificate file not found.
+
- Unable to connect, check URI.
-
+ Unable to connect, check URI.
+
- Unable to fetch configuration, please check API key.
-
+ Unable to fetch configuration, please check API key.
+
- Unable to connect, please check URI and configuration.
-
+ Unable to connect, please check URI and configuration.
+
- quick action: action failed, check the logs for info
-
+ quick action: action failed, check the logs for info
+
- quick action: action failed, entity not found
-
+ quick action: action failed, entity not found
+
- MQTT: Error while connecting
-
+ MQTT: Error while connecting
+
- MQTT: Failed to connect
-
+ MQTT: Failed to connect
+
- MQTT: Disconnected
-
+ MQTT: Disconnected
+
- Error trying to bind the API to port {0}.
+ Error trying to bind the API to port {0}.
Make sure no other instance of HASS.Agent is running and the port is available and registered.
-
+
- Provides the title of the current active window.
-
+ Provides the title of the current active window.
+
- Provides information various aspects of your device's audio:
+ Provides information various aspects of your device's audio:
Current peak volume level (can be used as a simple 'is something playing' value).
Default audio device: name, state and volume.
Summary of your audio sessions: application name, muted state, volume and current peak volume.
-
+
- Provides a sensor with the current charging status, estimated amount of minutes on a full charge, remaining charge as a percentage, remaining charge in minutes and the powerline status.
-
+ Provides a sensor with the current charging status, estimated amount of minutes on a full charge, remaining charge as a percentage, remaining charge in minutes and the powerline status.
+
- Provides the current load of the first CPU as a percentage.
-
+ Provides the current load of the first CPU as a percentage.
+
- Provides the current clockspeed of the first CPU.
-
+ Provides the current clockspeed of the first CPU.
+
- Provides the current volume level as a percentage.
+ Provides the current volume level as a percentage.
Currently takes the volume of your default device.
-
+
- Provides a sensor with the amount of displays, name of the primary display, and per display its name, resolution and bits per pixel.
-
+ Provides a sensor with the amount of displays, name of the primary display, and per display its name, resolution and bits per pixel.
+
- Dummy sensor for testing purposes, sends a random integer value between 0 and 100.
-
+ Dummy sensor for testing purposes, sends a random integer value between 0 and 100.
+
- Provides the current load of the first GPU as a percentage.
-
+ Provides the current load of the first GPU as a percentage.
+
- Provides the current temperature of the first GPU.
-
+ Provides the current temperature of the first GPU.
+
- Provides a datetime value containing the last moment the user provided any input.
-
+ Provides a datetime value containing the last moment the user provided any input.
+
- Provides a datetime value containing the last moment the system (re)booted.
+ Provides a datetime value containing the last moment the system (re)booted.
Important: Windows' FastBoot option can throw this value off, because that's a form of hibernation. You can disable it through Power Options -> 'Choose what the power buttons do' -> uncheck 'Turn on fast start-up'. It doesn't make much difference for modern machines with SSDs, but disabling makes sure you get a clean state after rebooting.
-
+
- Provides the last system state change:
+ Provides the last system state change:
ApplicationStarted, Logoff, SystemShutdown, Resume, Suspend, ConsoleConnect, ConsoleDisconnect, RemoteConnect, RemoteDisconnect, SessionLock, SessionLogoff, SessionLogon, SessionRemoteControl and SessionUnlock.
-
+
- Returns the name of the currently logged user.
+ Returns the name of the currently logged user.
This will only show active users, and falls back to 'Empty' if there are none. If there are multiple, the first will be used.
-
+
- Returns a json-formatted list of currently logged users.
+ Returns a json-formatted list of currently logged users.
This will also contain users that aren't active. If you only want the current active user, use the LoggedUser sensor instead.
-
+
- Provides the amount of used memory as a percentage.
-
+ Provides the amount of used memory as a percentage.
+
- Provides a bool value based on whether the microphone is currently being used.
+ Provides a bool value based on whether the microphone is currently being used.
Note: if used in the satellite service, it won't detect userspace applications.
-
+
- Provides an ON/OFF value based on whether the window is currently open (doesn't have to be active).
-
+ Provides an ON/OFF value based on whether the window is currently open (doesn't have to be active).
+
- Provides card info, configuration, transfer- & package statistics and addresses (ip, mac, dhcp, dns) of the selected network card(s).
+ Provides card info, configuration, transfer- & package statistics and addresses (ip, mac, dhcp, dns) of the selected network card(s).
This is a multi-value sensor.
-
+
- Provides the values of a performance counter.
+ Provides the values of a performance counter.
For example, the built-in CPU load sensor uses these values:
@@ -1198,415 +1218,415 @@ Counter: % Processor Time
Instance: _Total
You can explore the counters through Windows' 'perfmon.exe' tool.
-
+
- Provides the number of active instances of the process.
+ Provides the number of active instances of the process.
Note: don't add the extension (eg. notepad.exe becomes notepad).
-
+
- Returns the state of the provided service: NotFound, Stopped, StartPending, StopPending, Running, ContinuePending, PausePending or Paused.
+ Returns the state of the provided service: NotFound, Stopped, StartPending, StopPending, Running, ContinuePending, PausePending or Paused.
Make sure to provide the 'Service name', not the 'Display name'.
-
+
- Provides the current session state:
+ Provides the current session state:
Locked, Unlocked or Unknown.
Use a LastSystemStateChangeSensor to monitor session state changes.
-
+
- Provides the labels, total size (MB), available space (MB), used space (MB) and file system of all present non-removable disks.
-
+ Provides the labels, total size (MB), available space (MB), used space (MB) and file system of all present non-removable disks.
+
- Provides the current user state:
+ Provides the current user state:
NotPresent, Busy, RunningDirect3dFullScreen, PresentationMode, AcceptsNotifications, QuietTime or RunningWindowsStoreApp.
Can for instance be used to determine whether to send notifications or TTS messages.
-
+
- Provides a bool value based on whether the webcam is currently being used.
+ Provides a bool value based on whether the webcam is currently being used.
Note: if used in the satellite service, it won't detect userspace applications.
-
+
- Provides a sensor with the amount of pending driver updates, a sensor with the amount of pending software updates, a sensor containing all pending driver updates information (title, kb article id's, hidden, type and categories) and a sensor containing the same for pending software updates.
+ Provides a sensor with the amount of pending driver updates, a sensor with the amount of pending software updates, a sensor containing all pending driver updates information (title, kb article id's, hidden, type and categories) and a sensor containing the same for pending software updates.
This is a costly request, so the recommended interval is 15 minutes (900 seconds). But it's capped at 10 minutes, if you provide a lower value, you'll get the last known list.
-
+
- Provides the result of the WMI query.
-
+ Provides the result of the WMI query.
+
- Error loading settings:
+ Error loading settings:
{0}
-
+
- Error storing initial settings:
+ Error storing initial settings:
{0}
-
+
- Error storing settings:
+ Error storing settings:
{0}
-
+
- Error loading commands:
+ Error loading commands:
{0}
-
+
- Error storing commands:
+ Error storing commands:
{0}
-
+
- Error loading quick actions:
+ Error loading quick actions:
{0}
-
+
- Error storing quick actions:
+ Error storing quick actions:
{0}
-
+
- Error loading sensors:
+ Error loading sensors:
{0}
-
+
- Error storing sensors:
+ Error storing sensors:
{0}
-
+
- Wiki
-
+ Wiki
+
- Busy, please wait..
-
+ Busy, please wait..
+
- Finish
-
+ Finish
+
- Connected
-
+ Connected
+
- Connecting..
-
+ Connecting..
+
- Configuration missing
-
+ Configuration missing
+
- Disconnected
-
+ Disconnected
+
- Error
-
+ Error
+
- Custom
-
+ Custom
+
- CustomExecutor
-
+ CustomExecutor
+
- Hibernate
-
+ Hibernate
+
- Key
-
+ Key
+
- LaunchUrl
-
+ LaunchUrl
+
- Lock
-
+ Lock
+
- LogOff
-
+ LogOff
+
- MediaMute
-
+ MediaMute
+
- MediaNext
-
+ MediaNext
+
- MediaPlayPause
-
+ MediaPlayPause
+
- MediaPrevious
-
+ MediaPrevious
+
- MediaVolumeDown
-
+ MediaVolumeDown
+
- MediaVolumeUp
-
+ MediaVolumeUp
+
- MultipleKeys
-
+ MultipleKeys
+
- Powershell
-
+ Powershell
+
- PublishAllSensors
-
+ PublishAllSensors
+
- Restart
-
+ Restart
+
- Shutdown
-
+ Shutdown
+
- Sleep
-
+ Sleep
+
- Button
-
+ Button
+
- Light
-
+ Light
+
- Lock
-
+ Lock
+
- Siren
-
+ Siren
+
- Switch
-
+ Switch
+
- Locked
-
+ Locked
+
- Unknown
-
+ Unknown
+
- Unlocked
-
+ Unlocked
+
- ActiveWindow
-
+ ActiveWindow
+
- Audio
-
+ Audio
+
- Battery
-
+ Battery
+
- CpuLoad
-
+ CpuLoad
+
- CurrentClockSpeed
-
+ CurrentClockSpeed
+
- CurrentVolume
-
+ CurrentVolume
+
- Display
-
+ Display
+
- Dummy
-
+ Dummy
+
- GpuLoad
-
+ GpuLoad
+
- GpuTemperature
-
+ GpuTemperature
+
- LastActive
-
+ LastActive
+
- LastBoot
-
+ LastBoot
+
- LastSystemStateChange
-
+ LastSystemStateChange
+
- LoggedUser
-
+ LoggedUser
+
- LoggedUsers
-
+ LoggedUsers
+
- MemoryUsage
-
+ MemoryUsage
+
- MicrophoneActive
-
+ MicrophoneActive
+
- NamedWindow
-
+ NamedWindow
+
- Network
-
+ Network
+
- PerformanceCounter
-
+ PerformanceCounter
+
- ProcessActive
-
+ ProcessActive
+
- ServiceState
-
+ ServiceState
+
- SessionState
-
+ SessionState
+
- Storage
-
+ Storage
+
- UserNotification
-
+ UserNotification
+
- WebcamActive
-
+ WebcamActive
+
- WindowsUpdates
-
+ WindowsUpdates
+
- WmiQuery
-
+ WmiQuery
+
- ApplicationStarted
-
+ ApplicationStarted
+
- Logoff
-
+ Logoff
+
- SystemShutdown
-
+ SystemShutdown
+
- Resume
-
+ Resume
+
- Suspend
-
+ Suspend
+
- ConsoleConnect
-
+ ConsoleConnect
+
- ConsoleDisconnect
-
+ ConsoleDisconnect
+
- RemoteConnect
-
+ RemoteConnect
+
- RemoteDisconnect
-
+ RemoteDisconnect
+
- SessionLock
-
+ SessionLock
+
- SessionLogoff
-
+ SessionLogoff
+
- SessionLogon
-
+ SessionLogon
+
- SessionRemoteControl
-
+ SessionRemoteControl
+
- SessionUnlock
-
+ SessionUnlock
+
- NotPresent
-
+ NotPresent
+
- Busy
-
+ Busy
+
- RunningDirect3dFullScreen
-
+ RunningDirect3dFullScreen
+
- PresentationMode
-
+ PresentationMode
+
- AcceptsNotifications
-
+ AcceptsNotifications
+
- QuietTime
-
+ QuietTime
+
- RunningWindowsStoreApp
-
+ RunningWindowsStoreApp
+
- On
-
+ On
+
- Off
-
+ Off
+
- Open
-
+ Open
+
- Close
-
+ Close
+
- Play
-
+ Play
+
- Pause
-
+ Pause
+
- Stop
-
+ Stop
+
- Toggle
-
+ Toggle
+
- Automation
-
+ Automation
+
- Climate
-
+ Climate
+
- Cover
-
+ Cover
+
- InputBoolean
-
+ InputBoolean
+
- Light
-
+ Light
+
- MediaPlayer
-
+ MediaPlayer
+
- Scene
-
+ Scene
+
- Script
-
+ Script
+
- Switch
-
+ Switch
+
- Connecting..
-
+ Connecting..
+
- Failed
-
+ Failed
+
- Loading..
-
+ Loading..
+
- Running
-
+ Running
+
- Stopped
-
+ Stopped
+
- Disabled
-
+ Disabled
+
- It looks like your scope is malformed, it should probably start like this:
+ It looks like your scope is malformed, it should probably start like this:
\\.\ROOT\
@@ -1617,1588 +1637,1588 @@ The scope you entered:
Tip: make sure you haven't switched the scope and query fields around.
Do you still want to use the current values?
-
+
- Enter a WMI query first.
-
+ Enter a WMI query first.
+
- Query succesfully executed, result value:
+ Query succesfully executed, result value:
{0}
-
+
- The query failed to execute:
+ The query failed to execute:
{0}
Do you want to open the logs folder?
-
+
- Enter a category and counter first.
-
+ Enter a category and counter first.
+
- Test succesfully executed, result value:
+ Test succesfully executed, result value:
{0}
-
+
- The test failed to execute:
+ The test failed to execute:
{0}
Do you want to open the logs folder?
-
+
- Test WMI Query
-
+ Test WMI Query
+
- Test Performance Counter
-
+ Test Performance Counter
+
- GeoLocation
-
+ GeoLocation
+
- All
-
+ All
+
- Network Card
-
+ Network Card
+
- Last Known Value
-
+ Last Known Value
+
- SendWindowToFront
-
+ SendWindowToFront
+
- Cleaning..
-
+ Cleaning..
+
- The audio cache has been cleared!
-
+ The audio cache has been cleared!
+
- Cleaning..
-
+ Cleaning..
+
- The WebView cache has been cleared!
-
+ The WebView cache has been cleared!
+
- WebView
-
+ WebView
+
- Looks for the specified process, and tries to send its main window to the front.
+ Looks for the specified process, and tries to send its main window to the front.
If the application is minimized, it'll get restored.
Example: if you want to send VLC to the foreground, use 'vlc'.
-
+
- Shows a window with the provided URL.
+ Shows a window with the provided URL.
This differs from the 'LaunchUrl' command in that it doesn't load a full-fledged browser, just the provided URL in its own window.
You can use this to for instance quickly show Home Assistant's dashboard.
By default, it stores cookies indefinitely so you only have to log in once.
-
+
- Unable to load the stored command settings, resetting to default.
-
+ Unable to load the stored command settings, resetting to default.
+
- If you do not configure the command, you may only use this entity with an 'action' value via Home Assistant and it will appear using the default settings, running it as-is will have no action.
+ If you do not configure the command, you may only use this entity with an 'action' value via Home Assistant and it will appear using the default settings, running it as-is will have no action.
Are you sure you want to do this?
-
+
- Please select an domain!
-
+ Please select an domain!
+
- HASS.Agent Commands
-
+ HASS.Agent Commands
+
- It looks like you're using an alternative scaling. This may result in some parts of HASS.Agent not looking as intended.
+ It looks like you're using an alternative scaling. This may result in some parts of HASS.Agent not looking as intended.
Please report any unusable aspects on GitHub. Thanks!
Note: this message only shows once.
-
+
- Your device name contained some characters that are not allowed by HA (only letters, digits and whitespace).
+ Your device name contained some characters that are not allowed by HA (only letters, digits and whitespace).
The final name is: {0}
Do you want to use that version?
-
+
- No keys found
-
+ No keys found
+
- brackets missing, start and close all keys with [ ]
-
+ brackets missing, start and close all keys with [ ]
+
- Error while parsing keys, please check the logs for more information.
-
+ Error while parsing keys, please check the logs for more information.
+
- Your input language '{0}' is known to collide with the default CTRL-ALT-Q hotkey. Please set your own.
-
+ Your input language '{0}' is known to collide with the default CTRL-ALT-Q hotkey. Please set your own.
+
- Your input language '{0}' is unknown, and might collide with the default CTRL-ALT-Q hotkey. Please check to be sure. If it does, consider opening a ticket on GitHub so it can be added to the list.
-
+ Your input language '{0}' is unknown, and might collide with the default CTRL-ALT-Q hotkey. Please check to be sure. If it does, consider opening a ticket on GitHub so it can be added to the list.
+
- The number of open brackets ('[') does not correspond to the number of closed brackets. (']')! ({0} to {1})
-
+ The number of open brackets ('[') does not correspond to the number of closed brackets. (']')! ({0} to {1})
+
- &Hide
-
+ &Hide
+
- Browser Name
-
+ Browser Name
+
- Browser Binary
-
+ Browser Binary
+
- Additional Launch Arguments
-
+ Additional Launch Arguments
+
- Custom Executor Binary
-
+ Custom Executor Binary
+
- Tip: Double-click to Browse
-
+ Tip: Double-click to Browse
+
- &Test
-
+ &Test
+
- Seconds
-
+ Seconds
+
- Disconnected Grace &Period
-
+ Disconnected Grace &Period
+
- This page contains general configuration settings, for more settings you can browse the tabs on the left.
-
+ This page contains general configuration settings, for more settings you can browse the tabs on the left.
+
- Device &Name
-
+ Device &Name
+
- Interface &Language
-
+ Interface &Language
+
- Tip: Double-click this field to browse
-
+ Tip: Double-click this field to browse
+
- Client &Certificate
-
+ Client &Certificate
+
- Use &automatic client certificate selection
-
+ Use &automatic client certificate selection
+
- &Test Connection
-
+ &Test Connection
+
- &API Token
-
+ &API Token
+
- Server &URI
-
+ Server &URI
+
- &Clear
-
+ &Clear
+
- An easy way to pull up your quick actions is to use a global hotkey.
+ An easy way to pull up your quick actions is to use a global hotkey.
This way, whatever you're doing on your machine, you can always interact with Home Assistant.
-
+
- &Enable Quick Actions Hotkey
-
+ &Enable Quick Actions Hotkey
+
- &Hotkey Combination
-
+ &Hotkey Combination
+
- &Port
-
+ &Port
+
- Execute Port &Reservation
-
+ Execute Port &Reservation
+
- &Enable Local API
-
+ &Enable Local API
+
- To be able to listen to the requests, HASS.Agent needs to have its port reserved and opened in your firewall. You can use this button to have it done for you.
-
+ To be able to listen to the requests, HASS.Agent needs to have its port reserved and opened in your firewall. You can use this button to have it done for you.
+
- Image Cache Location
-
+ Image Cache Location
+
- days
-
+ days
+
- Some items like images shown in notifications have to be temporarily stored locally. You can
+ Some items like images shown in notifications have to be temporarily stored locally. You can
configure the amount of days they should be kept before HASS.Agent deletes them.
Enter '0' to keep them permanently.
-
+
- Keep images for
-
+ Keep images for
+
- Keep audio for
-
+ Keep audio for
+
- Clear Audio Cache
-
+ Clear Audio Cache
+
- Audio Cache Location
-
+ Audio Cache Location
+
- Clear WebView Cache
-
+ Clear WebView Cache
+
- WebView Cache Location
-
+ WebView Cache Location
+
- Clear cache every
-
+ Clear cache every
+
- Extended logging provides more verbose and in-depth logging, in case the default logging isn't
+ Extended logging provides more verbose and in-depth logging, in case the default logging isn't
sufficient. Please note that enabling this can cause the logfiles to grow large, and should only be
used when you suspect something's wrong with HASS.Agent itself or when asked by the
developers.
-
+
- &Enable Extended Logging
-
+ &Enable Extended Logging
+
- &Open Logs Folder
-
+ &Open Logs Folder
+
- Media Player &Documentation
-
+ Media Player &Documentation
+
- &Enable Media Player Functionality
-
+ &Enable Media Player Functionality
+
- Tip: Double-click these fields to browse
-
+ Tip: Double-click these fields to browse
+
- Client Certificate
-
+ Client Certificate
+
- Root Certificate
-
+ Root Certificate
+
- Use &Retain Flag
-
+ Use &Retain Flag
+
- &Allow Untrusted Certificates
-
+ &Allow Untrusted Certificates
+
- &Clear Configuration
-
+ &Clear Configuration
+
- (leave default if unsure)
-
+ (leave default if unsure)
+
- Discovery Prefix
-
+ Discovery Prefix
+
- &TLS
-
+ &TLS
+
- Password
-
+ Password
+
- Username
-
+ Username
+
- Port
-
+ Port
+
- Broker IP Address or Hostname
-
+ Broker IP Address or Hostname
+
- (leave empty to auto generate)
-
+ (leave empty to auto generate)
+
- Client ID
-
+ Client ID
+
- Notifications &Documentation
-
+ Notifications &Documentation
+
- &Accept Notifications
-
+ &Accept Notifications
+
- &Ignore certificate errors for images
-
+ &Ignore certificate errors for images
+
- The local API is disabled however the media player requires it in order to function.
-
+ The local API is disabled however the media player requires it in order to function.
+
- Service Status:
-
+ Service Status:
+
- S&tart Service
-
+ S&tart Service
+
- &Disable Service
-
+ &Disable Service
+
- &Stop Service
-
+ &Stop Service
+
- &Enable Service
-
+ &Enable Service
+
- &Reinstall Service
-
+ &Reinstall Service
+
- If you do not configure the service, it won't do anything. However, you can still decide to disable it as well.
+ If you do not configure the service, it won't do anything. However, you can still decide to disable it as well.
The installer will leave the disabled service alone(if you remove the service, the installer will reinstall it).
-
+
- You can try reinstalling the service if it's not working correctly.
+ You can try reinstalling the service if it's not working correctly.
Your configuration and entities won't be removed.
-
+
- Open Service &Logs Folder
-
+ Open Service &Logs Folder
+
- If the service still fails after reinstalling, please open a ticket and send the content of the latest log.
-
+ If the service still fails after reinstalling, please open a ticket and send the content of the latest log.
+
- HASS.Agent can start when you login by creating an entry in your user profile's registry.
+ HASS.Agent can start when you login by creating an entry in your user profile's registry.
Since HASS.Agent is user based, if you want to launch for another user, just install and config
HASS.Agent there.
-
+
- &Enable Start-on-Login
-
+ &Enable Start-on-Login
+
- Start-on-Login Status:
-
+ Start-on-Login Status:
+
- Control the behaviour of the tray icon when it is right-clicked.
-
+ Control the behaviour of the tray icon when it is right-clicked.
+
- Show &Default Menu
-
+ Show &Default Menu
+
- Show &WebView
-
+ Show &WebView
+
- &WebView URL (For instance, your Home Assistant Dashboard URL)
-
+ &WebView URL (For instance, your Home Assistant Dashboard URL)
+
- Size (px)
-
+ Size (px)
+
- Show &Preview
-
+ Show &Preview
+
- &Keep page loaded in the background
-
+ &Keep page loaded in the background
+
- (This uses extra resources, but reduces loading time.)
-
+ (This uses extra resources, but reduces loading time.)
+
- Notify me of &beta releases
-
+ Notify me of &beta releases
+
- When a new update is available, HASS.Agent can download the installer and launch it for you.
+ When a new update is available, HASS.Agent can download the installer and launch it for you.
The certificate of the downloaded file will get checked before running,you will still get to review the release notes and manually approve the update.
-
+
- Automatically &download future updates
-
+ Automatically &download future updates
+
- HASS.Agent checks for updates in the background if enabled.
+ HASS.Agent checks for updates in the background if enabled.
You will be sent a push notification if a new update is discovered, letting you know a
new version is ready to be installed.
-
+
- Notify me when a new &release is available
-
+ Notify me when a new &release is available
+
- Welcome to the HASS.Agent! It looks like this is the first time you are launching the agent.
+ Welcome to the HASS.Agent! It looks like this is the first time you are launching the agent.
To assist you with a first time setup, proceed with the configuration steps below
or alternatively, click 'Close'.
-
+
- The device name is used to identify your machine on Home Assistant, it is also used as a suggested prefix for your commands and sensors.
-
+ The device name is used to identify your machine on Home Assistant, it is also used as a suggested prefix for your commands and sensors.
+
- Device &Name
-
+ Device &Name
+
- Interface &Language
-
+ Interface &Language
+
- Yes, &start HASS.Agent on System Login
-
+ Yes, &start HASS.Agent on System Login
+
- HASS.Agent can start with your system, this allows for any sensors and data transmission between your device and Home Assistant to begin as soon as you login.
+ HASS.Agent can start with your system, this allows for any sensors and data transmission between your device and Home Assistant to begin as soon as you login.
This setting can be changed any time later in the HASS.Agent configuration window.
-
+
- Fetching current state, please wait..
-
+ Fetching current state, please wait..
+
- Note: 5115 is the default port, only change it if you changed it in Home Assistant.
-
+ Note: 5115 is the default port, only change it if you changed it in Home Assistant.
+
- Yes, &enable the local API on port
-
+ Yes, &enable the local API on port
+
- HASS.Agent has its own internal API, so Home Assistant can send requests (like notifications or text-to-speech).
+ HASS.Agent has its own internal API, so Home Assistant can send requests (like notifications or text-to-speech).
Do you want to enable it?
-
+
- Enable &Notifications
-
+ Enable &Notifications
+
- Enable &Media Player and text-to-speech (TTS)
-
+ Enable &Media Player and text-to-speech (TTS)
+
- You can choose what modules you want to enable. They require HA integrations, but don't worry, the next page will give you more info on how to set them up.
-
+ You can choose what modules you want to enable. They require HA integrations, but don't worry, the next page will give you more info on how to set them up.
+
- To use notifications, you need to install and configure the HASS.Agent-notifier integration in
+ To use notifications, you need to install and configure the HASS.Agent-notifier integration in
Home Assistant.
This is very easy using HACS, but you can also install manually. Visit the link below for more
information.
-
+
- HASS.Agent-MediaPlayer GitHub Page
-
+ HASS.Agent-MediaPlayer GitHub Page
+
- The same goes for the media player, this integration allows you to control your device as a media_player entity, see what's playing and send text-to-speech.
-
+ The same goes for the media player, this integration allows you to control your device as a media_player entity, see what's playing and send text-to-speech.
+
- API &Token
-
+ API &Token
+
- Server &URI (should be ok like this)
-
+ Server &URI (should be ok like this)
+
- Test &Connection
-
+ Test &Connection
+
- Tip: Specialized settings can be found in the Configuration Window.
-
+ Tip: Specialized settings can be found in the Configuration Window.
+
- &TLS
-
+ &TLS
+
- Discovery Prefix
-
+ Discovery Prefix
+
- (leave default if not sure)
-
+ (leave default if not sure)
+
- Tip: Specialized settings can be found in the Configuration Window.
-
+ Tip: Specialized settings can be found in the Configuration Window.
+
- &Hotkey Combination
-
+ &Hotkey Combination
+
- An easy way to pull up your quick actions is to use a global hotkey.
+ An easy way to pull up your quick actions is to use a global hotkey.
This way, whatever you're doing on your machine, you can always interact with Home Assistant.
-
+
- &Clear
-
+ &Clear
+
- Yes, notify me on new &updates
-
+ Yes, notify me on new &updates
+
- Yes, &download and launch the installer for me
-
+ Yes, &download and launch the installer for me
+
- HASS.Agent GitHub page
-
+ HASS.Agent GitHub page
+
- Low Integrity
-
+ Low Integrity
+
- &Remove
-
+ &Remove
+
- &Modify
-
+ &Modify
+
- &Add New
-
+ &Add New
+
- &Send && Activate Commands
-
+ &Send && Activate Commands
+
- commands stored!
-
+ commands stored!
+
- &Apply
-
+ &Apply
+
- Auth &ID
-
+ Auth &ID
+
- Authenticate
-
+ Authenticate
+
- Connect with service
-
+ Connect with service
+
- Connecting satellite service, please wait..
-
+ Connecting satellite service, please wait..
+
- Fetch Configuration
-
+ Fetch Configuration
+
- (leave empty to auto generate)
-
+ (leave empty to auto generate)
+
- Client ID
-
+ Client ID
+
- Tip: Double-click these fields to browse
-
+ Tip: Double-click these fields to browse
+
- Client Certificate
-
+ Client Certificate
+
- Root Certificate
-
+ Root Certificate
+
- Use &Retain Flag
-
+ Use &Retain Flag
+
- &Allow Untrusted Certificates
-
+ &Allow Untrusted Certificates
+
- &Clear Configuration
-
+ &Clear Configuration
+
- (leave default if not sure)
-
+ (leave default if not sure)
+
- Commands and sensors are sent through MQTT. Please provide credentials for your server. If you're using the HA addon,
+ Commands and sensors are sent through MQTT. Please provide credentials for your server. If you're using the HA addon,
you can probably use the preset address.
-
+
- Discovery Prefix
-
+ Discovery Prefix
+
- &TLS
-
+ &TLS
+
- Password
-
+ Password
+
- Username
-
+ Username
+
- Port
-
+ Port
+
- Broker IP Address or Hostname
-
+ Broker IP Address or Hostname
+
- &Send && Activate Configuration
-
+ &Send && Activate Configuration
+
- Copy from &HASS.Agent
-
+ Copy from &HASS.Agent
+
- Configuration stored!
-
+ Configuration stored!
+
- Status
-
+ Status
+
- Querying..
-
+ Querying..
+
- Refresh
-
+ Refresh
+
- &Remove
-
+ &Remove
+
- &Add New
-
+ &Add New
+
- &Modify
-
+ &Modify
+
- &Send && Activate Sensors
-
+ &Send && Activate Sensors
+
- Sensors stored!
-
+ Sensors stored!
+
- Please wait a bit while the task is performed ..
-
+ Please wait a bit while the task is performed ..
+
- Create API Port Binding
-
+ Create API Port Binding
+
- Set Firewall Rule
-
+ Set Firewall Rule
+
- Please wait a bit while some post-update tasks are performed ..
-
+ Please wait a bit while some post-update tasks are performed ..
+
- Configuring Satellite Service
-
+ Configuring Satellite Service
+
- Create API Port Binding
-
+ Create API Port Binding
+
- Please wait while HASS.Agent restarts..
-
+ Please wait while HASS.Agent restarts..
+
- Waiting for previous instance to close..
-
+ Waiting for previous instance to close..
+
- Relaunch HASS.Agent
-
+ Relaunch HASS.Agent
+
- Please wait while the satellite service is re-installed..
-
+ Please wait while the satellite service is re-installed..
+
- Remove Satellite Service
-
+ Remove Satellite Service
+
- Install Satellite Service
-
+ Install Satellite Service
+
- HASS.Agent Reinstall Satellite Service
-
+ HASS.Agent Reinstall Satellite Service
+
- Please wait while the satellite service is configured..
-
+ Please wait while the satellite service is configured..
+
- Enable Satellite Service
-
+ Enable Satellite Service
+
- HASS.Agent Configure Satellite Service
-
+ HASS.Agent Configure Satellite Service
+
- &Close
-
+ &Close
+
- This is the MQTT topic on which you can publish action commands:
-
+ This is the MQTT topic on which you can publish action commands:
+
- Copy &to Clipboard
-
+ Copy &to Clipboard
+
- help and examples
-
+ help and examples
+
- MQTT Action Topic
-
+ MQTT Action Topic
+
- &Save
-
+ &Save
+
- Drag and resize this window to set the size and location of your webview command.
-
+ Drag and resize this window to set the size and location of your webview command.
+
- &URL
-
+ &URL
+
- Size
-
+ Size
+
- Location
-
+ Location
+
- Show the window's &title bar
-
+ Show the window's &title bar
+
- &Always show centered in screen
-
+ &Always show centered in screen
+
- Set window as 'Always on &Top'
-
+ Set window as 'Always on &Top'
+
- Tip: Press ESCAPE to close a WebView.
-
+ Tip: Press ESCAPE to close a WebView.
+
- WebView Configuration
-
+ WebView Configuration
+
- &Remove
-
+ &Remove
+
- &Modify
-
+ &Modify
+
- &Add New
-
+ &Add New
+
- &Store and Activate Commands
-
+ &Store and Activate Commands
+
- Low Integrity
-
+ Low Integrity
+
- Action
-
+ Action
+
- Commands Config
-
+ Commands Config
+
- &Store Command
-
+ &Store Command
+
- &Configuration
-
+ &Configuration
+
- &Name
-
+ &Name
+
- Description
-
+ Description
+
- &Run as 'Low Integrity'
-
+ &Run as 'Low Integrity'
+
- What's this?
-
+ What's this?
+
- Service
-
+ Service
+
- agent
-
+ agent
+
- Configure Command &Parameters
-
+ Configure Command &Parameters
+
- Command
-
+ Command
+
- Retrieving entities, please wait..
-
+ Retrieving entities, please wait..
+
- Quick Actions
-
+ Quick Actions
+
- &Store Quick Actions
-
+ &Store Quick Actions
+
- &Add New
-
+ &Add New
+
- &Modify
-
+ &Modify
+
- &Remove
-
+ &Remove
+
- &Preview
-
+ &Preview
+
- Hotkey Enabled
-
+ Hotkey Enabled
+
- Quick Actions Configuration
-
+ Quick Actions Configuration
+
- &Store Quick Action
-
+ &Store Quick Action
+
- &Entity
-
+ &Entity
+
- Desired &Action
-
+ Desired &Action
+
- &Description
-
+ &Description
+
- Retrieving entities, please wait..
-
+ Retrieving entities, please wait..
+
- &hotkey combination
-
+ &hotkey combination
+
- (optional, will be used instead of entity name)
-
+ (optional, will be used instead of entity name)
+
- Quick Action
-
+ Quick Action
+
- &Remove
-
+ &Remove
+
- &Modify
-
+ &Modify
+
- &Add New
-
+ &Add New
+
- &Store && Activate Sensors
-
+ &Store && Activate Sensors
+
- Refresh
-
+ Refresh
+
- &Store Sensor
-
+ &Store Sensor
+
- Selected Type
-
+ Selected Type
+
- &Name
-
+ &Name
+
- &Update every
-
+ &Update every
+
- seconds
-
+ seconds
+
- Description
-
+ Description
+
- Agent
-
+ Agent
+
- Service
-
+ Service
+
- HASS.Agent only!
-
+ HASS.Agent only!
+
- &Test
-
+ &Test
+
- Sensor
-
+ Sensor
+
- Satellite Service Configuration
-
+ Satellite Service Configuration
+
- &Close
-
+ &Close
+
- A Windows-based client for the Home Assistant platform.
-
+ A Windows-based client for the Home Assistant platform.
+
- This application is open source and completely free, please check the project pages of
+ This application is open source and completely free, please check the project pages of
the used components for their individual licenses:
-
+
- A big 'thank you' to the developers of these projects, who were kind enough to share
+ A big 'thank you' to the developers of these projects, who were kind enough to share
their hard work with the rest of us mere mortals.
-
+
- And of course; thanks to Paulus Shoutsen and the entire team of developers that
+ And of course; thanks to Paulus Shoutsen and the entire team of developers that
created and maintain Home Assistant :-)
-
+
- Created with love by
-
+ Created with love by
+
- or
-
+ or
+
- About
-
+ About
+
- Local API
-
+ Local API
+
- Media Player
-
+ Media Player
+
- Tray Icon
-
+ Tray Icon
+
- &About
-
+ &About
+
- &Help && Contact
-
+ &Help && Contact
+
- &Save Configuration
-
+ &Save Configuration
+
- Close &Without Saving
-
+ Close &Without Saving
+
- Configuration
-
+ Configuration
+
- What would you like to do?
-
+ What would you like to do?
+
- &Restart
-
+ &Restart
+
- &Hide
-
+ &Hide
+
- &Exit
-
+ &Exit
+
- Exit Dialog
-
+ Exit Dialog
+
- &Close
-
+ &Close
+
- If you are having trouble with HASS.Agent and require support
+ If you are having trouble with HASS.Agent and require support
with any sensors, commands, or for general support and feedback,
there are few ways you can reach us:
-
+
- About
-
+ About
+
- Home Assistant Forum
-
+ Home Assistant Forum
+
- GitHub Issues
-
+ GitHub Issues
+
- Bit of everything, with the addition that other
+ Bit of everything, with the addition that other
HA users can help you out too!
-
+
- Report bugs, post feature requests, see latest changes, etc.
-
+ Report bugs, post feature requests, see latest changes, etc.
+
- Get help with setting up and using HASS.Agent,
+ Get help with setting up and using HASS.Agent,
report bugs or get involved in general chit-chat!
-
+
- Documentation and Usage Examples
-
+ Documentation and Usage Examples
+
- Documentation
-
+ Documentation
+
- Help
-
+ Help
+
- Manage Satellite Service
-
+ Manage Satellite Service
+
- Controls
-
+ Controls
+
- S&atellite Service
-
+ S&atellite Service
+
- C&onfiguration
-
+ C&onfiguration
+
- &Quick Actions
-
+ &Quick Actions
+
- System Status
-
+ System Status
+
- Satellite Service:
-
+ Satellite Service:
+
- MQTT:
-
+ MQTT:
+
- Commands:
-
+ Commands:
+
- Sensors:
-
+ Sensors:
+
- Quick Actions:
-
+ Quick Actions:
+
- Home Assistant API:
-
+ Home Assistant API:
+
- Local API:
-
+ Local API:
+
- Check for &Updates
-
+ Check for &Updates
+
- &Next
-
+ &Next
+
- &Close
-
+ &Close
+
- &Previous
-
+ &Previous
+
- HASS.Agent Onboarding
-
+ HASS.Agent Onboarding
+
- There's a new release available:
-
+ There's a new release available:
+
- Release notes
-
+ Release notes
+
- &Ignore Update
-
+ &Ignore Update
+
- Release Page
-
+ Release Page
+
- HASS.Agent Update
-
+ HASS.Agent Update
+
- WebView
-
+ WebView
+
- By default HASS.Agent will launch URLs using your default browser. You can also configure
+ By default HASS.Agent will launch URLs using your default browser. You can also configure
a specific browser to be used instead along with launch arguments to run in private mode.
-
+
- Make sure you follow these steps:
+ Make sure you follow these steps:
- Install the HASS.Agent-Notifier and / or HASS.Agent-MediaPlayer integration
- Restart Home Assistant
-Configure a notifier and / or media_player entity
-Restart Home Assistant
-
+
- To learn which entities you have configured and to send quick actions, HASS.Agent uses
+ To learn which entities you have configured and to send quick actions, HASS.Agent uses
Home Assistant's API.
Please provide a long-lived access token and the address of your Home Assistant instance.
You can get a token in Home Assistant by clicking your profile picture at the bottom-left
and navigating to the bottom of the page until you see the 'CREATE TOKEN' button.
-
+
- When a new update is available, HASS.Agent can download the installer and launch it for you.
+ When a new update is available, HASS.Agent can download the installer and launch it for you.
The certificate of the downloaded file will get checked before running,you will still get to review the release notes and manually approve the update.
-
+
- HASS.Agent checks for updates in the background if enabled.
+ HASS.Agent checks for updates in the background if enabled.
You will be sent a push notification if a new update is discovered, letting you know a
new version is ready to be installed.
Do you want to enable this automatic update checks?
-
+
- You can configure the HASS.Agent to use a specific interpreter such as Perl or Python.
+ You can configure the HASS.Agent to use a specific interpreter such as Perl or Python.
Use the 'custom executor' command to launch this executor.
-
+
- Custom Executor Name
-
+ Custom Executor Name
+
- HASS.Agent will wait a grace period before notifying you of disconnects from MQTT or HA's API.
+ HASS.Agent will wait a grace period before notifying you of disconnects from MQTT or HA's API.
You can set the amount of seconds to wait in this grace period below.
-
+
- IMPORTANT: if you change this value, HASS.Agent will unpublish all your sensors, commands and force a restart of itself so they can be republished under the new device name.
+ IMPORTANT: if you change this value, HASS.Agent will unpublish all your sensors, commands and force a restart of itself so they can be republished under the new device name.
Your automations and scripts will keep working.
-
+
- The device name is used to identify your machine on Home Assistant.
+ The device name is used to identify your machine on Home Assistant.
It is also used as a prefix for your command/sensor names (this can be changed per entity).
-
+
- The local API is disabled however the media player requires it in order to function.
-
+ The local API is disabled however the media player requires it in order to function.
+
- Password
-
+ Password
+
- Username
-
+ Username
+
- Port
-
+ Port
+
- IP Address or Hostname
-
+ IP Address or Hostname
+
- This page contains general configuration settings, for MQTT settings, commands, and sensors, browse the different tabs above.
-
+ This page contains general configuration settings, for MQTT settings, commands, and sensors, browse the different tabs above.
+
- Auth &ID
-
+ Auth &ID
+
- Device &Name
-
+ Device &Name
+
- Tip: Double-click these fields to browse
-
+ Tip: Double-click these fields to browse
+
- Custom Executor &Binary
-
+ Custom Executor &Binary
+
- Custom &Executor Name
-
+ Custom &Executor Name
+
- seconds
-
+ seconds
+
- Disconnected Grace &Period
-
+ Disconnected Grace &Period
+
- Version
-
+ Version
+
- Tip: Double-click to generate random
-
+ Tip: Double-click to generate random
+
- stored!
-
+ stored!
+
- You can use the satellite service to run sensors and commands without having to be logged in. Not all types are available, for instance the 'LaunchUrl' command can only be added as a regular command.
-
+ You can use the satellite service to run sensors and commands without having to be logged in. Not all types are available, for instance the 'LaunchUrl' command can only be added as a regular command.
+
- The keycode you have provided is not a valid number!
+ The keycode you have provided is not a valid number!
Please ensure the keycode field is in focus and press the key you want simulated, the keycode should then be generated for you.
-
+
- HASS.Agent will sanitize your device name to make sure HA will accept it, you can overrule this rule below if you're sure that your name will be accepted as-is.
-
+ HASS.Agent will sanitize your device name to make sure HA will accept it, you can overrule this rule below if you're sure that your name will be accepted as-is.
+
- Enable Device Name &Sanitation
-
+ Enable Device Name &Sanitation
+
- You've changed your device's name.
+ You've changed your device's name.
All your sensors and commands will now be unpublished and published again after the HASS.Agent restarts.
Don't worry! they'll keep their current names so your automations and scripts will continue to work.
Note: You disabled sanitation, so make sure your device name is accepted by Home Assistant.
-
+
- Enable State Notifications
-
+ Enable State Notifications
+
- HASS.Agent sends notifications when the state of a module changes, you can adjust whether or not you want to receive these notifications below.
-
+ HASS.Agent sends notifications when the state of a module changes, you can adjust whether or not you want to receive these notifications below.
+
- Error trying to bind the API to port {0}.
+ Error trying to bind the API to port {0}.
Make sure no other instance of HASS.Agent is running and the port is available and registered.
-
+
- Printers
-
+ Printers
+
- No URL has been set! Please configure the webview through Configuration -> Tray Icon.
-
+ No URL has been set! Please configure the webview through Configuration -> Tray Icon.
+
- MonitorSleep
-
+ MonitorSleep
+
- MonitorWake
-
+ MonitorWake
+
- PowerOn
-
+ PowerOn
+
- PowerOff
-
+ PowerOff
+
- Dimmed
-
+ Dimmed
+
- Unknown
-
+ Unknown
+
- MonitorPowerState
-
+ MonitorPowerState
+
- PowershellSensor
-
+ PowershellSensor
+
- powershell command or script
-
+ powershell command or script
+
- Test Command/Script
-
+ Test Command/Script
+
- Test succesfully executed, result value:
+ Test succesfully executed, result value:
{0}
-
+
- Please enter a command or script!
-
+ Please enter a command or script!
+
- The test failed to execute:
+ The test failed to execute:
{0}
Do you want to open the logs folder?
-
+
- SetVolume
-
+ SetVolume
+
- Please enter a value between 0-100 as the desired volume level!
-
+ Please enter a value between 0-100 as the desired volume level!
+
- If you don't enter a volume value, you can only use this entity with an 'action' value through Home Assistant. Running it as-is won't do anything.
+ If you don't enter a volume value, you can only use this entity with an 'action' value through Home Assistant. Running it as-is won't do anything.
Are you sure you want this?
-
+
- Maximized
-
+ Maximized
+
- Minimized
-
+ Minimized
+
- Normal
-
+ Normal
+
- Unknown
-
+ Unknown
+
- Hidden
-
+ Hidden
+
- WindowState
-
+ WindowState
+
- Puts all monitors in sleep (low power) mode.
-
+ Puts all monitors in sleep (low power) mode.
+
- Tries to wake up all monitors by simulating a 'arrow up' keypress.
-
+ Tries to wake up all monitors by simulating a 'arrow up' keypress.
+
- Sets the volume of the current default audiodevice to the specified level.
-
+ Sets the volume of the current default audiodevice to the specified level.
+
- Returns your current latitude, longitude and altitude as a comma-seperated value.
+ Returns your current latitude, longitude and altitude as a comma-seperated value.
Make sure Windows' location services are enabled!
Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'.
-
+
- Provides the last monitor power state change:
+ Provides the last monitor power state change:
Dimmed, PowerOff, PowerOn and Unkown.
-
+
- Returns the result of the provided Powershell command or script.
+ Returns the result of the provided Powershell command or script.
Converts the outcome to text.
-
+
- Provides information about all installed printers and their queues.
-
+ Provides information about all installed printers and their queues.
+
- Provides the current state of the process' window:
+ Provides the current state of the process' window:
Hidden, Maximized, Minimized, Normal and Unknown.
-
+
- Testing..
-
+ Testing..
+
- WebcamProcess
-
+ WebcamProcess
+
- MicrophoneProcess
-
+ MicrophoneProcess
+
- Provides the name of the process that's currently using the webcam.
+ Provides the name of the process that's currently using the webcam.
Note: if used in the satellite service, it won't detect userspace applications.
-
+
- Provides the name of the process that's currently using the microphone.
+ Provides the name of the process that's currently using the microphone.
Note: if used in the satellite service, it won't detect userspace applications.
-
+
- BluetoothDevices
-
+ BluetoothDevices
+
- BluetoothLeDevices
-
+ BluetoothLeDevices
+
- Provides a sensor with the amount of bluetooth devices found.
+ Provides a sensor with the amount of bluetooth devices found.
The devices and their connected state are added as attributes.
-
+
- Provides a sensors with the amount of bluetooth LE devices found.
+ Provides a sensors with the amount of bluetooth LE devices found.
The devices and their connected state are added as attributes.
Only shows devices that were seen since the last report, ie. when the sensor publishes, the list clears.
-
+
- The service is currently stopped and cannot be configured.
+ The service is currently stopped and cannot be configured.
Please start the service first in order to configure it.
-
+
- The name you provided contains unsupported characters and won't work. The suggested version is:
+ The name you provided contains unsupported characters and won't work. The suggested version is:
{0}
Do you want to use this version?
-
+
- The name you provided contains unsupported characters and won't work. The suggested version is:
+ The name you provided contains unsupported characters and won't work. The suggested version is:
{0}
Do you want to use this version?
-
+
- To learn which entities you have configured and to send quick actions, HASS.Agent uses
+ To learn which entities you have configured and to send quick actions, HASS.Agent uses
Home Assistant's API.
Please provide a long-lived access token and the address of your Home Assistant instance.
You can get a token in Home Assistant by clicking your profile picture at the bottom-left
and navigating to the bottom of the page until you see the 'CREATE TOKEN' button.
-
+
- HASS.Agent has its own local API, so Home Assistant can send requests (for instance to send a notification). You can configure it globally here, and afterwards you can configure the dependent sections (currently notifications and mediaplayer).
+ HASS.Agent has its own local API, so Home Assistant can send requests (for instance to send a notification). You can configure it globally here, and afterwards you can configure the dependent sections (currently notifications and mediaplayer).
Note: this is not required for the new integration to function. Only enable and use it if you don't use MQTT.
-
+
- If something is not working, make sure you try the following steps:
+ If something is not working, make sure you try the following steps:
- Install the HASS.Agent integration
- Restart Home Assistant
- Make sure HASS.Agent is active with MQTT enabled!
- Your device should get detected and added as an entity automatically
- Optionally: manually add it using the local API
-
+
- HASS.Agent can act as a media player for Home Assistant, so you'll be able to see and control any media that's playing, and send text-to-speech. If you have MQTT enabled, your device will automatically get added. Otherwise, manually configure the integration to use the local API.
-
+ HASS.Agent can act as a media player for Home Assistant, so you'll be able to see and control any media that's playing, and send text-to-speech. If you have MQTT enabled, your device will automatically get added. Otherwise, manually configure the integration to use the local API.
+
- both the local API and MQTT are disabled, but the integration needs at least one for it to work
-
+ both the local API and MQTT are disabled, but the integration needs at least one for it to work
+
- Commands and sensors use MQTT, as well as notifications and media player functions when using the new integration.
+ Commands and sensors use MQTT, as well as notifications and media player functions when using the new integration.
Please provide credentials for your broker, if you're using the HA Mosquitto addon, you can probably use the preset address.
Note: these settings (excluding the Client ID) will also be applied to the satellite service.
-
+
- Enable MQTT
-
+ Enable MQTT
+
- If MQTT is not enabled, commands and sensors will not work!
-
+ If MQTT is not enabled, commands and sensors will not work!
+
- If something is not working, make sure you try the following steps:
+ If something is not working, make sure you try the following steps:
- Install the HASS.Agent integration
- Restart Home Assistant
- Make sure HASS.Agent is active with MQTT enabled!
- Your device should get detected and added as an entity automatically
- Optionally: manually add it using the local API
-
+
- HASS.Agent can receive notifications from Home Assistant, using text, images and actions. If you have MQTT enabled, your device will automatically get added. Otherwise, manually configure the integration to use the local API.
-
+ HASS.Agent can receive notifications from Home Assistant, using text, images and actions. If you have MQTT enabled, your device will automatically get added. Otherwise, manually configure the integration to use the local API.
+
- both the local API and MQTT are disabled, but the integration needs at least one for it to work
-
+ both the local API and MQTT are disabled, but the integration needs at least one for it to work
+
- The satellite service allows you to run sensors and commands even when no user's logged in.
+ The satellite service allows you to run sensors and commands even when no user's logged in.
Use the 'satellite service' button on the main window to manage it.
-
+
- If you want to manage the service (add commands and sensor, change settings) you can do so here, or by using the 'satellite service' button on the main window.
-
+ If you want to manage the service (add commands and sensor, change settings) you can do so here, or by using the 'satellite service' button on the main window.
+
- &Manage Service
-
+ &Manage Service
+
- Show default menu on mouse left-click
-
+ Show default menu on mouse left-click
+
- Commands and sensors are sent through MQTT. The notifications- and media player integration also make use of them.
+ Commands and sensors are sent through MQTT. The notifications- and media player integration also make use of them.
Tip: if you're using the HA addon, you can probably use the preset address - just provide credentials.
-
+
- Enable MQTT
-
+ Enable MQTT
+
- HASS.Agent-Integration GitHub Page
-
+ HASS.Agent-Integration GitHub Page
+
- Enable &Media Player (including text-to-speech)
-
+ Enable &Media Player (including text-to-speech)
+
- Enable &Notifications
-
+ Enable &Notifications
+
- Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated!
-
+ Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated!
+
- There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow!
+ There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow!
Thank you for using HASS.Agent, hopefully it'll be useful for you :-)
-
+
- HASS.Agent will now restart to apply your configuration changes.
-
+ HASS.Agent will now restart to apply your configuration changes.
+
- Yay, done!
-
+ Yay, done!
+
- Tip: Other donation methods are available on the About Window.
-
+ Tip: Other donation methods are available on the About Window.
+
- HASS.Agent Post Update
-
+ HASS.Agent Post Update
+
- Command
-
+ Command
+
- Like this tool? Support us (read: keep us awake) by buying a cup of coffee:
-
+ Like this tool? Support us (read: keep us awake) by buying a cup of coffee:
+
- HASS.Agent is completely free, and will always stay that way without restrictions!
+ HASS.Agent is completely free, and will always stay that way without restrictions!
However, developing and maintaining this tool (and everything that surrounds it, like support and the docs) takes up a lot of time.
Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated!
-
+
- &Close
-
+ &Close
+
- I already donated, hide the button on the main window.
-
+ I already donated, hide the button on the main window.
+
- Donate
-
+ Donate
+
- Check for Updates
-
+ Check for Updates
+
- The API token you have provided doesn't appear to be valid, please ensure you selected the entire token (Don't use CTRL + A or double-click). A valid API key contains three sections, separated by two dots.
+ The API token you have provided doesn't appear to be valid, please ensure you selected the entire token (Don't use CTRL + A or double-click). A valid API key contains three sections, separated by two dots.
Are you sure you want to use this key anyway?
-
+
- The URI you have provided does not appear to be valid, a valid URI may look like either of the following:
+ The URI you have provided does not appear to be valid, a valid URI may look like either of the following:
- http://homeassistant.local:8123
- http://192.168.0.1:8123
Are you sure you want to use this URI anyway?
-
+
- The API token you have provided doesn't appear to be valid, please ensure you selected the entire token (Don't use CTRL + A or double-click). A valid API key contains three sections, separated by two dots.
+ The API token you have provided doesn't appear to be valid, please ensure you selected the entire token (Don't use CTRL + A or double-click). A valid API key contains three sections, separated by two dots.
Are you sure you want to use this key anyway?
-
+
- The URI you have provided does not appear to be valid, a valid URI may look like either of the following:
+ The URI you have provided does not appear to be valid, a valid URI may look like either of the following:
- http://homeassistant.local:8123
- http://192.168.0.1:8123
Are you sure you want to use this URI anyway?
-
+
- Your Home Assistant API token doesn't look right. Make sure you selected the entire token (don't use CTRL+A or doubleclick).
+ Your Home Assistant API token doesn't look right. Make sure you selected the entire token (don't use CTRL+A or doubleclick).
It should contain three sections (seperated by two dots).
Are you sure you want to use it like this?
-
+
- Your Home Assistant URI doesn't look right. It should look something like 'http://homeassistant.local:8123' or 'https://192.168.0.1:8123'.
+ Your Home Assistant URI doesn't look right. It should look something like 'http://homeassistant.local:8123' or 'https://192.168.0.1:8123'.
Are you sure you want to use it like this?
-
+
- Your MQTT broker URI doesn't look right. It should look something like 'homeassistant.local' or '192.168.0.1'.
+ Your MQTT broker URI doesn't look right. It should look something like 'homeassistant.local' or '192.168.0.1'.
Are you sure you want to use it like this?
-
+
- Microsoft's WebView2 runtime isn't found on your machine. Usually this is handled by the installer, but you can install it manually.
+ Microsoft's WebView2 runtime isn't found on your machine. Usually this is handled by the installer, but you can install it manually.
Do you want to download the runtime installer?
-
+
- Something went wrong while initializing the WebView! Please check your logs and open a GitHub issue for further assistance.
-
+ Something went wrong while initializing the WebView! Please check your logs and open a GitHub issue for further assistance.
+
- unable to open Service Manager
-
+ unable to open Service Manager
+
- unable to open service
-
+ unable to open service
+
- Error configuring startup mode, please check the logs for more information.
-
+ Error configuring startup mode, please check the logs for more information.
+
- Error setting startup mode, please check the logs for more information.
-
+ Error setting startup mode, please check the logs for more information.
+
- Timeout expired
-
+ Timeout expired
+
- Fatal error, please check logs for information!
-
+ Fatal error, please check logs for information!
+
- unknown reason
-
+ unknown reason
+
- HassAgentSatelliteServiceStarted
-
+ HassAgentSatelliteServiceStarted
+
- HassAgentStarted
-
+ HassAgentStarted
+
- Selected Type
-
+ Selected Type
+
- HASS.Agent only!
-
+ HASS.Agent only!
+
- &Entity Type
-
+ &Entity Type
+
- Show MQTT Action Topic
-
+ Show MQTT Action Topic
+
- Action
-
+ Action
+
- Multivalue
-
+ Multivalue
+
- domain
-
+ domain
+
InternalDeviceSensor
@@ -3235,4 +3255,7 @@ Do you want to download the runtime installer?
Button
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
index 6f5d7c6e..9dadee0b 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx
@@ -3320,4 +3320,7 @@ Home Assistant.
Нажимать
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
index 1edd421c..3e4b4bd0 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx
@@ -3401,4 +3401,7 @@ Ali želite prenesti runtime installer?
Pritisnite
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
index 28956a1c..8d211787 100644
--- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx
@@ -2859,4 +2859,7 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku
Basmak
+
+ DeviceLocationTracker
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
index 1612eadf..7c1b50ef 100644
--- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
+++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj
@@ -1,11 +1,11 @@
-
-
+
+
WinExe
net6.0-windows10.0.19041.0
disable
true
- true
+ true
enable
HASS.Agent
hassagent.ico
@@ -28,25 +28,31 @@
None
win10-x64;win10-x86
true
- true
- false
- false
- false
+ true
+ false
+ false
+ false
-
+
+ 4.0
+ en-US
+ true
+ true
+
+
+
+
+
-
-
-
-
+
+
-
@@ -75,13 +81,12 @@
+
-
-
Libraries\HADotNet.Core.dll
@@ -90,7 +95,6 @@
Libraries\HotkeyListener.dll
-
UserControl
@@ -144,7 +148,6 @@
Languages.resx
-
ConfigExternalTools.resx
@@ -1844,7 +1847,6 @@
Languages.resx
-
PreserveNewest
@@ -1854,9 +1856,7 @@
\
-
-
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/DeviceTrackerSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/DeviceTrackerSensor.cs
new file mode 100644
index 00000000..9a6029d8
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/DeviceTrackerSensor.cs
@@ -0,0 +1,104 @@
+#nullable enable
+using System.Runtime.Caching;
+using HASS.Agent.Shared.Extensions;
+using HASS.Agent.Shared.Models.HomeAssistant;
+using Windows.Devices.Geolocation;
+using HASS.Agent.Models.Internal;
+using Newtonsoft.Json;
+
+namespace HASS.Agent.HomeAssistant.Sensors.GeneralSensors.SingleValue
+{
+ ///
+ /// Sensor containing the coördinates of the device
+ /// XXXX Track location change event, and make non breaking
+ ///
+ public class DeviceTrackerSensor : AbstractSingleValueSensor
+ {
+ private const string DefaultName = "devicetracksensor";
+
+ public DeviceTrackerSensor(int? updateInterval = 10, string entityName = DefaultName, string name = DefaultName,
+ string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null,
+ updateInterval ?? 30, id, advancedSettings: advancedSettings)
+ {
+ UseAttributes = true;
+ }
+
+ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
+ {
+ if (Variables.MqttManager == null) return null;
+
+ var deviceConfig = Variables.MqttManager.GetDeviceConfigModel();
+ if (deviceConfig == null) return null;
+
+ return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
+ {
+ EntityName = EntityName,
+ Name = Name,
+ Unique_id = Id,
+ Device = deviceConfig,
+ State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state",
+ Icon = "mdi:earth",
+ Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes",
+ });
+ }
+
+ public override string GetState()
+ {
+ var state = "ON";
+ return state;
+ }
+
+ public override string? GetAttributes()
+ {
+ ObjectCache cache = System.Runtime.Caching.MemoryCache.Default;
+ GeolocationInfo? fileContents = cache["location_" + this.Id] as GeolocationInfo;
+ string jsonPayload = "";
+
+ if (fileContents != null)
+ {
+ jsonPayload = JsonConvert.SerializeObject(fileContents, Formatting.Indented);
+ return jsonPayload;
+ }
+
+ GeolocationInfo gli = new GeolocationInfo();
+
+ var accessStatus = Geolocator.RequestAccessAsync().GetAwaiter().GetResult();
+ switch (accessStatus)
+ {
+ case GeolocationAccessStatus.Allowed:
+ // https://community.home-assistant.io/t/attributes-latitude-and-longitude-in-a-lovelace-map/318760
+ var geolocator = new Geolocator();
+
+ var position = geolocator.GetGeopositionAsync().GetAwaiter().GetResult();
+ var lat = position.Coordinate.Latitude.ConvertToStringDotDecimalSeperator();
+ var lon = position.Coordinate.Longitude.ConvertToStringDotDecimalSeperator();
+ var alt = position.Coordinate.Altitude?.ConvertToStringDotDecimalSeperator();
+ var accuracy = position.Coordinate.Accuracy.ConvertToStringDotDecimalSeperator();
+ var sourceType = position.Coordinate.PositionSource;
+
+ gli = new GeolocationInfo(lon, lat, sourceType)
+ {
+ altitude = alt,
+ gps_accuracy = accuracy,
+ not_permitted = "false"
+ };
+
+ cache.Set("location_" + Id, gli, DateTimeOffset.Now.AddSeconds(UpdateIntervalSeconds - 1));
+ break;
+
+ case GeolocationAccessStatus.Denied:
+ // notify user: Access to location is denied
+ gli.not_permitted = "true - enable location access";
+ break;
+
+ case GeolocationAccessStatus.Unspecified:
+ // notify user: Unspecified error
+ gli.not_permitted = "true";
+ break;
+ }
+
+ jsonPayload = JsonConvert.SerializeObject(gli, Formatting.Indented);
+ return jsonPayload;
+ }
+ }
+}
diff --git a/src/HASS.Agent/HASS.Agent/Models/Internal/GeolocationInfo.cs b/src/HASS.Agent/HASS.Agent/Models/Internal/GeolocationInfo.cs
new file mode 100644
index 00000000..045500cc
--- /dev/null
+++ b/src/HASS.Agent/HASS.Agent/Models/Internal/GeolocationInfo.cs
@@ -0,0 +1,37 @@
+using Windows.Devices.Geolocation;
+
+namespace HASS.Agent.Models.Internal
+{
+ public class GeolocationInfo
+ {
+ public string latitude { get; set; }
+ public string longitude { get; set; }
+ public string altitude { get; set; }
+ public string source_type { get; set; }
+ public string gps_accuracy { get; set; }
+ public string not_permitted { get; set; }
+
+ public GeolocationInfo()
+ {
+
+ }
+
+ public GeolocationInfo(string lon, string lat, PositionSource source)
+ {
+ longitude = lon;
+ latitude = lat;
+ source_type = "";
+ gps_accuracy = "1.2";
+
+ switch (source)
+ {
+ case PositionSource.Satellite:
+ source_type = "gps";
+ break;
+ case PositionSource.WiFi:
+ source_type = "router";
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
index 269df973..14e09776 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs
@@ -5990,6 +5990,19 @@ internal static string SensorsManager_CurrentVolumeSensorDescription {
}
}
+ ///
+ /// Looks up a localized string similar to Returns your current latitude, longitude and altitude as as location/device tracker, to directly display and track on a map.
+ ///
+ ///Make sure Windows' location services are enabled!
+ ///
+ ///Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'..
+ ///
+ internal static string SensorsManager_DeviceTrackerSensorDescription {
+ get {
+ return ResourceManager.GetString("SensorsManager_DeviceTrackerSensorDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Provides a sensor with the amount of displays, name of the primary display, and per display its name, resolution and bits per pixel..
///
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
index 63a460db..2664b871 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx
@@ -3528,4 +3528,10 @@ Muss unter „Konfiguration -> Tray-Icon“ konfiguriert werden.
Presse
+
+ Gibt den aktuellen Breitengrad, Längengrad und die Höhe als Location zurück, um sie direkt auf einer Karte anzuzeigen und zu verfolgen.
+
+Stelle sicher, dass die Ortungsdienste von Windows aktiviert sind!
+Abhängig von Ihrer Windows-Version finden Sie dies in der neuen Systemsteuerung > "Datenschutz und Sicherheit" > "Standort".
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
index 8dc6f1b7..33bdce83 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx
@@ -3433,4 +3433,11 @@ Requires it to be configured in "Configuration -> Tray Icon"
Press
+
+ Returns your current latitude, longitude and altitude as as location/device tracker, to directly display and track on a map.
+
+Make sure Windows' location services are enabled!
+
+Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
index 16b9e7d7..aea7b6f8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx
@@ -3404,4 +3404,11 @@ Requiere que se configure en "Configuración -> Icono de la bandeja"
Prensa
+
+ Devuelve su latitud, longitud y altitud actuales como rastreador de ubicación / dispositivo, para mostrar y rastrear directamente en un mapa.
+
+¡Asegúrate de que los servicios de ubicación de Windows estén habilitados!
+
+Dependiendo de su versión de Windows, esto se puede encontrar en el nuevo panel de control > 'privacidad y seguridad' > 'ubicación'.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
index 1bfdf5df..b4ba691b 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx
@@ -3437,4 +3437,11 @@ Nécessite sa configuration dans « Configuration -> Icône de la barre d'ét
Presse
+
+ Renvoie votre latitude, longitude et altitude actuelles sous forme de traqueur de localisation/appareil, pour les afficher et les suivre directement sur une carte.
+
+Assurez-vous que les services de localisation de Windows sont activés !
+
+Selon votre version de Windows, vous pouvez le trouver dans le nouveau panneau de configuration > 'confidentialité et sécurité' > 'localisation'.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
index ea161750..dd4d12fe 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx
@@ -3425,4 +3425,11 @@ Vereist dat het geconfigureerd is in "Configuratie -> Tray Icon"
Pers
+
+ Retourneert uw huidige breedtegraad, lengtegraad en hoogte als locatie-/apparaattracker, om direct weer te geven en te volgen op een kaart.
+
+Zorg ervoor dat de locatieservices van Windows zijn ingeschakeld!
+
+Afhankelijk van je Windows-versie is dit terug te vinden in het nieuwe configuratiescherm > 'privacy en beveiliging' > 'locatie'.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
index b8a5706d..9ed5d707 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx
@@ -3514,4 +3514,11 @@ Wymaga konfiguracji w „Configuration -> Tray Icon”
Naciśnij
+
+ Zwraca aktualną szerokość, długość i wysokość jako lokalizację/urządzenie śledzące, aby bezpośrednio wyświetlić i śledzić na mapie.
+
+Upewnij się, że usługi lokalizacyjne systemu Windows są włączone!
+
+W zależności od wersji systemu Windows można to znaleźć w nowym panelu sterowania > "prywatność i bezpieczeństwo" > "lokalizacja".
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
index f171b0c2..d46cecb5 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx
@@ -3450,4 +3450,11 @@ Requer que seja configurado em "Configuration -> Tray Icon"
Imprensa
+
+ Retorna sua latitude, longitude e altitude atuais como rastreador de localização/dispositivo, para exibir e rastrear diretamente em um mapa.
+
+Certifique-se de que os serviços de localização do Windows estejam ativados!
+
+Dependendo da sua versão do Windows, isso pode ser encontrado no novo painel de controle > 'privacidade e segurança' > 'localização'.
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
index da531004..df9e9686 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx
@@ -2916,6 +2916,13 @@ Are you sure you want this?
Make sure Windows' location services are enabled!
+Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'.
+
+
+ Returns your current latitude, longitude and altitude as as location/device tracker, to directly display and track on a map.
+
+Make sure Windows' location services are enabled!
+
Depending on your Windows version, this can be found in the new control panel -> 'privacy and security' -> 'location'.
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
index 24b2bb4f..bb90c870 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx
@@ -3473,4 +3473,11 @@ Home Assistant.
Нажимать
+
+ Возвращает текущую широту, долготу и высоту в виде отслеживания местоположения/устройства для непосредственного отображения и отслеживания на карте.
+
+Убедитесь, что службы определения местоположения Windows включены!
+
+В зависимости от вашей версии Windows, это можно найти в новой панели управления > разделах «Конфиденциальность и безопасность» > «Местоположение».
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
index 0c0728a7..884780a1 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx
@@ -3553,4 +3553,11 @@ Zahteva, da je konfiguriran v "Konfiguracija -> Ikona pladnja"
Pritisnite
+
+ Vráti vašu aktuálnu zemepisnú šírku, dĺžku a nadmorskú výšku ako sledovanie polohy/zariadenia na priame zobrazenie a sledovanie na mape.
+
+Uistite sa, že sú povolené lokalizačné služby systému Windows!
+
+V závislosti od verzie systému Windows ho nájdete v novom ovládacom paneli > "súkromie a zabezpečenie" > "umiestnenie".
+
\ No newline at end of file
diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
index e1d3413b..9d30ffb8 100644
--- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
+++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx
@@ -1,6 +1,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
Bu sayfa, dış araçlarla bağlantı kurmanızı sağlar.
-
+
Tarayıcı adı
-
+
Varsayılan olarak HASS.Agent, varsayılan tarayıcınızı kullanarak URL'leri başlatır.
Ayrıca,
özel modda çalışacak başlatma argümanlarıyla birlikte kullanılacak belirli bir tarayıcıyı da yapılandırabilirsiniz.
Fuzzy
-
+
Tarayıcı İkili
-
+
Ek Başlatma Argümanları
-
+
Özel Yürütücü İkili Dosyası
-
+
HASS.Agent'ı Perl veya Python gibi belirli bir yorumlayıcı kullanacak şekilde yapılandırabilirsiniz.
Bu yürütücüyü başlatmak için 'özel yürütücü' komutunu kullanın.
-
+
Özel Yürütücü Adı
-
+
İpucu: Göz atmak için çift tıklayın
-
+
&Ölçek
-
+
HASS.Agent, MQTT veya HA'nın API'si ile olan bağlantı kesintilerini size bildirmeden önce bir ek süre bekleyecektir.
Aşağıda bu ek süre içinde beklenecek saniye miktarını ayarlayabilirsiniz.
-
+
saniye
-
+
Bağlantı Kesildi İzin ve Süresi
-
+
ÖNEMLİ: Bu değeri değiştirirseniz, HASS.Agent tüm sensörlerinizi, komutlarınızı yayından kaldıracak ve yeni cihaz adı altında yeniden yayınlanabilmeleri için kendini yeniden başlatmaya zorlayacaktır.
Otomasyonlarınız ve komut dosyalarınız çalışmaya devam edecek.
-
+
Cihaz adı, Home Assistant'ta makinenizi tanımlamak için kullanılır. Ayrıca komut/sensör adlarınız için bir önek olarak kullanılır (bu, varlık başına değiştirilebilir).
Fuzzy
-
+
Bu sayfa genel yapılandırma ayarlarını içerir, daha fazla ayar için soldaki sekmelere göz atabilirsiniz.
-
+
Cihaz adı
-
+
İpucu: Göz atmak için bu alana çift tıklayın
-
+
Müşteri ve Sertifika
-
+
&Otomatik istemci sertifikası seçimini kullan
-
+
&Test bağlantısı
-
+
Hangi varlıkları yapılandırdığınızı öğrenmek ve hızlı eylemler göndermek için HASS.Agent,
Home Assistant'ın API'sini kullanır.
@@ -197,991 +197,991 @@ ve 'TOKEN OLUŞTUR' düğmesini görene kadar sayfanın en altına giderek bir t
İşlem yapılabilir bildirim işlevi için yönetici hesabı belirteci sağlamanız gerektiğini lütfen unutmayın.
-
+
&API Simgesi
-
+
Sunucu &URI
-
+
&Temizlemek
-
+
Hızlı işlemlerinizi almanın kolay bir yolu, genel bir global hotkey kullanmaktır.
Bu şekilde, makinenizde ne yapıyorsanız yapın, Home Assistant ile her zaman etkileşimde bulunabilirsiniz.
-
+
&Hızlı İşlemler Kısayol Tuşunu Etkinleştir
-
+
&Kısayol Kombinasyonu
-
+
Görüntü Önbelleğini Temizle
-
+
Açık dosya
-
+
Görüntü Önbelleği Konumu
-
+
günler
-
+
Bildirimlerde gösterilen resimler gibi bazı öğelerin geçici olarak yerel olarak depolanması gerekir. HASS.Agent
bunları silmeden önce tutulması gereken gün miktarını yapılandırabilirsiniz.
Bunları kalıcı olarak tutmak için '0' girin.
-
+
Genişletilmiş günlük kaydı, varsayılan günlük kaydının yeterli olmaması durumunda daha ayrıntılı ve derinlemesine günlük
kaydı sağlar. Lütfen bunun etkinleştirilmesinin günlük dosyalarının büyümesine neden olabileceğini
ve yalnızca HASS.Agent'ın kendisinde bir sorun olduğundan şüphelendiğinizde veya
geliştiriciler tarafından istendiğinde kullanılması gerektiğini unutmayın.
-
+
&Genişletilmiş Günlüğe Kaydetmeyi Etkinleştir
-
+
&Günlükler Klasörünü Aç
-
+
İpucu: Göz atmak için bu alanları çift tıklayın
-
+
Müşteri Sertifikası
-
+
Kök Sertifikası
-
+
Bayrağı &Kaldır Kullan
-
+
&Güvenilmeyen Sertifikalara İzin Ver
-
+
&Yapılandırmayı Temizle
-
+
(emin değilseniz varsayılanı bırakın)
-
+
Komutlar ve sensörler, yeni entegrasyonu kullanırken bildirimler ve medya oynatıcı işlevlerinin yanı sıra MQTT'yi kullanır.
Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini kullanıyorsanız, muhtemelen önceden ayarlanmış adresi kullanabilirsiniz.
Not: Bu ayarlar (Client ID hariç) uydu hizmetine de uygulanacaktır.
-
+
Keşif Öneki
-
+
Şifre
-
+
Kullanıcı adı
-
+
Liman
-
+
Aracı IP Adresi veya Ana Bilgisayar Adı
-
+
(otomatik oluşturmak için boş bırakın)
-
+
Müşteri Kimliği
-
+
Bir şey çalışmıyorsa, aşağıdaki adımları denediğinizden emin olun: - HASS.Agent entegrasyonunu kurun - Home Assistant'ı yeniden başlatın - MQTT etkinken HASS.Agent'ın etkin olduğundan emin olun! - Cihazınız otomatik olarak bir varlık olarak algılanmalı ve eklenmelidir - İsteğe bağlı olarak: yerel API'yi kullanarak manuel olarak ekleyin
-
+
HASS.Agent metin, resim ve eylemleri kullanarak Home Assistant'tan bildirimler alabilir. MQTT'yi etkinleştirdiyseniz, cihazınız otomatik olarak eklenir. Aksi takdirde, yerel API'yi kullanmak için entegrasyonu manuel olarak yapılandırın.
-
+
Bildirimler ve Belgeler
-
+
Liman
-
+
&Bildirimleri Kabul Et
-
+
Test Bildirimini Göster
-
+
Liman Rezervasyonunu Yürüt
-
+
&Görüntüler için sertifika hatalarını yoksay
-
+
Uydu hizmeti, hiçbir kullanıcı oturum açmadığında bile sensörleri ve komutları çalıştırmanıza izin verir. Yönetmek için ana penceredeki 'uydu hizmeti' düğmesini kullanın.
-
+
Servis durumu:
-
+
Hizmeti&başlat
-
+
&Hizmeti Devre Dışı Bırak
-
+
&Hizmeti durdur
-
+
&Hizmeti Etkinleştir
-
+
&Hizmeti Yeniden Yükle
-
+
Hizmeti yapılandırmazsanız, hiçbir şey yapmaz. Ancak, yine de devre dışı bırakmaya karar verebilirsiniz. Yükleyici, devre dışı bırakılan hizmeti kendi başına bırakacaktır (hizmeti kaldırırsanız, yükleyici yeniden yükleyecektir).
-
+
Düzgün çalışmıyorsa hizmeti yeniden yüklemeyi deneyebilirsiniz. Yapılandırmanız ve varlıklarınız kaldırılmayacak.
-
+
Hizmet ve Günlükler Klasörünü Aç
-
+
Yeniden yüklemeden sonra hizmet hala başarısız olursa, lütfen bir bilet açın ve en son günlüğün içeriğini gönderin.
-
+
HASS.Agent, kullanıcı profilinizin kayıt defterinde bir giriş oluşturarak oturum açtığınızda başlayabilir. HASS.Agent kullanıcı tabanlı olduğundan, başka bir kullanıcı için başlatmak istiyorsanız, HASS.Agent'ı orada kurun ve yapılandırın.
-
+
&Oturum Açıldığında Başlatmayı Etkinleştir
-
+
Girişte Başlama Durumu:
-
+
Beni &beta sürümlerinden haberdar et
-
+
Yeni bir güncelleme mevcut olduğunda, HASS.Agent yükleyiciyi indirebilir ve sizin için başlatabilir. İndirilen dosyanın sertifikası çalıştırmadan önce kontrol edilecek, yine de sürüm notlarını gözden geçirecek ve güncellemeyi manuel olarak onaylayacaksınız.
-
+
Gelecekteki güncellemeleri otomatik olarak &indir
-
+
HASS.Agent, etkinleştirilirse arka planda güncellemeleri kontrol eder. Yeni bir güncelleme keşfedilirse, yeni bir sürümün yüklenmeye hazır olduğunu bildiren bir anında iletme bildirimi gönderilecektir.
-
+
Yeni bir &sürüm çıktığında bana haber ver
-
+
HASS.Agent'a hoş geldiniz! Aracıyı ilk kez başlatıyorsunuz gibi görünüyor. İlk kurulumda size yardımcı olmak için aşağıdaki yapılandırma adımlarını uygulayın veya alternatif olarak 'Kapat'ı tıklayın.
-
+
Cihaz adı, Home Assistant'ta makinenizi tanımlamak için kullanılır, ayrıca komutlarınız ve sensörleriniz için önerilen bir önek olarak kullanılır.
-
+
Cihaz adı
-
+
Evet, Sistem Girişinde HASS.Agent'ı &başlatın
-
+
HASS.Agent, sisteminizle başlayabilir, bu, oturum açar açmaz cihazınız ve Home Assistant arasındaki tüm sensörlerin ve veri aktarımının başlamasına olanak tanır. Bu ayar, daha sonra HASS.Agent yapılandırma penceresinde herhangi bir zamanda değiştirilebilir.
-
+
Mevcut durum getiriliyor, lütfen bekleyin..
-
+
Not: 5115 varsayılan bağlantı noktasıdır, yalnızca Home Assistant'ta değiştirdiyseniz değiştirin.
-
+
Evet, bağlantı noktasındaki bildirimleri kabul et
-
+
HASS.Agent, metin ve/veya resimler kullanarak Home Assistant'tan bildirimler alabilir. Bu işlevi etkinleştirmek istiyor musunuz?
-
+
HASS.Agent-Notifier GitHub Sayfası
-
+
Şu adımları uyguladığınızdan emin olun: - HASS.Agent-Notifier entegrasyonunu kurun - Home Assistant'ı yeniden başlatın - Bir bildirim varlığı yapılandırın - Home Assistant'ı yeniden başlatın
-
+
Bildirimleri kullanmak için Home Assistant'ta HASS.Agent-notifier entegrasyonunu kurmanız ve yapılandırmanız gerekir. Bu, HACS'yi kullanmak çok kolaydır, ancak manuel olarak da kurulabilir, daha fazla bilgi için aşağıdaki bağlantıyı ziyaret edin.
-
+
API & Jeton
-
+
Sunucu &URI (böyle olması gerekir)
-
+
Hangi varlıkları yapılandırdığınızı öğrenmek ve hızlı eylemler göndermek için HASS.Agent, Home Assistant'ın API'sini kullanır. Lütfen uzun ömürlü bir erişim belirteci ve Home Assistant örneğinizin adresini sağlayın. Home Assistant'ta sol alttaki profil resminize tıklayarak ve 'TOKEN OLUŞTUR' düğmesini görene kadar sayfanın en altına giderek bir jeton alabilirsiniz.
-
+
Test bağlantısı
-
+
İpucu: Özel ayarlar, Yapılandırma Penceresinde bulunabilir.
-
+
Şifre
-
+
Kullanıcı adı
-
+
Liman
-
+
IP Adresi veya Ana Bilgisayar Adı
-
+
Komutlar ve sensörler MQTT aracılığıyla gönderilir. Bildirimler ve medya oynatıcı entegrasyonu da bunlardan yararlanır. İpucu: HA eklentisini kullanıyorsanız, muhtemelen önceden ayarlanmış adresi kullanabilirsiniz - sadece kimlik bilgilerini sağlayın.
-
+
Keşif Öneki
-
+
(emin değilseniz varsayılanı bırakın)
-
+
İpucu: Özel ayarlar, Yapılandırma Penceresinde bulunabilir.
-
+
&Kısayol Kombinasyonu
-
+
Hızlı işlemlerinizi almanın kolay bir yolu, genel bir kısayol tuşu kullanmaktır. Bu şekilde, makinenizde ne yapıyorsanız yapın, Home Assistant ile her zaman etkileşimde bulunabilirsiniz.
-
+
&Temizlemek
-
+
HASS.Agent, etkinleştirilirse arka planda güncellemeleri kontrol eder. Yeni bir güncelleme keşfedilirse, yeni bir sürümün yüklenmeye hazır olduğunu bildiren bir anında iletme bildirimi gönderilecektir. Bu otomatik güncelleme kontrollerini etkinleştirmek istiyor musunuz?
-
+
Evet, beni yeni &güncellemelerden haberdar et
-
+
Evet, benim için yükleyiciyi indirip başlatın
-
+
Yeni bir güncelleme mevcut olduğunda, HASS.Agent yükleyiciyi indirebilir ve sizin için başlatabilir. İndirilen dosyanın sertifikası çalıştırmadan önce kontrol edilecek, yine de sürüm notlarını gözden geçirecek ve güncellemeyi manuel olarak onaylayacaksınız.
-
+
HASS.Agent GitHub sayfası
-
+
Kurcalanacak daha çok şey var, bu yüzden Yapılandırma Penceresine bir göz attığınızdan emin olun! HASS.Agent'ı kullandığınız için teşekkür ederiz, umarım işinize yarar :-)
-
+
HASS.Agent şimdi yapılandırma değişikliklerinizi uygulamak için yeniden başlatılacak.
-
+
Tamam, bitti!
-
+
Düşük Bütünlük
-
+
İsim
-
+
Tip
-
+
&Kaldırmak
-
+
&Değiştir
-
+
&Yeni ekle
-
+
&Gönder && Komutları Etkinleştir
-
+
saklanan komutlar!
-
+
&Uygulamak
-
+
Yetki ve Kimlik
-
+
kimlik doğrulama
-
+
Hizmetle bağlantı kurun
-
+
Uydu servisi bağlanıyor, lütfen bekleyin..
-
+
Yapılandırmayı Getir
-
+
Bu sayfa, MQTT ayarları, komutları ve sensörler için genel yapılandırma ayarlarını içerir, yukarıdaki farklı sekmelere göz atın.
-
+
Yetki ve Kimlik
-
+
Cihaz adı
-
+
İpucu: Göz atmak için bu alanları çift tıklayın
-
+
Özel Yürütücü ve İkili
-
+
Özel ve Yürütücü Adı
-
+
saniye
-
+
Bağlantı Kesildi İzin ve Süresi
-
+
Uygulamak
-
+
Sürüm
-
+
İpucu: Rastgele oluşturmak için çift tıklayın
-
+
Saklanmış!
-
+
(otomatik oluşturmak için boş bırakın)
-
+
Müşteri Kimliği
-
+
İpucu: Göz atmak için bu alanları çift tıklayın
-
+
Müşteri Sertifikası
-
+
Kök Sertifikası
-
+
Bayrağı &Kaldır Kullan
-
+
&Güvenilmeyen Sertifikalara İzin Ver
-
+
&Yapılandırmayı Temizle
-
+
(emin değilseniz varsayılanı bırakın)
-
+
Komutlar ve sensörler MQTT aracılığıyla gönderilir. Lütfen sunucunuz için kimlik bilgilerini sağlayın. HA eklentisini kullanıyorsanız, muhtemelen önceden ayarlanmış adresi kullanabilirsiniz.
-
+
Keşif Öneki
-
+
Şifre
-
+
Kullanıcı adı
-
+
Liman
-
+
Aracı IP Adresi veya Ana Bilgisayar Adı
-
+
&Gönder && Yapılandırmayı Etkinleştir
-
+
&HASS.Agent'tan kopyala
-
+
Yapılandırma kaydedildi!
-
+
Durum
-
+
sorgulanıyor..
-
+
İsim
-
+
Tip
-
+
Yenile
-
+
&Kaldırmak
-
+
&Yeni ekle
-
+
&Değiştir
-
+
&Gönder && Sensörleri Etkinleştir
-
+
Sensörler kaydedildi!
-
+
Lütfen görev yapılırken biraz bekleyin ..
-
+
API Bağlantı Noktası Bağlama Oluştur
-
+
Güvenlik Duvarı Kuralı Belirle
-
+
HASS.Agent Port Rezervasyonu
-
+
Bazı güncelleme sonrası görevler yapılırken lütfen biraz bekleyin..
-
+
Uydu Hizmetini Yapılandırma
-
+
API Bağlantı Noktası Bağlama Oluştur
-
+
HASS.Agent Gönderi Güncellemesi
-
+
HASS.Agent yeniden başlatılırken lütfen bekleyin..
-
+
Önceki örneğin kapanması bekleniyor..
-
+
HASS.Agent'ı Yeniden Başlatın
-
+
HASS.Agent Yeniden Başlatıcı
-
+
Uydu servisi yeniden kurulurken lütfen bekleyiniz..
-
+
Uydu Hizmetini Kaldır
-
+
Uydu Hizmetini Yükle
-
+
HASS.Agent Uydu Hizmetini Yeniden Yükleme
-
+
Uydu hizmeti yapılandırılırken lütfen bekleyin..
-
+
Uydu Hizmetini Etkinleştir
-
+
HASS.Agent Uydu Hizmetini Yapılandırma
-
+
&Kapat
-
+
Bu, eylem komutlarını yayınlayabileceğiniz MQTT konusudur:
-
+
Panoya kopyala
-
+
yardım ve örnekler
-
+
MQTT Eylem Konusu
-
+
&Kaldırmak
-
+
&Değiştir
-
+