add more sensors, implement update interval, extend docs

pull/9/head
sleevezipper 4 years ago
parent de8c04f1cd
commit 1fee1cb1d1

@ -38,6 +38,42 @@ This sensor watches the UserNotificationState. This is normally used in applicat
|QuietTime|Introduced in Windows 7. The current user is in "quiet time", which is the first hour after a new user logs into his or her account for the first time. During this time, most notifications should not be sent or shown. This lets a user become accustomed to a new computer system without those distractions. Quiet time also occurs for each user after an operating system upgrade or clean installation.| |QuietTime|Introduced in Windows 7. The current user is in "quiet time", which is the first hour after a new user logs into his or her account for the first time. During this time, most notifications should not be sent or shown. This lets a user become accustomed to a new computer system without those distractions. Quiet time also occurs for each user after an operating system upgrade or clean installation.|
|RunningWindowsStoreApp|A Windows Store app is running.| |RunningWindowsStoreApp|A Windows Store app is running.|
### CPULoad
This sensor checks the current CPU load. It averages the load on all logical cores every second and rounds the output to two decimals.
### UsedMemory
This sensor calculates the percentage of used memory.
### CurrentClockSpeed
This sensor returns the BIOS configured baseclock for the processor.
### WMIQuery
This advanced sensor executes a user defined [WMI query](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-and-sql) and exposes the result. The query should return a single value.
For example:
```sql
SELECT * FROM Win32_Processor
```
returns
`|64|9|To Be Filled By O.E.M.|3|Intel64 Family 6 Model 94 Stepping 3|252|1|Win32_Processor|4008|12|64|Intel64 Family 6 Model 94 Stepping 3|CPU0|100|198|1024|8192|0|6|4|GenuineIntel|4008|Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz|4|4|8|To Be Filled By O.E.M.|False|BFEBFBFF000506E3|3|24067|CPU|False|To Be Filled By O.E.M.|U3E1|OK|3|Win32_ComputerSystem|GAME-PC-2016|8|1|False|False|`
This cannot not be used for this sensor. Instead try
```sql
SELECT CurrentClockSpeed FROM Win32_Processor
```
which results in `4008` for my PC.
You can use [WMI Explorer](https://github.com/vinaypamnani/wmie2/tree/v2.0.0.2) to find see what data is available.
### Dummy ### Dummy
This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it. This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it.

@ -10,10 +10,13 @@ namespace UserInterface.ViewModels
{ {
private AvailableSensors selectedType; private AvailableSensors selectedType;
private string description; private string description;
private bool showQueryInput;
public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); } public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); }
public bool ShowQueryInput { get => showQueryInput; set => this.RaiseAndSetIfChanged(ref showQueryInput, value); }
private string moreInfoLink; private string moreInfoLink;
private int updateInterval;
public string MoreInfoLink public string MoreInfoLink
{ {
@ -24,5 +27,7 @@ namespace UserInterface.ViewModels
public AvailableSensors SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); } public AvailableSensors SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
public string Name { get; set; } public string Name { get; set; }
public string Query { get; set; }
public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); }
} }
} }

@ -10,6 +10,10 @@ namespace UserInterface.ViewModels
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();
}
} }
public class SensorViewModel : ViewModelBase public class SensorViewModel : ViewModelBase
@ -19,6 +23,28 @@ namespace UserInterface.ViewModels
public Guid Id { get; set; } public Guid Id { get; set; }
public string Type { get; set; } public string Type { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Value { get => _value; set => this.RaiseAndSetIfChanged(ref _value, value); } public int UpdateInterval { get; set; }
public string Value
{
get => _value; set
{
this.RaiseAndSetIfChanged(ref _value, value);
this.RaisePropertyChanged("ValueString");
}
}
public string UnitOfMeasurement { get; set; }
public string ValueString
{
get
{
if (!string.IsNullOrWhiteSpace(_value))
{
return _value + UnitOfMeasurement;
}
else return "";
}
}
} }
} }

