diff --git a/UserInterface/Views/AddSensorDialog.axaml.cs b/UserInterface/Views/AddSensorDialog.axaml.cs index 9ab9bde..b75f2aa 100644 --- a/UserInterface/Views/AddSensorDialog.axaml.cs +++ b/UserInterface/Views/AddSensorDialog.axaml.cs @@ -102,7 +102,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#dummysensor"; @@ -110,7 +110,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#cpuloadsensor"; @@ -118,7 +118,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#currentclockspeedsensor"; @@ -126,7 +126,7 @@ namespace UserInterface.Views 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/sleevezipperhass-workstation-service/blob/master/documentation/WMIQuery.md#wmiquerysensor"; @@ -134,7 +134,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#memoryusagesensorsensor"; @@ -142,7 +142,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#activewindowsensor"; @@ -150,28 +150,35 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#webcamactivesensor"; item.ShowQueryInput = false; item.UpdateInterval = 10; break; - + case AvailableSensors.WebcamProcessSensor: item.Description = "This sensor shows which process is using the webcam."; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#webcamprocesssensor"; 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/blob/master/documentation/Sensors.md#microphoneactivesensor"; item.ShowQueryInput = false; item.UpdateInterval = 10; break; - + + case AvailableSensors.MicrophoneProcessSensor: + item.Description = "This sensor shows which process is using the microphone."; + item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#microphoneprocesssensor"; + 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/blob/master/documentation/Sensors.md#namedwindowsensor"; @@ -179,7 +186,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#lastactivesensor"; @@ -187,7 +194,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#lastbootsensor"; @@ -195,7 +202,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#sessionstatesensor"; @@ -203,7 +210,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#currentvolumesensor"; @@ -211,7 +218,7 @@ namespace UserInterface.Views item.ShowWindowNameInput = false; item.UpdateInterval = 5; break; - + case AvailableSensors.MasterVolumeSensor: item.Description = "This sensor returns the master volume of the currently selected default audio device as a percentage value."; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/Sensors.md#mastervolumesensor"; @@ -219,7 +226,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#gputemperaturesensor"; @@ -227,7 +234,7 @@ namespace UserInterface.Views 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/blob/master/documentation/Sensors.md#gpuloadsensor"; @@ -235,7 +242,7 @@ namespace UserInterface.Views item.ShowWindowNameInput = false; item.UpdateInterval = 5; break; - + default: item.Description = null; item.MoreInfoLink = null; diff --git a/documentation/Sensors.md b/documentation/Sensors.md index 2938c7d..6d35695 100644 --- a/documentation/Sensors.md +++ b/documentation/Sensors.md @@ -24,7 +24,7 @@ This sensor returns the current volume of playing audio. **It does not return th This sensor returns the master volume for the currently selected default audio device. -### DummySensor +### DummySensor This sensor produces a random output every second, and is intended to test latency and connectivity. @@ -89,6 +89,10 @@ The webcam active sensor returns the status of the webcam. The webcam process sensor returns the process which is using the webcam. +### MicrophoneProcessSensor + +The microphone process sensor returns the process which is using the microphone. + ### WMIQuerySensor Please see the specific documentaion page [here](https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/WMIQuery.md#wmiquerysensor). diff --git a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs index 9c9824e..bf4b3a4 100644 --- a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs +++ b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs @@ -30,7 +30,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication /// /// You can use this to check if the application responds. - /// + /// /// /// public string Ping(string str) => str == "ping" ? "pong" : "what?"; @@ -44,7 +44,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication public void WriteMqttBrokerSettingsAsync(MqttSettings settings) => _configurationService.WriteMqttBrokerSettingsAsync(settings); /// - /// Enables or disables autostart. + /// Enables or disables autostart. /// /// public void EnableAutostart(bool enable) => _configurationService.EnableAutoStart(enable); @@ -108,7 +108,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication public void RemoveCommandById(Guid id) => _configurationService.DeleteConfiguredCommand(id); /// - /// Adds a command to the configured commands. This properly initializes the class and writes it to the config file. + /// Adds a command to the configured commands. This properly initializes the class and writes it to the config file. /// /// /// @@ -123,7 +123,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication } /// - /// Adds a command to the configured commands. This properly initializes the class, subscribes to the command topic and writes it to the config file. + /// Adds a command to the configured commands. This properly initializes the class, subscribes to the command topic and writes it to the config file. /// /// /// @@ -179,6 +179,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication AvailableSensors.WebcamActiveSensor => new WebcamActiveSensor(_publisher, (int)model.UpdateInterval, model.Name), AvailableSensors.WebcamProcessSensor => new WebcamProcessSensor(_publisher, (int)model.UpdateInterval, model.Name), AvailableSensors.MicrophoneActiveSensor => new MicrophoneActiveSensor(_publisher, (int)model.UpdateInterval, model.Name), + AvailableSensors.MicrophoneProcessSensor => new MicrophoneProcessSensor(_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), diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs index fafa58d..86f7065 100644 --- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs +++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs @@ -45,7 +45,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models { this.WindowName = namedWindowSensor.WindowName; } - this.UpdateInterval = sensor.UpdateInterval; + this.UpdateInterval = sensor.UpdateInterval; this.UnitOfMeasurement = ((SensorDiscoveryConfigModel)sensor.GetAutoDiscoveryConfig()).Unit_of_measurement; } public ConfiguredSensorModel() @@ -95,6 +95,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models WebcamActiveSensor, WebcamProcessSensor, MicrophoneActiveSensor, + MicrophoneProcessSensor, ActiveWindowSensor, NamedWindowSensor, LastActiveSensor, diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs index 3ef2a15..f00bb3c 100644 --- a/hass-workstation-service/Data/ConfigurationService.cs +++ b/hass-workstation-service/Data/ConfigurationService.cs @@ -135,6 +135,9 @@ namespace hass_workstation_service.Data case "MicrophoneActiveSensor": sensor = new MicrophoneActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); break; + case "MicrophoneProcessSensor": + sensor = new MicrophoneProcessSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); + break; case "SessionStateSensor": sensor = new SessionStateSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); break; @@ -462,7 +465,7 @@ namespace hass_workstation_service.Data } /// - /// + /// /// /// The Id of the sensor to replace /// The new sensor diff --git a/hass-workstation-service/Domain/Sensors/MicrophoneProcessSensor.cs b/hass-workstation-service/Domain/Sensors/MicrophoneProcessSensor.cs new file mode 100644 index 0000000..17ec875 --- /dev/null +++ b/hass-workstation-service/Domain/Sensors/MicrophoneProcessSensor.cs @@ -0,0 +1,130 @@ +using hass_workstation_service.Communication; +using Microsoft.Win32; +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace hass_workstation_service.Domain.Sensors +{ + public class MicrophoneProcessSensor : AbstractSensor + { + public MicrophoneProcessSensor(MqttPublisher publisher, int? updateInterval = null, string name = "MicrophoneProcess", Guid id = default) : base(publisher, name ?? "MicrophoneProcess", updateInterval ?? 10, id) + { + } + + public override string GetState() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return IsMicrophoneInUseRegistry(); + } + else + { + return "unsupported"; + } + } + public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig() + { + return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + { + Name = this.Name, + NamePrefix = Publisher.NamePrefix, + Unique_id = this.Id.ToString(), + Device = this.Publisher.DeviceConfigModel, + State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(Publisher.NamePrefix, this.ObjectId)}/state", + Availability_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/availability" + }); + } + + [SupportedOSPlatform("windows")] + private string IsMicrophoneInUseRegistry() + { + using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone")) + { + foreach (var subKeyName in key.GetSubKeyNames()) + { + // NonPackaged has multiple subkeys + if (subKeyName == "NonPackaged") + { + using (var nonpackagedkey = key.OpenSubKey(subKeyName)) + { + foreach (var nonpackagedSubKeyName in nonpackagedkey.GetSubKeyNames()) + { + using (var subKey = nonpackagedkey.OpenSubKey(nonpackagedSubKeyName)) + { + if (subKey.GetValueNames().Contains("LastUsedTimeStop")) + { + var endTime = subKey.GetValue("LastUsedTimeStop") is long ? (long)subKey.GetValue("LastUsedTimeStop") : -1; + if (endTime <= 0) + { + return nonpackagedSubKeyName; + } + } + } + } + } + } + else + { + 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 subKeyName; + } + } + } + } + } + } + + using (var key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone")) + { + foreach (var subKeyName in key.GetSubKeyNames()) + { + // NonPackaged has multiple subkeys + if (subKeyName == "NonPackaged") + { + using (var nonpackagedkey = key.OpenSubKey(subKeyName)) + { + foreach (var nonpackagedSubKeyName in nonpackagedkey.GetSubKeyNames()) + { + using (var subKey = nonpackagedkey.OpenSubKey(nonpackagedSubKeyName)) + { + if (subKey.GetValueNames().Contains("LastUsedTimeStop")) + { + var endTime = subKey.GetValue("LastUsedTimeStop") is long ? (long)subKey.GetValue("LastUsedTimeStop") : -1; + if (endTime <= 0) + { + return nonpackagedSubKeyName; + } + } + } + } + } + } + else + { + 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 subKeyName; + } + } + } + } + } + } + + return "off"; + } + } +}