Merge branch 'master' into develop. This fixes #17

pull/90/head
Sleevezipper 4 years ago
commit 59c9ea53c5

@ -5,7 +5,6 @@
<StartupObject>UserInterface.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>

@ -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); }
}
}
}

@ -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); }
}
}
}

@ -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<CommandViewModel> configuredCommands;
private ICollection<CommandViewModel> _configuredCommands;
public ICollection<CommandViewModel> ConfiguredCommands
{
get => _configuredCommands;
set => this.RaiseAndSetIfChanged(ref _configuredCommands, value);
}
public ICollection<CommandViewModel> ConfiguredCommands { get => configuredCommands; set => this.RaiseAndSetIfChanged(ref configuredCommands, value); }
public void TriggerUpdate()
{
this.RaisePropertyChanged();
@ -19,7 +24,7 @@ namespace UserInterface.ViewModels
public class CommandViewModel : ViewModelBase
{
public Guid Id { get; set; }
public string Type { get; set; }
public AvailableCommands Type { get; set; }
public string Name { get; set; }
}
}
}

@ -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<SensorViewModel> configuredSensors;
private ICollection<SensorViewModel> _configuredSensors;
public ICollection<SensorViewModel> ConfiguredSensors
{
get => _configuredSensors;
set => this.RaiseAndSetIfChanged(ref _configuredSensors, value);
}
public ICollection<SensorViewModel> ConfiguredSensors { get => configuredSensors; set => this.RaiseAndSetIfChanged(ref configuredSensors, value); }
public void TriggerUpdate()
{
this.RaisePropertyChanged();
@ -21,30 +26,21 @@ namespace UserInterface.ViewModels
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}";
}
}
}

@ -5,24 +5,26 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UserInterface.Views.AddCommandDialog"
SizeToContent="WidthAndHeight"
Title="Add command">
<StackPanel Margin="40" MinWidth="200">
<ContentControl Margin="0 20 0 10">Command type</ContentControl>
Title="Add / edit command">
<StackPanel Margin="40" MinWidth="200">
<ContentControl Margin="0 20 0 10">Command type</ContentControl>
<ComboBox x:Name="ComboBox" SelectionChanged="ComboBoxClosed" SelectedItem="{Binding SelectedType}" MinHeight="27"></ComboBox>
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock>
<Button IsVisible="{Binding MoreInfoLink, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Click="OpenInfo" Margin="0 10 0 10">Click for more information.</Button>
<ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" MinWidth="150"/>
<ComboBox x:Name="ComboBox" SelectionChanged="ComboBoxClosed" SelectedItem="{Binding SelectedType}" MinHeight="27"></ComboBox>
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock>
<Button IsVisible="{Binding MoreInfoLink, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Click="OpenInfo" Margin="0 10 0 10">Click for more information.</Button>
<ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" MinWidth="150"/>
<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowCommandInput}" Margin="0 20 0 10">Command</ContentControl>
<TextBox IsVisible="{Binding ShowCommandInput}" Text="{Binding Command}" Watermark="Rundll32.exe user32.dll,LockWorkStation" HorizontalAlignment="Left" MinWidth="300"/>
<ContentControl IsVisible="{Binding ShowKeyInput}" Margin="0 20 0 10">Key</ContentControl>
<TextBox IsVisible="{Binding ShowKeyInput}" Text="{Binding Key}" Watermark="0xAD" HorizontalAlignment="Left" MinWidth="300"/>
<Button IsVisible="{Binding ShowCommandInput}" Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Test">Test</Button>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button>
</StackPanel>
<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowCommandInput}" Margin="0 20 0 10">Command</ContentControl>
<TextBox IsVisible="{Binding ShowCommandInput}" Text="{Binding Command}" Watermark="Rundll32.exe user32.dll,LockWorkStation" HorizontalAlignment="Left" MinWidth="300"/>
<ContentControl IsVisible="{Binding ShowKeyInput}" Margin="0 20 0 10">Key</ContentControl>
<TextBox IsVisible="{Binding ShowKeyInput}" Text="{Binding Key}" Watermark="0xAD" HorizontalAlignment="Left" MinWidth="300"/>
<Grid>
<Button IsVisible="{Binding ShowCommandInput}" Width="75" HorizontalAlignment="Left" Margin="0 40 0 10" Click="Test">Test</Button>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button>
</Grid>
</StackPanel>
</Window>