@ -13,7 +13,16 @@
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock> <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> <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> <ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" Width="150"/> <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"/>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button> <Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button>
</StackPanel> </StackPanel>
</Window> </Window>

@ -47,7 +47,7 @@ namespace UserInterface.Views
public async void Save(object sender, RoutedEventArgs args) public async void Save(object sender, RoutedEventArgs args)
{ {
var item = ((AddSensorViewModel)this.DataContext); var item = ((AddSensorViewModel)this.DataContext);
dynamic model = new { Name = item.Name }; dynamic model = new { Name = item.Name, Query = item.Query, UpdateInterval = item.UpdateInterval };
string json = JsonSerializer.Serialize(model); string json = JsonSerializer.Serialize(model);
await this.client.InvokeAsync(x => x.AddSensor(item.SelectedType, json)); await this.client.InvokeAsync(x => x.AddSensor(item.SelectedType, json));
Close(); Close();
@ -61,14 +61,43 @@ namespace UserInterface.Views
case AvailableSensors.UserNotificationStateSensor: 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.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.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usernotificationstate";
item.ShowQueryInput = false;
item.UpdateInterval = 5;
break; break;
case AvailableSensors.DummySensor: 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.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.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#dummy";
item.ShowQueryInput = 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.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.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.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#usedmemorysensor";
item.ShowQueryInput = false;
item.UpdateInterval = 10;
break; break;
default: default:
item.Description = null; item.Description = null;
item.MoreInfoLink = null; item.MoreInfoLink = null;
item.ShowQueryInput = false;
break; break;
} }
} }

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
MaxWidth="500" MaxWidth="800"
x:Class="UserInterface.Views.SensorSettings" > x:Class="UserInterface.Views.SensorSettings" >
<StackPanel Margin="30" HorizontalAlignment="Left" > <StackPanel Margin="30" HorizontalAlignment="Left" >
<DataGrid x:Name="Grid" IsVisible="{Binding ConfiguredSensors.Count}" AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Single" Items="{Binding ConfiguredSensors}"> <DataGrid x:Name="Grid" IsVisible="{Binding ConfiguredSensors.Count}" AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Single" Items="{Binding ConfiguredSensors}">
@ -15,9 +15,12 @@
<DataGridTextColumn Header="Type" <DataGridTextColumn Header="Type"
Binding="{Binding Type}" Binding="{Binding Type}"
Width="1*" /> Width="1*" />
<DataGridTextColumn Header="Value" <DataGridTextColumn Header="Update Interval"
Binding="{Binding Value}" Binding="{Binding UpdateInterval}"
Width="1*" /> Width="1*" />
<DataGridTextColumn Header="Value"
Binding="{Binding ValueString}"
Width="2*" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<TextBlock IsVisible="{Binding !ConfiguredSensors.Count}">Add some sensors by clicking the "Add" button. </TextBlock> <TextBlock IsVisible="{Binding !ConfiguredSensors.Count}">Add some sensors by clicking the "Add" button. </TextBlock>

