implement webcam and microphone sensors

pull/9/head^2
sleevezipper 4 years ago
parent 94dd333231
commit d55eaa76f4

@ -54,6 +54,13 @@ This sensor watches the UserNotificationState. This is normally used in applicat
This sensor exposes the name of the currently focused window.
### WebcamActive
This sensor shows if the webcam is currently being used. It has two detection modes:
- 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.
### 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.

@ -9,14 +9,18 @@ namespace UserInterface.ViewModels
public class AddSensorViewModel : ViewModelBase
{
private AvailableSensors selectedType;
private WebcamDetectionMode selectedDetectionMode;
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 ShowDetectionModeOptions { get => showDetectionModeOptions; set => this.RaiseAndSetIfChanged(ref showDetectionModeOptions, value); }
private string moreInfoLink;
private int updateInterval;
private bool showDetectionModeOptions;
public string MoreInfoLink
{
@ -26,6 +30,8 @@ namespace UserInterface.ViewModels
public AvailableSensors SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
public WebcamDetectionMode SelectedDetectionMode { get => selectedDetectionMode; set => this.RaiseAndSetIfChanged(ref selectedDetectionMode, value); }
public string Name { get; set; }
public string Query { get; set; }
public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); }

@ -23,6 +23,8 @@
<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 ShowDetectionModeOptions}" Margin="0 20 0 10">Detection mode</ContentControl>
<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>
</StackPanel>
</Window>

@ -19,6 +19,7 @@ namespace UserInterface.Views
{
private readonly IIpcClient<ServiceContractInterfaces> client;
public ComboBox comboBox { get; set; }
public ComboBox detectionModecomboBox { get; set; }
public AddSensorDialog()
{
this.InitializeComponent();
@ -28,6 +29,9 @@ namespace UserInterface.Views
this.comboBox = this.FindControl<ComboBox>("ComboBox");
this.comboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast<AvailableSensors>();
this.comboBox = this.FindControl<ComboBox>("DetectionModeComboBox");
this.comboBox.Items = Enum.GetValues(typeof(WebcamDetectionMode)).Cast<WebcamDetectionMode>();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("addsensor", pipeName: "pipeinternal")
@ -47,7 +51,7 @@ namespace UserInterface.Views
public async void Save(object sender, RoutedEventArgs args)
{
var item = ((AddSensorViewModel)this.DataContext);
dynamic model = new { Name = item.Name, Query = item.Query, UpdateInterval = item.UpdateInterval };
dynamic model = new { Name = item.Name, Query = item.Query, UpdateInterval = item.UpdateInterval, DetectionMode = item.SelectedDetectionMode };
string json = JsonSerializer.Serialize(model);
await this.client.InvokeAsync(x => x.AddSensor(item.SelectedType, json));
Close();
@ -61,48 +65,63 @@ namespace UserInterface.Views
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.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.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.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.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.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.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.UpdateInterval = 5;
break;
case AvailableSensors.WebcamActiveSensor:
item.Description = "This sensor shows if the webcam is currently in use.";
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;

@ -107,7 +107,23 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
sensorToCreate = new ActiveWindowSensor(this._publisher, (int)model.UpdateInterval, model.Name);
break;
case AvailableSensors.WebcamActiveSensor:
sensorToCreate = new WebcamActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name);
DetectionMode detectionMode;
switch ((WebcamDetectionMode)model.DetectionMode)
{
case WebcamDetectionMode.Registry:
detectionMode = DetectionMode.Registry;
break;
case WebcamDetectionMode.OpenCV:
detectionMode = DetectionMode.OpenCV;
break;
default:
detectionMode = DetectionMode.Registry;
break;
}
sensorToCreate = new WebcamActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name, detectionMode);
break;
case AvailableSensors.MicrophoneActiveSensor:
sensorToCreate = new MicrophoneActiveSensor(this._publisher, (int)model.UpdateInterval, model.Name);
break;
default:
Log.Logger.Error("Unknown sensortype");

@ -37,6 +37,13 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
WMIQuerySensor,
MemoryUsageSensor,
ActiveWindowSensor,
WebcamActiveSensor
WebcamActiveSensor,
MicrophoneActiveSensor
}
public enum WebcamDetectionMode
{
Registry,
OpenCV
}
}