@ -1,156 +1,193 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Dynamic;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
using UserInterface.ViewModels;
namespace UserInterface.Views
{
public class AddCommandDialog : Window
{
private readonly IIpcClient<ServiceContractInterfaces> client;
public ComboBox comboBox { get; set; }
public ComboBox detectionModecomboBox { get; set; }
public AddCommandDialog()
{
this.InitializeComponent();
DataContext = new AddCommandViewModel();
this.comboBox = this.FindControl<ComboBox>("ComboBox");
this.comboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast<AvailableCommands>().OrderBy(v => v.ToString());
this.comboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("addCommand", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
// create client
this.client = clientFactory.CreateClient("addCommand");
}
public async void Save(object sender, RoutedEventArgs args)
{
var item = ((AddCommandViewModel)this.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));
Close();
}
public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
{
var item = ((AddCommandViewModel)this.DataContext);
switch (this.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.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.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.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.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.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.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.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.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.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.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.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
default:
item.Description = null;
item.MoreInfoLink = null;
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
}
}
public void OpenInfo(object sender, RoutedEventArgs args)
{
var item = ((AddCommandViewModel)this.DataContext);
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
public void Test(object sender, RoutedEventArgs args)
{
var item = ((AddCommandViewModel)this.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}";
process.StartInfo = startInfo;
process.Start();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
using UserInterface.ViewModels;
namespace UserInterface.Views
{
public class AddCommandDialog : Window
{
private readonly IIpcClient<IServiceContractInterfaces> _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()
{
InitializeComponent();
DataContext = new AddCommandViewModel();
ComboBox = this.FindControl<ComboBox>("ComboBox");
ComboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast<AvailableCommands>().OrderBy(v => v.ToString());
ComboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<IServiceContractInterfaces>("addCommand", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
_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)DataContext;
dynamic model = new { item.Name, item.Command, item.Key };
string json = JsonSerializer.Serialize(model);
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)
{
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.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.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.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.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.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.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.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.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.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.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.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
default:
item.Description = null;
item.MoreInfoLink = null;
item.ShowCommandInput = false;
item.ShowKeyInput = false;
break;
}
}
public void OpenInfo(object sender, RoutedEventArgs args)
{
var item = (AddCommandViewModel)DataContext;
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
public void Test(object sender, RoutedEventArgs args)
{
var item = (AddCommandViewModel)DataContext;
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();
}
}
}

@ -5,27 +5,27 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UserInterface.Views.AddSensorDialog"
SizeToContent="WidthAndHeight"
Title="Add sensor">
<StackPanel Margin="40" MinWidth="200">
<ContentControl Margin="0 20 0 10">Sensor type</ContentControl>
Title="Add / edit sensor">
<StackPanel Margin="40" MinWidth="200">
<ContentControl Margin="0 20 0 10">Sensor type</ContentControl>
<ComboBox x:Name="ComboBox" SelectionChanged="ComboBoxClosed" SelectedItem="{Binding SelectedType}" MinHeight="27"></ComboBox>
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock>
<Button IsVisible="{Binding MoreInfoLink, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Click="OpenInfo" Margin="0 10 0 10">Click for more information.</Button>
<ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" MinWidth="150"/>
<ComboBox x:Name="ComboBox" SelectionChanged="ComboBoxClosed" SelectedItem="{Binding SelectedType}" MinHeight="27"></ComboBox>
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock>
<Button IsVisible="{Binding MoreInfoLink, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Click="OpenInfo" Margin="0 10 0 10">Click for more information.</Button>
<ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl Margin="0 20 0 10">Update interval</ContentControl>
<StackPanel Orientation="Horizontal">
<Slider Value="{Binding UpdateInterval}" Minimum="1" Maximum="300" HorizontalAlignment="Left" Width="150"/>
<TextBox Text="{Binding UpdateInterval}" HorizontalAlignment="Right" MaxWidth="30"/>
</StackPanel>
<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowQueryInput}" Margin="0 20 0 10">Query</ContentControl>
<TextBox IsVisible="{Binding ShowQueryInput}" Text="{Binding Query}" Watermark="SELECT Name FROM Win32_Processor" HorizontalAlignment="Left" MinWidth="300"/>
<ContentControl IsVisible="{Binding ShowWindowNameInput}" Margin="0 20 0 5">Window name</ContentControl>
<TextBlock TextWrapping="Wrap" MaxWidth="300" FontStyle="Italic" IsVisible="{Binding ShowWindowNameInput}" Margin="0 0 0 10">This is case-insensitive and loosely matched. A window called "Spotify Premium" will match "spotify" or "premium".</TextBlock>
<TextBox IsVisible="{Binding ShowWindowNameInput}" Text="{Binding WindowName}" Watermark="Visual Studio Code" HorizontalAlignment="Left" MinWidth="300"/>
<ContentControl Margin="0 20 0 10">Update interval</ContentControl>
<StackPanel Orientation="Horizontal">
<Slider Value="{Binding UpdateInterval}" Minimum="1" Maximum="300" HorizontalAlignment="Left" Width="150"/>
<TextBox Text="{Binding UpdateInterval}" HorizontalAlignment="Right" MaxWidth="30"/>
</StackPanel>
<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowQueryInput}" Margin="0 20 0 10">Query</ContentControl>
<TextBox IsVisible="{Binding ShowQueryInput}" Text="{Binding Query}" Watermark="SELECT Name FROM Win32_Processor" HorizontalAlignment="Left" MinWidth="300"/>
<ContentControl IsVisible="{Binding ShowWindowNameInput}" Margin="0 20 0 5">Window name</ContentControl>
<TextBlock TextWrapping="Wrap" MaxWidth="300" FontStyle="Italic" IsVisible="{Binding ShowWindowNameInput}" Margin="0 0 0 10">This is case-insensitive and loosely matched. A window called "Spotify Premium" will match "spotify" or "premium".</TextBlock>
<TextBox IsVisible="{Binding ShowWindowNameInput}" Text="{Binding WindowName}" Watermark="Visual Studio Code" HorizontalAlignment="Left" MinWidth="300"/>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button>
</StackPanel>
</StackPanel>
</Window>

@ -1,195 +1,222 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Dynamic;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
using UserInterface.ViewModels;
namespace UserInterface.Views
{
public class AddSensorDialog : Window
{
private readonly IIpcClient<ServiceContractInterfaces> client;
public ComboBox comboBox { get; set; }
public ComboBox detectionModecomboBox { get; set; }
public AddSensorDialog()
{
this.InitializeComponent();
DataContext = new AddSensorViewModel();
this.comboBox = this.FindControl<ComboBox>("ComboBox");
this.comboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast<AvailableSensors>().OrderBy(v => v.ToString());
this.comboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("addsensor", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
// create client
this.client = clientFactory.CreateClient("addsensor");
}
public async void Save(object sender, RoutedEventArgs args)
{
var item = ((AddSensorViewModel)this.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));
Close();
}
public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
{
var item = ((AddSensorViewModel)this.DataContext);
switch (this.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
default:
item.Description = null;
item.MoreInfoLink = null;
item.ShowQueryInput = false;
break;
}
}
public void OpenInfo(object sender, RoutedEventArgs args)
{
var item = ((AddSensorViewModel)this.DataContext);
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
using JKang.IpcServiceFramework.Client;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Text.Json;
using UserInterface.Util;
using UserInterface.ViewModels;
namespace UserInterface.Views
{
public class AddSensorDialog : Window
{
private readonly IIpcClient<IServiceContractInterfaces> _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()
{
InitializeComponent();
DataContext = new AddSensorViewModel();
ComboBox = this.FindControl<ComboBox>("ComboBox");
ComboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast<AvailableSensors>().OrderBy(v => v.ToString());
ComboBox.SelectedIndex = 0;
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<IServiceContractInterfaces>("addsensor", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
_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)DataContext;
dynamic model = new { item.Name, item.Query, item.UpdateInterval, item.WindowName };
string json = JsonSerializer.Serialize(model);
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)
{
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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5;
break;
default:
item.Description = null;
item.MoreInfoLink = null;
item.ShowQueryInput = false;
break;
}
}
public void OpenInfo(object sender, RoutedEventArgs args)
{
var item = (AddSensorViewModel)DataContext;
BrowserUtil.OpenBrowser(item.MoreInfoLink);
}
}
}

@ -16,19 +16,19 @@ namespace UserInterface.Views
{
public class AppInfo : UserControl
{
private readonly IIpcClient<ServiceContractInterfaces> client;
private readonly IIpcClient<IServiceContractInterfaces> client;
public AppInfo()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("info", pipeName: "pipeinternal")
.AddNamedPipeIpcClient<IServiceContractInterfaces>("info", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
this.client = clientFactory.CreateClient("info");

@ -15,19 +15,19 @@ namespace UserInterface.Views
{
public class BackgroundServiceSettings : UserControl
{
private readonly IIpcClient<ServiceContractInterfaces> _client;
private readonly IIpcClient<IServiceContractInterfaces> _client;
public BackgroundServiceSettings()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("broker", pipeName: "pipeinternal")
.AddNamedPipeIpcClient<IServiceContractInterfaces>("broker", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
this._client = clientFactory.CreateClient("broker");

@ -17,19 +17,19 @@ namespace UserInterface.Views
{
public class BrokerSettings : UserControl
{
private readonly IIpcClient<ServiceContractInterfaces> client;
private readonly IIpcClient<IServiceContractInterfaces> client;
public BrokerSettings()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("broker", pipeName: "pipeinternal")
.AddNamedPipeIpcClient<IServiceContractInterfaces>("broker", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
this.client = clientFactory.CreateClient("broker");

@ -22,7 +22,7 @@
<TextBlock Grid.Row="2" IsVisible="{Binding !ConfiguredCommands.Count}" Text="Add some commands by clicking the 'Add' button."/>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width="75" Margin="10 10 0 0" Click="AddCommand" Content="Add"/>
<Button Width="75" Margin="10 10 0 0" Click="EditCommand" Content="Edit" IsVisible="False"/>
<Button Width="75" Margin="10 10 0 0" Click="EditCommand" Content="Edit"/>
<Button Width="75" Margin="10 10 0 0" Click="DeleteCommand" Content="Delete"/>
</StackPanel>
</Grid>

@ -1,97 +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<ServiceContractInterfaces> _client;
private readonly DataGrid _dataGrid;
private bool _sensorsNeedToRefresh;
public CommandSettings()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("commands", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
// create client
this._client = clientFactory.CreateClient("commands");
DataContext = new CommandSettingsViewModel();
GetConfiguredCommands();
this._dataGrid = this.FindControl<DataGrid>("Grid");
}
public async void GetConfiguredCommands()
{
_sensorsNeedToRefresh = false;
List<ConfiguredCommandModel> 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 async void AddCommand(object sender, RoutedEventArgs args)
{
var dialog = new AddCommandDialog();
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
await dialog.ShowDialog(desktop.MainWindow);
_sensorsNeedToRefresh = true;
GetConfiguredCommands();
}
}
public void EditCommand(object sender, RoutedEventArgs args)
{
}
public void DeleteCommand(object sender, RoutedEventArgs args)
{
if (_dataGrid.SelectedItem is not CommandViewModel item)
return;
this._client.InvokeAsync(x => x.RemoveCommandById(item.Id));
if (DataContext is not CommandSettingsViewModel viewModel)
return;
viewModel.ConfiguredCommands.Remove(item);
_dataGrid.SelectedIndex = -1;
viewModel.TriggerUpdate();
}
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<IServiceContractInterfaces> _client;
private readonly DataGrid _dataGrid;
private bool _commandsNeedToRefresh;
public CommandSettings()
{
InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<IServiceContractInterfaces>("commands", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
_client = clientFactory.CreateClient("commands");
_dataGrid = this.FindControl<DataGrid>("Grid");
DataContext = new CommandSettingsViewModel();
GetConfiguredCommands();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public async void GetConfiguredCommands()
{
List<ConfiguredCommandModel> 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();
}
}
}

@ -28,7 +28,7 @@
<TextBlock Grid.Row="2" IsVisible="{Binding !ConfiguredSensors.Count}" Text="Add some sensors by clicking the 'Add' button."/>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Width="75" Margin="10 10 0 0" Click="AddSensor" Content="Add"/>
<Button Width="75" Margin="10 10 0 0" Click="EditSensor" Content="Edit" IsVisible="False"/>
<Button Width="75" Margin="10 10 0 0" Click="EditSensor" Content="Edit"/>
<Button Width="75" Margin="10 10 0 0" Click="DeleteSensor" Content="Delete"/>
</StackPanel>
</Grid>

@ -8,7 +8,6 @@ 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;
@ -18,38 +17,41 @@ namespace UserInterface.Views
{
public class SensorSettings : UserControl
{
private readonly IIpcClient<ServiceContractInterfaces> _client;
private readonly IIpcClient<IServiceContractInterfaces> _client;
private readonly DataGrid _dataGrid;
private bool _sensorsNeedToRefresh;
public SensorSettings()
{
this.InitializeComponent();
InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("sensors", pipeName: "pipeinternal")
.AddNamedPipeIpcClient<IServiceContractInterfaces>("sensors", pipeName: "pipeinternal")
.BuildServiceProvider();
// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();
IIpcClientFactory<IServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<IServiceContractInterfaces>>();
// create client
this._client = clientFactory.CreateClient("sensors");
_client = clientFactory.CreateClient("sensors");
_dataGrid = this.FindControl<DataGrid>("Grid");
DataContext = new SensorSettingsViewModel();
GetConfiguredSensors();
}
this._dataGrid = this.FindControl<DataGrid>("Grid");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public async void GetConfiguredSensors()
{
_sensorsNeedToRefresh = false;
List<ConfiguredSensorModel> status = await this._client.InvokeAsync(x => x.GetConfiguredSensors());
List<ConfiguredSensorModel> status = await _client.InvokeAsync(x => x.GetConfiguredSensors());
((SensorSettingsViewModel)this.DataContext).ConfiguredSensors = status.Select(s =>
((SensorSettingsViewModel)DataContext).ConfiguredSensors = status.Select(s =>
new SensorViewModel()
{
Name = s.Name,
@ -63,8 +65,8 @@ namespace UserInterface.Views
while (!_sensorsNeedToRefresh)
{
await Task.Delay(1000);
List<ConfiguredSensorModel> statusUpdated = await this._client.InvokeAsync(x => x.GetConfiguredSensors());
var configuredSensors = ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors;
List<ConfiguredSensorModel> 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)
{
@ -95,9 +97,18 @@ namespace UserInterface.Views
}
}
public void EditSensor(object sender, RoutedEventArgs args)
public async void EditSensor(object sender, RoutedEventArgs args)
{
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)
@ -105,7 +116,7 @@ namespace UserInterface.Views
if (_dataGrid.SelectedItem is not SensorViewModel item)
return;
this._client.InvokeAsync(x => x.RemoveSensorById(item.Id));
_client.InvokeAsync(x => x.RemoveSensorById(item.Id));
if (DataContext is not SensorSettingsViewModel viewModel)
return;
@ -114,10 +125,5 @@ namespace UserInterface.Views
_dataGrid.SelectedIndex = -1;
viewModel.TriggerUpdate();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

@ -1,12 +1,11 @@
using hass_workstation_service.Communication.InterProcesCommunication.Models;
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<MqttSettings> GetMqttBrokerSettings();
public string Ping(string str);
@ -14,12 +13,16 @@ namespace hass_workstation_service.Communication.NamedPipe
MqqtClientStatus GetMqqtClientStatus();
void EnableAutostart(bool enable);
bool IsAutoStartEnabled();
Task<ConfiguredSensorModel> GetConfiguredSensor(Guid id);
Task<List<ConfiguredSensorModel>> 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<ConfiguredCommandModel> GetConfiguredCommands();
void AddCommand(AvailableCommands commandType, string json);
void RemoveCommandById(Guid id);
void UpdateCommandById(Guid id, string json);
string GetCurrentVersion();
}
}
}

@ -7,15 +7,13 @@ using hass_workstation_service.Domain.Sensors;
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 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
_configurationService = configurationService;
}
public MqqtClientStatus GetMqqtClientStatus()
{
return this._publisher.GetStatus();
}
public MqqtClientStatus GetMqqtClientStatus() => _publisher.GetStatus();
public Task<MqttSettings> GetMqttBrokerSettings()
{
return this._configurationService.GetMqttBrokerSettings();
}
public Task<MqttSettings> GetMqttBrokerSettings() => _configurationService.GetMqttBrokerSettings();
/// <summary>
/// You can use this to check if the application responds.
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
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();
/// <summary>
/// This writes the provided settings to the config file.
/// </summary>
/// <param name="settings"></param>
public void WriteMqttBrokerSettingsAsync(MqttSettings settings)
{
this._configurationService.WriteMqttBrokerSettingsAsync(settings);
}
public void WriteMqttBrokerSettingsAsync(MqttSettings settings) => _configurationService.WriteMqttBrokerSettingsAsync(settings);
/// <summary>
/// Enables or disables autostart.
/// </summary>
/// <param name="enable"></param>
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<List<ConfiguredSensorModel>> 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<ConfiguredCommandModel> GetConfiguredCommands()
public async Task<ConfiguredSensorModel> 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<ConfiguredCommandModel> 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);
/// <summary>
/// Adds a command to the configured commands. This properly initializes the class and writes it to the config file.
/// </summary>
@ -100,71 +114,12 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
/// <param name="json"></param>
public void AddSensor(AvailableSensors sensorType, string json)
{
var serializerOptions = new JsonSerializerOptions
{
Converters = { new DynamicJsonConverter() }
};
dynamic model = JsonSerializer.Deserialize<dynamic>(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);
}
/// <summary>
@ -173,6 +128,38 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
/// <param name="commandType"></param>
/// <param name="json"></param>
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,51 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
};
dynamic model = JsonSerializer.Deserialize<dynamic>(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),
_ => 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<dynamic>(json, serializerOptions);
return commandType switch
{
AvailableCommands.ShutdownCommand => new ShutdownCommand(_publisher, model.Name),
AvailableCommands.RestartCommand => new RestartCommand(_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
};
}
}
}
}

@ -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 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
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,
@ -70,4 +118,4 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
VolumeDownCommand,
MuteCommand
}
}
}

@ -181,25 +181,25 @@ namespace hass_workstation_service.Data
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);
@ -340,54 +340,91 @@ namespace hass_workstation_service.Data
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<AbstractSensor> 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<AbstractCommand> 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();
}
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<AbstractSensor> 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);
}
/// <summary>

@ -13,13 +13,18 @@ namespace hass_workstation_service.Data
{
public interface IConfigurationService
{
ICollection<AbstractSensor> ConfiguredSensors { get; }
Action<IManagedMqttClientOptions> MqqtConfigChangedHandler { get; set; }
ICollection<AbstractSensor> ConfiguredSensors { get; }
ICollection<AbstractCommand> ConfiguredCommands { get; }
void AddConfiguredCommand(AbstractCommand command);
void AddConfiguredSensor(AbstractSensor sensor);
void AddConfiguredCommand(AbstractCommand command);
void AddConfiguredSensors(List<AbstractSensor> sensors);
void AddConfiguredCommands(List<AbstractCommand> commands);
void DeleteConfiguredSensor(Guid id);
void DeleteConfiguredCommand(Guid id);
void UpdateConfiguredSensor(Guid id, AbstractSensor sensor);
void UpdateConfiguredCommand(Guid id, AbstractCommand command);
Task<IManagedMqttClientOptions> GetMqttClientOptionsAsync();
void ReadSensorSettings(MqttPublisher publisher);
void WriteMqttBrokerSettingsAsync(MqttSettings settings);
@ -27,8 +32,6 @@ namespace hass_workstation_service.Data
Task<MqttSettings> GetMqttBrokerSettings();
void EnableAutoStart(bool enable);
bool IsAutoStartEnabled();
void DeleteConfiguredSensor(Guid id);
void DeleteConfiguredCommand(Guid id);
void WriteCommandSettingsAsync();
void ReadCommandSettings(MqttPublisher publisher);
Task<ICollection<AbstractSensor>> GetSensorsAfterLoadingAsync();

@ -12,16 +12,8 @@ namespace hass_workstation_service.Domain
{
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();
}
}
}

@ -5,72 +5,60 @@ using MQTTnet;
namespace hass_workstation_service.Domain.Commands
{
public abstract class AbstractCommand : AbstractDiscoverable
{
/// <summary>
/// The update interval in seconds. It checks state only if the interval has passed.
/// </summary>
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, Domain);
public async Task UnPublishAutoDiscoveryConfigAsync() => await Publisher.AnnounceAutoDiscoveryConfig(this, Domain, 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();
}

@ -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;
@ -54,7 +54,8 @@ namespace hass_workstation_service.Domain.Commands
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);
}
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -7,8 +7,8 @@ using System.Threading.Tasks;
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) { }
}
}