@ -50,10 +50,10 @@ namespace UserInterface.Views
sensorsNeedToRefresh = false; sensorsNeedToRefresh = false;
List<ConfiguredSensorModel> status = await this.client.InvokeAsync(x => x.GetConfiguredSensors()); List<ConfiguredSensorModel> status = await this.client.InvokeAsync(x => x.GetConfiguredSensors());
((SensorSettingsViewModel)this.DataContext).ConfiguredSensors = status.Select(s => new SensorViewModel() { Name = s.Name, Type = s.Type, Value = s.Value, Id = s.Id }).ToList(); ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors = status.Select(s => new SensorViewModel() { Name = s.Name, Type = s.Type, Value = s.Value, Id = s.Id , UpdateInterval = s.UpdateInterval, UnitOfMeasurement = s.UnitOfMeasurement}).ToList();
while (!sensorsNeedToRefresh) while (!sensorsNeedToRefresh)
{ {
await Task.Delay(2000); await Task.Delay(1000);
List<ConfiguredSensorModel> statusUpdated = await this.client.InvokeAsync(x => x.GetConfiguredSensors()); List<ConfiguredSensorModel> statusUpdated = await this.client.InvokeAsync(x => x.GetConfiguredSensors());
var configuredSensors = ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors; var configuredSensors = ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors;
statusUpdated.ForEach(s => statusUpdated.ForEach(s =>
@ -73,9 +73,9 @@ namespace UserInterface.Views
{ {
var item = ((SensorViewModel)this._dataGrid.SelectedItem); var item = ((SensorViewModel)this._dataGrid.SelectedItem);
this.client.InvokeAsync(x => x.RemoveSensorById(item.Id)); this.client.InvokeAsync(x => x.RemoveSensorById(item.Id));
// TODO: improve this. it is not working well. ((SensorSettingsViewModel)this.DataContext).ConfiguredSensors.Remove(item);
sensorsNeedToRefresh = true; this._dataGrid.SelectedIndex = -1;
GetConfiguredSensors(); ((SensorSettingsViewModel)this.DataContext).TriggerUpdate();
} }
public async void AddSensor(object sender, RoutedEventArgs args) public async void AddSensor(object sender, RoutedEventArgs args)
@ -84,6 +84,7 @@ namespace UserInterface.Views
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
await dialog.ShowDialog(desktop.MainWindow); await dialog.ShowDialog(desktop.MainWindow);
sensorsNeedToRefresh = true;
GetConfiguredSensors(); GetConfiguredSensors();
} }
} }

@ -66,7 +66,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
public List<ConfiguredSensorModel> GetConfiguredSensors() public List<ConfiguredSensorModel> GetConfiguredSensors()
{ {
return this._configurationService.ConfiguredSensors.Select(s => new ConfiguredSensorModel() { Name = s.Name, Type = s.GetType().Name, Value = s.PreviousPublishedState, Id = s.Id }).ToList(); return this._configurationService.ConfiguredSensors.Select(s => new ConfiguredSensorModel() { Name = s.Name, Type = s.GetType().Name, Value = s.PreviousPublishedState, Id = s.Id, UpdateInterval = s.UpdateInterval, UnitOfMeasurement = s.GetAutoDiscoveryConfig().Unit_of_measurement }).ToList();
} }
public void RemoveSensorById(Guid id) public void RemoveSensorById(Guid id)
@ -86,10 +86,25 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
switch (sensorType) switch (sensorType)
{ {
case AvailableSensors.UserNotificationStateSensor: case AvailableSensors.UserNotificationStateSensor:
sensorToCreate = new UserNotificationStateSensor(this._publisher, model.Name); sensorToCreate = new UserNotificationStateSensor(this._publisher, (int)model.UpdateInterval, model.Name);
break; break;
case AvailableSensors.DummySensor: case AvailableSensors.DummySensor:
sensorToCreate = new DummySensor(this._publisher, model.Name); 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; break;
default: default:
Log.Logger.Error("Unknown sensortype"); Log.Logger.Error("Unknown sensortype");

@ -24,11 +24,18 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
public string Type { get; set; } public string Type { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Value { get; set; } public string Value { get; set; }
public int UpdateInterval { get; set; }
public string UnitOfMeasurement { get; set; }
} }
public enum AvailableSensors public enum AvailableSensors
{ {
UserNotificationStateSensor, UserNotificationStateSensor,
DummySensor DummySensor,
CurrentClockSpeedSensor,
CPULoadSensor,
WMIQuerySensor,
MemoryUsageSensor,
ActiveWindowSensor
} }
} }

@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Dynamic; using System.Dynamic;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
namespace hass_workstation_service.Communication.Util namespace hass_workstation_service.Communication.Util
{ {
/// <summary> /// <summary>
@ -125,3 +127,4 @@ namespace hass_workstation_service.Communication.Util
} }
} }
} }
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

