diff --git a/UserInterface/ViewModels/AddSensorViewModel.cs b/UserInterface/ViewModels/AddSensorViewModel.cs
index 0ea2220..665e4ad 100644
--- a/UserInterface/ViewModels/AddSensorViewModel.cs
+++ b/UserInterface/ViewModels/AddSensorViewModel.cs
@@ -14,9 +14,11 @@ namespace UserInterface.ViewModels
public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); }
public bool ShowQueryInput { get => showQueryInput; set => this.RaiseAndSetIfChanged(ref showQueryInput, value); }
+ public bool ShowWindowNameInput { get => showWindowNameInput; set => this.RaiseAndSetIfChanged(ref showWindowNameInput, value); }
private string moreInfoLink;
private int updateInterval;
+ private bool showWindowNameInput;
public string MoreInfoLink
{
@@ -28,6 +30,7 @@ namespace UserInterface.ViewModels
public AvailableSensors SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
public string Name { get; set; }
public string Query { get; set; }
+ public string WindowName { get; set; }
public int UpdateInterval { get => updateInterval; set => this.RaiseAndSetIfChanged(ref updateInterval, value); }
}
}
diff --git a/UserInterface/Views/AddSensorDialog.axaml b/UserInterface/Views/AddSensorDialog.axaml
index 7dff3e4..e80f016 100644
--- a/UserInterface/Views/AddSensorDialog.axaml
+++ b/UserInterface/Views/AddSensorDialog.axaml
@@ -23,6 +23,9 @@
Query
+ Window name
+ This is case-insensitive and loosely matched. A window called "Spotify Premium" will match "spotify" or "premium".
+
diff --git a/UserInterface/Views/AddSensorDialog.axaml.cs b/UserInterface/Views/AddSensorDialog.axaml.cs
index 3e31aef..ba83103 100644
--- a/UserInterface/Views/AddSensorDialog.axaml.cs
+++ b/UserInterface/Views/AddSensorDialog.axaml.cs
@@ -47,7 +47,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 { item.Name, item.Query, item.UpdateInterval, item.WindowName };
string json = JsonSerializer.Serialize(model);
await this.client.InvokeAsync(x => x.AddSensor(item.SelectedType, json));
Close();
@@ -62,42 +62,56 @@ namespace UserInterface.Views
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.ShowQueryInput = false;
+ 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#dummy";
item.ShowQueryInput = false;
+ 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#cpuload";
item.ShowQueryInput = false;
+ 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#currentclockspeed";
item.ShowQueryInput = false;
+ 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/sleevezipper/hass-workstation-service#wmiquerysensor";
item.ShowQueryInput = true;
+ 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#usedmemorysensor";
item.ShowQueryInput = false;
+ 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#activewindow";
item.ShowQueryInput = false;
+ item.ShowWindowNameInput = false;
+ item.UpdateInterval = 5;
+ 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:
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
index 64f77c7..dea8726 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
@@ -106,6 +106,9 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
case AvailableSensors.ActiveWindowSensor:
sensorToCreate = new ActiveWindowSensor(this._publisher, (int)model.UpdateInterval, model.Name);
break;
+ case AvailableSensors.NamedWindowSensor:
+ sensorToCreate = new NamedWindowSensor(this._publisher, model.WindowName, model.Name, (int)model.UpdateInterval);
+ 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 d6e73f2..9fc20e5 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
@@ -36,6 +36,7 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
CPULoadSensor,
WMIQuerySensor,
MemoryUsageSensor,
- ActiveWindowSensor
+ ActiveWindowSensor,
+ NamedWindowSensor
}
}
diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs
index daee9a9..d46bd6c 100644
--- a/hass-workstation-service/Data/ConfigurationService.cs
+++ b/hass-workstation-service/Data/ConfigurationService.cs
@@ -88,6 +88,9 @@ namespace hass_workstation_service.Data
case "ActiveWindowSensor":
sensor = new ActiveWindowSensor(publisher, configuredSensor.UpdateInterval, configuredSensor.Name, configuredSensor.Id);
break;
+ case "NamedWindowSensor":
+ sensor = new NamedWindowSensor(publisher, configuredSensor.WindowName, configuredSensor.Name, configuredSensor.UpdateInterval, configuredSensor.Id);
+ break;
default:
Log.Logger.Error("unsupported sensor type in config");
break;
@@ -164,6 +167,11 @@ namespace hass_workstation_service.Data
var wmiSensor = (WMIQuerySensor)sensor;
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
{
configuredSensorsToSave.Add(new ConfiguredSensor() { Id = sensor.Id, Name = sensor.Name, Type = sensor.GetType().Name, UpdateInterval = sensor.UpdateInterval });
diff --git a/hass-workstation-service/Data/ConfiguredSensor.cs b/hass-workstation-service/Data/ConfiguredSensor.cs
index 06dfd50..fd629dd 100644
--- a/hass-workstation-service/Data/ConfiguredSensor.cs
+++ b/hass-workstation-service/Data/ConfiguredSensor.cs
@@ -9,5 +9,6 @@ namespace hass_workstation_service.Data
public string Name { get; set; }
public string Query { get; set; }
public int? UpdateInterval { get; set; }
+ public string WindowName { get; set; }
}
}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
new file mode 100644
index 0000000..fe6c620
--- /dev/null
+++ b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
@@ -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";
+ }
+
+
+ /// Returns a dictionary that contains the handle and title of all the open windows.
+ /// A dictionary that contains the handle and title of all the open windows.
+ public static IDictionary GetOpenWindows()
+ {
+ HWND shellWindow = GetShellWindow();
+ Dictionary windows = new Dictionary();
+
+ 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();
+ }
+}