Merge remote-tracking branch 'origin/master' into webcam-usage-sensor

pull/9/head^2
sleevezipper 4 years ago
commit 9a5fc6f031

@ -61,6 +61,10 @@ This sensor shows if the webcam is currently being used. It has two detection mo
- Registry - this is the preferred method. This will work from Windows 10 version 1903 and higher. - Registry - this is the preferred method. This will work from Windows 10 version 1903 and higher.
- OpenCV - this method tries to access the webcam and if that fails, it assumes it is currently in use. This will flash the webcam activity light at every update interval. It also uses more CPU cycles and memory. - OpenCV - this method tries to access the webcam and if that fails, it assumes it is currently in use. This will flash the webcam activity light at every update interval. It also uses more CPU cycles and memory.
### MicrophoneActive
This sensor shows if the microphone is currently being used. It uses the Windows registry to check and wil work from Windows 10 version 1903 and higher.
### CPULoad ### 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. This sensor checks the current CPU load. It averages the load on all logical cores every second and rounds the output to two decimals.

@ -15,11 +15,13 @@ namespace UserInterface.ViewModels
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); } 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); } public bool ShowDetectionModeOptions { get => showDetectionModeOptions; set => this.RaiseAndSetIfChanged(ref showDetectionModeOptions, value); }
private string moreInfoLink; private string moreInfoLink;
private int updateInterval; private int updateInterval;
private bool showWindowNameInput;
private bool showDetectionModeOptions; private bool showDetectionModeOptions;
public string MoreInfoLink public string MoreInfoLink
@ -34,6 +36,7 @@ namespace UserInterface.ViewModels
public string Name { get; set; } public string Name { get; set; }
public string Query { get; set; } public string Query { get; set; }
public string WindowName { get; set; }
public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); } public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); }
} }
} }

@ -23,6 +23,9 @@
<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/> <TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowQueryInput}" Margin="0 20 0 10">Query</ContentControl> <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"/> <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 IsVisible="{Binding ShowDetectionModeOptions}" Margin="0 20 0 10">Detection mode</ContentControl> <ContentControl IsVisible="{Binding ShowDetectionModeOptions}" Margin="0 20 0 10">Detection mode</ContentControl>
<ComboBox IsVisible="{Binding ShowDetectionModeOptions}" x:Name="DetectionModeComboBox" SelectedItem="{Binding SelectedDetectionMode}" MinHeight="27"></ComboBox> <ComboBox IsVisible="{Binding ShowDetectionModeOptions}" x:Name="DetectionModeComboBox" SelectedItem="{Binding SelectedDetectionMode}" MinHeight="27"></ComboBox>
<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>

@ -51,7 +51,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, Query = item.Query, UpdateInterval = item.UpdateInterval, DetectionMode = item.SelectedDetectionMode }; dynamic model = new { item.Name, item.Query, item.UpdateInterval, item.WindowName, DetectionMode = item.SelectedDetectionMode };
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();
@ -67,6 +67,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usernotificationstate"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usernotificationstate";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5; item.UpdateInterval = 5;
break; break;
case AvailableSensors.DummySensor: case AvailableSensors.DummySensor:
@ -74,6 +75,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#dummy"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#dummy";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 1; item.UpdateInterval = 1;
break; break;
case AvailableSensors.CPULoadSensor: case AvailableSensors.CPULoadSensor:
@ -81,6 +83,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#cpuload"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#cpuload";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5; item.UpdateInterval = 5;
break; break;
case AvailableSensors.CurrentClockSpeedSensor: case AvailableSensors.CurrentClockSpeedSensor:
@ -88,6 +91,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#currentclockspeed"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#currentclockspeed";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 3600; item.UpdateInterval = 3600;
break; break;
case AvailableSensors.WMIQuerySensor: case AvailableSensors.WMIQuerySensor:
@ -95,6 +99,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#wmiquerysensor"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#wmiquerysensor";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = true; item.ShowQueryInput = true;
item.ShowWindowNameInput = false;
item.UpdateInterval = 10; item.UpdateInterval = 10;
break; break;
case AvailableSensors.MemoryUsageSensor: case AvailableSensors.MemoryUsageSensor:
@ -102,6 +107,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usedmemory"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#usedmemory";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 10; item.UpdateInterval = 10;
break; break;
case AvailableSensors.ActiveWindowSensor: case AvailableSensors.ActiveWindowSensor:
@ -109,6 +115,7 @@ namespace UserInterface.Views
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#activewindow"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#activewindow";
item.ShowDetectionModeOptions = false; item.ShowDetectionModeOptions = false;
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.ShowWindowNameInput = false;
item.UpdateInterval = 5; item.UpdateInterval = 5;
break; break;
case AvailableSensors.WebcamActiveSensor: case AvailableSensors.WebcamActiveSensor:
@ -125,6 +132,13 @@ namespace UserInterface.Views
item.ShowQueryInput = false; item.ShowQueryInput = false;
item.UpdateInterval = 10; item.UpdateInterval = 10;
break; 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;
default: default:
item.Description = null; item.Description = null;
item.MoreInfoLink = null; item.MoreInfoLink = null;