@ -67,21 +67,40 @@ namespace hass_workstation_service.Data
foreach (ConfiguredSensor configuredSensor in sensors) foreach (ConfiguredSensor configuredSensor in sensors)
{ {
AbstractSensor sensor; AbstractSensor sensor = null;
switch (configuredSensor.Type) switch (configuredSensor.Type)
{ {
case "UserNotificationStateSensor": case "UserNotificationStateSensor":
sensor = new UserNotificationStateSensor(publisher, configuredSensor.Name, configuredSensor.Id); sensor = new UserNotificationStateSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break; break;
case "DummySensor": case "DummySensor":
sensor = new DummySensor(publisher, configuredSensor.Name, configuredSensor.Id); sensor = new DummySensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "CurrentClockSpeedSensor":
sensor = new CurrentClockSpeedSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "WMIQuerySensor":
sensor = new WMIQuerySensor(publisher, configuredSensor.Query, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "CPULoadSensor":
sensor = new CPULoadSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "MemoryUsageSensor":
sensor = new MemoryUsageSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "ActiveWindowSensor":
sensor = new ActiveWindowSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break; break;
default: default:
throw new InvalidOperationException("unsupported sensor type in config"); Log.Logger.Error("unsupported sensor type in config");
break;
} }
if (sensor != null)
{
this.ConfiguredSensors.Add(sensor); this.ConfiguredSensors.Add(sensor);
} }
} }
}
public async Task<IMqttClientOptions> GetMqttClientOptionsAsync() public async Task<IMqttClientOptions> GetMqttClientOptionsAsync()
{ {
@ -143,7 +162,16 @@ namespace hass_workstation_service.Data
Log.Logger.Information($"writing configured sensors to: {stream.Name}"); Log.Logger.Information($"writing configured sensors to: {stream.Name}");
foreach (AbstractSensor sensor in this.ConfiguredSensors) foreach (AbstractSensor sensor in this.ConfiguredSensors)
{ {
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = sensor.Id, Name = sensor.Name, Type = sensor.GetType().Name }); if (sensor is WMIQuerySensor)
{
var wmiSensor = (WMIQuerySensor)sensor;
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = wmiSensor.Id, Name = wmiSensor.Name, Type = wmiSensor.GetType().Name, UpdateInterval = wmiSensor.UpdateInterval, Query = wmiSensor.Query });
}
else
{
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = sensor.Id, Name = sensor.Name, Type = sensor.GetType().Name, UpdateInterval = sensor.UpdateInterval });
}
} }
await JsonSerializer.SerializeAsync(stream, configuredSensorsToSave); await JsonSerializer.SerializeAsync(stream, configuredSensorsToSave);
@ -155,16 +183,25 @@ namespace hass_workstation_service.Data
public void AddConfiguredSensor(AbstractSensor sensor) public void AddConfiguredSensor(AbstractSensor sensor)
{ {
this.ConfiguredSensors.Add(sensor); this.ConfiguredSensors.Add(sensor);
sensor.PublishAutoDiscoveryConfigAsync();
WriteSettingsAsync(); WriteSettingsAsync();
} }
public async void DeleteConfiguredSensor(Guid id) public async void DeleteConfiguredSensor(Guid id)
{ {
var sensorToRemove = this.ConfiguredSensors.FirstOrDefault(s => s.Id == id); var sensorToRemove = this.ConfiguredSensors.FirstOrDefault(s => s.Id == id);
if (sensorToRemove != null)
{
await sensorToRemove.UnPublishAutoDiscoveryConfigAsync(); await sensorToRemove.UnPublishAutoDiscoveryConfigAsync();
this.ConfiguredSensors.Remove(sensorToRemove); this.ConfiguredSensors.Remove(sensorToRemove);
WriteSettingsAsync(); WriteSettingsAsync();
} }
else
{
Log.Logger.Warning($"sensor with id {id} not found");
}
}
public void AddConfiguredSensors(List<AbstractSensor> sensors) public void AddConfiguredSensors(List<AbstractSensor> sensors)
{ {
@ -226,7 +263,7 @@ namespace hass_workstation_service.Data
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); RegistryKey rkApp = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
//Path to launch shortcut //Path to launch shortcut
string startPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs) + @"\hass-workstation-service\hass-workstation-service.appref-ms"; string startPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs) + @"\Sleevezipper\Hass Workstation Service.appref-ms";
rkApp.SetValue("hass-workstation-service", startPath); rkApp.SetValue("hass-workstation-service", startPath);
rkApp.Close(); rkApp.Close();

