From 9020c72cae87560dcb6141f7a178bfae0d1ace68 Mon Sep 17 00:00:00 2001 From: sleevezipper Date: Sat, 9 Jan 2021 22:49:47 +0100 Subject: [PATCH] add sessionState sensor. This fixes #3 --- README.md | 14 ++- .../ViewModels/SensorSettingsViewModel.cs | 2 +- UserInterface/Views/AddSensorDialog.axaml.cs | 18 ++-- .../InterProcessApi.cs | 3 + .../ServiceContractModels.cs | 3 +- .../Data/ConfigurationService.cs | 3 + .../Domain/Sensors/IdleTimeSensor.cs | 1 + .../Domain/Sensors/MemoryUsageSensor.cs | 2 +- .../Domain/Sensors/SessionStateSensor.cs | 101 ++++++++++++++++++ .../Domain/Sensors/UpTimeSensor.cs | 1 + 10 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 hass-workstation-service/Domain/Sensors/SessionStateSensor.cs diff --git a/README.md b/README.md index 77e1ef0..14f1fb6 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,19 @@ This sensor returns the amount of seconds the workstation has been idle for. It ### UpTime -This sensor returns the up time from windows in seconds +This sensor returns theup time from Windows in seconds. + +### SessionState + +This sensor returns the current session state. It has the following possible states: + +|State|Explanation| +|---|---| +|Locked|All user sessions are locked.| +|LoggedOff|No users are logged in.| +|InUse|A user is currently logged in.| +|Unknown|Something went wrong while getting the status.| + ### Dummy This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it. diff --git a/UserInterface/ViewModels/SensorSettingsViewModel.cs b/UserInterface/ViewModels/SensorSettingsViewModel.cs index 0e4e9f4..1939bf6 100644 --- a/UserInterface/ViewModels/SensorSettingsViewModel.cs +++ b/UserInterface/ViewModels/SensorSettingsViewModel.cs @@ -40,7 +40,7 @@ namespace UserInterface.ViewModels { if (!string.IsNullOrWhiteSpace(_value)) { - return _value + UnitOfMeasurement; + return _value + " " + UnitOfMeasurement; } else return ""; diff --git a/UserInterface/Views/AddSensorDialog.axaml.cs b/UserInterface/Views/AddSensorDialog.axaml.cs index 9803984..3cd05a7 100644 --- a/UserInterface/Views/AddSensorDialog.axaml.cs +++ b/UserInterface/Views/AddSensorDialog.axaml.cs @@ -26,8 +26,10 @@ namespace UserInterface.Views #if DEBUG this.AttachDevTools(); #endif + DataContext = new AddSensorViewModel(); this.comboBox = this.FindControl("ComboBox"); - this.comboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast(); + this.comboBox.Items = Enum.GetValues(typeof(AvailableSensors)).Cast().OrderBy(v => v.ToString()); + this.comboBox.SelectedIndex = 0; // register IPC clients ServiceProvider serviceProvider = new ServiceCollection() @@ -40,9 +42,6 @@ namespace UserInterface.Views // create client this.client = clientFactory.CreateClient("addsensor"); - - - DataContext = new AddSensorViewModel(); } public async void Save(object sender, RoutedEventArgs args) @@ -144,12 +143,19 @@ namespace UserInterface.Views item.UpdateInterval = 5; break; case AvailableSensors.UpTimeSensor: - item.Description = "This sensor returns the up time from windows in seconds"; + item.Description = "This sensor returns the uptime from Windows in seconds"; item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#uptime"; item.ShowQueryInput = false; item.ShowWindowNameInput = false; item.UpdateInterval = 5; - break; + 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; default: item.Description = null; item.MoreInfoLink = null; diff --git a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs index 2765c63..51e9313 100644 --- a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs +++ b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs @@ -121,6 +121,9 @@ namespace hass_workstation_service.Communication.InterProcesCommunication case AvailableSensors.UpTimeSensor: sensorToCreate = new UpTimeSensor(this._publisher, (int)model.UpdateInterval, model.Name); break; + case AvailableSensors.SessionStateSensor: + sensorToCreate = new SessionStateSensor(this._publisher, (int)model.UpdateInterval, model.Name); + break; default: Log.Logger.Error("Unknown sensortype"); break; diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs index 8c67523..e7fa359 100644 --- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs +++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs @@ -43,6 +43,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models ActiveWindowSensor, NamedWindowSensor, IdleTimeSensor, - UpTimeSensor + UpTimeSensor, + SessionStateSensor } } diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs index b1093aa..4309a1e 100644 --- a/hass-workstation-service/Data/ConfigurationService.cs +++ b/hass-workstation-service/Data/ConfigurationService.cs @@ -108,6 +108,9 @@ namespace hass_workstation_service.Data case "MicrophoneActiveSensor": sensor = new MicrophoneActiveSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); break; + case "SessionStateSensor": + sensor = new SessionStateSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id); + break; default: Log.Logger.Error("unsupported sensor type in config"); break; diff --git a/hass-workstation-service/Domain/Sensors/IdleTimeSensor.cs b/hass-workstation-service/Domain/Sensors/IdleTimeSensor.cs index 03e7b61..5d69032 100644 --- a/hass-workstation-service/Domain/Sensors/IdleTimeSensor.cs +++ b/hass-workstation-service/Domain/Sensors/IdleTimeSensor.cs @@ -18,6 +18,7 @@ namespace hass_workstation_service.Domain.Sensors Device = this.Publisher.DeviceConfigModel, State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state", Icon = "mdi:clock-time-three-outline", + Unit_of_measurement = "seconds" }); } diff --git a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs index 19b3e50..ebb51dc 100644 --- a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs +++ b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs @@ -12,7 +12,7 @@ namespace hass_workstation_service.Domain.Sensors [SupportedOSPlatform("windows")] 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 MemoryUsageSensor(MqttPublisher publisher, int? updateInterval = null, string name = "MemoryUsage", Guid id = default) : base(publisher, "SELECT FreePhysicalMemory,TotalVisibleMemorySize FROM Win32_OperatingSystem", updateInterval ?? 10, name, id) { } public override string GetState() diff --git a/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs new file mode 100644 index 0000000..dc20f38 --- /dev/null +++ b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs @@ -0,0 +1,101 @@ +using hass_workstation_service.Communication; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace hass_workstation_service.Domain.Sensors +{ + enum PCUserStatuses + { + /// + /// all users are locked + /// + Locked, + /// + /// No users are logged in + /// + LoggedOff, + /// + /// A user is using this computer + /// + InUse, + /// + /// unable to connect to computer / other error + /// + Unknown + } + + public class SessionStateSensor : AbstractSensor + { + public SessionStateSensor(MqttPublisher publisher, int? updateInterval = null, string name = "SessionState", Guid id = default(Guid)) : base(publisher, name ?? "SessionState", 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/{Publisher.DeviceConfigModel.Name}/{this.Name}/state", + Icon = "mdi:lock", + }); + } + + public override string GetState() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return GetPCUserStatus().ToString(); + } + else return "unsupported"; + } + + + [SupportedOSPlatform("windows")] + PCUserStatuses GetPCUserStatus() + { + try + { + + var scope = new ManagementScope(); + scope.Connect(); + + var explorerProcesses = Process.GetProcessesByName("explorer") + .Select(p => p.Id.ToString()) + .ToHashSet(); + + var REprocessid = new Regex(@"(?<=Handle="").*?(?="")", RegexOptions.Compiled); + + var numberOfLogonSessionsWithExplorer = new ManagementObjectSearcher(scope, new SelectQuery("SELECT * FROM Win32_SessionProcess")).Get() + .Cast() + .Where(mo => explorerProcesses.Contains(REprocessid.Match(mo["Dependent"].ToString()).Value)) + .Select(mo => mo["Antecedent"].ToString()) + .Distinct() + .Count(); + + var numberOfUserDesktops = new ManagementObjectSearcher(scope, new SelectQuery("select * from win32_Perfrawdata_TermService_TerminalServicesSession")).Get().Count - 1; // don't count Service desktop + var numberOflogonUIProcesses = Process.GetProcessesByName("LogonUI").Length; + + if (numberOflogonUIProcesses >= numberOfUserDesktops) + { + if (numberOfLogonSessionsWithExplorer > 0) + return PCUserStatuses.Locked; + else + return PCUserStatuses.LoggedOff; + } + else + return PCUserStatuses.InUse; + } + catch + { + return PCUserStatuses.Unknown; + } + } + } +} diff --git a/hass-workstation-service/Domain/Sensors/UpTimeSensor.cs b/hass-workstation-service/Domain/Sensors/UpTimeSensor.cs index c1e4260..33edabb 100644 --- a/hass-workstation-service/Domain/Sensors/UpTimeSensor.cs +++ b/hass-workstation-service/Domain/Sensors/UpTimeSensor.cs @@ -26,6 +26,7 @@ namespace hass_workstation_service.Domain.Sensors Device = this.Publisher.DeviceConfigModel, State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state", Icon = "mdi:clock-time-three-outline", + Unit_of_measurement = "seconds" }); }