@ -45,7 +45,7 @@ namespace UserInterface.Views
public void Configure(object sender, RoutedEventArgs args) public void Configure(object sender, RoutedEventArgs args)
{ {
var model = (BrokerSettingsViewModel)this.DataContext; var model = (BrokerSettingsViewModel)this.DataContext;
var result = this.client.InvokeAsync(x => x.WriteMqttBrokerSettingsAsync(new MqttSettings() { Host = model.Host, Username = model.Username, Password = model.Password })); var result = this.client.InvokeAsync(x => x.WriteMqttBrokerSettingsAsync(new MqttSettings() { Host = model.Host, Username = model.Username, Password = model.Password ?? "" }));
} }
public async void GetSettings() public async void GetSettings()

@ -125,6 +125,9 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
case AvailableSensors.MicrophoneActiveSensor: case AvailableSensors.MicrophoneActiveSensor:
sensorToCreate = new MicrophoneActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name); sensorToCreate = new MicrophoneActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name);
break; break;
case AvailableSensors.NamedWindowSensor:
sensorToCreate = new NamedWindowSensor(this._publisher, model.WindowName, model.Name, (int)model.UpdateInterval);
break;
default: default:
Log.Logger.Error("Unknown sensortype"); Log.Logger.Error("Unknown sensortype");
break; break;

@ -36,9 +36,10 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
CPULoadSensor, CPULoadSensor,
WMIQuerySensor, WMIQuerySensor,
MemoryUsageSensor, MemoryUsageSensor,
ActiveWindowSensor,
WebcamActiveSensor, WebcamActiveSensor,
MicrophoneActiveSensor MicrophoneActiveSensor,
ActiveWindowSensor,
NamedWindowSensor
} }
public enum WebcamDetectionMode public enum WebcamDetectionMode

@ -88,6 +88,9 @@ namespace hass_workstation_service.Data
case "ActiveWindowSensor": case "ActiveWindowSensor":
sensor = new ActiveWindowSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); sensor = new ActiveWindowSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break; break;
case "NamedWindowSensor":
sensor = new NamedWindowSensor(publisher, configuredSensor.WindowName, configuredSensor.Name, configuredSensor.UpdateInterval, configuredSensor.Id);
break;
case "WebcamActiveSensor": case "WebcamActiveSensor":
sensor = new WebcamActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.DetectionMode, configuredSensor.Id); sensor = new WebcamActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.DetectionMode, configuredSensor.Id);
break; break;
@ -170,6 +173,11 @@ namespace hass_workstation_service.Data
var wmiSensor = (WMIQuerySensor)sensor; var wmiSensor = (WMIQuerySensor)sensor;
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = wmiSensor.Id, Name = wmiSensor.Name, Type = wmiSensor.GetType().Name, UpdateInterval = wmiSensor.UpdateInterval, Query = wmiSensor.Query }); configuredSensorsToSave.Add(new ConfiguredSensor() { Id = wmiSensor.Id, Name = wmiSensor.Name, Type = wmiSensor.GetType().Name, UpdateInterval = wmiSensor.UpdateInterval, Query = wmiSensor.Query });
} }
if (sensor is NamedWindowSensor)
{
var namedWindowSensor = (NamedWindowSensor)sensor;
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = namedWindowSensor.Id, Name = namedWindowSensor.Name, Type = namedWindowSensor.GetType().Name, UpdateInterval = namedWindowSensor.UpdateInterval, WindowName = namedWindowSensor.WindowName });
}
else else
{ {
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = sensor.Id, Name = sensor.Name, Type = sensor.GetType().Name, UpdateInterval = sensor.UpdateInterval }); configuredSensorsToSave.Add(new ConfiguredSensor() { Id = sensor.Id, Name = sensor.Name, Type = sensor.GetType().Name, UpdateInterval = sensor.UpdateInterval });
@ -232,7 +240,7 @@ namespace hass_workstation_service.Data
{ {
Host = settings.Host, Host = settings.Host,
Username = settings.Username, Username = settings.Username,
Password = settings.Password Password = settings.Password ?? ""
}; };
await JsonSerializer.SerializeAsync(stream, configuredBroker); await JsonSerializer.SerializeAsync(stream, configuredBroker);

@ -10,6 +10,7 @@ namespace hass_workstation_service.Data
public string Name { get; set; } public string Name { get; set; }
public string Query { get; set; } public string Query { get; set; }
public int? UpdateInterval { get; set; } public int? UpdateInterval { get; set; }
public string WindowName { get; set; }
public DetectionMode DetectionMode { get; set; } public DetectionMode DetectionMode { get; set; }
} }
} }

@ -0,0 +1,81 @@
using hass_workstation_service.Communication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using HWND = System.IntPtr;
namespace hass_workstation_service.Domain.Sensors
{
public class NamedWindowSensor : AbstractSensor
{
public string WindowName { get; protected set; }
public NamedWindowSensor(MqttPublisher publisher, string windowName, string name = "NamedWindow", int? updateInterval = 10, Guid id = default) : base(publisher, name ?? "NamedWindow", updateInterval ?? 10, id)
{
this.WindowName = windowName;
}
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()
{
var windowNames = GetOpenWindows().Values;
return windowNames.Any(v => v.Contains(this.WindowName, StringComparison.OrdinalIgnoreCase)) ? "True" : "False";
}
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();
EnumWindows(delegate (HWND hWnd, int lParam)
{
if (hWnd == shellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int length = GetWindowTextLength(hWnd);
if (length == 0) return true;
StringBuilder builder = new StringBuilder(length);
GetWindowText(hWnd, builder, length + 1);
windows[hWnd] = builder.ToString();
return true;
}, 0);
return windows;
}
private delegate bool EnumWindowsProc(HWND hWnd, int lParam);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
}
}
Loading…
Cancel
Save