@ -7,8 +7,8 @@ using System.Threading.Tasks;
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) { }
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -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) { }
}
}

@ -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
{
/// <summary>
@ -17,61 +15,48 @@ namespace hass_workstation_service.Domain.Sensors
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;
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, Domain);
public async Task UnPublishAutoDiscoveryConfigAsync() => await Publisher.AnnounceAutoDiscoveryConfig(this, Domain, true);
protected SensorDiscoveryConfigModel _autoDiscoveryConfigModel;
protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
{
await this.Publisher.AnnounceAutoDiscoveryConfig(this, this.Domain, true);
_autoDiscoveryConfigModel = config;
return config;
}
}
}

@ -84,14 +84,14 @@ namespace hass_workstation_service
Sw_version = GetVersion()
};
services.AddSingleton(deviceConfig);
services.AddSingleton<ServiceContractInterfaces, InterProcessApi>();
services.AddSingleton<IServiceContractInterfaces, InterProcessApi>();
services.AddSingleton<IConfigurationService, ConfigurationService>();
services.AddSingleton<MqttPublisher>();
services.AddHostedService<Worker>();
}).ConfigureIpcHost(builder =>
{
// configure IPC endpoints
builder.AddNamedPipeEndpoint<ServiceContractInterfaces>(pipeName: "pipeinternal");
builder.AddNamedPipeEndpoint<IServiceContractInterfaces>(pipeName: "pipeinternal");
});
static internal string GetVersion()
{

Loading…
Cancel
Save