@ -89,7 +89,10 @@ namespace hass_workstation_service.Data
sensor = new ActiveWindowSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
case "WebcamActiveSensor":
sensor = new WebcamActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
sensor = new WebcamActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.DetectionMode, configuredSensor.Id);
break;
case "MicrophoneActiveSensor":
sensor = new MicrophoneActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
default:
Log.Logger.Error("unsupported sensor type in config");

@ -1,3 +1,4 @@
using hass_workstation_service.Domain.Sensors;
using System;
namespace hass_workstation_service.Data
@ -9,5 +10,6 @@ namespace hass_workstation_service.Data
public string Name { get; set; }
public string Query { get; set; }
public int? UpdateInterval { get; set; }
public DetectionMode DetectionMode { get; set; }
}
}

@ -0,0 +1,53 @@
using hass_workstation_service.Communication;
using Microsoft.Win32;
using System;
using System.Linq;
namespace hass_workstation_service.Domain.Sensors
{
public class MicrophoneActiveSensor : AbstractSensor
{
public MicrophoneActiveSensor(MqttPublisher publisher, int? updateInterval = null, string name = "MicrophoneActive", Guid id = default(Guid)) : base(publisher, name, updateInterval ?? 10, id)
{
}
public override string GetState()
{
return IsMicrophoneInUse() ? "True" : "False";
}
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:microphone",
});
}
private bool IsMicrophoneInUse()
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged"))
{
foreach (var subKeyName in key.GetSubKeyNames())
{
using (var subKey = key.OpenSubKey(subKeyName))
{
if (subKey.GetValueNames().Contains("LastUsedTimeStop"))
{
var endTime = subKey.GetValue("LastUsedTimeStop") is long ? (long)subKey.GetValue("LastUsedTimeStop") : -1;
if (endTime <= 0)
{
return true;
}
}
}
}
}
return false;
}
}
}

@ -1,17 +1,35 @@
using hass_workstation_service.Communication;
using Microsoft.Win32;
using OpenCvSharp;
using System;
using System.Linq;
namespace hass_workstation_service.Domain.Sensors
{
public enum DetectionMode
{
Registry,
OpenCV
}
public class WebcamActiveSensor : AbstractSensor
{
public WebcamActiveSensor(MqttPublisher publisher, int? updateInterval = null, string name = "WebcamActive", Guid id = default) : base(publisher, name, updateInterval ?? 10, id)
public DetectionMode DetectionMode { get; private set; }
public WebcamActiveSensor(MqttPublisher publisher, int? updateInterval = null, string name = "WebcamActive", DetectionMode detectionMode = DetectionMode.Registry, Guid id = default(Guid)) : base(publisher, name, updateInterval ?? 10, id)
{
this.DetectionMode = detectionMode;
}
public override string GetState()
{
return IsWebCamInUse() ? "True" : "False";
switch (this.DetectionMode)
{
case DetectionMode.Registry:
return IsWebCamInUseRegistry() ? "True" : "False";
case DetectionMode.OpenCV:
return IsWebCamInUseOpenCV() ? "True" : "False";
default:
return "Error";
}
}
public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
{
@ -25,7 +43,7 @@ namespace hass_workstation_service.Domain.Sensors
});
}
private bool IsWebCamInUse()
private bool IsWebCamInUseOpenCV()
{
try
{
@ -35,13 +53,13 @@ namespace hass_workstation_service.Domain.Sensors
{
capture.Release();
capture.Dispose();
return true;
return false;
}
else
{
capture.Release();
capture.Dispose();
return false;
return true;
}
}
@ -51,5 +69,28 @@ namespace hass_workstation_service.Domain.Sensors
return false;
}
}
private bool IsWebCamInUseRegistry()
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam\NonPackaged"))
{
foreach (var subKeyName in key.GetSubKeyNames())
{
using (var subKey = key.OpenSubKey(subKeyName))
{
if (subKey.GetValueNames().Contains("LastUsedTimeStop"))
{
var endTime = subKey.GetValue("LastUsedTimeStop") is long ? (long)subKey.GetValue("LastUsedTimeStop") : -1;
if (endTime <= 0)
{
return true;
}
}
}
}
}
return false;
}
}
}

Loading…
Cancel
Save