@ -7,5 +7,7 @@ namespace hass_workstation_service.Data
public string Type { get; set; } public string Type { get; set; }
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Query { get; set; }
public int? UpdateInterval { get; set; }
} }
} }

@ -10,8 +10,28 @@ namespace hass_workstation_service.Domain.Sensors
{ {
public Guid Id { get; protected set; } public Guid Id { get; protected set; }
public string Name { get; protected set; } public string Name { get; protected set; }
/// <summary>
/// The update interval in seconds. It checks state only if the interval has passed.
/// </summary>
public int UpdateInterval { get; protected set; }
public DateTime? LastUpdated { get; protected set; }
public string PreviousPublishedState { get; protected set; } public string PreviousPublishedState { get; protected set; }
public MqttPublisher Publisher { get; protected set; } public MqttPublisher Publisher { get; protected set; }
public AbstractSensor(MqttPublisher publisher, string name, int updateInterval = 10, Guid id = default(Guid))
{
if (id == Guid.Empty || id == null)
{
this.Id = Guid.NewGuid();
}
else
{
this.Id = id;
}
this.Name = name;
this.Publisher = publisher;
this.UpdateInterval = updateInterval;
}
protected AutoDiscoveryConfigModel _autoDiscoveryConfigModel; protected AutoDiscoveryConfigModel _autoDiscoveryConfigModel;
protected AutoDiscoveryConfigModel SetAutoDiscoveryConfigModel(AutoDiscoveryConfigModel config) protected AutoDiscoveryConfigModel SetAutoDiscoveryConfigModel(AutoDiscoveryConfigModel config)
{ {
@ -24,6 +44,11 @@ namespace hass_workstation_service.Domain.Sensors
public async Task PublishStateAsync() 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
return;
}
string state = this.GetState(); string state = this.GetState();
if (this.PreviousPublishedState == state) if (this.PreviousPublishedState == state)
{ {
@ -38,8 +63,9 @@ namespace hass_workstation_service.Domain.Sensors
.Build(); .Build();
await Publisher.Publish(message); await Publisher.Publish(message);
this.PreviousPublishedState = state; this.PreviousPublishedState = state;
this.LastUpdated = DateTime.UtcNow;
} }
public async Task PublishAutoDiscoveryConfigAsync() public async void PublishAutoDiscoveryConfigAsync()
{ {
await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig()); await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig());
} }

@ -0,0 +1,48 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace hass_workstation_service.Domain.Sensors
{
public class ActiveWindowSensor : AbstractSensor
{
public ActiveWindowSensor(MqttPublisher publisher, int? updateInterval = null, string name = "ActiveWindow", Guid id = default(Guid)) : base(publisher, name ?? "ActiveWindow", updateInterval ?? 10, id) { }
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
State_topic = $"homeassistant/sensor/{this.Name}/state",
Icon = "mdi:window-maximize",
});
}
public override string GetState()
{
return GetActiveWindowTitle();
}
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
}
}

@ -0,0 +1,44 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management;
using System.Text;
namespace hass_workstation_service.Domain.Sensors
{
public class CPULoadSensor : WMIQuerySensor
{
public CPULoadSensor(MqttPublisher publisher, int? updateInterval = null, string name = "CPULoadSensor", Guid id = default) : base(publisher, "SELECT PercentProcessorTime FROM Win32_PerfFormattedData_PerfOS_Processor", updateInterval ?? 10, name ?? "CPULoadSensor", id)
{
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
State_topic = $"homeassistant/sensor/{this.Name}/state",
Icon = "mdi:chart-areaspline",
Unit_of_measurement = "%"
});
}
public override string GetState()
{
ManagementObjectCollection collection = _searcher.Get();
List<int> processorLoadPercentages = new List<int>();
foreach (ManagementObject mo in collection)
{
foreach (PropertyData property in mo.Properties)
{
processorLoadPercentages.Add(int.Parse(property.Value.ToString()));
}
}
double average = processorLoadPercentages.Count > 0 ? processorLoadPercentages.Average() : 0.0;
return average.ToString("#.##", CultureInfo.InvariantCulture);
}
}
}

