diff --git a/README.md b/README.md
index faeeb4f..932310b 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Not convinced yet? Check out [this excellent video](https://youtu.be/D5A7le79R5M
## Installation
You can get the installer from [here](https://hassworkstationstorage.z6.web.core.windows.net/publish/setup.exe). When using the installer, the application checks for updates on startup. This is the recommended way to install for most users.
-Note: You'll get a Windows Smartscreen warning because the code was self signed. You can click "More info" and then "Run anyway" to proceed with installing.
+Note: You'll get a Windows Smartscreen warning because the code was self signed. You can click "More info" and then "Run anyway" to proceed with installing. If you get an error stating your system's settings not allowing installation, please refer to [this StackOverflow answer](https://superuser.com/a/1252757).
### Standalone
@@ -47,14 +47,14 @@ Find us on [Discord](https://discord.gg/VraYT2N3wd), or check out the [frequentl
## Development
-Want to develop or build the application yourself? Make sure to install the .NET Runtime [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/current/runtime) and [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/current). Run the following commands from the `hass-workstation-service\hass-workstation-service` directory to get you started:
+If you want to help develop Hass Workstation service, make sure to install the .NET Runtime [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/current/runtime) and [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/current). Run the following commands from the `hass-workstation-service\hass-workstation-service` directory to get you started:
```` powershell
dotnet build
dotnet publish
````
-In case you are using Visual Studio Code, open the `hass-workstation-service\hass-workstation-service` folder to take advantage of the predefined build and publish tasks.
+If you are using [Visual Studio](https://visualstudio.microsoft.com/), open the `hass-workstation-service\hass-workstation-service` folder to take advantage of the predefined build and publish tasks, alternatively you can open the project directly from github using the green download button to use the integrated git tools.
## Sensors
@@ -62,151 +62,25 @@ The application provides several sensors. Sensors can be configured with a name
Sensors publish their state on their own interval which you can configure and only publish when the state changes.
-### UserNotificationState
+Here is a list of the most commonly used sensors with the full documentation [here](https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md):
-This sensor watches the UserNotificationState. This is normally used in applications to determine if it is appropriate to send a notification but we can use it to expose this state. Notice that this status does not watch Focus Assist. It has the following possible states:
-
-|State|Explanation|
-|---|---|
-|NotPresent|A screen saver is displayed, the machine is locked, or a nonactive Fast User Switching session is in progress. |
-|Busy|A full-screen application is running or Presentation Settings are applied. Presentation Settings allow a user to put their machine into a state fit for an uninterrupted presentation, such as a set of PowerPoint slides, with a single click.|
-|RunningDirect3dFullScreen|A full-screen (exclusive mode) Direct3D application is running.|
-|PresentationMode|The user has activated Windows presentation settings to block notifications and pop-up messages.|
-|AcceptsNotifications|None of the other states are found, notifications can be freely sent.|
-|QuietTime|Introduced in Windows 7. The current user is in "quiet time", which is the first hour after a new user logs into his or her account for the first time. During this time, most notifications should not be sent or shown. This lets a user become accustomed to a new computer system without those distractions. Quiet time also occurs for each user after an operating system upgrade or clean installation.|
-|RunningWindowsStoreApp|A Windows Store app is running.|
-
-### ActiveWindow
-
-This sensor exposes the name of the currently focused window.
-
-### WebcamActive
-
-This sensor shows if the webcam is currently being used. It uses the Windows registry to check will work from Windows 10 version 1903 and higher.
-
-### MicrophoneActive
-
-This sensor shows if the microphone is currently being used. It uses the Windows registry to check and will work from Windows 10 version 1903 and higher.
-
-### CPULoad
-
-This sensor checks the current CPU load. It averages the load on all logical cores every second and rounds the output to two decimals.
-
-### GPULoad
-
-This sensor returns the current GPU load. This should work for both NVidia and AMD GPU's.
-
-### GPUTemperature
-
-This sensor returns the current temperature of the GPU in °C. This should work for both NVidia and AMD GPU's.
-
-### UsedMemory
-
-This sensor calculates the percentage of used memory.
-
-### CurrentClockSpeed
-
-This sensor returns the BIOS configured baseclock for the processor.
-
-### WMIQuery
-
-This advanced sensor executes a user defined [WMI query](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-and-sql) and exposes the result. The query should return a single value.
-
-For example:
-
-```sql
-SELECT * FROM Win32_Processor
-```
-
-returns
-
-`|64|9|To Be Filled By O.E.M.|3|Intel64 Family 6 Model 94 Stepping 3|252|1|Win32_Processor|4008|12|64|Intel64 Family 6 Model 94 Stepping 3|CPU0|100|198|1024|8192|0|6|4|GenuineIntel|4008|Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz|4|4|8|To Be Filled By O.E.M.|False|BFEBFBFF000506E3|3|24067|CPU|False|To Be Filled By O.E.M.|U3E1|OK|3|Win32_ComputerSystem|GAME-PC-2016|8|1|False|False|`
-
-This cannot not be used for this sensor. Instead try
-
-```sql
-SELECT CurrentClockSpeed FROM Win32_Processor
-```
-
-which results in `4008` for my PC.
-
-You can use [WMI Explorer](https://github.com/vinaypamnani/wmie2/tree/v2.0.0.2) to find see what data is available.
-
-Here's some queries from other users:
-
-|Query|Explanation|Thanks|
-|---|---|---|
-|`SELECT username FROM Win32_ComputerSystem`|Shows the current user|@grizzlyjere|
-
-Want to add you query here? Please create a pull request or open an issue.
-
-### LastActive
-
-This sensor returns the date/time that the workstation was last active. Typing or moving your mouse will reset the date/time.
-
-### LastBoot
-
-This sensor returns the date/time that Windows was last booted.
-
-### SessionState
-
-This sensor returns the current session state. It has the following possible states:
-
-|State|Explanation|
+|sensor|use|
|---|---|
-|Locked|All user sessions are locked.|
-|LoggedOff|No users are logged in.|
-|InUse|A user is currently logged in.|
-|Unknown|Something went wrong while getting the status.|
-
-### CurrentVolume
-
-This sensor returns the volume of the currently playing audio. So if you're listening to music and you pause, this sensor will return 0 (or at least a very low value).
-
-### Dummy
-
-This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it.
+|ActiveWindow|Exposes the currently selected window|
+|WebcamActive|Exposes the microphone state|
+|MicrophoneActive|Exposes the webcam state|
## Commands
-Commands can be used to trigger certain things on the client. For each command, a switch will be available in Home Assistant. Turning on the switch fires the command on the client and it will turn the switch off when it's done. Turning it off will cancel the running command.
-
-### ShutdownCommand
-
-This command shuts down the computer immediately. It runs `shutdown /s`.
-
-### RestartCommand
-
-This command restarts the computer immediately. It runs `shutdown /r`.
-
-### LogOffCommand
-
-This command logs off the current user. It runs `shutdown /l`.
+This application allows you to send commands over MQTT to control the host system, and will be exposed using [MQTT discovery](https://www.home-assistant.io/docs/mqtt/discovery/). Alternatively you can directly send a command from Home Assistant using this topic : `homeassistant/switch/{DeviceName}/{Name}/set`, with the payload `ON`.
-### CustomCommand
+Here is a list of the most commonly used sensors with the full documentation [here](https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md)
-This command allows you to run any Windows Commands. The command will be run in a hidden Command Prompt. Some examples:
-
-|Command|Explanation|
+|command|use|
|---|---|
-|Rundll32.exe user32.dll,LockWorkStation|This locks the current session.|
-|shutdown /s /t 300|Shuts the PC down after 5 minutes (300 seconds).|
-|C:\path\to\your\batchfile.bat|Run the specified batch file.|
-
-### KeyCommand
-
-Sends a keystroke with the specified key. You can pick [any of these](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes) key codes.
-
-### Media Commands
-
-There's several media commands available which are very self exlanatory.
-
-- Play/Pause
-- Next
-- Previous
-- Volume up
-- Volume down
-- Mute (toggle)
+|ShutdownCommand|Shutdown the PC|
+|RestartCommand|Restart the PC|
+|MuteCommand|Mute the speakers|
## Credits
diff --git a/UserInterface/UserInterface.csproj b/UserInterface/UserInterface.csproj
index 342d878..73c7ed9 100644
--- a/UserInterface/UserInterface.csproj
+++ b/UserInterface/UserInterface.csproj
@@ -5,20 +5,19 @@
UserInterface.Program
-
-
-
-
-
-
+
+
+
+
+
-
+
@@ -33,6 +32,9 @@
BackgroundServiceSettings.axaml
+
+ GeneralSettings.axaml
+
CommandSettings.axaml
diff --git a/UserInterface/ViewModels/AddCommandViewModel.cs b/UserInterface/ViewModels/AddCommandViewModel.cs
index 133bfca..8aa0633 100644
--- a/UserInterface/ViewModels/AddCommandViewModel.cs
+++ b/UserInterface/ViewModels/AddCommandViewModel.cs
@@ -1,34 +1,26 @@
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using ReactiveUI;
-using System;
-using System.Collections.Generic;
-using System.Text;
namespace UserInterface.ViewModels
{
public class AddCommandViewModel : ViewModelBase
{
- private AvailableCommands selectedType;
- private string description;
+ private AvailableCommands _selectedType;
+ private string _name;
+ private string _description;
+ private bool _showCommandInput;
+ private bool _showKeyInput;
+ private string _moreInfoLink;
+ private string _command;
+ private string _key;
- public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); }
- public bool ShowCommandInput { get => showCommandInput; set => this.RaiseAndSetIfChanged(ref showCommandInput, value); }
- public bool ShowKeyInput { get => showKeyInput; set => this.RaiseAndSetIfChanged(ref showKeyInput, value); }
-
- private string moreInfoLink;
- private bool showCommandInput;
- private bool showKeyInput;
-
- public string MoreInfoLink
- {
- get { return moreInfoLink; }
- set { this.RaiseAndSetIfChanged(ref moreInfoLink, value); }
- }
-
- public AvailableCommands SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
-
- public string Name { get; set; }
- public string Command { get; set; }
- public string Key { get; set; }
+ public AvailableCommands SelectedType { get => _selectedType; set => this.RaiseAndSetIfChanged(ref _selectedType, value); }
+ public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); }
+ public string Description { get => _description; set => this.RaiseAndSetIfChanged(ref _description, value); }
+ public bool ShowCommandInput { get => _showCommandInput; set => this.RaiseAndSetIfChanged(ref _showCommandInput, value); }
+ public bool ShowKeyInput { get => _showKeyInput; set => this.RaiseAndSetIfChanged(ref _showKeyInput, value); }
+ public string MoreInfoLink { get => _moreInfoLink; set => this.RaiseAndSetIfChanged(ref _moreInfoLink, value); }
+ public string Command { get => _command; set => this.RaiseAndSetIfChanged(ref _command, value); }
+ public string Key { get => _key; set => this.RaiseAndSetIfChanged(ref _key, value); }
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/ViewModels/AddSensorViewModel.cs b/UserInterface/ViewModels/AddSensorViewModel.cs
index e3303f6..4e22e0f 100644
--- a/UserInterface/ViewModels/AddSensorViewModel.cs
+++ b/UserInterface/ViewModels/AddSensorViewModel.cs
@@ -1,40 +1,28 @@
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using ReactiveUI;
-using System;
-using System.Collections.Generic;
-using System.Text;
namespace UserInterface.ViewModels
{
public class AddSensorViewModel : ViewModelBase
{
- private AvailableSensors selectedType;
- private string description;
- private bool showQueryInput;
-
- public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); }
- public bool ShowQueryInput { get => showQueryInput; set => this.RaiseAndSetIfChanged(ref showQueryInput, value); }
- public bool ShowWindowNameInput { get => showWindowNameInput; set => this.RaiseAndSetIfChanged(ref showWindowNameInput, value); }
-
- public bool ShowDetectionModeOptions { get => showDetectionModeOptions; set => this.RaiseAndSetIfChanged(ref showDetectionModeOptions, value); }
-
- private string moreInfoLink;
- private int updateInterval;
- private bool showWindowNameInput;
- private bool showDetectionModeOptions;
-
- public string MoreInfoLink
- {
- get { return moreInfoLink; }
- set { this.RaiseAndSetIfChanged(ref moreInfoLink, value); }
- }
-
-
- public AvailableSensors SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
-
- public string Name { get; set; }
- public string Query { get; set; }
- public string WindowName { get; set; }
- public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); }
+ private AvailableSensors _selectedType;
+ private string _name;
+ private int _updateInterval;
+ private string _description;
+ private bool _showQueryInput;
+ private bool _showWindowNameInput;
+ private string _moreInfoLink;
+ private string _query;
+ private string _windowName;
+
+ public AvailableSensors SelectedType { get => _selectedType; set => this.RaiseAndSetIfChanged(ref _selectedType, value); }
+ public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); }
+ public int UpdateInterval { get => _updateInterval; set => this.RaiseAndSetIfChanged(ref _updateInterval, value); }
+ public string Description { get => _description; set => this.RaiseAndSetIfChanged(ref _description, value); }
+ public bool ShowQueryInput { get => _showQueryInput; set => this.RaiseAndSetIfChanged(ref _showQueryInput, value); }
+ public bool ShowWindowNameInput { get => _showWindowNameInput; set => this.RaiseAndSetIfChanged(ref _showWindowNameInput, value); }
+ public string MoreInfoLink { get => _moreInfoLink; set => this.RaiseAndSetIfChanged(ref _moreInfoLink, value); }
+ public string Query { get => _query; set => this.RaiseAndSetIfChanged(ref _query, value); }
+ public string WindowName { get => _windowName; set => this.RaiseAndSetIfChanged(ref _windowName, value); }
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/ViewModels/CommandSettingsViewModel.cs b/UserInterface/ViewModels/CommandSettingsViewModel.cs
index 4477508..ac7eb8a 100644
--- a/UserInterface/ViewModels/CommandSettingsViewModel.cs
+++ b/UserInterface/ViewModels/CommandSettingsViewModel.cs
@@ -1,15 +1,20 @@
-using ReactiveUI;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using ReactiveUI;
using System;
using System.Collections.Generic;
-using System.Text;
namespace UserInterface.ViewModels
{
public class CommandSettingsViewModel : ViewModelBase
{
- private ICollection configuredCommands;
+ private ICollection _configuredCommands;
+
+ public ICollection ConfiguredCommands
+ {
+ get => _configuredCommands;
+ set => this.RaiseAndSetIfChanged(ref _configuredCommands, value);
+ }
- public ICollection ConfiguredCommands { get => configuredCommands; set => this.RaiseAndSetIfChanged(ref configuredCommands, value); }
public void TriggerUpdate()
{
this.RaisePropertyChanged();
@@ -19,7 +24,7 @@ public void TriggerUpdate()
public class CommandViewModel : ViewModelBase
{
public Guid Id { get; set; }
- public string Type { get; set; }
+ public AvailableCommands Type { get; set; }
public string Name { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/ViewModels/GeneralSettingsViewModel.cs b/UserInterface/ViewModels/GeneralSettingsViewModel.cs
new file mode 100644
index 0000000..bda2e48
--- /dev/null
+++ b/UserInterface/ViewModels/GeneralSettingsViewModel.cs
@@ -0,0 +1,22 @@
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using hass_workstation_service.Data;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Text;
+
+namespace UserInterface.ViewModels
+{
+ public class GeneralSettingsViewModel : ViewModelBase
+ {
+ private string namePrefix;
+
+ public string NamePrefix { get => namePrefix; set => this.RaiseAndSetIfChanged(ref namePrefix, value); }
+
+ public void Update(GeneralSettings settings)
+ {
+ this.NamePrefix = settings.NamePrefix;
+ }
+ }
+}
diff --git a/UserInterface/ViewModels/SensorSettingsViewModel.cs b/UserInterface/ViewModels/SensorSettingsViewModel.cs
index 1939bf6..2a52fd0 100644
--- a/UserInterface/ViewModels/SensorSettingsViewModel.cs
+++ b/UserInterface/ViewModels/SensorSettingsViewModel.cs
@@ -1,15 +1,20 @@
-using ReactiveUI;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using ReactiveUI;
using System;
using System.Collections.Generic;
-using System.Text;
namespace UserInterface.ViewModels
{
public class SensorSettingsViewModel : ViewModelBase
{
- private ICollection configuredSensors;
+ private ICollection _configuredSensors;
+
+ public ICollection ConfiguredSensors
+ {
+ get => _configuredSensors;
+ set => this.RaiseAndSetIfChanged(ref _configuredSensors, value);
+ }
- public ICollection ConfiguredSensors { get => configuredSensors; set => this.RaiseAndSetIfChanged(ref configuredSensors, value); }
public void TriggerUpdate()
{
this.RaisePropertyChanged();
@@ -21,30 +26,21 @@ public class SensorViewModel : ViewModelBase
private string _value;
public Guid Id { get; set; }
- public string Type { get; set; }
+ public AvailableSensors Type { get; set; }
public string Name { get; set; }
public int UpdateInterval { get; set; }
public string Value
{
- get => _value; set
+ get => _value;
+ set
{
this.RaiseAndSetIfChanged(ref _value, value);
- this.RaisePropertyChanged("ValueString");
+ this.RaisePropertyChanged(nameof(ValueString));
}
}
+
public string UnitOfMeasurement { get; set; }
- public string ValueString
- {
- get
- {
- if (!string.IsNullOrWhiteSpace(_value))
- {
- return _value + " " + UnitOfMeasurement;
- }
- else return "";
-
- }
- }
+ public string ValueString => string.IsNullOrWhiteSpace(_value) ? string.Empty : $"{_value} {UnitOfMeasurement}";
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/Views/AddCommandDialog.axaml b/UserInterface/Views/AddCommandDialog.axaml
index dc2e967..30bdcf7 100644
--- a/UserInterface/Views/AddCommandDialog.axaml
+++ b/UserInterface/Views/AddCommandDialog.axaml
@@ -5,24 +5,26 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UserInterface.Views.AddCommandDialog"
SizeToContent="WidthAndHeight"
- Title="Add command">
-
- Command type
+ Title="Add / edit command">
+
+ Command type
-
-
-
- Name
-
+
+
+
+ Name
+
-
- Command
-
- Key
-
-
-
-
+
+ Command
+
+ Key
+
+
+
+
+
+
diff --git a/UserInterface/Views/AddCommandDialog.axaml.cs b/UserInterface/Views/AddCommandDialog.axaml.cs
index a1ed877..70a6ade 100644
--- a/UserInterface/Views/AddCommandDialog.axaml.cs
+++ b/UserInterface/Views/AddCommandDialog.axaml.cs
@@ -7,7 +7,6 @@
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
-using System.Dynamic;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
@@ -17,107 +16,153 @@ namespace UserInterface.Views
{
public class AddCommandDialog : Window
{
- private readonly IIpcClient client;
- public ComboBox comboBox { get; set; }
- public ComboBox detectionModecomboBox { get; set; }
+ private readonly IIpcClient _client;
+ public ComboBox ComboBox { get; set; }
+ public ComboBox DetectionModecomboBox { get; set; }
+ public Guid CommandId { get; }
+
+ public AddCommandDialog(Guid commandId) : this()
+ {
+ CommandId = commandId;
+ GetCommandInfo(CommandId);
+ Title = "Edit command";
+ }
+
public AddCommandDialog()
{
- this.InitializeComponent();
+ InitializeComponent();
DataContext = new AddCommandViewModel();
- this.comboBox = this.FindControl("ComboBox");
- this.comboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast().OrderBy(v => v.ToString());
- this.comboBox.SelectedIndex = 0;
+ ComboBox = this.FindControl("ComboBox");
+ ComboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast().OrderBy(v => v.ToString());
+ ComboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("addCommand", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("addCommand", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
- this.client = clientFactory.CreateClient("addCommand");
+ _client = clientFactory.CreateClient("addCommand");
+ Title = "Add command";
+
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ private async void GetCommandInfo(Guid commandId)
+ {
+ var command = await _client.InvokeAsync(x => x.GetConfiguredCommand(commandId));
+
+ ComboBox.SelectedItem = command.Type;
+ FillDefaultValues();
+ ComboBox.IsEnabled = false;
+ var item = (AddCommandViewModel)DataContext;
+ item.SelectedType = command.Type;
+ item.Name = command.Name;
+ item.Command = command.Command;
+ item.Key = command.Key;
+
}
public async void Save(object sender, RoutedEventArgs args)
{
- var item = ((AddCommandViewModel)this.DataContext);
- dynamic model = new { item.Name, item.Command, item.Key};
+ var item = (AddCommandViewModel)DataContext;
+ dynamic model = new { item.Name, item.Command, item.Key };
string json = JsonSerializer.Serialize(model);
- await this.client.InvokeAsync(x => x.AddCommand(item.SelectedType, json));
+ if (CommandId == Guid.Empty)
+ await _client.InvokeAsync(x => x.AddCommand(item.SelectedType, json));
+ else
+ await _client.InvokeAsync(x => x.UpdateCommandById(CommandId, json));
+
Close();
}
public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
{
- var item = ((AddCommandViewModel)this.DataContext);
- switch (this.comboBox.SelectedItem)
+ FillDefaultValues();
+ }
+
+ private void FillDefaultValues()
+ {
+ var item = (AddCommandViewModel)DataContext;
+ switch (ComboBox.SelectedItem)
{
case AvailableCommands.CustomCommand:
item.Description = "This command lets you execute any command you want. It will run in a Windows Command Prompt silently. ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#customcommand";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#customcommand";
item.ShowCommandInput = true;
item.ShowKeyInput = false;
break;
case AvailableCommands.ShutdownCommand:
item.Description = "This command shuts down the PC immediately. ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#shutdowncommand";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#shutdowncommand";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.RestartCommand:
item.Description = "This command restarts the PC immediately. ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#restartcommand";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#restartcommand";
+ item.ShowCommandInput = false;
+ item.ShowKeyInput = false;
+ break;
+ case AvailableCommands.HibernateCommand:
+ item.Description = "This command hibernates the PC immediately. ";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#hibernatecommand";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.LogOffCommand:
item.Description = "This command logs the current user off immediately. ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#logoffcommand";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#logoffcommand";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.KeyCommand:
item.Description = "This command can be used to emulate a keystroke. It requires a key code which you can find by clicking the info button below.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#keycommand";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#keycommand";
item.ShowCommandInput = false;
item.ShowKeyInput = true;
break;
case AvailableCommands.PlayPauseCommand:
item.Description = "This command plays or pauses currently playing media.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.NextCommand:
item.Description = "This command skips to the next media.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.PreviousCommand:
item.Description = "This command plays previous media.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.VolumeDownCommand:
item.Description = "Lowers the system volume.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.VolumeUpCommand:
item.Description = "Raises the system volume.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
case AvailableCommands.MuteCommand:
item.Description = "Toggles muting the system volume.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#media-commands";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Commands.md#media-commands";
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
@@ -129,28 +174,26 @@ public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
break;
}
}
- public void OpenInfo(object sender, RoutedEventArgs args)
+
+ public void OpenInfo(object sender, RoutedEventArgs args)
{
- var item = ((AddCommandViewModel)this.DataContext);
+ var item = (AddCommandViewModel)DataContext;
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
public void Test(object sender, RoutedEventArgs args)
{
- var item = ((AddCommandViewModel)this.DataContext);
+ var item = (AddCommandViewModel)DataContext;
- System.Diagnostics.Process process = new System.Diagnostics.Process();
- System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
- startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
- startInfo.FileName = "cmd.exe";
- startInfo.Arguments = $"/k {"echo You won't see this window normally. &&" + item.Command}";
+ var process = new System.Diagnostics.Process();
+ var startInfo = new System.Diagnostics.ProcessStartInfo
+ {
+ WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal,
+ FileName = "cmd.exe",
+ Arguments = $"/k {"echo You won't see this window normally. &&" + item.Command}"
+ };
process.StartInfo = startInfo;
process.Start();
}
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/Views/AddSensorDialog.axaml b/UserInterface/Views/AddSensorDialog.axaml
index 10d62ec..8b3b295 100644
--- a/UserInterface/Views/AddSensorDialog.axaml
+++ b/UserInterface/Views/AddSensorDialog.axaml
@@ -5,27 +5,27 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UserInterface.Views.AddSensorDialog"
SizeToContent="WidthAndHeight"
- Title="Add sensor">
-
- Sensor type
+ Title="Add / edit sensor">
+
+ Sensor type
-
-
-
- Name
-
+
+
+
+ Name
+
- Update interval
-
-
-
-
-
- Query
-
- Window name
- This is case-insensitive and loosely matched. A window called "Spotify Premium" will match "spotify" or "premium".
-
+ Update interval
+
+
+
+
+
+ Query
+
+ Window name
+ This is case-insensitive and loosely matched. A window called "Spotify Premium" will match "spotify" or "premium".
+
-
+
diff --git a/UserInterface/Views/AddSensorDialog.axaml.cs b/UserInterface/Views/AddSensorDialog.axaml.cs
index 8c12a81..5d83bd7 100644
--- a/UserInterface/Views/AddSensorDialog.axaml.cs
+++ b/UserInterface/Views/AddSensorDialog.axaml.cs
@@ -7,7 +7,6 @@
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
-using System.Dynamic;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
@@ -17,163 +16,219 @@ namespace UserInterface.Views
{
public class AddSensorDialog : Window
{
- private readonly IIpcClient client;
- public ComboBox comboBox { get; set; }
- public ComboBox detectionModecomboBox { get; set; }
+ private readonly IIpcClient _client;
+ public ComboBox ComboBox { get; set; }
+ public ComboBox DetectionModecomboBox { get; set; }
+ public Guid SensorId { get; }
+
+ public AddSensorDialog(Guid sensorId) : this()
+ {
+ SensorId = sensorId;
+ GetSensorInfo(SensorId);
+ Title = "Edit sensor";
+ }
+
public AddSensorDialog()
{
- this.InitializeComponent();
+ InitializeComponent();
DataContext = new AddSensorViewModel();
- this.comboBox = this.FindControl("ComboBox");
- this.comboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast().OrderBy(v => v.ToString());
- this.comboBox.SelectedIndex = 0;
+ ComboBox = this.FindControl("ComboBox");
+ ComboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast().OrderBy(v => v.ToString());
+ ComboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("addsensor", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("addsensor", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
- this.client = clientFactory.CreateClient("addsensor");
+ _client = clientFactory.CreateClient("addsensor");
+ Title = "Add sensor";
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ private async void GetSensorInfo(Guid sensorId)
+ {
+ ConfiguredSensorModel sensor = await _client.InvokeAsync(x => x.GetConfiguredSensor(sensorId));
+
+ ComboBox.SelectedItem = sensor.Type;
+ FillDefaultValues();
+ ComboBox.IsEnabled = false;
+ var item = (AddSensorViewModel)DataContext;
+ item.SelectedType = sensor.Type;
+ item.Name = sensor.Name;
+ item.UpdateInterval = sensor.UpdateInterval;
+ item.Query = sensor.Query;
+ item.WindowName = sensor.WindowName;
+
+ Title = $"Edit {sensor.Name}";
}
public async void Save(object sender, RoutedEventArgs args)
{
- var item = ((AddSensorViewModel)this.DataContext);
- dynamic model = new { item.Name, item.Query, item.UpdateInterval, item.WindowName};
+ var item = (AddSensorViewModel)DataContext;
+ dynamic model = new { item.Name, item.Query, item.UpdateInterval, item.WindowName };
string json = JsonSerializer.Serialize(model);
- await this.client.InvokeAsync(x => x.AddSensor(item.SelectedType, json));
+ if (SensorId == Guid.Empty)
+ await _client.InvokeAsync(x => x.AddSensor(item.SelectedType, json));
+ else
+ await _client.InvokeAsync(x => x.UpdateSensorById(SensorId, json));
+
Close();
}
public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
{
- var item = ((AddSensorViewModel)this.DataContext);
- switch (this.comboBox.SelectedItem)
+ FillDefaultValues();
+ }
+
+ private void FillDefaultValues()
+ {
+ var item = (AddSensorViewModel)DataContext;
+ switch (ComboBox.SelectedItem)
{
case AvailableSensors.UserNotificationStateSensor:
item.Description = "This sensor watches the UserNotificationState. This is normally used in applications to determine if it is appropriate to send a notification but we can use it to expose this state. \n ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usernotificationstate";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#usernotificationstate";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.DummySensor:
item.Description = "This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#dummy";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#dummysensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 1;
break;
+
case AvailableSensors.CPULoadSensor:
item.Description = "This sensor checks the current CPU load. It averages the load on all logical cores every second and rounds the output to two decimals.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#cpuload";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#cpuloadsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.CurrentClockSpeedSensor:
item.Description = "This sensor returns the BIOS configured baseclock for the processor.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#currentclockspeed";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#currentclockspeedsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 3600;
break;
+
case AvailableSensors.WMIQuerySensor:
item.Description = "This advanced sensor executes a user defined WMI query and exposes the result. The query should return a single value.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#wmiquerysensor";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipperhass-workstation-service/blob/master/documentation/WMIQuery.md#wmiquerysensor";
item.ShowQueryInput = true;
item.ShowWindowNameInput = false;
item.UpdateInterval = 10;
break;
+
case AvailableSensors.MemoryUsageSensor:
item.Description = "This sensor calculates the percentage of used memory.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usedmemory";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#memoryusagesensorsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 10;
break;
+
case AvailableSensors.ActiveWindowSensor:
item.Description = "This sensor exposes the name of the currently active window.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#activewindow";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#activewindowsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.WebcamActiveSensor:
item.Description = "This sensor shows if the webcam is currently being used.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#webcamactive";
- item.ShowDetectionModeOptions = true;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#webcamactivesensor";
item.ShowQueryInput = false;
item.UpdateInterval = 10;
break;
+
case AvailableSensors.MicrophoneActiveSensor:
item.Description = "This sensor shows if the microphone is currently in use.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#microphoneactive";
- item.ShowDetectionModeOptions = false;
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#microphoneactivesensor";
item.ShowQueryInput = false;
item.UpdateInterval = 10;
break;
+
case AvailableSensors.NamedWindowSensor:
item.Description = "This sensor returns true if a window was found with the name you search for. ";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#namedwindow";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#namedwindowsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = true;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.LastActiveSensor:
item.Description = "This sensor returns the date/time that the workstation was last active.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#lastactive";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#lastactivesensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.LastBootSensor:
item.Description = "This sensor returns the date/time that Windows was last booted";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#lastboot";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#lastbootsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.SessionStateSensor:
item.Description = "This sensor returns the state of the Windows session.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#sessionstate";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#sessionstatesensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.CurrentVolumeSensor:
item.Description = "This sensor returns the volume of currently playing audio.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#currentvolume";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#currentvolumesensor";
+ item.ShowQueryInput = false;
+ item.ShowWindowNameInput = false;
+ item.UpdateInterval = 5;
+ break;
+
+ case AvailableSensors.MasterVolumeSensor:
+ item.Description = "This sensor returns the master volume of the currently selected default audio device as a percentage value.";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#mastervolumesensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.GPUTemperatureSensor:
item.Description = "This sensor returns the current temperature of the GPU in °C.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#gputemperature";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#gputemperaturesensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
case AvailableSensors.GPULoadSensor:
item.Description = "This sensor returns the current GPU load.";
- item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#gpuload";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#gpuloadsensor";
item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
+
default:
item.Description = null;
item.MoreInfoLink = null;
@@ -181,15 +236,11 @@ public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
break;
}
}
+
public void OpenInfo(object sender, RoutedEventArgs args)
{
- var item = ((AddSensorViewModel)this.DataContext);
+ var item = (AddSensorViewModel)DataContext;
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
}
}
diff --git a/UserInterface/Views/AppInfo.axaml b/UserInterface/Views/AppInfo.axaml
index afab0c5..87a9324 100644
--- a/UserInterface/Views/AppInfo.axaml
+++ b/UserInterface/Views/AppInfo.axaml
@@ -13,6 +13,14 @@
+
+Having issues? Check out the log files.
+Using the configuration files is recommended
+for advanced users only.
+
+
+
+
diff --git a/UserInterface/Views/AppInfo.axaml.cs b/UserInterface/Views/AppInfo.axaml.cs
index 388d13c..20df1be 100644
--- a/UserInterface/Views/AppInfo.axaml.cs
+++ b/UserInterface/Views/AppInfo.axaml.cs
@@ -11,28 +11,31 @@
using System.Security;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using UserInterface.Util;
+using System;
+using System.IO;
+using System.Diagnostics;
namespace UserInterface.Views
{
public class AppInfo : UserControl
{
- private readonly IIpcClient client;
-
+ private readonly IIpcClient client;
+ private readonly string _basePath;
public AppInfo()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("info", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("info", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
this.client = clientFactory.CreateClient("info");
-
+ this._basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Hass Workstation Service");
@@ -63,6 +66,17 @@ public void Discord(object sender, RoutedEventArgs args)
BrowserUtil.OpenBrowser("https://discord.gg/VraYT2N3wd");
}
+ public void OpenLogDirectory(object sender, RoutedEventArgs args)
+ {
+ string path = Path.Combine(this._basePath, "logs");
+ Process.Start("explorer.exe", path);
+ }
+
+ public void OpenConfigDirectory(object sender, RoutedEventArgs args)
+ {
+ Process.Start("explorer.exe", this._basePath);
+ }
+
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
diff --git a/UserInterface/Views/BackgroundServiceSettings.axaml.cs b/UserInterface/Views/BackgroundServiceSettings.axaml.cs
index 046f362..f2b518f 100644
--- a/UserInterface/Views/BackgroundServiceSettings.axaml.cs
+++ b/UserInterface/Views/BackgroundServiceSettings.axaml.cs
@@ -15,49 +15,50 @@ namespace UserInterface.Views
{
public class BackgroundServiceSettings : UserControl
{
- private readonly IIpcClient client;
+ private readonly IIpcClient _client;
public BackgroundServiceSettings()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("broker", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("broker", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
- this.client = clientFactory.CreateClient("broker");
-
+ this._client = clientFactory.CreateClient("broker");
DataContext = new BackgroundServiceSettingsViewModel();
Ping();
}
- public async void Ping() {
+
+ public async void Ping()
+ {
while (true)
{
+ if (DataContext is not BackgroundServiceSettingsViewModel viewModel)
+ throw new System.Exception("Wrong viewmodel class!");
+
try
{
- var result = await this.client.InvokeAsync(x => x.Ping("ping"));
+ var result = await this._client.InvokeAsync(x => x.Ping("ping"));
+
if (result == "pong")
- {
- ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(true, "All good");
- }
+ viewModel.UpdateStatus(true, "All good");
else
- {
- ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(false, "Not running");
- }
+ viewModel.UpdateStatus(false, "Not running");
}
catch (System.Exception)
{
- ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(false, "Not running");
+ viewModel.UpdateStatus(false, "Not running");
}
- var autostartresult = await this.client.InvokeAsync(x => x.IsAutoStartEnabled());
- ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateAutostartStatus(autostartresult);
+ var autostartresult = await this._client.InvokeAsync(x => x.IsAutoStartEnabled());
+ viewModel.UpdateAutostartStatus(autostartresult);
await Task.Delay(1000);
}
@@ -71,11 +72,12 @@ public void Start(object sender, RoutedEventArgs args)
public void EnableAutostart(object sender, RoutedEventArgs args)
{
- this.client.InvokeAsync(x => x.EnableAutostart(true));
+ this._client.InvokeAsync(x => x.EnableAutostart(true));
}
+
public void DisableAutostart(object sender, RoutedEventArgs args)
{
- this.client.InvokeAsync(x => x.EnableAutostart(false));
+ this._client.InvokeAsync(x => x.EnableAutostart(false));
}
private void InitializeComponent()
@@ -83,4 +85,4 @@ private void InitializeComponent()
AvaloniaXamlLoader.Load(this);
}
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/Views/BrokerSettings.axaml.cs b/UserInterface/Views/BrokerSettings.axaml.cs
index 3e59837..e870a80 100644
--- a/UserInterface/Views/BrokerSettings.axaml.cs
+++ b/UserInterface/Views/BrokerSettings.axaml.cs
@@ -17,19 +17,19 @@ namespace UserInterface.Views
{
public class BrokerSettings : UserControl
{
- private readonly IIpcClient client;
+ private readonly IIpcClient client;
public BrokerSettings()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("broker", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("broker", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
this.client = clientFactory.CreateClient("broker");
diff --git a/UserInterface/Views/CommandSettings.axaml b/UserInterface/Views/CommandSettings.axaml
index efc93f0..0bc4aac 100644
--- a/UserInterface/Views/CommandSettings.axaml
+++ b/UserInterface/Views/CommandSettings.axaml
@@ -3,24 +3,27 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
- MaxWidth="800"
- x:Class="UserInterface.Views.CommandSettings" >
-
- Commands
-
+ x:Class="UserInterface.Views.CommandSettings">
-
-
-
-
-
- Add some commands by clicking the "Add" button.
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/UserInterface/Views/CommandSettings.axaml.cs b/UserInterface/Views/CommandSettings.axaml.cs
index fb963f4..3088a45 100644
--- a/UserInterface/Views/CommandSettings.axaml.cs
+++ b/UserInterface/Views/CommandSettings.axaml.cs
@@ -1,82 +1,108 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using Microsoft.Extensions.DependencyInjection;
-using hass_workstation_service.Communication.NamedPipe;
-using JKang.IpcServiceFramework.Client;
-using System.Threading.Tasks;
-using Avalonia.Interactivity;
-using System.Reactive.Linq;
-using UserInterface.ViewModels;
-using System.Security;
-using hass_workstation_service.Communication.InterProcesCommunication.Models;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Controls.ApplicationLifetimes;
-
-namespace UserInterface.Views
-{
- public class CommandSettings : UserControl
- {
- private readonly IIpcClient client;
- private DataGrid _dataGrid { get; set; }
- private bool sensorsNeedToRefresh { get; set; }
-
- public CommandSettings()
- {
- this.InitializeComponent();
- // register IPC clients
- ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("commands", pipeName: "pipeinternal")
- .BuildServiceProvider();
-
- // resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
-
- // create client
- this.client = clientFactory.CreateClient("commands");
-
-
- DataContext = new CommandSettingsViewModel();
- GetConfiguredCommands();
-
- this._dataGrid = this.FindControl("Grid");
- }
-
-
- public async void GetConfiguredCommands()
- {
- sensorsNeedToRefresh = false;
- List status = await this.client.InvokeAsync(x => x.GetConfiguredCommands());
-
- ((CommandSettingsViewModel)this.DataContext).ConfiguredCommands = status.Select(s => new CommandViewModel() { Name = s.Name, Type = s.Type, Id = s.Id}).ToList();
-
- }
- public void Delete(object sender, RoutedEventArgs args)
- {
- var item = ((CommandViewModel)this._dataGrid.SelectedItem);
- this.client.InvokeAsync(x => x.RemoveCommandById(item.Id));
- ((CommandSettingsViewModel)this.DataContext).ConfiguredCommands.Remove(item);
- this._dataGrid.SelectedIndex = -1;
- ((CommandSettingsViewModel)this.DataContext).TriggerUpdate();
- }
-
- public async void Add(object sender, RoutedEventArgs args)
- {
- AddCommandDialog dialog = new AddCommandDialog();
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- await dialog.ShowDialog(desktop.MainWindow);
- sensorsNeedToRefresh = true;
- GetConfiguredCommands();
- }
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
-
- }
-}
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Microsoft.Extensions.DependencyInjection;
+using hass_workstation_service.Communication.NamedPipe;
+using JKang.IpcServiceFramework.Client;
+using Avalonia.Interactivity;
+using System.Reactive.Linq;
+using UserInterface.ViewModels;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls.ApplicationLifetimes;
+using System.Threading.Tasks;
+
+namespace UserInterface.Views
+{
+ public class CommandSettings : UserControl
+ {
+ private readonly IIpcClient _client;
+ private readonly DataGrid _dataGrid;
+ private bool _commandsNeedToRefresh;
+
+ public CommandSettings()
+ {
+ InitializeComponent();
+ // register IPC clients
+ ServiceProvider serviceProvider = new ServiceCollection()
+ .AddNamedPipeIpcClient("commands", pipeName: "pipeinternal")
+ .BuildServiceProvider();
+
+ // resolve IPC client factory
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
+
+ // create client
+ _client = clientFactory.CreateClient("commands");
+ _dataGrid = this.FindControl("Grid");
+
+ DataContext = new CommandSettingsViewModel();
+ GetConfiguredCommands();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public async void GetConfiguredCommands()
+ {
+ List status = await _client.InvokeAsync(x => x.GetConfiguredCommands());
+
+ ((CommandSettingsViewModel)DataContext).ConfiguredCommands = status.Select(s =>
+ new CommandViewModel()
+ {
+ Name = s.Name,
+ Type = s.Type,
+ Id = s.Id
+ }).ToList();
+
+ if (_commandsNeedToRefresh)
+ {
+ await Task.Delay(1000);
+ GetConfiguredCommands();
+ _commandsNeedToRefresh = false;
+ }
+ }
+
+ public async void AddCommand(object sender, RoutedEventArgs args)
+ {
+ var dialog = new AddCommandDialog();
+ if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ await dialog.ShowDialog(desktop.MainWindow);
+ GetConfiguredCommands();
+ }
+ }
+
+ public async void EditCommand(object sender, RoutedEventArgs args)
+ {
+ if (_dataGrid.SelectedItem is not CommandViewModel item)
+ return;
+
+ var dialog = new AddCommandDialog(item.Id);
+ if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ await dialog.ShowDialog(desktop.MainWindow);
+ _commandsNeedToRefresh = true;
+ GetConfiguredCommands();
+ }
+ }
+
+ public void DeleteCommand(object sender, RoutedEventArgs args)
+ {
+ if (_dataGrid.SelectedItem is not CommandViewModel item)
+ return;
+
+ _client.InvokeAsync(x => x.RemoveCommandById(item.Id));
+
+ if (DataContext is not CommandSettingsViewModel viewModel)
+ return;
+
+ viewModel.ConfiguredCommands.Remove(item);
+ _dataGrid.SelectedIndex = -1;
+ viewModel.TriggerUpdate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/UserInterface/Views/GeneralSettings.axaml b/UserInterface/Views/GeneralSettings.axaml
new file mode 100644
index 0000000..4ca197d
--- /dev/null
+++ b/UserInterface/Views/GeneralSettings.axaml
@@ -0,0 +1,27 @@
+
+
+ Settings
+
+ Name prefix
+ (What's this?)
+
+
+
+[Experimental]
+This allows you to set a name which will be used to prefix all sensor- and command names. For example:
+If a sensor is called "ActiveWindow" and the name prefix is set to "laptop", the sensor will be named "laptop-ActiveWindow" and its entityId will be "laptop_activewindow".
+
+
+
+
+
+
+
+
+
+
diff --git a/UserInterface/Views/GeneralSettings.axaml.cs b/UserInterface/Views/GeneralSettings.axaml.cs
new file mode 100644
index 0000000..39b2618
--- /dev/null
+++ b/UserInterface/Views/GeneralSettings.axaml.cs
@@ -0,0 +1,66 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Microsoft.Extensions.DependencyInjection;
+using hass_workstation_service.Communication.NamedPipe;
+using JKang.IpcServiceFramework.Client;
+using System.Threading.Tasks;
+using Avalonia.Interactivity;
+using System.Reactive.Linq;
+using UserInterface.ViewModels;
+using System.Security;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using System.ComponentModel.DataAnnotations;
+using System.Collections.Generic;
+using hass_workstation_service.Data;
+
+namespace UserInterface.Views
+{
+ public class GeneralSettingsView : UserControl
+ {
+ private readonly IIpcClient client;
+
+ public GeneralSettingsView()
+ {
+ this.InitializeComponent();
+ // register IPC clients
+ ServiceProvider serviceProvider = new ServiceCollection()
+ .AddNamedPipeIpcClient("general", pipeName: "pipeinternal")
+ .BuildServiceProvider();
+
+ // resolve IPC client factory
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
+
+ // create client
+ this.client = clientFactory.CreateClient("general");
+
+
+ DataContext = new GeneralSettingsViewModel();
+ GetSettings();
+
+ }
+
+ public void Configure(object sender, RoutedEventArgs args)
+ {
+ var model = (GeneralSettingsViewModel)this.DataContext;
+ ICollection results;
+ if (model.IsValid(model, out results))
+ {
+ var result = this.client.InvokeAsync(x => x.WriteGeneralSettings(new GeneralSettings() { NamePrefix = model.NamePrefix }));
+ }
+ }
+
+ public async void GetSettings()
+ {
+ GeneralSettings settings = await this.client.InvokeAsync(x => x.GetGeneralSettings());
+ ((GeneralSettingsViewModel)this.DataContext).Update(settings);
+ }
+
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UserInterface/Views/MainWindow.axaml b/UserInterface/Views/MainWindow.axaml
index 1da7f05..d559359 100644
--- a/UserInterface/Views/MainWindow.axaml
+++ b/UserInterface/Views/MainWindow.axaml
@@ -4,23 +4,24 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:UserInterface.Views"
- mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="450"
+ mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="500"
x:Class="UserInterface.Views.MainWindow"
Icon="/Assets/hass-workstation-logo.ico"
- SizeToContent="WidthAndHeight"
+ MinWidth="700"
+ MinHeight="500"
Title="Settings">
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/UserInterface/Views/MainWindow.axaml.cs b/UserInterface/Views/MainWindow.axaml.cs
index 3d1beba..8fb5437 100644
--- a/UserInterface/Views/MainWindow.axaml.cs
+++ b/UserInterface/Views/MainWindow.axaml.cs
@@ -11,7 +11,7 @@ public MainWindow()
{
InitializeComponent();
- WindowsTrayIcon icon = new WindowsTrayIcon();
+ _ = new WindowsTrayIcon();
}
private void InitializeComponent()
@@ -19,4 +19,4 @@ private void InitializeComponent()
AvaloniaXamlLoader.Load(this);
}
}
-}
+}
\ No newline at end of file
diff --git a/UserInterface/Views/SensorSettings.axaml b/UserInterface/Views/SensorSettings.axaml
index 4d8d085..c5e6a75 100644
--- a/UserInterface/Views/SensorSettings.axaml
+++ b/UserInterface/Views/SensorSettings.axaml
@@ -3,30 +3,33 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
- MaxWidth="800"
- x:Class="UserInterface.Views.SensorSettings" >
-
- Sensors
-
+ x:Class="UserInterface.Views.SensorSettings">
-
-
-
-
-
-
-
- Add some sensors by clicking the "Add" button.
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/UserInterface/Views/SensorSettings.axaml.cs b/UserInterface/Views/SensorSettings.axaml.cs
index 532006c..4b7ea67 100644
--- a/UserInterface/Views/SensorSettings.axaml.cs
+++ b/UserInterface/Views/SensorSettings.axaml.cs
@@ -8,7 +8,6 @@
using Avalonia.Interactivity;
using System.Reactive.Linq;
using UserInterface.ViewModels;
-using System.Security;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using System.Collections.Generic;
using System.Linq;
@@ -18,48 +17,61 @@ namespace UserInterface.Views
{
public class SensorSettings : UserControl
{
- private readonly IIpcClient client;
- private DataGrid _dataGrid { get; set; }
- private bool sensorsNeedToRefresh { get; set; }
+ private readonly IIpcClient _client;
+ private readonly DataGrid _dataGrid;
+ private bool _sensorsNeedToRefresh;
public SensorSettings()
{
- this.InitializeComponent();
+ InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
- .AddNamedPipeIpcClient("sensors", pipeName: "pipeinternal")
+ .AddNamedPipeIpcClient("sensors", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
- IIpcClientFactory clientFactory = serviceProvider
- .GetRequiredService>();
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
// create client
- this.client = clientFactory.CreateClient("sensors");
-
+ _client = clientFactory.CreateClient("sensors");
+ _dataGrid = this.FindControl("Grid");
DataContext = new SensorSettingsViewModel();
GetConfiguredSensors();
-
- this._dataGrid = this.FindControl("Grid");
}
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
public async void GetConfiguredSensors()
{
- sensorsNeedToRefresh = false;
- List status = await this.client.InvokeAsync(x => x.GetConfiguredSensors());
+ _sensorsNeedToRefresh = false;
+ List status = await _client.InvokeAsync(x => x.GetConfiguredSensors());
- ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors = status.Select(s => new SensorViewModel() { Name = s.Name, Type = s.Type, Value = s.Value, Id = s.Id, UpdateInterval = s.UpdateInterval, UnitOfMeasurement = s.UnitOfMeasurement }).ToList();
- while (!sensorsNeedToRefresh)
+ ((SensorSettingsViewModel)DataContext).ConfiguredSensors = status.Select(s =>
+ new SensorViewModel()
+ {
+ Name = s.Name,
+ Type = s.Type,
+ Value = s.Value,
+ Id = s.Id,
+ UpdateInterval = s.UpdateInterval,
+ UnitOfMeasurement = s.UnitOfMeasurement
+ }).ToList();
+
+ while (!_sensorsNeedToRefresh)
{
await Task.Delay(1000);
- List statusUpdated = await this.client.InvokeAsync(x => x.GetConfiguredSensors());
- var configuredSensors = ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors;
+ List statusUpdated = await _client.InvokeAsync(x => x.GetConfiguredSensors());
+ var configuredSensors = ((SensorSettingsViewModel)DataContext).ConfiguredSensors;
// this is a workaround for the list showing before it has been completely loaded in the service
- if (statusUpdated.Count != configuredSensors.Count) {
- sensorsNeedToRefresh = true;
- GetConfiguredSensors();
+ if (statusUpdated.Count != configuredSensors.Count)
+ {
+ _sensorsNeedToRefresh = true;
+ GetConfiguredSensors();
}
statusUpdated.ForEach(s =>
{
@@ -72,33 +84,46 @@ public async void GetConfiguredSensors()
}
});
}
-
- }
- public void Delete(object sender, RoutedEventArgs args)
- {
- var item = ((SensorViewModel)this._dataGrid.SelectedItem);
- this.client.InvokeAsync(x => x.RemoveSensorById(item.Id));
- ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors.Remove(item);
- this._dataGrid.SelectedIndex = -1;
- ((SensorSettingsViewModel)this.DataContext).TriggerUpdate();
}
public async void AddSensor(object sender, RoutedEventArgs args)
{
- AddSensorDialog dialog = new AddSensorDialog();
+ var dialog = new AddSensorDialog();
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
await dialog.ShowDialog(desktop.MainWindow);
- sensorsNeedToRefresh = true;
+ _sensorsNeedToRefresh = true;
GetConfiguredSensors();
}
}
- private void InitializeComponent()
+ public async void EditSensor(object sender, RoutedEventArgs args)
{
- AvaloniaXamlLoader.Load(this);
+ if (_dataGrid.SelectedItem is not SensorViewModel item)
+ return;
+
+ var dialog = new AddSensorDialog(item.Id);
+ if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ await dialog.ShowDialog(desktop.MainWindow);
+ _sensorsNeedToRefresh = true;
+ GetConfiguredSensors();
+ }
}
+ public void DeleteSensor(object sender, RoutedEventArgs args)
+ {
+ if (_dataGrid.SelectedItem is not SensorViewModel item)
+ return;
+
+ _client.InvokeAsync(x => x.RemoveSensorById(item.Id));
+ if (DataContext is not SensorSettingsViewModel viewModel)
+ return;
+
+ viewModel.ConfiguredSensors.Remove(item);
+ _dataGrid.SelectedIndex = -1;
+ viewModel.TriggerUpdate();
+ }
}
-}
+}
\ No newline at end of file
diff --git a/documentation/Commands.md b/documentation/Commands.md
new file mode 100644
index 0000000..0c0555a
--- /dev/null
+++ b/documentation/Commands.md
@@ -0,0 +1,47 @@
+# Commands
+
+Commands can be used to trigger certain things on the client. For each command, a switch will be available in Home Assistant. Turning on the switch fires the command on the client and it will turn the switch off when it's done. Turning it off will cancel the running command.
+
+### ShutdownCommand
+
+This command shuts down the computer immediately. It runs `shutdown /s`.
+
+### RestartCommand
+
+This command restarts the computer immediately. It runs `shutdown /r`.
+
+### HibernateCommand
+
+This command hibernates the computer immediately. It runs `shutdown /h`.
+
+### LogOffCommand
+
+This command logs off the current user. It runs `shutdown /l`.
+
+### CustomCommand
+
+This command allows you to run any Windows Commands. The command will be run in a hidden Command Prompt. Some examples:
+
+|Command|Explanation|
+|---|---|
+|shutdown /s /f /t 000|Forcefully shutdown the PC immediately.|
+|Rundll32.exe user32.dll,LockWorkStation|This locks the current session.|
+|shutdown /s /t 300|Shuts the PC down after 5 minutes (300 seconds).|
+|C:\path\to\your\batchfile.bat|Run the specified batch file.|
+
+### KeyCommand
+
+Sends a keystroke with the specified key. You can pick [any of these](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes) key codes.
+
+### Media Commands
+
+There's several media commands available that allow you to control media playback. You can combine them into a media player entity as shown [here](https://pastebin.com/1VdL5iQm).
+
+|Command|use|
+|---|---|
+|Play/Pause|The same as pressing the play/pause media key|
+|Next|skip to next track|
+|Previous|skip to previous track|
+|Volume up|Increase system master volume|
+|Volume down|Decrease system master volume|
+|Mute (toggle)|Mute the system|
diff --git a/documentation/Sensors.md b/documentation/Sensors.md
new file mode 100644
index 0000000..958efa8
--- /dev/null
+++ b/documentation/Sensors.md
@@ -0,0 +1,90 @@
+# Sensors
+
+Sensors are used to transfer data about the host system to an automation hub, where it can be processed. `hass-workstation-service` provides many sensors, and they are lisited below in the same order as listed in the configuration GUI.
+
+### ActiveWindowSensor
+
+The active window sensor returns the title of the currently selected window, and is the same value shown when hovering over the icon in the windows task bar.
+
+This sensor is commonly used to trigger automations when a specific program is in use, such as pausing audio during a skype call. This sensor can be unreliable when used with applications such as web browsers that update their title based on the current context. You can partially resolve this using regular expressions.
+
+### CPULoadSensor
+
+The CPU load sensor is used to determine the current utilization of the CPU, and returns a percentage value.
+
+### CurrentClockSpeedSensor
+
+The current clock speed sensor returns the base system clock as configured in the bios. **It does not return the current operating frequency of the CPU**
+
+### CurrentVolumeSensor
+
+This sensor returns the current volume of playing audio. **It does not return the master volume.** If you have no sound playing the value will be 0.
+
+### MasterVolumeSensor
+
+This sensor returns the master volume for the currently selected default audio device.
+
+### DummySensor
+
+This sensor produces a random output every second, and is intended to test latency and connectivity.
+
+### GPULoadSensor
+
+This is the same as the [CPULoadSensor](https://github.com/sleevezipper/hass-workstation-service/new/master/documentation#cpuloadsensor), but for the GPU.
+
+### GPUTemperatureSensor
+
+The GPU temperature returns the current operating temperature of the GPU. This sensor is useful for controling external cooling systems such as air conditioning.
+
+### LastActiveSensor
+
+The last active sensor returns the time when the workstation was last active (last keyboard and mouse input). It is useful as a form of presence detection when combined with motion sensors or software such as room assistant, although may not be reliable if used with auto clickers or other macro software commonly used for video game automation.
+
+### LastBootSensor
+
+The last boot sensor returns the time the windows computer booted. It can be used to calculate uptime, if combined with another sensor to detect system shutdowns.
+
+### MemoryUsageSensor
+
+This returns the amount of system memory used as a percentage value, as indicated by the task manager.
+
+### MicrophoneActiveSensor
+
+This is a binary sensor that can be used to detect if the microphone is in use. **It does not return what process is using it**
+
+### NamedWindowSensor
+
+The named window sensor is similar to the [ActiveWindowSensor](https://github.com/sleevezipper/hass-workstation-service/new/master/documentation#activewindowsensor), however it is a binary sensor that returns true if a window with a title matching a pre determined value is detected.
+
+### SessionStateSensor
+
+The session state sensor can be used to detect if someone is logged in. It has the following values :
+
+|State|Explanation|
+|---|---|
+|Locked|All user sessions are locked.|
+|LoggedOff|No users are logged in.|
+|InUse|A user is currently logged in.|
+|Unknown|Something went wrong while getting the status.|
+
+### UserNotificationState
+
+This sensor watches the UserNotificationState. This is normally used in applications to determine if it is appropriate to send a notification but we can use it to expose this state. Notice that this status does not watch Focus Assist. It has the following possible states:
+
+|State|Explanation|
+|---|---|
+|NotPresent|A screen saver is displayed, the machine is locked, or a nonactive Fast User Switching session is in progress. |
+|Busy|A full-screen application is running or Presentation Settings are applied. Presentation Settings allow a user to put their machine into a state fit for an uninterrupted presentation, such as a set of PowerPoint slides, with a single click.|
+|RunningDirect3dFullScreen|A full-screen (exclusive mode) Direct3D application is running.|
+|PresentationMode|The user has activated Windows presentation settings to block notifications and pop-up messages.|
+|AcceptsNotifications|None of the other states are found, notifications can be freely sent.|
+|QuietTime|Introduced in Windows 7. The current user is in "quiet time", which is the first hour after a new user logs into his or her account for the first time. During this time, most notifications should not be sent or shown. This lets a user become accustomed to a new computer system without those distractions. Quiet time also occurs for each user after an operating system upgrade or clean installation.|
+|RunningWindowsStoreApp|A Windows Store app is running.|
+
+### WebcamActiveSensor
+
+The webcam active sensor returns the status of the webcam.
+
+### WMIQuerySensor
+
+Please see the specific documentaion page [here](https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/WMIQuery.md#wmiquerysensor).
diff --git a/documentation/WMIQuery.md b/documentation/WMIQuery.md
new file mode 100644
index 0000000..199eb32
--- /dev/null
+++ b/documentation/WMIQuery.md
@@ -0,0 +1,21 @@
+# WMIQuerySensor
+
+The WMI query sensor is an advanced sensor that executes a user defined [WMI query](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-and-sql) and exposes the result.
+
+To use the WMI query sensor, you should create a WMI query and paste it in the box. For example, If you wanted to find the current CPU frequency you can use this command:
+
+```sql
+SELECT CurrentClockSpeed FROM Win32_Processor
+```
+which results in `4008` for my PC. Because this query retuens a single value (CPU frequency in MHz), it can be used with the current WMI query sensor implementation.
+
+The command ```sql SELECT * FROM Win32_Processor``` cannot be used because it returns `|64|9|To Be Filled By O.E.M.|3| ... |GAME-PC-2016|8|1|False|False|`, and the current WMI query implementation only supports commands that return a single value.
+
+
+You can use [WMI Explorer](https://github.com/vinaypamnani/wmie2/tree/v2.0.0.2) to construct a query, or alternatively look at the user submited sensors below:
+
+
+|Query|Explanation|Submitted by|
+|---|---|---|
+|`SELECT username FROM Win32_ComputerSystem`|Shows the current user|@grizzlyjere|
+|`Select * from Win32_Process Where Name = 'notepad.exe'`|Shows if the defined process is running|@lafferlaffer|
diff --git a/documentation/direct communication.md b/documentation/direct communication.md
index 414c2ae..863110c 100644
--- a/documentation/direct communication.md
+++ b/documentation/direct communication.md
@@ -23,7 +23,7 @@ We can execute the shutdown command, and turn off the media center PC.
## Reading sensor data
-Reading sensor data can be done by subscribing to an MQTT topic. For example, if I wanted to know the CPU load, i can subscribe to this topic :
+Reading sensor data can be done by subscribing to an MQTT topic. For example, if I wanted to know the CPU load, I can subscribe to this topic :
```
homeassistant/sensor/DESKTOP-1/MediaCenterPC/State
```
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs b/hass-workstation-service/Communication/InterProcesCommunication/IServiceContractInterfaces.cs
similarity index 67%
rename from hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs
rename to hass-workstation-service/Communication/InterProcesCommunication/IServiceContractInterfaces.cs
index 57c29f6..ed3f329 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/IServiceContractInterfaces.cs
@@ -1,25 +1,31 @@
using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using hass_workstation_service.Data;
using System;
using System.Collections.Generic;
-using System.Text;
using System.Threading.Tasks;
namespace hass_workstation_service.Communication.NamedPipe
{
- public interface ServiceContractInterfaces
+ public interface IServiceContractInterfaces
{
Task GetMqttBrokerSettings();
+ Task GetGeneralSettings();
+ void WriteGeneralSettings(GeneralSettings settings);
public string Ping(string str);
void WriteMqttBrokerSettingsAsync(MqttSettings settings);
MqqtClientStatus GetMqqtClientStatus();
void EnableAutostart(bool enable);
bool IsAutoStartEnabled();
+ Task GetConfiguredSensor(Guid id);
Task> GetConfiguredSensors();
- void RemoveSensorById(Guid id);
void AddSensor(AvailableSensors sensorType, string json);
- void RemoveCommandById(Guid id);
+ void RemoveSensorById(Guid id);
+ void UpdateSensorById(Guid id, string json);
+ ConfiguredCommandModel GetConfiguredCommand(Guid id);
List GetConfiguredCommands();
void AddCommand(AvailableCommands commandType, string json);
+ void RemoveCommandById(Guid id);
+ void UpdateCommandById(Guid id, string json);
string GetCurrentVersion();
}
-}
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
index 2846115..0a53b1a 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
@@ -7,15 +7,13 @@
using Serilog;
using System;
using System.Collections.Generic;
-using System.Dynamic;
using System.Linq;
-using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace hass_workstation_service.Communication.InterProcesCommunication
{
- public class InterProcessApi : ServiceContractInterfaces
+ public class InterProcessApi : IServiceContractInterfaces
{
private readonly MqttPublisher _publisher;
private readonly IConfigurationService _configurationService;
@@ -26,73 +24,89 @@ public InterProcessApi(MqttPublisher publisher, IConfigurationService configurat
_configurationService = configurationService;
}
- public MqqtClientStatus GetMqqtClientStatus()
- {
- return this._publisher.GetStatus();
- }
+ public MqqtClientStatus GetMqqtClientStatus() => _publisher.GetStatus();
- public Task GetMqttBrokerSettings()
- {
- return this._configurationService.GetMqttBrokerSettings();
- }
+ public Task GetMqttBrokerSettings() => _configurationService.GetMqttBrokerSettings();
///
/// You can use this to check if the application responds.
///
///
///
- public string Ping(string str)
- {
- if (str == "ping")
- {
- return "pong";
- }
- return "what?";
- }
+ public string Ping(string str) => str == "ping" ? "pong" : "what?";
+
+ public string GetCurrentVersion() => Program.GetVersion();
///
/// This writes the provided settings to the config file.
///
///
- public void WriteMqttBrokerSettingsAsync(MqttSettings settings)
- {
- this._configurationService.WriteMqttBrokerSettingsAsync(settings);
- }
+ public void WriteMqttBrokerSettingsAsync(MqttSettings settings) => _configurationService.WriteMqttBrokerSettingsAsync(settings);
///
/// Enables or disables autostart.
///
///
- public void EnableAutostart(bool enable)
- {
- this._configurationService.EnableAutoStart(enable);
- }
+ public void EnableAutostart(bool enable) => _configurationService.EnableAutoStart(enable);
- public bool IsAutoStartEnabled()
- {
- return this._configurationService.IsAutoStartEnabled();
- }
+ public bool IsAutoStartEnabled() => _configurationService.IsAutoStartEnabled();
public async Task> GetConfiguredSensors()
{
- var sensors = await this._configurationService.GetSensorsAfterLoadingAsync();
- return sensors.Select(s => new ConfiguredSensorModel() { Name = s.Name, Type = s.GetType().Name, Value = s.PreviousPublishedState, Id = s.Id, UpdateInterval = s.UpdateInterval, UnitOfMeasurement = ((SensorDiscoveryConfigModel)s.GetAutoDiscoveryConfig()).Unit_of_measurement }).ToList();
+ var sensors = await _configurationService.GetSensorsAfterLoadingAsync();
+ return sensors.Select(s =>
+ {
+ if (!Enum.TryParse(s.GetType().Name, out AvailableSensors type))
+ Log.Logger.Error("Unknown sensor");
+
+ return new ConfiguredSensorModel(s);
+ }).ToList();
}
- public List GetConfiguredCommands()
+ public async Task GetConfiguredSensor(Guid id)
{
- return this._configurationService.ConfiguredCommands.Select(s => new ConfiguredCommandModel() { Name = s.Name, Type = s.GetType().Name, Id = s.Id }).ToList();
+ var sensors = await _configurationService.GetSensorsAfterLoadingAsync();
+ var s = sensors.FirstOrDefault(x => id == x.Id);
+ if (s == null)
+ return null;
+ else
+ {
+ if (!Enum.TryParse(s.GetType().Name, out AvailableSensors type))
+ Log.Logger.Error("Unknown sensor");
+
+ return new ConfiguredSensorModel(s);
+ }
}
- public void RemoveCommandById(Guid id)
+
+ public List GetConfiguredCommands()
{
- this._configurationService.DeleteConfiguredCommand(id);
+ return _configurationService.ConfiguredCommands.Select(c =>
+ {
+ if (!Enum.TryParse(c.GetType().Name, out AvailableCommands type))
+ Log.Logger.Error("Unknown command");
+
+ return new ConfiguredCommandModel(c);
+ }).ToList();
}
- public void RemoveSensorById(Guid id)
+ public ConfiguredCommandModel GetConfiguredCommand(Guid id)
{
- this._configurationService.DeleteConfiguredSensor(id);
+ var c = _configurationService.ConfiguredCommands.FirstOrDefault(x => id == x.Id);
+ if (c == null)
+ return null;
+ else
+ {
+ if (!Enum.TryParse(c.GetType().Name, out AvailableCommands type))
+ Log.Logger.Error("Unknown command");
+
+ return new ConfiguredCommandModel(c);
+ }
}
+ public void RemoveSensorById(Guid id) => _configurationService.DeleteConfiguredSensor(id);
+
+ public void RemoveCommandById(Guid id) => _configurationService.DeleteConfiguredCommand(id);
+
///
/// Adds a command to the configured commands. This properly initializes the class and writes it to the config file.
///
@@ -100,71 +114,12 @@ public void RemoveSensorById(Guid id)
///
public void AddSensor(AvailableSensors sensorType, string json)
{
- var serializerOptions = new JsonSerializerOptions
- {
- Converters = { new DynamicJsonConverter() }
- };
- dynamic model = JsonSerializer.Deserialize(json, serializerOptions);
+ var sensorToCreate = GetSensorToCreate(sensorType, json);
- AbstractSensor sensorToCreate = null;
- switch (sensorType)
- {
- case AvailableSensors.UserNotificationStateSensor:
- sensorToCreate = new UserNotificationStateSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.DummySensor:
- sensorToCreate = new DummySensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.CurrentClockSpeedSensor:
- sensorToCreate = new CurrentClockSpeedSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.CPULoadSensor:
- sensorToCreate = new CPULoadSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.WMIQuerySensor:
- sensorToCreate = new WMIQuerySensor(this._publisher, model.Query, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.MemoryUsageSensor:
- sensorToCreate = new MemoryUsageSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.ActiveWindowSensor:
- sensorToCreate = new ActiveWindowSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.WebcamActiveSensor:
- sensorToCreate = new WebcamActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.MicrophoneActiveSensor:
- sensorToCreate = new MicrophoneActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.NamedWindowSensor:
- sensorToCreate = new NamedWindowSensor(this._publisher, model.WindowName, model.Name, (int)model.UpdateInterval);
- break;
- case AvailableSensors.LastActiveSensor:
- sensorToCreate = new LastActiveSensor(this._publisher,(int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.LastBootSensor:
- sensorToCreate = new LastBootSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.SessionStateSensor:
- sensorToCreate = new SessionStateSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.CurrentVolumeSensor:
- sensorToCreate = new CurrentVolumeSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.GPUTemperatureSensor:
- sensorToCreate = new GpuTemperatureSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- case AvailableSensors.GPULoadSensor:
- sensorToCreate = new GpuLoadSensor(this._publisher, (int)model.UpdateInterval, model.Name);
- break;
- default:
- Log.Logger.Error("Unknown sensortype");
- break;
- }
- if (sensorToCreate != null)
- {
- this._configurationService.AddConfiguredSensor(sensorToCreate);
- }
+ if (sensorToCreate == null)
+ Log.Logger.Error("Unknown sensortype");
+ else
+ _configurationService.AddConfiguredSensor(sensorToCreate);
}
///
@@ -173,6 +128,38 @@ public void AddSensor(AvailableSensors sensorType, string json)
///
///
public void AddCommand(AvailableCommands commandType, string json)
+ {
+ var commandToCreate = GetCommandToCreate(commandType, json);
+
+ if (commandToCreate == null)
+ Log.Logger.Error("Unknown command type");
+ else
+ _configurationService.AddConfiguredCommand(commandToCreate);
+ }
+
+ public async void UpdateSensorById(Guid id, string json)
+ {
+ var existingSensor = await GetConfiguredSensor(id);
+ var sensorToUpdate = GetSensorToCreate(existingSensor.Type, json);
+
+ if (sensorToUpdate == null)
+ Log.Logger.Error("Unknown sensortype");
+ else
+ _configurationService.UpdateConfiguredSensor(id, sensorToUpdate);
+ }
+
+ public void UpdateCommandById(Guid id, string json)
+ {
+ var existingCommand = GetConfiguredCommand(id);
+ var commandToUpdate = GetCommandToCreate(existingCommand.Type, json);
+
+ if (commandToUpdate == null)
+ Log.Logger.Error("Unknown commandtype");
+ else
+ _configurationService.UpdateConfiguredCommand(id, commandToUpdate);
+ }
+
+ private AbstractSensor GetSensorToCreate(AvailableSensors sensorType, string json)
{
var serializerOptions = new JsonSerializerOptions
{
@@ -180,55 +167,58 @@ public void AddCommand(AvailableCommands commandType, string json)
};
dynamic model = JsonSerializer.Deserialize(json, serializerOptions);
- AbstractCommand commandToCreate = null;
- switch (commandType)
- {
- case AvailableCommands.ShutdownCommand:
- commandToCreate = new ShutdownCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.RestartCommand:
- commandToCreate = new RestartCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.LogOffCommand:
- commandToCreate = new LogOffCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.CustomCommand:
- commandToCreate = new CustomCommand(this._publisher, model.Command, model.Name);
- break;
- case AvailableCommands.PlayPauseCommand:
- commandToCreate = new MediaPlayPauseCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.NextCommand:
- commandToCreate = new MediaNextCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.PreviousCommand:
- commandToCreate = new MediaPreviousCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.VolumeUpCommand:
- commandToCreate = new MediaVolumeUpCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.VolumeDownCommand:
- commandToCreate = new MediaVolumeDownCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.MuteCommand:
- commandToCreate = new MediaMuteCommand(this._publisher, model.Name);
- break;
- case AvailableCommands.KeyCommand:
- commandToCreate = new KeyCommand(this._publisher, Convert.ToByte(model.Key, 16), model.Name);
- break;
- default:
- Log.Logger.Error("Unknown sensortype");
- break;
- }
- if (commandToCreate != null)
+ return sensorType switch
{
- this._configurationService.AddConfiguredCommand(commandToCreate);
- }
+ AvailableSensors.UserNotificationStateSensor => new UserNotificationStateSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.DummySensor => new DummySensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.CurrentClockSpeedSensor => new CurrentClockSpeedSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.CPULoadSensor => new CPULoadSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.WMIQuerySensor => new WMIQuerySensor(_publisher, model.Query, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.MemoryUsageSensor => new MemoryUsageSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.ActiveWindowSensor => new ActiveWindowSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.WebcamActiveSensor => new WebcamActiveSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.MicrophoneActiveSensor => new MicrophoneActiveSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.NamedWindowSensor => new NamedWindowSensor(_publisher, model.WindowName, model.Name, (int)model.UpdateInterval),
+ AvailableSensors.LastActiveSensor => new LastActiveSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.LastBootSensor => new LastBootSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.SessionStateSensor => new SessionStateSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.CurrentVolumeSensor => new CurrentVolumeSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.GPUTemperatureSensor => new GpuTemperatureSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.GPULoadSensor => new GpuLoadSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ AvailableSensors.MasterVolumeSensor => new MasterVolumeSensor(_publisher, (int)model.UpdateInterval, model.Name),
+ _ => null
+ };
}
- public string GetCurrentVersion()
+ private AbstractCommand GetCommandToCreate(AvailableCommands commandType, string json)
{
- return Program.GetVersion();
+ var serializerOptions = new JsonSerializerOptions
+ {
+ Converters = { new DynamicJsonConverter() }
+ };
+ dynamic model = JsonSerializer.Deserialize(json, serializerOptions);
+
+ return commandType switch
+ {
+ AvailableCommands.ShutdownCommand => new ShutdownCommand(_publisher, model.Name),
+ AvailableCommands.RestartCommand => new RestartCommand(_publisher, model.Name),
+ AvailableCommands.HibernateCommand => new HibernateCommand(_publisher, model.Name),
+ AvailableCommands.LogOffCommand => new LogOffCommand(_publisher, model.Name),
+ AvailableCommands.CustomCommand => new CustomCommand(_publisher, model.Command, model.Name),
+ AvailableCommands.PlayPauseCommand => new PlayPauseCommand(_publisher, model.Name),
+ AvailableCommands.NextCommand => new NextCommand(_publisher, model.Name),
+ AvailableCommands.PreviousCommand => new PreviousCommand(_publisher, model.Name),
+ AvailableCommands.VolumeUpCommand => new VolumeUpCommand(_publisher, model.Name),
+ AvailableCommands.VolumeDownCommand => new VolumeDownCommand(_publisher, model.Name),
+ AvailableCommands.MuteCommand => new MuteCommand(_publisher, model.Name),
+ AvailableCommands.KeyCommand => new KeyCommand(_publisher, Convert.ToByte(model.Key, 16), model.Name),
+ _ => null
+ };
}
+
+ public Task GetGeneralSettings() => _configurationService.ReadGeneralSettings();
+
+
+ public void WriteGeneralSettings(GeneralSettings settings) => _configurationService.WriteGeneralSettingsAsync(settings);
}
-}
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
index 1f87979..0507e5e 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
@@ -1,7 +1,6 @@
+using hass_workstation_service.Domain.Commands;
using hass_workstation_service.Domain.Sensors;
using System;
-using System.Collections.Generic;
-using System.Text;
namespace hass_workstation_service.Communication.InterProcesCommunication.Models
{
@@ -23,19 +22,68 @@ public class MqqtClientStatus
public class ConfiguredSensorModel
{
public Guid Id { get; set; }
- public string Type { get; set; }
+ public AvailableSensors Type { get; set; }
public string Name { get; set; }
public string Value { get; set; }
+ public string Query { get; set; }
+ public string WindowName { get; set; }
public int UpdateInterval { get; set; }
public string UnitOfMeasurement { get; set; }
+
+ public ConfiguredSensorModel(AbstractSensor sensor)
+ {
+ this.Id = sensor.Id;
+ this.Name = sensor.Name;
+ Enum.TryParse(sensor.GetType().Name, out AvailableSensors type);
+ this.Type = type;
+ this.Value = sensor.PreviousPublishedState;
+ if (sensor is WMIQuerySensor wMIQuerySensor)
+ {
+ this.Query = wMIQuerySensor.Query;
+ }
+ if (sensor is NamedWindowSensor namedWindowSensor)
+ {
+ this.WindowName = namedWindowSensor.WindowName;
+ }
+ this.UpdateInterval = sensor.UpdateInterval;
+ this.UnitOfMeasurement = ((SensorDiscoveryConfigModel)sensor.GetAutoDiscoveryConfig()).Unit_of_measurement;
+ }
+ public ConfiguredSensorModel()
+ {
+
+ }
}
+
public class ConfiguredCommandModel
{
public Guid Id { get; set; }
- public string Type { get; set; }
+ public AvailableCommands Type { get; set; }
public string Name { get; set; }
public string Command { get; set; }
+ public string Key { get; set; }
+
+ public ConfiguredCommandModel(AbstractCommand command)
+ {
+ this.Id = command.Id;
+ Enum.TryParse(command.GetType().Name, out AvailableCommands type);
+ this.Type = type;
+ this.Name = command.Name;
+ if (command is CustomCommand customCommand)
+ {
+ this.Command = customCommand.Command;
+ }
+ if (command is KeyCommand keyCommand)
+ {
+ this.Key = "0x" + Convert.ToString(keyCommand.KeyCode, 16);
+ }
+ }
+
+ public ConfiguredCommandModel()
+ {
+
+ }
}
+
public enum AvailableSensors
{
UserNotificationStateSensor,
@@ -52,6 +100,7 @@ public enum AvailableSensors
LastBootSensor,
SessionStateSensor,
CurrentVolumeSensor,
+ MasterVolumeSensor,
GPUTemperatureSensor,
GPULoadSensor
}
@@ -62,6 +111,7 @@ public enum AvailableCommands
ShutdownCommand,
LogOffCommand,
RestartCommand,
+ HibernateCommand,
KeyCommand,
PlayPauseCommand,
NextCommand,
@@ -70,4 +120,4 @@ public enum AvailableCommands
VolumeDownCommand,
MuteCommand
}
-}
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
index bcdd12f..7449d67 100644
--- a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
+++ b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
@@ -31,6 +31,7 @@ public class MqttPublisher
public DateTime LastAvailabilityAnnounce { get; private set; }
public DeviceConfigModel DeviceConfigModel { get; private set; }
public ICollection Subscribers { get; private set; }
+ public string NamePrefix { get; private set; }
public bool IsConnected
{
get
@@ -55,9 +56,11 @@ public MqttPublisher(
this._logger = logger;
this.DeviceConfigModel = deviceConfigModel;
this._configurationService = configurationService;
+ this.NamePrefix = configurationService.GeneralSettings?.NamePrefix;
var options = _configurationService.GetMqttClientOptionsAsync().Result;
_configurationService.MqqtConfigChangedHandler = this.ReplaceMqttClient;
+ _configurationService.NamePrefixChangedHandler = this.UpdateNamePrefix;
var factory = new MqttFactory();
this._mqttClient = factory.CreateManagedMqttClient();
@@ -96,9 +99,9 @@ public async Task Publish(MqttApplicationMessage message)
this._logger.LogInformation($"Message dropped because mqtt not connected: {message}");
}
}
- // TODO: This should take a sensor/command instead of a config.
- // Then we can ask the sensor about the topic based on ObjectId instead of referencing Name directly
- public async Task AnnounceAutoDiscoveryConfig(AbstractDiscoverable discoverable, string domain, bool clearConfig = false)
+
+
+ public async Task AnnounceAutoDiscoveryConfig(AbstractDiscoverable discoverable, bool clearConfig = false)
{
if (this._mqttClient.IsConnected)
{
@@ -111,11 +114,21 @@ public async Task AnnounceAutoDiscoveryConfig(AbstractDiscoverable discoverable,
};
var message = new MqttApplicationMessageBuilder()
- .WithTopic($"homeassistant/{domain}/{this.DeviceConfigModel.Name}/{discoverable.ObjectId}/config")
+ .WithTopic($"homeassistant/{discoverable.Domain}/{this.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(discoverable.GetAutoDiscoveryConfig().NamePrefix, discoverable.ObjectId)}/config")
.WithPayload(clearConfig ? "" : JsonSerializer.Serialize(discoverable.GetAutoDiscoveryConfig(), discoverable.GetAutoDiscoveryConfig().GetType(), options))
.WithRetainFlag()
.Build();
await this.Publish(message);
+ // if clearconfig is true, also remove previous state messages
+ if (clearConfig)
+ {
+ var stateMessage = new MqttApplicationMessageBuilder()
+ .WithTopic($"homeassistant/{discoverable.Domain}/{this.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(discoverable.GetAutoDiscoveryConfig().NamePrefix, discoverable.ObjectId)}/state")
+ .WithPayload("")
+ .WithRetainFlag()
+ .Build();
+ await this.Publish(stateMessage);
+ }
LastConfigAnnounce = DateTime.UtcNow;
}
}
@@ -195,6 +208,11 @@ public async void Subscribe(AbstractCommand command)
Subscribers.Add(command);
}
+ public void UpdateNamePrefix(string prefix)
+ {
+ this.NamePrefix = prefix;
+ }
+
private void HandleMessageReceived(MqttApplicationMessage applicationMessage)
{
foreach (AbstractCommand command in this.Subscribers)
diff --git a/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs b/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
index 3d942e1..7626fda 100644
--- a/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
+++ b/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Text.Json.Serialization;
namespace hass_workstation_service.Communication
{
@@ -14,12 +15,49 @@ public abstract class DiscoveryConfigModel
/// (Optional) The name of the MQTT sensor. Defaults to MQTT Sensor in hass.
///
///
+ [JsonIgnore]
public string Name { get; set; }
+ ///
+ /// (Optional) The first part of the name.
+ ///
+ ///
+ [JsonIgnore]
+ public string NamePrefix { get; set; }
+
+ [JsonPropertyName("name")]
+ public string CompiledName { get => GetName(); }
+
///
/// The MQTT topic subscribed to receive sensor values.
///
///
public string State_topic { get; set; }
+
+ ///
+ /// Gets the name including the prefix
+ ///
+ ///
+ public string GetName()
+ {
+ if (string.IsNullOrWhiteSpace(NamePrefix))
+ {
+ return Name;
+ }
+ return $"{NamePrefix}-{Name}";
+ }
+
+ ///
+ /// Gets the name including the prefix if the class has not been instantiated yet.
+ ///
+ ///
+ public static string GetNameWithPrefix(string namePrefix, string name)
+ {
+ if (string.IsNullOrWhiteSpace(namePrefix))
+ {
+ return name;
+ }
+ return $"{namePrefix}-{name}";
+ }
}
public class SensorDiscoveryConfigModel : DiscoveryConfigModel
{
diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs
index db11c32..dc8e12f 100644
--- a/hass-workstation-service/Data/ConfigurationService.cs
+++ b/hass-workstation-service/Data/ConfigurationService.cs
@@ -26,36 +26,49 @@ public class ConfigurationService : IConfigurationService
{
public ICollection ConfiguredSensors { get; private set; }
public ICollection ConfiguredCommands { get; private set; }
+ public GeneralSettings GeneralSettings { get; private set; }
public Action MqqtConfigChangedHandler { get; set; }
+ public Action NamePrefixChangedHandler { get; set; }
private readonly DeviceConfigModel _deviceConfigModel;
private bool BrokerSettingsFileLocked { get; set; }
private bool SensorsSettingsFileLocked { get; set; }
private bool CommandSettingsFileLocked { get; set; }
+ private bool GeneralSettingsFileLocked { get; set; }
private bool _sensorsLoading { get; set; }
private readonly string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Hass Workstation Service");
+ private const string MQTT_SETTINGS_FILENAME = "mqttbroker.json";
+ private const string SENSORS_SETTINGS_FILENAME = "configured-sensors.json";
+ private const string COMMANDS_SETTINGS_FILENAME = "configured-commands.json";
+ private const string GENERAL_SETTINGS_FILENAME = "general-settings.json";
+
public ConfigurationService(DeviceConfigModel deviceConfigModel)
{
this._deviceConfigModel = deviceConfigModel;
- if (!File.Exists(Path.Combine(path, "mqttbroker.json")))
+ if (!File.Exists(Path.Combine(path, MQTT_SETTINGS_FILENAME)))
{
- File.Create(Path.Combine(path, "mqttbroker.json")).Close();
+ File.Create(Path.Combine(path, MQTT_SETTINGS_FILENAME)).Close();
}
- if (!File.Exists(Path.Combine(path, "configured-sensors.json")))
+ if (!File.Exists(Path.Combine(path, SENSORS_SETTINGS_FILENAME)))
{
- File.Create(Path.Combine(path, "configured-sensors.json")).Close();
+ File.Create(Path.Combine(path, SENSORS_SETTINGS_FILENAME)).Close();
}
- if (!File.Exists(Path.Combine(path, "configured-commands.json")))
+ if (!File.Exists(Path.Combine(path, COMMANDS_SETTINGS_FILENAME)))
+ {
+ File.Create(Path.Combine(path, COMMANDS_SETTINGS_FILENAME)).Close();
+ }
+ if (!File.Exists(Path.Combine(path, GENERAL_SETTINGS_FILENAME)))
{
- File.Create(Path.Combine(path, "configured-commands.json")).Close();
+ File.Create(Path.Combine(path, GENERAL_SETTINGS_FILENAME)).Close();
}
ConfiguredSensors = new List();
ConfiguredCommands = new List();
+ this.ReadGeneralSettings();
}
public async void ReadSensorSettings(MqttPublisher publisher)
@@ -67,7 +80,7 @@ public async void ReadSensorSettings(MqttPublisher publisher)
}
this.SensorsSettingsFileLocked = true;
List sensors = new List();
- using (var stream = new FileStream(Path.Combine(path, "configured-sensors.json"), FileMode.Open))
+ using (var stream = new FileStream(Path.Combine(path, SENSORS_SETTINGS_FILENAME), FileMode.Open))
{
Log.Logger.Information($"reading configured sensors from: {stream.Name}");
if (stream.Length > 0)
@@ -125,6 +138,9 @@ public async void ReadSensorSettings(MqttPublisher publisher)
case "CurrentVolumeSensor":
sensor = new CurrentVolumeSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
+ case "MasterVolumeSensor":
+ sensor = new MasterVolumeSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
+ break;
case "GpuTemperatureSensor":
sensor = new GpuTemperatureSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
@@ -155,7 +171,7 @@ public async void ReadCommandSettings(MqttPublisher publisher)
}
this.CommandSettingsFileLocked = true;
List commands = new List();
- using (var stream = new FileStream(Path.Combine(path, "configured-commands.json"), FileMode.Open))
+ using (var stream = new FileStream(Path.Combine(path, COMMANDS_SETTINGS_FILENAME), FileMode.Open))
{
Log.Logger.Information($"reading configured commands from: {stream.Name}");
if (stream.Length > 0)
@@ -177,29 +193,32 @@ public async void ReadCommandSettings(MqttPublisher publisher)
case "RestartCommand":
command = new RestartCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
+ case "HibernateCommand":
+ command = new HibernateCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ break;
case "LogOffCommand":
command = new LogOffCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "CustomCommand":
- command = new CustomCommand(publisher, configuredCommand.Command, configuredCommand.Name, configuredCommand.Id);
+ command = new CustomCommand(publisher, configuredCommand.Command, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaPlayPauseCommand":
- command = new MediaPlayPauseCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new PlayPauseCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaNextCommand":
- command = new MediaNextCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new NextCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaPreviousCommand":
- command = new MediaPreviousCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new PreviousCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaVolumeUpCommand":
- command = new MediaVolumeUpCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new VolumeUpCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaVolumeDownCommand":
- command = new MediaVolumeDownCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new VolumeDownCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "MediaMuteCommand":
- command = new MediaMuteCommand(publisher, configuredCommand.Name, configuredCommand.Id);
+ command = new MuteCommand(publisher, configuredCommand.Name, configuredCommand.Id);
break;
case "KeyCommand":
command = new KeyCommand(publisher, configuredCommand.KeyCode, configuredCommand.Name, configuredCommand.Id);
@@ -215,6 +234,70 @@ public async void ReadCommandSettings(MqttPublisher publisher)
}
}
+ public async Task ReadGeneralSettings()
+ {
+ while (this.GeneralSettingsFileLocked)
+ {
+ await Task.Delay(500);
+ }
+ this.GeneralSettingsFileLocked = true;
+ GeneralSettings settings = new GeneralSettings();
+ using (var stream = new FileStream(Path.Combine(path, GENERAL_SETTINGS_FILENAME), FileMode.Open))
+ {
+ Log.Logger.Information($"reading general settings from: {stream.Name}");
+ if (stream.Length > 0)
+ {
+ settings = await JsonSerializer.DeserializeAsync(stream);
+ }
+ stream.Close();
+ this.GeneralSettings = settings;
+ this.GeneralSettingsFileLocked = false;
+ return settings;
+ }
+ }
+
+ ///
+ /// Writes provided settings to the config file and reconfigures all sensors and commands if the nameprefix changed
+ ///
+ ///
+ public async void WriteGeneralSettingsAsync(GeneralSettings settings)
+ {
+ while (this.GeneralSettingsFileLocked)
+ {
+ await Task.Delay(500);
+ }
+ this.GeneralSettingsFileLocked = true;
+ using (FileStream stream = new FileStream(Path.Combine(path, GENERAL_SETTINGS_FILENAME), FileMode.Open))
+ {
+ stream.SetLength(0);
+ Log.Logger.Information($"writing general settings to: {stream.Name}");
+
+
+ await JsonSerializer.SerializeAsync(stream, settings);
+ stream.Close();
+ }
+ this.GeneralSettingsFileLocked = false;
+
+ // if the nameprefix changed, we need to update all sensors and commands to reflect the new name
+ if (settings.NamePrefix != this.GeneralSettings.NamePrefix)
+ {
+ // notify the mqtt publisher of the new prefix
+ this.NamePrefixChangedHandler.Invoke(settings.NamePrefix);
+
+ foreach (AbstractSensor sensor in this.ConfiguredSensors)
+ {
+ await sensor.UnPublishAutoDiscoveryConfigAsync();
+ sensor.PublishAutoDiscoveryConfigAsync();
+ }
+
+ foreach (AbstractCommand command in this.ConfiguredCommands)
+ {
+ await command.UnPublishAutoDiscoveryConfigAsync();
+ command.PublishAutoDiscoveryConfigAsync();
+ }
+ }
+ }
+
public async Task GetMqttClientOptionsAsync()
{
ConfiguredMqttBroker configuredBroker = await ReadMqttSettingsAsync();
@@ -226,7 +309,8 @@ public async Task GetMqttClientOptionsAsync()
.WithTls(new MqttClientOptionsBuilderTlsParameters()
{
UseTls = configuredBroker.UseTLS,
- AllowUntrustedCertificates = true
+ AllowUntrustedCertificates = true,
+ SslProtocol = configuredBroker.UseTLS ? System.Security.Authentication.SslProtocols.Tls12 : System.Security.Authentication.SslProtocols.None
})
.WithCredentials(configuredBroker.Username, configuredBroker.Password.ToString())
.WithKeepAlivePeriod(TimeSpan.FromSeconds(30))
@@ -257,7 +341,7 @@ public async Task ReadMqttSettingsAsync()
}
this.BrokerSettingsFileLocked = true;
ConfiguredMqttBroker configuredBroker = null;
- using (FileStream stream = new FileStream(Path.Combine(path, "mqttbroker.json"), FileMode.Open))
+ using (FileStream stream = new FileStream(Path.Combine(path, MQTT_SETTINGS_FILENAME), FileMode.Open))
{
Log.Logger.Information($"reading configured mqttbroker from: {stream.Name}");
if (stream.Length > 0)
@@ -279,7 +363,7 @@ public async void WriteSensorSettingsAsync()
}
this.SensorsSettingsFileLocked = true;
List configuredSensorsToSave = new List();
- using (FileStream stream = new FileStream(Path.Combine(path, "configured-sensors.json"), FileMode.Open))
+ using (FileStream stream = new FileStream(Path.Combine(path, SENSORS_SETTINGS_FILENAME), FileMode.Open))
{
stream.SetLength(0);
Log.Logger.Information($"writing configured sensors to: {stream.Name}");
@@ -316,7 +400,7 @@ public async void WriteCommandSettingsAsync()
}
this.CommandSettingsFileLocked = true;
List configuredCommandsToSave = new List();
- using (FileStream stream = new FileStream(Path.Combine(path, "configured-commands.json"), FileMode.Open))
+ using (FileStream stream = new FileStream(Path.Combine(path, COMMANDS_SETTINGS_FILENAME), FileMode.Open))
{
stream.SetLength(0);
Log.Logger.Information($"writing configured commands to: {stream.Name}");
@@ -340,54 +424,96 @@ public async void WriteCommandSettingsAsync()
public void AddConfiguredSensor(AbstractSensor sensor)
{
- this.ConfiguredSensors.Add(sensor);
- sensor.PublishAutoDiscoveryConfigAsync();
+ AddSensor(sensor);
WriteSensorSettingsAsync();
}
public void AddConfiguredCommand(AbstractCommand command)
{
- this.ConfiguredCommands.Add(command);
- command.PublishAutoDiscoveryConfigAsync();
+ AddCommand(command);
WriteCommandSettingsAsync();
}
- public async void DeleteConfiguredSensor(Guid id)
+ public void AddConfiguredSensors(List sensors)
{
- var sensorToRemove = this.ConfiguredSensors.FirstOrDefault(s => s.Id == id);
- if (sensorToRemove != null)
- {
- await sensorToRemove.UnPublishAutoDiscoveryConfigAsync();
- this.ConfiguredSensors.Remove(sensorToRemove);
- WriteSensorSettingsAsync();
- }
- else
- {
- Log.Logger.Warning($"sensor with id {id} not found");
- }
+ sensors.ForEach(sensor => AddSensor(sensor));
+ WriteSensorSettingsAsync();
+ }
+
+ public void AddConfiguredCommands(List commands)
+ {
+ commands.ForEach(command => AddCommand(command));
+ WriteCommandSettingsAsync();
+ }
+ public async void DeleteConfiguredSensor(Guid id)
+ {
+ await DeleteSensor(id);
+ WriteSensorSettingsAsync();
}
public async void DeleteConfiguredCommand(Guid id)
{
- var commandToRemove = this.ConfiguredCommands.FirstOrDefault(s => s.Id == id);
- if (commandToRemove != null)
- {
- await commandToRemove.UnPublishAutoDiscoveryConfigAsync();
- this.ConfiguredCommands.Remove(commandToRemove);
- WriteCommandSettingsAsync();
- }
- else
+ await DeleteCommand(id);
+ WriteCommandSettingsAsync();
+ }
+
+ ///
+ ///
+ ///
+ /// The Id of the sensor to replace
+ /// The new sensor
+ public async void UpdateConfiguredSensor(Guid id, AbstractSensor sensor)
+ {
+ await DeleteSensor(id);
+ await Task.Delay(500);
+ AddSensor(sensor);
+ WriteSensorSettingsAsync();
+ }
+
+ public async void UpdateConfiguredCommand(Guid id, AbstractCommand command)
+ {
+ await DeleteCommand(id);
+ await Task.Delay(500);
+ AddCommand(command);
+ WriteCommandSettingsAsync();
+ }
+
+ private void AddSensor(AbstractSensor sensor)
+ {
+ ConfiguredSensors.Add(sensor);
+ sensor.PublishAutoDiscoveryConfigAsync();
+ }
+
+ private void AddCommand(AbstractCommand command)
+ {
+ ConfiguredCommands.Add(command);
+ command.PublishAutoDiscoveryConfigAsync();
+ }
+
+ private async Task DeleteSensor(Guid id)
+ {
+ var sensorToRemove = ConfiguredSensors.FirstOrDefault(s => s.Id == id);
+ if (sensorToRemove == null)
{
- Log.Logger.Warning($"command with id {id} not found");
+ Log.Logger.Warning($"sensor with id {id} not found");
+ return;
}
+ await sensorToRemove.UnPublishAutoDiscoveryConfigAsync();
+ ConfiguredSensors.Remove(sensorToRemove);
}
- public void AddConfiguredSensors(List sensors)
+ private async Task DeleteCommand(Guid id)
{
- sensors.ForEach((sensor) => this.ConfiguredSensors.Add(sensor));
- WriteSensorSettingsAsync();
+ var commandToRemove = ConfiguredCommands.FirstOrDefault(c => c.Id == id);
+ if (commandToRemove == null)
+ {
+ Log.Logger.Warning($"command with id {id} not found");
+ return;
+ }
+ await commandToRemove.UnPublishAutoDiscoveryConfigAsync();
+ ConfiguredCommands.Remove(commandToRemove);
}
///
@@ -401,7 +527,7 @@ public async void WriteMqttBrokerSettingsAsync(MqttSettings settings)
await Task.Delay(500);
}
this.BrokerSettingsFileLocked = true;
- using (FileStream stream = new FileStream(Path.Combine(path, "mqttbroker.json"), FileMode.Open))
+ using (FileStream stream = new FileStream(Path.Combine(path, MQTT_SETTINGS_FILENAME), FileMode.Open))
{
stream.SetLength(0);
Log.Logger.Information($"writing configured mqttbroker to: {stream.Name}");
diff --git a/hass-workstation-service/Data/GeneralSettings.cs b/hass-workstation-service/Data/GeneralSettings.cs
new file mode 100644
index 0000000..c2cc869
--- /dev/null
+++ b/hass-workstation-service/Data/GeneralSettings.cs
@@ -0,0 +1,13 @@
+using hass_workstation_service.Domain.Sensors;
+using System;
+
+namespace hass_workstation_service.Data
+{
+ public class GeneralSettings
+ {
+ ///
+ /// If set, all sensor and command names will be be prefixed with this
+ ///
+ public string NamePrefix { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Data/IConfigurationService.cs b/hass-workstation-service/Data/IConfigurationService.cs
index 43d81dc..1215ee1 100644
--- a/hass-workstation-service/Data/IConfigurationService.cs
+++ b/hass-workstation-service/Data/IConfigurationService.cs
@@ -13,13 +13,21 @@ namespace hass_workstation_service.Data
{
public interface IConfigurationService
{
- ICollection ConfiguredSensors { get; }
Action MqqtConfigChangedHandler { get; set; }
+ Action NamePrefixChangedHandler { get; set; }
+ ICollection ConfiguredSensors { get; }
ICollection ConfiguredCommands { get; }
+ GeneralSettings GeneralSettings { get; }
- void AddConfiguredCommand(AbstractCommand command);
void AddConfiguredSensor(AbstractSensor sensor);
+ void AddConfiguredCommand(AbstractCommand command);
void AddConfiguredSensors(List sensors);
+ void AddConfiguredCommands(List commands);
+ void DeleteConfiguredSensor(Guid id);
+ void DeleteConfiguredCommand(Guid id);
+ Task ReadGeneralSettings();
+ void UpdateConfiguredSensor(Guid id, AbstractSensor sensor);
+ void UpdateConfiguredCommand(Guid id, AbstractCommand command);
Task GetMqttClientOptionsAsync();
void ReadSensorSettings(MqttPublisher publisher);
void WriteMqttBrokerSettingsAsync(MqttSettings settings);
@@ -27,10 +35,9 @@ public interface IConfigurationService
Task GetMqttBrokerSettings();
void EnableAutoStart(bool enable);
bool IsAutoStartEnabled();
- void DeleteConfiguredSensor(Guid id);
- void DeleteConfiguredCommand(Guid id);
void WriteCommandSettingsAsync();
void ReadCommandSettings(MqttPublisher publisher);
Task> GetSensorsAfterLoadingAsync();
+ void WriteGeneralSettingsAsync(GeneralSettings settings);
}
}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/AbstractDiscoverable.cs b/hass-workstation-service/Domain/AbstractDiscoverable.cs
index aba479e..8f8fd85 100644
--- a/hass-workstation-service/Domain/AbstractDiscoverable.cs
+++ b/hass-workstation-service/Domain/AbstractDiscoverable.cs
@@ -12,16 +12,8 @@ public abstract class AbstractDiscoverable
{
public abstract string Domain { get; }
public string Name { get; protected set; }
-
- public string ObjectId
- {
- get
- {
- return Regex.Replace(this.Name, "[^a-zA-Z0-9_-]", "_");
- }
- }
+ public string ObjectId => Regex.Replace(Name, "[^a-zA-Z0-9_-]", "_");
public Guid Id { get; protected set; }
-
public abstract DiscoveryConfigModel GetAutoDiscoveryConfig();
}
-}
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/Commands/AbstractCommand.cs b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
index 849077e..94df49d 100644
--- a/hass-workstation-service/Domain/Commands/AbstractCommand.cs
+++ b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
@@ -5,72 +5,60 @@
namespace hass_workstation_service.Domain.Commands
{
-
public abstract class AbstractCommand : AbstractDiscoverable
{
///
/// The update interval in seconds. It checks state only if the interval has passed.
///
- public int UpdateInterval { get => 1; }
+ public static int UpdateInterval { get => 1; }
public DateTime? LastUpdated { get; protected set; }
public string PreviousPublishedState { get; protected set; }
public MqttPublisher Publisher { get; protected set; }
public override string Domain { get => "switch"; }
- public AbstractCommand(MqttPublisher publisher, string name, Guid id = default(Guid))
- {
- if (id == Guid.Empty)
- {
- this.Id = Guid.NewGuid();
- }
- else
- {
- this.Id = id;
- }
- this.Name = name;
- this.Publisher = publisher;
- publisher.Subscribe(this);
- }
- protected CommandDiscoveryConfigModel _autoDiscoveryConfigModel;
- protected CommandDiscoveryConfigModel SetAutoDiscoveryConfigModel(CommandDiscoveryConfigModel config)
+ public AbstractCommand(MqttPublisher publisher, string name, Guid id = default)
{
- this._autoDiscoveryConfigModel = config;
- return config;
+ Id = id == Guid.Empty ? Guid.NewGuid() : id;
+ Name = name;
+ Publisher = publisher;
+ publisher.Subscribe(this);
}
public abstract string GetState();
public async Task PublishStateAsync()
{
- if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(this.UpdateInterval) > DateTime.UtcNow)
- {
- // dont't even check the state if the update interval hasn't passed
+ // dont't even check the state if the update interval hasn't passed
+ if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(UpdateInterval) > DateTime.UtcNow)
return;
- }
- string state = this.GetState();
- if (this.PreviousPublishedState == state)
- {
- // don't publish the state if it hasn't changed
+
+ string state = GetState();
+ // don't publish the state if it hasn't changed
+ if (PreviousPublishedState == state)
return;
- }
+
var message = new MqttApplicationMessageBuilder()
- .WithTopic(this.GetAutoDiscoveryConfig().State_topic)
- .WithPayload(state)
- .WithExactlyOnceQoS()
- .WithRetainFlag()
- .Build();
+ .WithTopic(GetAutoDiscoveryConfig().State_topic)
+ .WithPayload(state)
+ .WithExactlyOnceQoS()
+ .WithRetainFlag()
+ .Build();
await Publisher.Publish(message);
- this.PreviousPublishedState = state;
- this.LastUpdated = DateTime.UtcNow;
+ PreviousPublishedState = state;
+ LastUpdated = DateTime.UtcNow;
}
- public async void PublishAutoDiscoveryConfigAsync()
- {
- await this.Publisher.AnnounceAutoDiscoveryConfig(this, this.Domain);
- }
- public async Task UnPublishAutoDiscoveryConfigAsync()
+
+ public async void PublishAutoDiscoveryConfigAsync() => await Publisher.AnnounceAutoDiscoveryConfig(this);
+
+ public async Task UnPublishAutoDiscoveryConfigAsync() => await Publisher.AnnounceAutoDiscoveryConfig(this, true);
+
+ protected CommandDiscoveryConfigModel _autoDiscoveryConfigModel;
+ protected CommandDiscoveryConfigModel SetAutoDiscoveryConfigModel(CommandDiscoveryConfigModel config)
{
- await this.Publisher.AnnounceAutoDiscoveryConfig(this, this.Domain, true);
+ _autoDiscoveryConfigModel = config;
+ return config;
}
+
public abstract void TurnOn();
public abstract void TurnOff();
}
diff --git a/hass-workstation-service/Domain/Commands/CustomCommand.cs b/hass-workstation-service/Domain/Commands/CustomCommand.cs
index 2cc1a9a..02d265a 100644
--- a/hass-workstation-service/Domain/Commands/CustomCommand.cs
+++ b/hass-workstation-service/Domain/Commands/CustomCommand.cs
@@ -30,6 +30,11 @@ public override async void TurnOn()
startInfo.FileName = "cmd.exe";
startInfo.Arguments = $"/C {this.Command}";
this.Process.StartInfo = startInfo;
+
+ // turn off the sensor to guarantee disable the switch
+ // useful if command changes power state of device
+ this.State = "OFF";
+
try
{
this.Process.Start();
@@ -39,12 +44,6 @@ public override async void TurnOn()
Log.Logger.Error($"Sensor {this.Name} failed", e);
this.State = "FAILED";
}
-
- while (!this.Process.HasExited)
- {
- await Task.Delay(1000);
- }
- this.State = "OFF";
}
@@ -54,10 +53,11 @@ public override CommandDiscoveryConfigModel GetAutoDiscoveryConfig()
return new CommandDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability",
- Command_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/set",
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ Command_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{Publisher.NamePrefix}{this.ObjectId}/set",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Device = this.Publisher.DeviceConfigModel,
};
}
diff --git a/hass-workstation-service/Domain/Commands/HibernateCommand.cs b/hass-workstation-service/Domain/Commands/HibernateCommand.cs
new file mode 100644
index 0000000..e2e3d7a
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/HibernateCommand.cs
@@ -0,0 +1,17 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class HibernateCommand : CustomCommand
+ {
+ public HibernateCommand(MqttPublisher publisher, string name = "Hibernate", Guid id = default(Guid)) : base(publisher, "shutdown /h", name ?? "Hibernate", id)
+ {
+ this.State = "OFF";
+ }
+ }
+}
diff --git a/hass-workstation-service/Domain/Commands/KeyCommand.cs b/hass-workstation-service/Domain/Commands/KeyCommand.cs
index bf66e9e..c0b560f 100644
--- a/hass-workstation-service/Domain/Commands/KeyCommand.cs
+++ b/hass-workstation-service/Domain/Commands/KeyCommand.cs
@@ -11,7 +11,7 @@ namespace hass_workstation_service.Domain.Commands
public class KeyCommand : AbstractCommand
{
public const int KEYEVENTF_EXTENTEDKEY = 1;
- public const int KEYEVENTF_KEYUP = 0;
+ public const int KEYEVENTF_KEYUP = 0x0002;
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
@@ -30,10 +30,11 @@ public override CommandDiscoveryConfigModel GetAutoDiscoveryConfig()
return new CommandDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability",
- Command_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/set",
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ Command_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{Publisher.NamePrefix}{this.ObjectId}/set",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Device = this.Publisher.DeviceConfigModel,
};
}
@@ -54,7 +55,8 @@ public override void TurnOff()
public override void TurnOn()
{
- keybd_event(this.KeyCode, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero);
+ keybd_event(this.KeyCode, 0, 0, IntPtr.Zero);
+ keybd_event(this.KeyCode, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
}
}
}
diff --git a/hass-workstation-service/Domain/Commands/MediaPlayPauseCommand.cs b/hass-workstation-service/Domain/Commands/MediaPlayPauseCommand.cs
deleted file mode 100644
index 695b258..0000000
--- a/hass-workstation-service/Domain/Commands/MediaPlayPauseCommand.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using hass_workstation_service.Communication;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace hass_workstation_service.Domain.Commands
-{
- public class MediaPlayPauseCommand : KeyCommand
- {
- public MediaPlayPauseCommand(MqttPublisher publisher, string name = "PlayPause", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_PLAY_PAUSE, name ?? "PlayPause", id) { }
- }
-}
diff --git a/hass-workstation-service/Domain/Commands/MediaPreviousCommand.cs b/hass-workstation-service/Domain/Commands/MediaPreviousCommand.cs
deleted file mode 100644
index fe35544..0000000
--- a/hass-workstation-service/Domain/Commands/MediaPreviousCommand.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using hass_workstation_service.Communication;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace hass_workstation_service.Domain.Commands
-{
- public class MediaPreviousCommand : KeyCommand
- {
- public MediaPreviousCommand(MqttPublisher publisher, string name = "Previous", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_PREV_TRACK, name ?? "Previous", id) { }
- }
-}
diff --git a/hass-workstation-service/Domain/Commands/MediaVolumeDownCommand.cs b/hass-workstation-service/Domain/Commands/MediaVolumeDownCommand.cs
deleted file mode 100644
index 4096e3c..0000000
--- a/hass-workstation-service/Domain/Commands/MediaVolumeDownCommand.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using hass_workstation_service.Communication;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace hass_workstation_service.Domain.Commands
-{
- public class MediaVolumeDownCommand : KeyCommand
- {
- public MediaVolumeDownCommand(MqttPublisher publisher, string name = "VolumeDown", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_DOWN, name ?? "VolumeDown", id) { }
- }
-}
diff --git a/hass-workstation-service/Domain/Commands/MediaVolumeUpCommand.cs b/hass-workstation-service/Domain/Commands/MediaVolumeUpCommand.cs
deleted file mode 100644
index 0631160..0000000
--- a/hass-workstation-service/Domain/Commands/MediaVolumeUpCommand.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using hass_workstation_service.Communication;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace hass_workstation_service.Domain.Commands
-{
- public class MediaVolumeUpCommand : KeyCommand
- {
- public MediaVolumeUpCommand(MqttPublisher publisher, string name = "VolumeUp", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_UP, name ?? "VolumeUp", id) { }
- }
-}
diff --git a/hass-workstation-service/Domain/Commands/MediaMuteCommand.cs b/hass-workstation-service/Domain/Commands/MuteCommand.cs
similarity index 51%
rename from hass-workstation-service/Domain/Commands/MediaMuteCommand.cs
rename to hass-workstation-service/Domain/Commands/MuteCommand.cs
index 3dd4dbc..c955716 100644
--- a/hass-workstation-service/Domain/Commands/MediaMuteCommand.cs
+++ b/hass-workstation-service/Domain/Commands/MuteCommand.cs
@@ -7,8 +7,8 @@
namespace hass_workstation_service.Domain.Commands
{
- public class MediaMuteCommand : KeyCommand
+ public class MuteCommand : KeyCommand
{
- public MediaMuteCommand(MqttPublisher publisher, string name = "Mute", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_MUTE, name ?? "Mute", id) { }
+ public MuteCommand(MqttPublisher publisher, string name = "Mute", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_MUTE, name ?? "Mute", id) { }
}
}
diff --git a/hass-workstation-service/Domain/Commands/MediaNextCommand.cs b/hass-workstation-service/Domain/Commands/NextCommand.cs
similarity index 50%
rename from hass-workstation-service/Domain/Commands/MediaNextCommand.cs
rename to hass-workstation-service/Domain/Commands/NextCommand.cs
index 6f7cb7f..45c13ef 100644
--- a/hass-workstation-service/Domain/Commands/MediaNextCommand.cs
+++ b/hass-workstation-service/Domain/Commands/NextCommand.cs
@@ -7,8 +7,8 @@
namespace hass_workstation_service.Domain.Commands
{
- public class MediaNextCommand : KeyCommand
+ public class NextCommand : KeyCommand
{
- public MediaNextCommand(MqttPublisher publisher, string name = "Next", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_NEXT_TRACK, name ?? "Next", id) { }
+ public NextCommand(MqttPublisher publisher, string name = "Next", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_NEXT_TRACK, name ?? "Next", id) { }
}
}
diff --git a/hass-workstation-service/Domain/Commands/PlayPauseCommand.cs b/hass-workstation-service/Domain/Commands/PlayPauseCommand.cs
new file mode 100644
index 0000000..723eab1
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/PlayPauseCommand.cs
@@ -0,0 +1,14 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class PlayPauseCommand : KeyCommand
+ {
+ public PlayPauseCommand(MqttPublisher publisher, string name = "PlayPause", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_PLAY_PAUSE, name ?? "PlayPause", id) { }
+ }
+}
diff --git a/hass-workstation-service/Domain/Commands/PreviousCommand.cs b/hass-workstation-service/Domain/Commands/PreviousCommand.cs
new file mode 100644
index 0000000..b5db78c
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/PreviousCommand.cs
@@ -0,0 +1,14 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class PreviousCommand : KeyCommand
+ {
+ public PreviousCommand(MqttPublisher publisher, string name = "Previous", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_MEDIA_PREV_TRACK, name ?? "Previous", id) { }
+ }
+}
diff --git a/hass-workstation-service/Domain/Commands/VolumeDownCommand.cs b/hass-workstation-service/Domain/Commands/VolumeDownCommand.cs
new file mode 100644
index 0000000..548a157
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/VolumeDownCommand.cs
@@ -0,0 +1,14 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class VolumeDownCommand : KeyCommand
+ {
+ public VolumeDownCommand(MqttPublisher publisher, string name = "VolumeDown", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_DOWN, name ?? "VolumeDown", id) { }
+ }
+}
diff --git a/hass-workstation-service/Domain/Commands/VolumeUpCommand.cs b/hass-workstation-service/Domain/Commands/VolumeUpCommand.cs
new file mode 100644
index 0000000..9a13beb
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/VolumeUpCommand.cs
@@ -0,0 +1,14 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class VolumeUpCommand : KeyCommand
+ {
+ public VolumeUpCommand(MqttPublisher publisher, string name = "VolumeUp", Guid id = default(Guid)) : base(publisher, KeyCommand.VK_VOLUME_UP, name ?? "VolumeUp", id) { }
+ }
+}
diff --git a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
index 22d9a37..73904bf 100644
--- a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
@@ -1,12 +1,10 @@
using System;
-using System.Text.RegularExpressions;
using System.Threading.Tasks;
using hass_workstation_service.Communication;
using MQTTnet;
namespace hass_workstation_service.Domain.Sensors
{
-
public abstract class AbstractSensor : AbstractDiscoverable
{
///
@@ -17,61 +15,52 @@ public abstract class AbstractSensor : AbstractDiscoverable
public string PreviousPublishedState { get; protected set; }
public MqttPublisher Publisher { get; protected set; }
public override string Domain { get => "sensor"; }
- public AbstractSensor(MqttPublisher publisher, string name, int updateInterval = 10, Guid id = default(Guid))
- {
- if (id == Guid.Empty)
- {
- this.Id = Guid.NewGuid();
- }
- else
- {
- this.Id = id;
- }
- this.Name = name;
- this.Publisher = publisher;
- this.UpdateInterval = updateInterval;
- }
- protected SensorDiscoveryConfigModel _autoDiscoveryConfigModel;
- protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
+ public AbstractSensor(MqttPublisher publisher, string name, int updateInterval = 10, Guid id = default)
{
- this._autoDiscoveryConfigModel = config;
- return config;
+ Id = id == Guid.Empty ? Guid.NewGuid() : id;
+ Name = name;
+ Publisher = publisher;
+ UpdateInterval = updateInterval;
}
public abstract string GetState();
public async Task PublishStateAsync()
{
- if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(this.UpdateInterval) > DateTime.UtcNow)
- {
- // dont't even check the state if the update interval hasn't passed
+ // dont't even check the state if the update interval hasn't passed
+ if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(UpdateInterval) > DateTime.UtcNow)
return;
- }
- string state = this.GetState();
- if (this.PreviousPublishedState == state)
- {
- // don't publish the state if it hasn't changed
+
+ string state = GetState();
+ // don't publish the state if it hasn't changed
+ if (PreviousPublishedState == state)
return;
- }
+
var message = new MqttApplicationMessageBuilder()
- .WithTopic(this.GetAutoDiscoveryConfig().State_topic)
- .WithPayload(state)
- .WithExactlyOnceQoS()
- .WithRetainFlag()
- .Build();
+ .WithTopic(GetAutoDiscoveryConfig().State_topic)
+ .WithPayload(state)
+ .WithExactlyOnceQoS()
+ .WithRetainFlag()
+ .Build();
await Publisher.Publish(message);
- this.PreviousPublishedState = state;
- this.LastUpdated = DateTime.UtcNow;
- }
- public async void PublishAutoDiscoveryConfigAsync()
- {
- await this.Publisher.AnnounceAutoDiscoveryConfig(this, this.Domain);
+ PreviousPublishedState = state;
+ LastUpdated = DateTime.UtcNow;
}
+
+ public async void PublishAutoDiscoveryConfigAsync() => await Publisher.AnnounceAutoDiscoveryConfig(this);
+
public async Task UnPublishAutoDiscoveryConfigAsync()
{
- await this.Publisher.AnnounceAutoDiscoveryConfig(this, this.Domain, true);
+ await Publisher.AnnounceAutoDiscoveryConfig(this, true);
+ this._autoDiscoveryConfigModel = null;
}
+ protected SensorDiscoveryConfigModel _autoDiscoveryConfigModel;
+ protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
+ {
+ _autoDiscoveryConfigModel = config;
+ return config;
+ }
}
}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs b/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
index 2ab6f2c..26ea9c2 100644
--- a/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
@@ -14,9 +14,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:window-maximize",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
diff --git a/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs b/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
index c5fcf7e..eb3752c 100644
--- a/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
@@ -22,9 +22,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:chart-areaspline",
Unit_of_measurement = "%",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
diff --git a/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs b/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
index 56754cc..7af44c4 100644
--- a/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
@@ -14,9 +14,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:speedometer",
Unit_of_measurement = "MHz",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
diff --git a/hass-workstation-service/Domain/Sensors/CurrentVolumeSensor.cs b/hass-workstation-service/Domain/Sensors/CurrentVolumeSensor.cs
index 6a46bde..2d01976 100644
--- a/hass-workstation-service/Domain/Sensors/CurrentVolumeSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/CurrentVolumeSensor.cs
@@ -23,9 +23,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:volume-medium",
Unit_of_measurement = "%",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
diff --git a/hass-workstation-service/Domain/Sensors/DummySensor.cs b/hass-workstation-service/Domain/Sensors/DummySensor.cs
index 66eaaad..77ffc9d 100644
--- a/hass-workstation-service/Domain/Sensors/DummySensor.cs
+++ b/hass-workstation-service/Domain/Sensors/DummySensor.cs
@@ -18,9 +18,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
}
diff --git a/hass-workstation-service/Domain/Sensors/GpuLoadSensor.cs b/hass-workstation-service/Domain/Sensors/GpuLoadSensor.cs
index 10ee77f..91e5a66 100644
--- a/hass-workstation-service/Domain/Sensors/GpuLoadSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/GpuLoadSensor.cs
@@ -35,9 +35,10 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Unit_of_measurement = "%",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
diff --git a/hass-workstation-service/Domain/Sensors/GpuTemperatureSensor.cs b/hass-workstation-service/Domain/Sensors/GpuTemperatureSensor.cs
index 6828f18..f16edda 100644
--- a/hass-workstation-service/Domain/Sensors/GpuTemperatureSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/GpuTemperatureSensor.cs
@@ -35,9 +35,10 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Device_class = "temperature",
Unit_of_measurement = "°C",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
diff --git a/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs b/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
index a6555a7..9aa38bc 100644
--- a/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
@@ -14,11 +14,11 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:clock-time-three-outline",
- Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
Device_class = "timestamp"
});
}
diff --git a/hass-workstation-service/Domain/Sensors/LastBootSensor.cs b/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
index 7d2230a..fda171c 100644
--- a/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
@@ -18,11 +18,11 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:clock-time-three-outline",
- Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
Device_class = "timestamp"
});
}
diff --git a/hass-workstation-service/Domain/Sensors/MasterVolumeSensor.cs b/hass-workstation-service/Domain/Sensors/MasterVolumeSensor.cs
new file mode 100644
index 0000000..699f9b5
--- /dev/null
+++ b/hass-workstation-service/Domain/Sensors/MasterVolumeSensor.cs
@@ -0,0 +1,41 @@
+using CoreAudio;
+using hass_workstation_service.Communication;
+using System;
+using System.Globalization;
+
+namespace hass_workstation_service.Domain.Sensors
+{
+ public class MasterVolumeSensor : AbstractSensor
+ {
+ private MMDeviceEnumerator deviceEnumerator;
+
+ public MasterVolumeSensor(MqttPublisher publisher, int? updateInterval = null, string name = "MasterVolume", Guid id = default(Guid)) : base(publisher, name ?? "CurrentVolume", updateInterval ?? 10, id)
+ {
+ this.deviceEnumerator = new MMDeviceEnumerator();
+ }
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
+ {
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
+ {
+ Name = this.Name,
+ Unique_id = this.Id.ToString(),
+ Device = this.Publisher.DeviceConfigModel,
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ Icon = "mdi:volume-medium",
+ Unit_of_measurement = "%",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
+ });
+ }
+
+ public override string GetState()
+ {
+ var defaultAudioDevice = deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
+
+ // check if the volume is muted
+ if (defaultAudioDevice.AudioEndpointVolume.Mute) return "0";
+
+ return Math.Round(defaultAudioDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0) // round volume and convert to percent
+ .ToString(CultureInfo.InvariantCulture); // convert to string
+ }
+ }
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
index 9b666ed..7b75cee 100644
--- a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
@@ -42,9 +42,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:memory",
Unit_of_measurement = "%",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
diff --git a/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs b/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
index 1b46811..7c94e54 100644
--- a/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
@@ -27,9 +27,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability"
});
}
diff --git a/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
index 095b5bd..7194a9e 100644
--- a/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
@@ -22,11 +22,12 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability"
- });
+ });;
}
public override string GetState()
diff --git a/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
index 650de5f..59c6ee2 100644
--- a/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
@@ -42,9 +42,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.ObjectId}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:lock",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
diff --git a/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs b/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
index 793df7e..942c0f5 100644
--- a/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
@@ -14,9 +14,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Icon = "mdi:laptop",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
diff --git a/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs b/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
index d2601b6..a0b6984 100644
--- a/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
+++ b/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
@@ -25,9 +25,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability"
});
}
diff --git a/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs b/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
index 598d3f9..0e2d440 100644
--- a/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
@@ -30,9 +30,10 @@ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
+ NamePrefix = Publisher.NamePrefix,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state",
Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability"
});
}
diff --git a/hass-workstation-service/Program.cs b/hass-workstation-service/Program.cs
index d720614..72b9cfa 100644
--- a/hass-workstation-service/Program.cs
+++ b/hass-workstation-service/Program.cs
@@ -70,7 +70,6 @@ public static async Task Main(string[] args)
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
-
.ConfigureLogging((hostContext, loggingBuilder) =>
loggingBuilder.AddSerilog(dispose: true))
.ConfigureServices((hostContext, services) =>
@@ -84,14 +83,14 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
Sw_version = GetVersion()
};
services.AddSingleton(deviceConfig);
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddHostedService();
}).ConfigureIpcHost(builder =>
{
// configure IPC endpoints
- builder.AddNamedPipeEndpoint(pipeName: "pipeinternal");
+ builder.AddNamedPipeEndpoint(pipeName: "pipeinternal");
});
static internal string GetVersion()
{
diff --git a/hass-workstation-service/Properties/PublishProfiles/AzureHosted.pubxml b/hass-workstation-service/Properties/PublishProfiles/AzureHosted.pubxml
index 5a82bea..64dd310 100644
--- a/hass-workstation-service/Properties/PublishProfiles/AzureHosted.pubxml
+++ b/hass-workstation-service/Properties/PublishProfiles/AzureHosted.pubxml
@@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
- 47
+ 55
1.0.0.*
True
Release
@@ -37,6 +37,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
True
True
Foreground
+ False|2021-11-14T15:44:38.1032015Z;
diff --git a/hass-workstation-service/UserInterface.exe b/hass-workstation-service/UserInterface.exe
index 8b7099a..a34952d 100644
Binary files a/hass-workstation-service/UserInterface.exe and b/hass-workstation-service/UserInterface.exe differ
diff --git a/hass-workstation-service/appsettings.Development.json b/hass-workstation-service/appsettings.Development.json
deleted file mode 100644
index c5296f4..0000000
--- a/hass-workstation-service/appsettings.Development.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "MqttBroker": {
- "Host": "192.168.2.6",
- "Username": "tester",
- "Password": "tester"
- }
-}
diff --git a/hass-workstation-service/hass-workstation-service.csproj b/hass-workstation-service/hass-workstation-service.csproj
index e7d0933..30c7c44 100644
--- a/hass-workstation-service/hass-workstation-service.csproj
+++ b/hass-workstation-service/hass-workstation-service.csproj
@@ -1,4 +1,4 @@
-
+
net5.0
@@ -42,6 +42,9 @@
Always
+
+ Always
+
@@ -52,14 +55,14 @@
-
+
-
-
+
+
-
-
+
+
@@ -67,5 +70,11 @@
..\lib\CoreAudio.dll
+
diff --git a/hass-workstation-service/hass-workstation-service.deps.json b/hass-workstation-service/hass-workstation-service.deps.json
new file mode 100644
index 0000000..e05835d
--- /dev/null
+++ b/hass-workstation-service/hass-workstation-service.deps.json
@@ -0,0 +1,1398 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v5.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v5.0": {
+ "hass-workstation-service/1.0.0": {
+ "dependencies": {
+ "JKang.IpcServiceFramework.Hosting.NamedPipe": "3.1.0",
+ "LibreHardwareMonitorLib": "0.8.8",
+ "MQTTnet": "3.0.16",
+ "MQTTnet.Extensions.ManagedClient": "3.0.16",
+ "Microsoft.Extensions.Hosting": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "Serilog.Extensions.Logging.File": "2.0.0",
+ "Serilog.Sinks.Console": "4.0.0",
+ "Serilog.Sinks.File": "5.0.0",
+ "System.Management": "5.0.0",
+ "CoreAudio": "2020.11.2.36"
+ },
+ "runtime": {
+ "hass-workstation-service.dll": {}
+ }
+ },
+ "HidSharp/2.1.0": {
+ "runtime": {
+ "lib/netstandard2.0/HidSharp.dll": {
+ "assemblyVersion": "2.1.0.0",
+ "fileVersion": "2.1.0.0"
+ }
+ }
+ },
+ "JKang.IpcServiceFramework.Core/3.1.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Newtonsoft.Json": "12.0.3"
+ },
+ "runtime": {
+ "lib/netstandard2.0/JKang.IpcServiceFramework.Core.dll": {
+ "assemblyVersion": "3.0.0.0",
+ "fileVersion": "3.0.0.0"
+ }
+ }
+ },
+ "JKang.IpcServiceFramework.Hosting/3.1.0": {
+ "dependencies": {
+ "JKang.IpcServiceFramework.Core": "3.1.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/JKang.IpcServiceFramework.Hosting.dll": {
+ "assemblyVersion": "3.0.0.0",
+ "fileVersion": "3.0.0.0"
+ }
+ }
+ },
+ "JKang.IpcServiceFramework.Hosting.NamedPipe/3.1.0": {
+ "dependencies": {
+ "JKang.IpcServiceFramework.Hosting": "3.1.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "System.IO.Pipes.AccessControl": "4.5.1"
+ },
+ "runtime": {
+ "lib/netstandard2.0/JKang.IpcServiceFramework.Hosting.NamedPipe.dll": {
+ "assemblyVersion": "3.0.0.0",
+ "fileVersion": "3.0.0.0"
+ }
+ }
+ },
+ "LibreHardwareMonitorLib/0.8.8": {
+ "dependencies": {
+ "HidSharp": "2.1.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "NvAPIWrapper.Net": "0.8.1.101",
+ "System.IO.FileSystem.AccessControl": "5.0.0",
+ "System.IO.Ports": "5.0.0",
+ "System.Management": "5.0.0"
+ },
+ "runtime": {
+ "lib/net5.0/LibreHardwareMonitorLib.dll": {
+ "assemblyVersion": "0.8.8.0",
+ "fileVersion": "0.8.8.0"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.Binder/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.CommandLine/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.EnvironmentVariables/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.FileExtensions/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Physical": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.Json/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.1/Microsoft.Extensions.Configuration.Json.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Configuration.UserSecrets/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Json": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Physical": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/net5.0/Microsoft.Extensions.DependencyInjection.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.FileProviders.Abstractions/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.FileProviders.Physical/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileSystemGlobbing": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.FileSystemGlobbing/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Hosting/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "5.0.0",
+ "Microsoft.Extensions.Configuration.CommandLine": "5.0.0",
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": "5.0.0",
+ "Microsoft.Extensions.Configuration.FileExtensions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Json": "5.0.0",
+ "Microsoft.Extensions.Configuration.UserSecrets": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Physical": "5.0.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging.Configuration": "5.0.0",
+ "Microsoft.Extensions.Logging.Console": "5.0.0",
+ "Microsoft.Extensions.Logging.Debug": "5.0.0",
+ "Microsoft.Extensions.Logging.EventLog": "5.0.0",
+ "Microsoft.Extensions.Logging.EventSource": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.1/Microsoft.Extensions.Hosting.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Hosting.Abstractions/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.FileProviders.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.1/Microsoft.Extensions.Hosting.Abstractions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.Configuration/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "5.0.0",
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.Console/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging.Configuration": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netcoreapp3.0/Microsoft.Extensions.Logging.Console.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.Debug/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.EventLog/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0",
+ "System.Diagnostics.EventLog": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Logging.EventLog.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Logging.EventSource/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netcoreapp3.0/Microsoft.Extensions.Logging.EventSource.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Options/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/net5.0/Microsoft.Extensions.Options.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Options.ConfigurationExtensions/5.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "5.0.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Options": "5.0.0",
+ "Microsoft.Extensions.Primitives": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.Extensions.Primitives/5.0.0": {
+ "runtime": {
+ "lib/netcoreapp3.0/Microsoft.Extensions.Primitives.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {},
+ "Microsoft.NETCore.Targets/1.0.1": {},
+ "Microsoft.Win32.Registry/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "MQTTnet/3.0.16": {
+ "runtime": {
+ "lib/net5.0/MQTTnet.dll": {
+ "assemblyVersion": "3.0.16.0",
+ "fileVersion": "3.0.16.0"
+ }
+ }
+ },
+ "MQTTnet.Extensions.ManagedClient/3.0.16": {
+ "dependencies": {
+ "MQTTnet": "3.0.16"
+ },
+ "runtime": {
+ "lib/net5.0/MQTTnet.Extensions.ManagedClient.dll": {
+ "assemblyVersion": "3.0.16.0",
+ "fileVersion": "3.0.16.0"
+ }
+ }
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "runtime": {
+ "lib/netstandard2.0/Newtonsoft.Json.dll": {
+ "assemblyVersion": "12.0.0.0",
+ "fileVersion": "12.0.3.23909"
+ }
+ }
+ },
+ "NvAPIWrapper.Net/0.8.1.101": {
+ "runtime": {
+ "lib/netstandard2.0/NvAPIWrapper.dll": {
+ "assemblyVersion": "0.8.1.100",
+ "fileVersion": "0.8.1.100"
+ }
+ }
+ },
+ "runtime.linux-arm.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "runtimeTargets": {
+ "runtimes/linux-arm/native/libSystem.IO.Ports.Native.so": {
+ "rid": "linux-arm",
+ "assetType": "native",
+ "fileVersion": "0.0.0.0"
+ }
+ }
+ },
+ "runtime.linux-arm64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "runtimeTargets": {
+ "runtimes/linux-arm64/native/libSystem.IO.Ports.Native.so": {
+ "rid": "linux-arm64",
+ "assetType": "native",
+ "fileVersion": "0.0.0.0"
+ }
+ }
+ },
+ "runtime.linux-x64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "runtimeTargets": {
+ "runtimes/linux-x64/native/libSystem.IO.Ports.Native.so": {
+ "rid": "linux-x64",
+ "assetType": "native",
+ "fileVersion": "0.0.0.0"
+ }
+ }
+ },
+ "runtime.native.System.IO.Ports/5.0.0": {
+ "dependencies": {
+ "runtime.linux-arm.runtime.native.System.IO.Ports": "5.0.0-rtm.20519.4",
+ "runtime.linux-arm64.runtime.native.System.IO.Ports": "5.0.0-rtm.20519.4",
+ "runtime.linux-x64.runtime.native.System.IO.Ports": "5.0.0-rtm.20519.4",
+ "runtime.osx-x64.runtime.native.System.IO.Ports": "5.0.0-rtm.20519.4"
+ }
+ },
+ "runtime.osx-x64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "runtimeTargets": {
+ "runtimes/osx-x64/native/libSystem.IO.Ports.Native.dylib": {
+ "rid": "osx-x64",
+ "assetType": "native",
+ "fileVersion": "0.0.0.0"
+ }
+ }
+ },
+ "Serilog/2.10.0": {
+ "runtime": {
+ "lib/netstandard2.1/Serilog.dll": {
+ "assemblyVersion": "2.0.0.0",
+ "fileVersion": "2.10.0.0"
+ }
+ }
+ },
+ "Serilog.Extensions.Logging/2.0.2": {
+ "dependencies": {
+ "Microsoft.Extensions.Logging": "5.0.0",
+ "Serilog": "2.10.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Serilog.Extensions.Logging.dll": {
+ "assemblyVersion": "2.0.0.0",
+ "fileVersion": "2.0.2.0"
+ }
+ }
+ },
+ "Serilog.Extensions.Logging.File/2.0.0": {
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "5.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "5.0.0",
+ "Serilog": "2.10.0",
+ "Serilog.Extensions.Logging": "2.0.2",
+ "Serilog.Formatting.Compact": "1.0.0",
+ "Serilog.Sinks.Async": "1.1.0",
+ "Serilog.Sinks.RollingFile": "3.3.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/Serilog.Extensions.Logging.File.dll": {
+ "assemblyVersion": "2.0.0.0",
+ "fileVersion": "2.0.0.0"
+ }
+ }
+ },
+ "Serilog.Formatting.Compact/1.0.0": {
+ "dependencies": {
+ "Serilog": "2.10.0"
+ },
+ "runtime": {
+ "lib/netstandard1.1/Serilog.Formatting.Compact.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.0.0.0"
+ }
+ }
+ },
+ "Serilog.Sinks.Async/1.1.0": {
+ "dependencies": {
+ "Serilog": "2.10.0",
+ "System.Collections.Concurrent": "4.0.12"
+ },
+ "runtime": {
+ "lib/netstandard1.1/Serilog.Sinks.Async.dll": {
+ "assemblyVersion": "1.0.0.0",
+ "fileVersion": "1.1.0.0"
+ }
+ }
+ },
+ "Serilog.Sinks.Console/4.0.0": {
+ "dependencies": {
+ "Serilog": "2.10.0"
+ },
+ "runtime": {
+ "lib/net5.0/Serilog.Sinks.Console.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "4.0.0.0"
+ }
+ }
+ },
+ "Serilog.Sinks.File/5.0.0": {
+ "dependencies": {
+ "Serilog": "2.10.0"
+ },
+ "runtime": {
+ "lib/net5.0/Serilog.Sinks.File.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.0.0"
+ }
+ }
+ },
+ "Serilog.Sinks.RollingFile/3.3.0": {
+ "dependencies": {
+ "Serilog.Sinks.File": "5.0.0",
+ "System.IO": "4.1.0",
+ "System.IO.FileSystem.Primitives": "4.0.1",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Text.Encoding.Extensions": "4.0.11"
+ },
+ "runtime": {
+ "lib/netstandard1.3/Serilog.Sinks.RollingFile.dll": {
+ "assemblyVersion": "2.0.0.0",
+ "fileVersion": "3.3.0.0"
+ }
+ }
+ },
+ "System.CodeDom/5.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/System.CodeDom.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Collections/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Collections.Concurrent/4.0.12": {
+ "dependencies": {
+ "System.Collections": "4.0.11",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Diagnostics.Tracing": "4.1.0",
+ "System.Globalization": "4.0.11",
+ "System.Reflection": "4.1.0",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Extensions": "4.1.0",
+ "System.Threading": "4.0.11",
+ "System.Threading.Tasks": "4.0.11"
+ }
+ },
+ "System.Diagnostics.Debug/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/System.Diagnostics.EventLog.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ },
+ "runtimeTargets": {
+ "runtimes/win/lib/netcoreapp2.0/System.Diagnostics.EventLog.dll": {
+ "rid": "win",
+ "assetType": "runtime",
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Diagnostics.Tracing/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Globalization/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.IO/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Text.Encoding": "4.0.11",
+ "System.Threading.Tasks": "4.0.11"
+ }
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.IO.FileSystem.Primitives/4.0.1": {
+ "dependencies": {
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.IO.Pipes.AccessControl/4.5.1": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.IO.Ports/5.0.0": {
+ "dependencies": {
+ "Microsoft.Win32.Registry": "5.0.0",
+ "runtime.native.System.IO.Ports": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/System.IO.Ports.dll": {
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ },
+ "runtimeTargets": {
+ "runtimes/linux/lib/netstandard2.0/System.IO.Ports.dll": {
+ "rid": "linux",
+ "assetType": "runtime",
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "runtimes/osx/lib/netstandard2.0/System.IO.Ports.dll": {
+ "rid": "osx",
+ "assetType": "runtime",
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ },
+ "runtimes/win/lib/netstandard2.0/System.IO.Ports.dll": {
+ "rid": "win",
+ "assetType": "runtime",
+ "assemblyVersion": "5.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Management/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.Win32.Registry": "5.0.0",
+ "System.CodeDom": "5.0.0"
+ },
+ "runtime": {
+ "lib/netstandard2.0/System.Management.dll": {
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ },
+ "runtimeTargets": {
+ "runtimes/win/lib/netcoreapp2.0/System.Management.dll": {
+ "rid": "win",
+ "assetType": "runtime",
+ "assemblyVersion": "4.0.0.0",
+ "fileVersion": "5.0.20.51904"
+ }
+ }
+ },
+ "System.Reflection/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.IO": "4.1.0",
+ "System.Reflection.Primitives": "4.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Reflection.Primitives/4.0.1": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Resources.ResourceManager/4.0.1": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Globalization": "4.0.11",
+ "System.Reflection": "4.1.0",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Runtime/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1"
+ }
+ },
+ "System.Runtime.Extensions/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Runtime.Handles/4.0.1": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Runtime.InteropServices/4.1.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Reflection": "4.1.0",
+ "System.Reflection.Primitives": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Runtime.Handles": "4.0.1"
+ }
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.Security.Principal.Windows/5.0.0": {},
+ "System.Text.Encoding/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "System.Text.Encoding.Extensions/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Text.Encoding": "4.0.11"
+ }
+ },
+ "System.Threading/4.0.11": {
+ "dependencies": {
+ "System.Runtime": "4.1.0",
+ "System.Threading.Tasks": "4.0.11"
+ }
+ },
+ "System.Threading.Tasks/4.0.11": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "5.0.0",
+ "Microsoft.NETCore.Targets": "1.0.1",
+ "System.Runtime": "4.1.0"
+ }
+ },
+ "CoreAudio/2020.11.2.36": {
+ "runtime": {
+ "CoreAudio.dll": {
+ "assemblyVersion": "2020.11.2.36",
+ "fileVersion": "2020.11.2.36"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "hass-workstation-service/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "HidSharp/2.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-UTdxWvbgp2xzT1Ajaa2va+Qi3oNHJPasYmVhbKI2VVdu1VYP6yUG+RikhsHvpD7iM0S8e8UYb5Qm/LTWxx9QAA==",
+ "path": "hidsharp/2.1.0",
+ "hashPath": "hidsharp.2.1.0.nupkg.sha512"
+ },
+ "JKang.IpcServiceFramework.Core/3.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-LIjYA34RJyLMlyvGqDcIfZ3YUuZvNxCjGr8LAw/Li1XEdQizW7eBAtGI4XKRWNcISax5yowme9t1XcUuHrSWCw==",
+ "path": "jkang.ipcserviceframework.core/3.1.0",
+ "hashPath": "jkang.ipcserviceframework.core.3.1.0.nupkg.sha512"
+ },
+ "JKang.IpcServiceFramework.Hosting/3.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-G25pxTaHtu1gjJKnkIV2T/VezeiPyaBUTUosOxA/5XIH+Q9kLOFx0QkAMh//4IpxsVk6kcXojSvdUXI3jYyb8Q==",
+ "path": "jkang.ipcserviceframework.hosting/3.1.0",
+ "hashPath": "jkang.ipcserviceframework.hosting.3.1.0.nupkg.sha512"
+ },
+ "JKang.IpcServiceFramework.Hosting.NamedPipe/3.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-WIAx8GZnJ7NWiBUvhThE0wpA2jK6dGDgelo+RPW++90B4ZvvZbRmJEcVmdBK7b6fhqyAjmk03vyvvD0FfLfWKg==",
+ "path": "jkang.ipcserviceframework.hosting.namedpipe/3.1.0",
+ "hashPath": "jkang.ipcserviceframework.hosting.namedpipe.3.1.0.nupkg.sha512"
+ },
+ "LibreHardwareMonitorLib/0.8.8": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-KBPf4HJkGtubkFG8PL7Hr0E736CW4KLk6vqiC4aXY6quEd861/3tBo9h8VVvrL/q2r58xVq5zaoylIjqB5bqHA==",
+ "path": "librehardwaremonitorlib/0.8.8",
+ "hashPath": "librehardwaremonitorlib.0.8.8.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-LN322qEKHjuVEhhXueTUe7RNePooZmS8aGid5aK2woX3NPjSnONFyKUc6+JknOS6ce6h2tCLfKPTBXE3mN/6Ag==",
+ "path": "microsoft.extensions.configuration/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ETjSBHMp3OAZ4HxGQYpwyGsD8Sw5FegQXphi0rpoGMT74S4+I2mm7XJEswwn59XAaKOzC15oDSOWEE8SzDCd6Q==",
+ "path": "microsoft.extensions.configuration.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.abstractions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.Binder/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Of1Irt1+NzWO+yEYkuDh5TpT4On7LKl98Q9iLqCdOZps6XXEWDj3AKtmyvzJPVXZe4apmkJJIiDL7rR1yC+hjQ==",
+ "path": "microsoft.extensions.configuration.binder/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.binder.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.CommandLine/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-OelM+VQdhZ0XMXsEQBq/bt3kFzD+EBGqR4TAgFDRAye0JfvHAaRi+3BxCRcwqUAwDhV0U0HieljBGHlTgYseRA==",
+ "path": "microsoft.extensions.configuration.commandline/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.commandline.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.EnvironmentVariables/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-fqh6y6hAi0Z0fRsb4B/mP9OkKkSlifh5osa+N/YSQ+/S2a//+zYApZMUC1XeP9fdjlgZoPQoZ72Q2eLHyKLddQ==",
+ "path": "microsoft.extensions.configuration.environmentvariables/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.environmentvariables.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.FileExtensions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rRdspYKA18ViPOISwAihhCMbusHsARCOtDMwa23f+BGEdIjpKPlhs3LLjmKlxfhpGXBjIsS0JpXcChjRUN+PAw==",
+ "path": "microsoft.extensions.configuration.fileextensions/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.fileextensions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.Json/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Pak8ymSUfdzPfBTLHxeOwcR32YDbuVfhnH2hkfOLnJNQd19ItlBdpMjIDY9C5O/nS2Sn9bzDMai0ZrvF7KyY/Q==",
+ "path": "microsoft.extensions.configuration.json/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.json.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Configuration.UserSecrets/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+tK3seG68106lN277YWQvqmfyI/89w0uTu/5Gz5VYSUu5TI4mqwsaWLlSmT9Bl1yW/i1Nr06gHJxqaqB5NU9Tw==",
+ "path": "microsoft.extensions.configuration.usersecrets/5.0.0",
+ "hashPath": "microsoft.extensions.configuration.usersecrets.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.DependencyInjection/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Rc2kb/p3Ze6cP6rhFC3PJRdWGbLvSHZc0ev7YlyeU6FmHciDMLrhoVoTUEzKPhN5ZjFgKF1Cf5fOz8mCMIkvpA==",
+ "path": "microsoft.extensions.dependencyinjection/5.0.0",
+ "hashPath": "microsoft.extensions.dependencyinjection.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
+ "path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.FileProviders.Abstractions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-iuZIiZ3mteEb+nsUqpGXKx2cGF+cv6gWPd5jqQI4hzqdiJ6I94ddLjKhQOuRW1lueHwocIw30xbSHGhQj0zjdQ==",
+ "path": "microsoft.extensions.fileproviders.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.fileproviders.abstractions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.FileProviders.Physical/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-1rkd8UO2qf21biwO7X0hL9uHP7vtfmdv/NLvKgCRHkdz1XnW8zVQJXyEYiN68WYpExgtVWn55QF0qBzgfh1mGg==",
+ "path": "microsoft.extensions.fileproviders.physical/5.0.0",
+ "hashPath": "microsoft.extensions.fileproviders.physical.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.FileSystemGlobbing/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ArliS8lGk8sWRtrWpqI8yUVYJpRruPjCDT+EIjrgkA/AAPRctlAkRISVZ334chAKktTLzD1+PK8F5IZpGedSqA==",
+ "path": "microsoft.extensions.filesystemglobbing/5.0.0",
+ "hashPath": "microsoft.extensions.filesystemglobbing.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Hosting/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hiokSU1TOVfcqpQAnpiOzP2rE9p+niq92g5yeAnwlbSrUlIdIS6M8emCknZvhdOagQA9x5YWNwe1n0kFUwE0NQ==",
+ "path": "microsoft.extensions.hosting/5.0.0",
+ "hashPath": "microsoft.extensions.hosting.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Hosting.Abstractions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-cbUOCePYBl1UhM+N2zmDSUyJ6cODulbtUd9gEzMFIK3RQDtP/gJsE08oLcBSXH3Q1RAQ0ex7OAB3HeTKB9bXpg==",
+ "path": "microsoft.extensions.hosting.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.hosting.abstractions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==",
+ "path": "microsoft.extensions.logging/5.0.0",
+ "hashPath": "microsoft.extensions.logging.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==",
+ "path": "microsoft.extensions.logging.abstractions/5.0.0",
+ "hashPath": "microsoft.extensions.logging.abstractions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.Configuration/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-N3/d0HeMRnBekadbZlmbp+In8EvNNkQHSdbtRzjrGVckdZWpYs5GNrAfaYqVplDFW0WUedSaFJ3khB50BWYGsw==",
+ "path": "microsoft.extensions.logging.configuration/5.0.0",
+ "hashPath": "microsoft.extensions.logging.configuration.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.Console/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-jH0wbWhfvXjOVmCkbra4vbiovDtTUIWLQjCeJ7Xun3h4AHvwfzm7V7wlsXKs3tNnPrsCxZ9oaV0vUAgGY1JxOA==",
+ "path": "microsoft.extensions.logging.console/5.0.0",
+ "hashPath": "microsoft.extensions.logging.console.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.Debug/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-9dvt0xqRrClvhaPNpfyS39WxnW9G55l5lrV5ZX7IrEgwo4VwtmJKtoPiKVYKbhAuOBGUI5WY3hWLvF+PSbJp5A==",
+ "path": "microsoft.extensions.logging.debug/5.0.0",
+ "hashPath": "microsoft.extensions.logging.debug.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.EventLog/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-CYzsgF2lqgahGl/HuErsIDaZZ9ueN+MBjGfO/0jVDLPaXLaywxlGKFpDgXMaB053DRYZwD1H2Lb1I60mTXS3jg==",
+ "path": "microsoft.extensions.logging.eventlog/5.0.0",
+ "hashPath": "microsoft.extensions.logging.eventlog.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Logging.EventSource/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-hF+D6PJkrM0qXcSEGs1BwZwgP8c0BRkj26P/5wmYTcHKOp52GRey/Z/YKRmRIHIrXxj9tz/JgIjU9oWmiJ5HMw==",
+ "path": "microsoft.extensions.logging.eventsource/5.0.0",
+ "hashPath": "microsoft.extensions.logging.eventsource.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Options/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==",
+ "path": "microsoft.extensions.options/5.0.0",
+ "hashPath": "microsoft.extensions.options.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Options.ConfigurationExtensions/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-280RxNJqOeQqq47aJLy5D9LN61CAWeuRA83gPToQ8B9jl9SNdQ5EXjlfvF66zQI5AXMl+C/3hGnbtIEN+X3mqA==",
+ "path": "microsoft.extensions.options.configurationextensions/5.0.0",
+ "hashPath": "microsoft.extensions.options.configurationextensions.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.Primitives/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-cI/VWn9G1fghXrNDagX9nYaaB/nokkZn0HYAawGaELQrl8InSezfe9OnfPZLcJq3esXxygh3hkq2c3qoV3SDyQ==",
+ "path": "microsoft.extensions.primitives/5.0.0",
+ "hashPath": "microsoft.extensions.primitives.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Platforms/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==",
+ "path": "microsoft.netcore.platforms/5.0.0",
+ "hashPath": "microsoft.netcore.platforms.5.0.0.nupkg.sha512"
+ },
+ "Microsoft.NETCore.Targets/1.0.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rkn+fKobF/cbWfnnfBOQHKVKIOpxMZBvlSHkqDWgBpwGDcLRduvs3D9OLGeV6GWGvVwNlVi2CBbTjuPmtHvyNw==",
+ "path": "microsoft.netcore.targets/1.0.1",
+ "hashPath": "microsoft.netcore.targets.1.0.1.nupkg.sha512"
+ },
+ "Microsoft.Win32.Registry/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+ "path": "microsoft.win32.registry/5.0.0",
+ "hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
+ },
+ "MQTTnet/3.0.16": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-xzqmKwVjTpT/X5zoLuhBCHmYVSRAlxeXqExvHLqfbt5OfTHfrD/q62IteJbOtlNG/SepeRpm76dYUZSU5o8y1A==",
+ "path": "mqttnet/3.0.16",
+ "hashPath": "mqttnet.3.0.16.nupkg.sha512"
+ },
+ "MQTTnet.Extensions.ManagedClient/3.0.16": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-rjtHYtYf+1JNLksZrKp2dvgT5pYe5+XTAoF8koRlK3s+kkUFykSecSleH7QCTaaPWCy8N7xoTSXS1v94pYGzhw==",
+ "path": "mqttnet.extensions.managedclient/3.0.16",
+ "hashPath": "mqttnet.extensions.managedclient.3.0.16.nupkg.sha512"
+ },
+ "Newtonsoft.Json/12.0.3": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==",
+ "path": "newtonsoft.json/12.0.3",
+ "hashPath": "newtonsoft.json.12.0.3.nupkg.sha512"
+ },
+ "NvAPIWrapper.Net/0.8.1.101": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-qX+5EQ32tCTqBkX/9BNfx7w4HPi18s3XhMubjbxSjPhYQ6pXWL+HGnaZEmGvFlOxvsQN+/6eV3x5bRo1CDY/mQ==",
+ "path": "nvapiwrapper.net/0.8.1.101",
+ "hashPath": "nvapiwrapper.net.0.8.1.101.nupkg.sha512"
+ },
+ "runtime.linux-arm.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Np6w3r1dSFB930GGZHIKCc5ZClRXZIqOrCAT0pzcd/zXnsZPvGqLZB1MnxAbVhvriJl71B0N0tJaaT1ICWXsyg==",
+ "path": "runtime.linux-arm.runtime.native.system.io.ports/5.0.0-rtm.20519.4",
+ "hashPath": "runtime.linux-arm.runtime.native.system.io.ports.5.0.0-rtm.20519.4.nupkg.sha512"
+ },
+ "runtime.linux-arm64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-VnGZmQ7pzMNkcTVdmGtXUQIbytK4Xk8F4/mxm0I+n7zbrsW/WNgLrWMTv9pb2Uyq09azXazNDQhZao4R4ebWcw==",
+ "path": "runtime.linux-arm64.runtime.native.system.io.ports/5.0.0-rtm.20519.4",
+ "hashPath": "runtime.linux-arm64.runtime.native.system.io.ports.5.0.0-rtm.20519.4.nupkg.sha512"
+ },
+ "runtime.linux-x64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-kvMZgZjtcC6cA8Y8imKpjCpiOJKDtwlNekS86GzUol4Jmzh0FWiRwAj4E9ZKO8R7rTBGIA4rkmra9Ko8j7l6AA==",
+ "path": "runtime.linux-x64.runtime.native.system.io.ports/5.0.0-rtm.20519.4",
+ "hashPath": "runtime.linux-x64.runtime.native.system.io.ports.5.0.0-rtm.20519.4.nupkg.sha512"
+ },
+ "runtime.native.System.IO.Ports/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-ME+/evR+UxVlWyGHUlLBoNTnsTdaylMbnvVwOp0Nl6XIZGGyXdqJqjlEew7e6TcKkJAA0lljhjKi3Kie+vzQ7g==",
+ "path": "runtime.native.system.io.ports/5.0.0",
+ "hashPath": "runtime.native.system.io.ports.5.0.0.nupkg.sha512"
+ },
+ "runtime.osx-x64.runtime.native.System.IO.Ports/5.0.0-rtm.20519.4": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-N+dbbqhT7JBnPVHa7n2+Z5fHYO4a4UUhm7cQkbuQQoNkjbxLpxYnQ4lpRjr1RuQptqYkPmunKvN5etdFOObaiw==",
+ "path": "runtime.osx-x64.runtime.native.system.io.ports/5.0.0-rtm.20519.4",
+ "hashPath": "runtime.osx-x64.runtime.native.system.io.ports.5.0.0-rtm.20519.4.nupkg.sha512"
+ },
+ "Serilog/2.10.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
+ "path": "serilog/2.10.0",
+ "hashPath": "serilog.2.10.0.nupkg.sha512"
+ },
+ "Serilog.Extensions.Logging/2.0.2": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-PYAkzUn/VV16Es7U06BfEhNZEltnYWu0WFCM4d2lLY/dvlA7xMwFXBuGRxR0XvEBPoOxPorjhFLy9txwiMO6rg==",
+ "path": "serilog.extensions.logging/2.0.2",
+ "hashPath": "serilog.extensions.logging.2.0.2.nupkg.sha512"
+ },
+ "Serilog.Extensions.Logging.File/2.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-usO0qr4v9VCMBWiTJ1nQmAbPNCt40FrkDol6CpfCXbsxGZS/hH+YCueF7vvPQ32ATI0GWcMWiKRdjXEE7/HxTQ==",
+ "path": "serilog.extensions.logging.file/2.0.0",
+ "hashPath": "serilog.extensions.logging.file.2.0.0.nupkg.sha512"
+ },
+ "Serilog.Formatting.Compact/1.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-r3QYz02y7+B7Ng30hyJM929OJhem7SsJ4XDUE0Zfptj2MRiQfpPUb5f58juAFjp/TnNeSX2QNzZEnHwLeoJfHQ==",
+ "path": "serilog.formatting.compact/1.0.0",
+ "hashPath": "serilog.formatting.compact.1.0.0.nupkg.sha512"
+ },
+ "Serilog.Sinks.Async/1.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-xll0Kanz2BkCxuv+F3p1WXr47jdsVM0GU1n1LZvK+18QiRZ/WGFNxSNw9EMKFV5ED5gr7MUpAe6PCMNL1HGUMA==",
+ "path": "serilog.sinks.async/1.1.0",
+ "hashPath": "serilog.sinks.async.1.1.0.nupkg.sha512"
+ },
+ "Serilog.Sinks.Console/4.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==",
+ "path": "serilog.sinks.console/4.0.0",
+ "hashPath": "serilog.sinks.console.4.0.0.nupkg.sha512"
+ },
+ "Serilog.Sinks.File/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
+ "path": "serilog.sinks.file/5.0.0",
+ "hashPath": "serilog.sinks.file.5.0.0.nupkg.sha512"
+ },
+ "Serilog.Sinks.RollingFile/3.3.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-2lT5X1r3GH4P0bRWJfhA7etGl8Q2Ipw9AACvtAHWRUSpYZ42NGVyHoVs2ALBZ/cAkkS+tA4jl80Zie144eLQPg==",
+ "path": "serilog.sinks.rollingfile/3.3.0",
+ "hashPath": "serilog.sinks.rollingfile.3.3.0.nupkg.sha512"
+ },
+ "System.CodeDom/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==",
+ "path": "system.codedom/5.0.0",
+ "hashPath": "system.codedom.5.0.0.nupkg.sha512"
+ },
+ "System.Collections/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==",
+ "path": "system.collections/4.0.11",
+ "hashPath": "system.collections.4.0.11.nupkg.sha512"
+ },
+ "System.Collections.Concurrent/4.0.12": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==",
+ "path": "system.collections.concurrent/4.0.12",
+ "hashPath": "system.collections.concurrent.4.0.12.nupkg.sha512"
+ },
+ "System.Diagnostics.Debug/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==",
+ "path": "system.diagnostics.debug/4.0.11",
+ "hashPath": "system.diagnostics.debug.4.0.11.nupkg.sha512"
+ },
+ "System.Diagnostics.EventLog/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-FHkCwUfsTs+/5tsK+c0egLfacUgbhvcwi3wUFWSEEArSXao343mYqcpOVVFMlcCkdNtjU4YwAWaKYwal6f02og==",
+ "path": "system.diagnostics.eventlog/5.0.0",
+ "hashPath": "system.diagnostics.eventlog.5.0.0.nupkg.sha512"
+ },
+ "System.Diagnostics.Tracing/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-vDN1PoMZCkkdNjvZLql592oYJZgS7URcJzJ7bxeBgGtx5UtR5leNm49VmfHGqIffX4FKacHbI3H6UyNSHQknBg==",
+ "path": "system.diagnostics.tracing/4.1.0",
+ "hashPath": "system.diagnostics.tracing.4.1.0.nupkg.sha512"
+ },
+ "System.Globalization/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==",
+ "path": "system.globalization/4.0.11",
+ "hashPath": "system.globalization.4.0.11.nupkg.sha512"
+ },
+ "System.IO/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==",
+ "path": "system.io/4.1.0",
+ "hashPath": "system.io.4.1.0.nupkg.sha512"
+ },
+ "System.IO.FileSystem.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==",
+ "path": "system.io.filesystem.accesscontrol/5.0.0",
+ "hashPath": "system.io.filesystem.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.IO.FileSystem.Primitives/4.0.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-kWkKD203JJKxJeE74p8aF8y4Qc9r9WQx4C0cHzHPrY3fv/L/IhWnyCHaFJ3H1QPOH6A93whlQ2vG5nHlBDvzWQ==",
+ "path": "system.io.filesystem.primitives/4.0.1",
+ "hashPath": "system.io.filesystem.primitives.4.0.1.nupkg.sha512"
+ },
+ "System.IO.Pipes.AccessControl/4.5.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-Z0/uF+PqmiMoqgTA0AQa9JSlNjsxCoQNfRzQGtK4dLfNQ3nsOqPVBgZ7F49U5Lb4yqGJSWJ79AdYaNjdJoZqhQ==",
+ "path": "system.io.pipes.accesscontrol/4.5.1",
+ "hashPath": "system.io.pipes.accesscontrol.4.5.1.nupkg.sha512"
+ },
+ "System.IO.Ports/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MZY/0cgRg5bcuvHR4LKHqWnlxWV7GkoTgBaOdwIoWGZKsfSBC1twDz+BzG0o1Rk46WdRhhV30E2qzsBABHwGUA==",
+ "path": "system.io.ports/5.0.0",
+ "hashPath": "system.io.ports.5.0.0.nupkg.sha512"
+ },
+ "System.Management/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
+ "path": "system.management/5.0.0",
+ "hashPath": "system.management.5.0.0.nupkg.sha512"
+ },
+ "System.Reflection/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==",
+ "path": "system.reflection/4.1.0",
+ "hashPath": "system.reflection.4.1.0.nupkg.sha512"
+ },
+ "System.Reflection.Primitives/4.0.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==",
+ "path": "system.reflection.primitives/4.0.1",
+ "hashPath": "system.reflection.primitives.4.0.1.nupkg.sha512"
+ },
+ "System.Resources.ResourceManager/4.0.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==",
+ "path": "system.resources.resourcemanager/4.0.1",
+ "hashPath": "system.resources.resourcemanager.4.0.1.nupkg.sha512"
+ },
+ "System.Runtime/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-v6c/4Yaa9uWsq+JMhnOFewrYkgdNHNG2eMKuNqRn8P733rNXeRCGvV5FkkjBXn2dbVkPXOsO0xjsEeM1q2zC0g==",
+ "path": "system.runtime/4.1.0",
+ "hashPath": "system.runtime.4.1.0.nupkg.sha512"
+ },
+ "System.Runtime.Extensions/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==",
+ "path": "system.runtime.extensions/4.1.0",
+ "hashPath": "system.runtime.extensions.4.1.0.nupkg.sha512"
+ },
+ "System.Runtime.Handles/4.0.1": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==",
+ "path": "system.runtime.handles/4.0.1",
+ "hashPath": "system.runtime.handles.4.0.1.nupkg.sha512"
+ },
+ "System.Runtime.InteropServices/4.1.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==",
+ "path": "system.runtime.interopservices/4.1.0",
+ "hashPath": "system.runtime.interopservices.4.1.0.nupkg.sha512"
+ },
+ "System.Security.AccessControl/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+ "path": "system.security.accesscontrol/5.0.0",
+ "hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
+ },
+ "System.Security.Principal.Windows/5.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
+ "path": "system.security.principal.windows/5.0.0",
+ "hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
+ },
+ "System.Text.Encoding/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==",
+ "path": "system.text.encoding/4.0.11",
+ "hashPath": "system.text.encoding.4.0.11.nupkg.sha512"
+ },
+ "System.Text.Encoding.Extensions/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-jtbiTDtvfLYgXn8PTfWI+SiBs51rrmO4AAckx4KR6vFK9Wzf6tI8kcRdsYQNwriUeQ1+CtQbM1W4cMbLXnj/OQ==",
+ "path": "system.text.encoding.extensions/4.0.11",
+ "hashPath": "system.text.encoding.extensions.4.0.11.nupkg.sha512"
+ },
+ "System.Threading/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
+ "path": "system.threading/4.0.11",
+ "hashPath": "system.threading.4.0.11.nupkg.sha512"
+ },
+ "System.Threading.Tasks/4.0.11": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==",
+ "path": "system.threading.tasks/4.0.11",
+ "hashPath": "system.threading.tasks.4.0.11.nupkg.sha512"
+ },
+ "CoreAudio/2020.11.2.36": {
+ "type": "reference",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/hass-workstation-service/hass-workstation-service.exe b/hass-workstation-service/hass-workstation-service.exe
index 3b26170..8c8bd7b 100644
Binary files a/hass-workstation-service/hass-workstation-service.exe and b/hass-workstation-service/hass-workstation-service.exe differ
diff --git a/hass-workstation-service/hass-workstation-service.runtimeconfig.json b/hass-workstation-service/hass-workstation-service.runtimeconfig.json
new file mode 100644
index 0000000..a8e7e82
--- /dev/null
+++ b/hass-workstation-service/hass-workstation-service.runtimeconfig.json
@@ -0,0 +1,9 @@
+{
+ "runtimeOptions": {
+ "tfm": "net5.0",
+ "framework": {
+ "name": "Microsoft.NETCore.App",
+ "version": "5.0.0"
+ }
+ }
+}
\ No newline at end of file