@ -0,0 +1,25 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Text;
namespace hass_workstation_service.Domain.Sensors
{
public class CurrentClockSpeedSensor : WMIQuerySensor
{
public CurrentClockSpeedSensor(MqttPublisher publisher, int? updateInterval = null, string name = "CurrentClockSpeed", Guid id = default(Guid)) : base(publisher, "SELECT CurrentClockSpeed FROM Win32_Processor", updateInterval ?? 10, name ?? "CurrentClockSpeed", id) { }
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
State_topic = $"homeassistant/sensor/{this.Name}/state",
Icon = "mdi:speedometer",
Unit_of_measurement = "MHz"
});
}
}
}

@ -8,21 +8,11 @@ namespace hass_workstation_service.Domain.Sensors
public class DummySensor : AbstractSensor public class DummySensor : AbstractSensor
{ {
private readonly Random _random; private readonly Random _random;
public DummySensor(MqttPublisher publisher, string name = "Dummy") public DummySensor(MqttPublisher publisher, int? updateInterval = null, string name = "Dummy", Guid id = default(Guid)) : base(publisher, name ?? "Dummy", updateInterval ?? 1, id)
{ {
this.Id = Guid.NewGuid();
this.Name = name;
this.Publisher = publisher;
this._random = new Random(); this._random = new Random();
} }
public DummySensor(MqttPublisher publisher, string name, Guid id)
{
this.Id = id;
this.Name = name;
this.Publisher = publisher;
this._random = new Random();
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig() public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{ {
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel() return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()

@ -0,0 +1,47 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Management;
using System.Text;
namespace hass_workstation_service.Domain.Sensors
{
public class MemoryUsageSensor : WMIQuerySensor
{
public MemoryUsageSensor(MqttPublisher publisher, int? updateInterval = null, string name = "WMIQuerySensor", Guid id = default) : base(publisher, "SELECT FreePhysicalMemory,TotalVisibleMemorySize FROM Win32_OperatingSystem", updateInterval ?? 10, name, id)
{
}
public override string GetState()
{
ManagementObjectCollection collection = _searcher.Get();
UInt64? totalMemory = null;
UInt64? freeMemory = null;
foreach (ManagementObject mo in collection)
{
totalMemory = (UInt64)mo.Properties["TotalVisibleMemorySize"]?.Value;
freeMemory = (UInt64)mo.Properties["FreePhysicalMemory"]?.Value;
}
if (totalMemory != null && freeMemory != null)
{
decimal totalMemoryDec = totalMemory.Value;
decimal freeMemoryDec = freeMemory.Value;
decimal precentageUsed = 100 - (freeMemoryDec / totalMemoryDec) * 100;
return precentageUsed.ToString("#.##", CultureInfo.InvariantCulture);
}
return "";
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
State_topic = $"homeassistant/sensor/{this.Name}/state",
Icon = "mdi:memory",
Unit_of_measurement = "%"
});
}
}
}

@ -7,19 +7,8 @@ namespace hass_workstation_service.Domain.Sensors
{ {
public class UserNotificationStateSensor : AbstractSensor public class UserNotificationStateSensor : AbstractSensor
{ {
public UserNotificationStateSensor(MqttPublisher publisher, string name = "NotificationState") public UserNotificationStateSensor(MqttPublisher publisher, int? updateInterval = null, string name = "NotificationState", Guid id = default(Guid)) : base(publisher, name ?? "NotificationState", updateInterval ?? 10, id) { }
{
this.Id = Guid.NewGuid();
this.Name = name;
this.Publisher = publisher;
}
public UserNotificationStateSensor(MqttPublisher publisher, string name, Guid id)
{
this.Id = id;
this.Name = name;
this.Publisher = publisher;
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig() public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{ {
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel() return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
@ -40,6 +29,10 @@ namespace hass_workstation_service.Domain.Sensors
[DllImport("shell32.dll")] [DllImport("shell32.dll")]
static extern int SHQueryUserNotificationState(out UserNotificationState state); static extern int SHQueryUserNotificationState(out UserNotificationState state);
[DllImport("shell32.dll")]
static extern int asdasdadsd(out int q);
public UserNotificationState GetStateEnum() public UserNotificationState GetStateEnum()
{ {
SHQueryUserNotificationState(out UserNotificationState state); SHQueryUserNotificationState(out UserNotificationState state);

@ -0,0 +1,46 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace hass_workstation_service.Domain.Sensors
{
public class WMIQuerySensor : AbstractSensor
{
public string Query { get; private set; }
protected readonly ObjectQuery _objectQuery;
protected readonly ManagementObjectSearcher _searcher;
public WMIQuerySensor(MqttPublisher publisher, string query, int? updateInterval = null, string name = "WMIQuerySensor", Guid id = default(Guid)) : base(publisher, name ?? "WMIQuerySensor", updateInterval ?? 10, id)
{
this.Query = query;
_objectQuery = new ObjectQuery(this.Query);
_searcher = new ManagementObjectSearcher(query);
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
State_topic = $"homeassistant/sensor/{this.Name}/state",
});
}
public override string GetState()
{
ManagementObjectCollection collection = _searcher.Get();
foreach (ManagementObject mo in collection)
{
foreach (PropertyData property in mo.Properties)
{
return property.Value.ToString();
}
}
return "";
}
}
}

@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
--> -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ApplicationRevision>20</ApplicationRevision> <ApplicationRevision>22</ApplicationRevision>
<ApplicationVersion>1.0.0.*</ApplicationVersion> <ApplicationVersion>1.0.0.*</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled> <BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>

@ -38,24 +38,27 @@ namespace hass_workstation_service
} }
_logger.LogInformation("Connected. Sending auto discovery messages."); _logger.LogInformation("Connected. Sending auto discovery messages.");
foreach (AbstractSensor sensor in _configurationService.ConfiguredSensors) List<AbstractSensor> sensors = _configurationService.ConfiguredSensors.ToList();
foreach (AbstractSensor sensor in sensors)
{ {
await sensor.PublishAutoDiscoveryConfigAsync(); sensor.PublishAutoDiscoveryConfigAsync();
} }
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
sensors = _configurationService.ConfiguredSensors.ToList();
_logger.LogDebug("Worker running at: {time}", DateTimeOffset.Now); _logger.LogDebug("Worker running at: {time}", DateTimeOffset.Now);
foreach (AbstractSensor sensor in _configurationService.ConfiguredSensors) foreach (AbstractSensor sensor in sensors)
{ {
await sensor.PublishStateAsync(); await sensor.PublishStateAsync();
} }
// announce autodiscovery every 30 seconds // announce autodiscovery every 30 seconds
if (_mqttPublisher.LastConfigAnnounce < DateTime.UtcNow.AddSeconds(-30)) if (_mqttPublisher.LastConfigAnnounce < DateTime.UtcNow.AddSeconds(-30))
{ {
foreach (AbstractSensor sensor in _configurationService.ConfiguredSensors) foreach (AbstractSensor sensor in sensors)
{ {
await sensor.PublishAutoDiscoveryConfigAsync(); sensor.PublishAutoDiscoveryConfigAsync();
} }
} }
await Task.Delay(1000, stoppingToken); await Task.Delay(1000, stoppingToken);

@ -46,5 +46,6 @@
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="MQTTnet" Version="3.0.13" /> <PackageReference Include="MQTTnet" Version="3.0.13" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="System.Management" Version="5.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Loading…
Cancel
Save