diff --git a/README.md b/README.md
index a508f42..cba50fc 100644
--- a/README.md
+++ b/README.md
@@ -124,3 +124,15 @@ This sensor returns the current session state. It has the following possible sta
### Dummy
This sensor spits out a random number every second. Useful for testing, maybe you'll find some other use for it.
+
+## Commands
+
+Commands can be used to trigger certain things on the client.
+
+### CustomCommand
+
+This command allows you to run any Windows Commands. The command will be run in a hidden Command Prompt. Some examples:
+
+|Command|Explanation|
+|---|---|
+|Rundll32.exe user32.dll,LockWorkStation|This locks the current session.|
\ No newline at end of file
diff --git a/UserInterface/UserInterface.csproj b/UserInterface/UserInterface.csproj
index c2877e3..7805ed5 100644
--- a/UserInterface/UserInterface.csproj
+++ b/UserInterface/UserInterface.csproj
@@ -23,9 +23,18 @@
+
+ AddCommandDialog.axaml
+
+
+ AppInfo.axaml
+
BackgroundServiceSettings.axaml
+
+ CommandSettings.axaml
+
SensorSettings.axaml
diff --git a/UserInterface/ViewModels/AddCommandViewModel.cs b/UserInterface/ViewModels/AddCommandViewModel.cs
new file mode 100644
index 0000000..7fc458a
--- /dev/null
+++ b/UserInterface/ViewModels/AddCommandViewModel.cs
@@ -0,0 +1,31 @@
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace UserInterface.ViewModels
+{
+ public class AddCommandViewModel : ViewModelBase
+ {
+ private AvailableCommands selectedType;
+ private string description;
+
+ public string Description { get => description; set => this.RaiseAndSetIfChanged(ref description, value); }
+ public bool ShowCommandInput { get => showCommandInput; set => this.RaiseAndSetIfChanged(ref showCommandInput, value); }
+
+ private string moreInfoLink;
+ private bool showCommandInput;
+
+ public string MoreInfoLink
+ {
+ get { return moreInfoLink; }
+ set { this.RaiseAndSetIfChanged(ref moreInfoLink, value); }
+ }
+
+ public AvailableCommands SelectedType { get => selectedType; set => this.RaiseAndSetIfChanged(ref selectedType, value); }
+
+ public string Name { get; set; }
+ public string Command { get; set; }
+ }
+}
diff --git a/UserInterface/ViewModels/CommandSettingsViewModel.cs b/UserInterface/ViewModels/CommandSettingsViewModel.cs
new file mode 100644
index 0000000..4477508
--- /dev/null
+++ b/UserInterface/ViewModels/CommandSettingsViewModel.cs
@@ -0,0 +1,25 @@
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace UserInterface.ViewModels
+{
+ public class CommandSettingsViewModel : ViewModelBase
+ {
+ private ICollection configuredCommands;
+
+ public ICollection ConfiguredCommands { get => configuredCommands; set => this.RaiseAndSetIfChanged(ref configuredCommands, value); }
+ public void TriggerUpdate()
+ {
+ this.RaisePropertyChanged();
+ }
+ }
+
+ public class CommandViewModel : ViewModelBase
+ {
+ public Guid Id { get; set; }
+ public string Type { get; set; }
+ public string Name { get; set; }
+ }
+}
diff --git a/UserInterface/Views/AddCommandDialog.axaml b/UserInterface/Views/AddCommandDialog.axaml
new file mode 100644
index 0000000..f359fc5
--- /dev/null
+++ b/UserInterface/Views/AddCommandDialog.axaml
@@ -0,0 +1,26 @@
+
+
+ Sensor type
+
+
+
+
+ Name
+
+
+
+
+
+ Command
+
+
+
+
+
diff --git a/UserInterface/Views/AddCommandDialog.axaml.cs b/UserInterface/Views/AddCommandDialog.axaml.cs
new file mode 100644
index 0000000..9db4d7b
--- /dev/null
+++ b/UserInterface/Views/AddCommandDialog.axaml.cs
@@ -0,0 +1,97 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using hass_workstation_service.Communication.NamedPipe;
+using JKang.IpcServiceFramework.Client;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Dynamic;
+using System.Linq;
+using System.Text.Json;
+using UserInterface.Util;
+using UserInterface.ViewModels;
+
+namespace UserInterface.Views
+{
+ public class AddCommandDialog : Window
+ {
+ private readonly IIpcClient client;
+ public ComboBox comboBox { get; set; }
+ public ComboBox detectionModecomboBox { get; set; }
+ public AddCommandDialog()
+ {
+ this.InitializeComponent();
+#if DEBUG
+ this.AttachDevTools();
+#endif
+ DataContext = new AddCommandViewModel();
+ this.comboBox = this.FindControl("ComboBox");
+ this.comboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast().OrderBy(v => v.ToString());
+ this.comboBox.SelectedIndex = 0;
+
+ // register IPC clients
+ ServiceProvider serviceProvider = new ServiceCollection()
+ .AddNamedPipeIpcClient("addCommand", pipeName: "pipeinternal")
+ .BuildServiceProvider();
+
+ // resolve IPC client factory
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
+
+ // create client
+ this.client = clientFactory.CreateClient("addCommand");
+ }
+
+ public async void Save(object sender, RoutedEventArgs args)
+ {
+ var item = ((AddCommandViewModel)this.DataContext);
+ dynamic model = new { item.Name, item.Command};
+ string json = JsonSerializer.Serialize(model);
+ await this.client.InvokeAsync(x => x.AddCommand(item.SelectedType, json));
+ Close();
+ }
+
+ public void ComboBoxClosed(object sender, SelectionChangedEventArgs args)
+ {
+ var item = ((AddCommandViewModel)this.DataContext);
+ switch (item.SelectedType)
+ {
+ case AvailableCommands.CustomCommand:
+ item.Description = "This command lets you execute any command you want. It will run in a Windows Command Prompt silently. ";
+ item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#customcommand";
+ item.ShowCommandInput = true;
+ break;
+ default:
+ item.Description = null;
+ item.MoreInfoLink = null;
+ item.ShowCommandInput = false;
+ break;
+ }
+ }
+ public void OpenInfo(object sender, RoutedEventArgs args)
+ {
+ var item = ((AddSensorViewModel)this.DataContext);
+ BrowserUtil.OpenBrowser(item.MoreInfoLink);
+ }
+
+ public void Test(object sender, RoutedEventArgs args)
+ {
+ var item = ((AddCommandViewModel)this.DataContext);
+
+ System.Diagnostics.Process process = new System.Diagnostics.Process();
+ System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
+ startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
+ startInfo.FileName = "cmd.exe";
+ startInfo.Arguments = $"/k {"echo You won't see this window normally. &&" + item.Command}";
+ process.StartInfo = startInfo;
+ process.Start();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UserInterface/Views/AppInfo.axaml b/UserInterface/Views/AppInfo.axaml
new file mode 100644
index 0000000..5bbdfbc
--- /dev/null
+++ b/UserInterface/Views/AppInfo.axaml
@@ -0,0 +1,17 @@
+
+
+
+ Info
+
+
+
+
+
+
+
+
diff --git a/UserInterface/Views/AppInfo.axaml.cs b/UserInterface/Views/AppInfo.axaml.cs
new file mode 100644
index 0000000..7a4c576
--- /dev/null
+++ b/UserInterface/Views/AppInfo.axaml.cs
@@ -0,0 +1,91 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Microsoft.Extensions.DependencyInjection;
+using hass_workstation_service.Communication.NamedPipe;
+using JKang.IpcServiceFramework.Client;
+using System.Threading.Tasks;
+using Avalonia.Interactivity;
+using System.Reactive.Linq;
+using UserInterface.ViewModels;
+using System.Security;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using UserInterface.Util;
+
+namespace UserInterface.Views
+{
+ public class AppInfo : UserControl
+ {
+ private readonly IIpcClient client;
+
+ public AppInfo()
+ {
+ this.InitializeComponent();
+ // register IPC clients
+ ServiceProvider serviceProvider = new ServiceCollection()
+ .AddNamedPipeIpcClient("broker", pipeName: "pipeinternal")
+ .BuildServiceProvider();
+
+ // resolve IPC client factory
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
+
+ // create client
+ this.client = clientFactory.CreateClient("broker");
+
+
+ DataContext = new BackgroundServiceSettingsViewModel();
+ Ping();
+ }
+ public async void Ping() {
+ while (true)
+ {
+ try
+ {
+ var result = await this.client.InvokeAsync(x => x.Ping("ping"));
+ if (result == "pong")
+ {
+ ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(true, "All good");
+ }
+ else
+ {
+ ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(false, "Not running");
+ }
+ }
+ catch (System.Exception)
+ {
+ ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateStatus(false, "Not running");
+ }
+
+ var autostartresult = await this.client.InvokeAsync(x => x.IsAutoStartEnabled());
+ ((BackgroundServiceSettingsViewModel)this.DataContext).UpdateAutostartStatus(autostartresult);
+
+ await Task.Delay(1000);
+ }
+ }
+
+ public void Github(object sender, RoutedEventArgs args)
+ {
+ BrowserUtil.OpenBrowser("https://github.com/sleevezipper/hass-workstation-service");
+ }
+
+ public void Discord(object sender, RoutedEventArgs args)
+ {
+ BrowserUtil.OpenBrowser("https://discord.gg/VraYT2N3wd");
+ }
+
+ public void EnableAutostart(object sender, RoutedEventArgs args)
+ {
+ this.client.InvokeAsync(x => x.EnableAutostart(true));
+ }
+ public void DisableAutostart(object sender, RoutedEventArgs args)
+ {
+ this.client.InvokeAsync(x => x.EnableAutostart(false));
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UserInterface/Views/CommandSettings.axaml b/UserInterface/Views/CommandSettings.axaml
new file mode 100644
index 0000000..587d109
--- /dev/null
+++ b/UserInterface/Views/CommandSettings.axaml
@@ -0,0 +1,26 @@
+
+
+ Commands
+
+
+
+
+
+
+
+ Add some commands by clicking the "Add" button.
+
+
+
+
+
diff --git a/UserInterface/Views/CommandSettings.axaml.cs b/UserInterface/Views/CommandSettings.axaml.cs
new file mode 100644
index 0000000..fb963f4
--- /dev/null
+++ b/UserInterface/Views/CommandSettings.axaml.cs
@@ -0,0 +1,82 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Microsoft.Extensions.DependencyInjection;
+using hass_workstation_service.Communication.NamedPipe;
+using JKang.IpcServiceFramework.Client;
+using System.Threading.Tasks;
+using Avalonia.Interactivity;
+using System.Reactive.Linq;
+using UserInterface.ViewModels;
+using System.Security;
+using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls.ApplicationLifetimes;
+
+namespace UserInterface.Views
+{
+ public class CommandSettings : UserControl
+ {
+ private readonly IIpcClient client;
+ private DataGrid _dataGrid { get; set; }
+ private bool sensorsNeedToRefresh { get; set; }
+
+ public CommandSettings()
+ {
+ this.InitializeComponent();
+ // register IPC clients
+ ServiceProvider serviceProvider = new ServiceCollection()
+ .AddNamedPipeIpcClient("commands", pipeName: "pipeinternal")
+ .BuildServiceProvider();
+
+ // resolve IPC client factory
+ IIpcClientFactory clientFactory = serviceProvider
+ .GetRequiredService>();
+
+ // create client
+ this.client = clientFactory.CreateClient("commands");
+
+
+ DataContext = new CommandSettingsViewModel();
+ GetConfiguredCommands();
+
+ this._dataGrid = this.FindControl("Grid");
+ }
+
+
+ public async void GetConfiguredCommands()
+ {
+ sensorsNeedToRefresh = false;
+ List status = await this.client.InvokeAsync(x => x.GetConfiguredCommands());
+
+ ((CommandSettingsViewModel)this.DataContext).ConfiguredCommands = status.Select(s => new CommandViewModel() { Name = s.Name, Type = s.Type, Id = s.Id}).ToList();
+
+ }
+ public void Delete(object sender, RoutedEventArgs args)
+ {
+ var item = ((CommandViewModel)this._dataGrid.SelectedItem);
+ this.client.InvokeAsync(x => x.RemoveCommandById(item.Id));
+ ((CommandSettingsViewModel)this.DataContext).ConfiguredCommands.Remove(item);
+ this._dataGrid.SelectedIndex = -1;
+ ((CommandSettingsViewModel)this.DataContext).TriggerUpdate();
+ }
+
+ public async void Add(object sender, RoutedEventArgs args)
+ {
+ AddCommandDialog dialog = new AddCommandDialog();
+ if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ await dialog.ShowDialog(desktop.MainWindow);
+ sensorsNeedToRefresh = true;
+ GetConfiguredCommands();
+ }
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ }
+}
diff --git a/UserInterface/Views/MainWindow.axaml b/UserInterface/Views/MainWindow.axaml
index 50d8e41..c8f2cc3 100644
--- a/UserInterface/Views/MainWindow.axaml
+++ b/UserInterface/Views/MainWindow.axaml
@@ -14,9 +14,11 @@
-
+
+
+
diff --git a/UserInterface/Views/SensorSettings.axaml b/UserInterface/Views/SensorSettings.axaml
index 48a2227..4d8d085 100644
--- a/UserInterface/Views/SensorSettings.axaml
+++ b/UserInterface/Views/SensorSettings.axaml
@@ -6,6 +6,7 @@
MaxWidth="800"
x:Class="UserInterface.Views.SensorSettings" >
+ Sensors
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
index 4fe7dc5..da03e81 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/InterProcessApi.cs
@@ -2,6 +2,7 @@ using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
using hass_workstation_service.Communication.Util;
using hass_workstation_service.Data;
+using hass_workstation_service.Domain.Commands;
using hass_workstation_service.Domain.Sensors;
using Serilog;
using System;
@@ -49,11 +50,19 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
return "what?";
}
+ ///
+ /// This writes the provided settings to the config file.
+ ///
+ ///
public void WriteMqttBrokerSettingsAsync(MqttSettings settings)
{
this._configurationService.WriteMqttBrokerSettingsAsync(settings);
}
+ ///
+ /// Enables or disables autostart.
+ ///
+ ///
public void EnableAutostart(bool enable)
{
this._configurationService.EnableAutoStart(enable);
@@ -69,11 +78,25 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
return this._configurationService.ConfiguredSensors.Select(s => new ConfiguredSensorModel() { Name = s.Name, Type = s.GetType().Name, Value = s.PreviousPublishedState, Id = s.Id, UpdateInterval = s.UpdateInterval, UnitOfMeasurement = s.GetAutoDiscoveryConfig().Unit_of_measurement }).ToList();
}
+ public List GetConfiguredCommands()
+ {
+ return this._configurationService.ConfiguredCommands.Select(s => new ConfiguredCommandModel() { Name = s.Name, Type = s.GetType().Name, Id = s.Id }).ToList();
+ }
+ public void RemoveCommandById(Guid id)
+ {
+ this._configurationService.DeleteConfiguredCommand(id);
+ }
+
public void RemoveSensorById(Guid id)
{
this._configurationService.DeleteConfiguredSensor(id);
}
+ ///
+ /// Adds a command to the configured commands. This properly initializes the class and writes it to the config file.
+ ///
+ ///
+ ///
public void AddSensor(AvailableSensors sensorType, string json)
{
var serializerOptions = new JsonSerializerOptions
@@ -133,5 +156,34 @@ namespace hass_workstation_service.Communication.InterProcesCommunication
this._configurationService.AddConfiguredSensor(sensorToCreate);
}
}
+
+ ///
+ /// Adds a command to the configured commands. This properly initializes the class, subscribes to the command topic and writes it to the config file.
+ ///
+ ///
+ ///
+ public void AddCommand(AvailableCommands commandType, string json)
+ {
+ var serializerOptions = new JsonSerializerOptions
+ {
+ Converters = { new DynamicJsonConverter() }
+ };
+ dynamic model = JsonSerializer.Deserialize(json, serializerOptions);
+
+ AbstractCommand commandToCreate = null;
+ switch (commandType)
+ {
+ case AvailableCommands.CustomCommand:
+ commandToCreate = new CustomCommand(this._publisher, model.Command, model.Name);
+ break;
+ default:
+ Log.Logger.Error("Unknown sensortype");
+ break;
+ }
+ if (commandToCreate != null)
+ {
+ this._configurationService.AddConfiguredCommand(commandToCreate);
+ }
+ }
}
}
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs
index a85f5fe..189bd5c 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractInterfaces.cs
@@ -17,5 +17,8 @@ namespace hass_workstation_service.Communication.NamedPipe
List GetConfiguredSensors();
void RemoveSensorById(Guid id);
void AddSensor(AvailableSensors sensorType, string json);
+ void RemoveCommandById(Guid id);
+ List GetConfiguredCommands();
+ void AddCommand(AvailableCommands commandType, string json);
}
}
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
index aee1b16..175ba8c 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
@@ -29,7 +29,13 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
public int UpdateInterval { get; set; }
public string UnitOfMeasurement { get; set; }
}
-
+ public class ConfiguredCommandModel
+ {
+ public Guid Id { get; set; }
+ public string Type { get; set; }
+ public string Name { get; set; }
+ public string Command { get; set; }
+ }
public enum AvailableSensors
{
UserNotificationStateSensor,
@@ -46,4 +52,9 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
LastBootSensor,
SessionStateSensor
}
+
+ public enum AvailableCommands
+ {
+ CustomCommand
+ }
}
diff --git a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
index 090cb2e..37e32ae 100644
--- a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
+++ b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
@@ -1,10 +1,13 @@
using System;
+using System.Collections.Generic;
+using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.Util;
using hass_workstation_service.Data;
+using hass_workstation_service.Domain.Commands;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Adapter;
@@ -24,6 +27,7 @@ namespace hass_workstation_service.Communication
private string _mqttClientMessage { get; set; }
public DateTime LastConfigAnnounce { get; private set; }
public DeviceConfigModel DeviceConfigModel { get; private set; }
+ public ICollection Subscribers { get; private set; }
public bool IsConnected
{
get
@@ -44,7 +48,7 @@ namespace hass_workstation_service.Communication
DeviceConfigModel deviceConfigModel,
IConfigurationService configurationService)
{
-
+ this.Subscribers = new List();
this._logger = logger;
this.DeviceConfigModel = deviceConfigModel;
this._configurationService = configurationService;
@@ -69,6 +73,8 @@ namespace hass_workstation_service.Communication
this._mqttClientMessage = "All good";
});
+ this._mqttClient.UseApplicationMessageReceivedHandler(e => this.HandleMessageReceived(e.ApplicationMessage));
+
// configure what happens on disconnect
this._mqttClient.UseDisconnectedHandler(async e =>
{
@@ -103,7 +109,7 @@ namespace hass_workstation_service.Communication
}
}
- public async Task AnnounceAutoDiscoveryConfig(AutoDiscoveryConfigModel config, bool clearConfig = false)
+ public async Task AnnounceAutoDiscoveryConfig(DiscoveryConfigModel config, string domain, bool clearConfig = false)
{
if (this._mqttClient.IsConnected)
{
@@ -111,11 +117,13 @@ namespace hass_workstation_service.Communication
{
PropertyNamingPolicy = new CamelCaseJsonNamingpolicy(),
IgnoreNullValues = true,
- PropertyNameCaseInsensitive = true
+ PropertyNameCaseInsensitive = true,
+
};
+
var message = new MqttApplicationMessageBuilder()
- .WithTopic($"homeassistant/sensor/{this.DeviceConfigModel.Name}/{config.Name}/config")
- .WithPayload(clearConfig ? "" : JsonSerializer.Serialize(config, options))
+ .WithTopic($"homeassistant/{domain}/{this.DeviceConfigModel.Name}/{config.Name}/config")
+ .WithPayload(clearConfig ? "" : JsonSerializer.Serialize(config, config.GetType(), options))
.WithRetainFlag()
.Build();
await this.Publish(message);
@@ -147,5 +155,72 @@ namespace hass_workstation_service.Communication
{
return new MqqtClientStatus() { IsConnected = _mqttClient.IsConnected, Message = _mqttClientMessage };
}
+
+ public async void AnnounceAvailability(string domain, bool offline = false)
+ {
+ if (this._mqttClient.IsConnected)
+ {
+ await this._mqttClient.PublishAsync(
+ new MqttApplicationMessageBuilder()
+ .WithTopic($"homeassistant/{domain}/{DeviceConfigModel.Name}/availability")
+ .WithPayload(offline ? "offline" : "online")
+ .Build()
+ );
+ }
+ else
+ {
+ this._logger.LogInformation($"Availability announce dropped because mqtt not connected");
+ }
+ }
+
+ public async Task DisconnectAsync()
+ {
+ if (this._mqttClient.IsConnected)
+ {
+ await this._mqttClient.DisconnectAsync();
+ }
+ else
+ {
+ this._logger.LogInformation($"Disconnected");
+ }
+ }
+
+ public async void Subscribe(AbstractCommand command)
+ {
+ if (this.IsConnected)
+ {
+ await this._mqttClient.SubscribeAsync(command.GetAutoDiscoveryConfig().Command_topic);
+ }
+ else
+ {
+ while (this.IsConnected == false)
+ {
+ await Task.Delay(5500);
+ }
+
+ await this._mqttClient.SubscribeAsync(command.GetAutoDiscoveryConfig().Command_topic);
+
+ }
+
+ Subscribers.Add(command);
+ }
+
+ private void HandleMessageReceived(MqttApplicationMessage applicationMessage)
+ {
+ foreach (AbstractCommand command in this.Subscribers)
+ {
+ if (command.GetAutoDiscoveryConfig().Command_topic == applicationMessage.Topic)
+ {
+ command.Execute();
+ }
+ }
+ Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
+ Console.WriteLine($"+ Topic = {applicationMessage.Topic}");
+
+ Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(applicationMessage?.Payload)}");
+ Console.WriteLine($"+ QoS = {applicationMessage.QualityOfServiceLevel}");
+ Console.WriteLine($"+ Retain = {applicationMessage.Retain}");
+ Console.WriteLine();
+ }
}
}
diff --git a/hass-workstation-service/Communication/MQTT/AutoDiscoveryConfigModel.cs b/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
similarity index 62%
rename from hass-workstation-service/Communication/MQTT/AutoDiscoveryConfigModel.cs
rename to hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
index 4a9ae66..a67f6b6 100644
--- a/hass-workstation-service/Communication/MQTT/AutoDiscoveryConfigModel.cs
+++ b/hass-workstation-service/Communication/MQTT/SensorDiscoveryConfigModel.cs
@@ -3,18 +3,26 @@ using System.Collections.Generic;
namespace hass_workstation_service.Communication
{
- public class AutoDiscoveryConfigModel
+ public abstract class DiscoveryConfigModel
{
///
- /// (Optional) The MQTT topic subscribed to receive availability (online/offline) updates.
+ /// (Optional) Information about the device this sensor is a part of to tie it into the device registry. Only works through MQTT discovery and when unique_id is set.
///
///
- public string Availability_topic { get; set; }
+ public DeviceConfigModel Device { get; set; }
///
- /// (Optional) Information about the device this sensor is a part of to tie it into the device registry. Only works through MQTT discovery and when unique_id is set.
+ /// (Optional) The name of the MQTT sensor. Defaults to MQTT Sensor in hass.
///
///
- public DeviceConfigModel Device { get; set; }
+ public string Name { get; set; }
+ }
+ public class SensorDiscoveryConfigModel : DiscoveryConfigModel
+ {
+ ///
+ /// (Optional) The MQTT topic subscribed to receive availability (online/offline) updates.
+ ///
+ ///
+ public string Availability_topic { get; set; }
///
/// (Optional) The type/class of the sensor to set the icon in the frontend. See https://www.home-assistant.io/integrations/sensor/#device-class for options.
///
@@ -47,11 +55,6 @@ namespace hass_workstation_service.Communication
///
public string Json_attributes_topic { get; set; }
///
- /// (Optional) The name of the MQTT sensor. Defaults to MQTT Sensor in hass.
- ///
- ///
- public string Name { get; set; }
- ///
/// (Optional) The payload that represents the available state.
///
///
@@ -89,6 +92,82 @@ namespace hass_workstation_service.Communication
public string Value_template { get; set; }
}
+ public class CommandDiscoveryConfigModel : DiscoveryConfigModel
+ {
+ ///
+ /// (Optional) The MQTT topic subscribed to receive availability (online/offline) updates.
+ ///
+ ///
+ public string Availability_topic { get; set; }
+ ///
+ /// (Optional) The MQTT topic to set the command
+ ///
+ ///
+ public string Command_topic { get; set; }
+ ///
+ /// (Optional) The type/class of the sensor to set the icon in the frontend. See https://www.home-assistant.io/integrations/sensor/#device-class for options.
+ ///
+ ///
+ public string Device_class { get; set; }
+ ///
+ /// (Optional) Defines the number of seconds after the sensor’s state expires, if it’s not updated. After expiry, the sensor’s state becomes unavailable. Defaults to 0 in hass.
+ ///
+ ///
+ public int? Expire_after { get; set; }
+ ///
+ /// Sends update events even if the value hasn’t changed. Useful if you want to have meaningful value graphs in history.
+ ///
+ ///
+ public bool? Force_update { get; set; }
+
+ ///
+ /// (Optional) The icon for the sensor.
+ ///
+ ///
+ public string Icon { get; set; }
+ ///
+ /// (Optional) Defines a template to extract the JSON dictionary from messages received on the json_attributes_topic.
+ ///
+ ///
+ public string Json_attributes_template { get; set; }
+ ///
+ /// (Optional) The MQTT topic subscribed to receive a JSON dictionary payload and then set as sensor attributes. Implies force_update of the current sensor state when a message is received on this topic.
+ ///
+ ///
+ public string Json_attributes_topic { get; set; }
+ ///
+ /// (Optional) The payload that represents the available state.
+ ///
+ ///
+ public string Payload_available { get; set; }
+ ///
+ /// (Optional) The payload that represents the unavailable state.
+ ///
+ ///
+ public string Payload_not_available { get; set; }
+ ///
+ /// (Optional) The maximum QoS level of the state topic.
+ ///
+ ///
+ public int? Qos { get; set; }
+ ///
+ /// The MQTT topic subscribed to receive sensor values.
+ ///
+ ///
+ public string State_topic { get; set; }
+ ///
+ /// (Optional) An ID that uniquely identifies this sensor. If two sensors have the same unique ID, Home Assistant will raise an exception.
+ ///
+ ///
+ public string Unique_id { get; set; }
+ ///
+ /// (Optional) Defines a template to extract the value.
+ ///
+ ///
+ public string Value_template { get; set; }
+ }
+
+
///
/// This information will be used when announcing this device on the mqtt topic
///
diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs
index 4527374..f130014 100644
--- a/hass-workstation-service/Data/ConfigurationService.cs
+++ b/hass-workstation-service/Data/ConfigurationService.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using hass_workstation_service.Communication;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
using hass_workstation_service.Communication.NamedPipe;
+using hass_workstation_service.Domain.Commands;
using hass_workstation_service.Domain.Sensors;
using Microsoft.Extensions.Configuration;
using Microsoft.Win32;
@@ -23,10 +24,12 @@ namespace hass_workstation_service.Data
public class ConfigurationService : IConfigurationService
{
public ICollection ConfiguredSensors { get; private set; }
+ public ICollection ConfiguredCommands { get; private set; }
public Action MqqtConfigChangedHandler { get; set; }
private bool BrokerSettingsFileLocked { get; set; }
private bool SensorsSettingsFileLocked { get; set; }
+ private bool CommandSettingsFileLocked { get; set; }
private readonly string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Hass Workstation Service");
@@ -42,7 +45,13 @@ namespace hass_workstation_service.Data
File.Create(Path.Combine(path, "configured-sensors.json")).Close();
}
+ if (!File.Exists(Path.Combine(path, "configured-commands.json")))
+ {
+ File.Create(Path.Combine(path, "configured-commands.json")).Close();
+ }
+
ConfiguredSensors = new List();
+ ConfiguredCommands = new List();
}
public async void ReadSensorSettings(MqttPublisher publisher)
@@ -123,6 +132,44 @@ namespace hass_workstation_service.Data
}
}
+ public async void ReadCommandSettings(MqttPublisher publisher)
+ {
+ while (this.CommandSettingsFileLocked)
+ {
+ await Task.Delay(500);
+ }
+ this.CommandSettingsFileLocked = true;
+ List commands = new List();
+ using (var stream = new FileStream(Path.Combine(path, "configured-commands.json"), FileMode.Open))
+ {
+ Log.Logger.Information($"reading configured commands from: {stream.Name}");
+ if (stream.Length > 0)
+ {
+ commands = await JsonSerializer.DeserializeAsync>(stream);
+ }
+ stream.Close();
+ this.CommandSettingsFileLocked = false;
+ }
+
+ foreach (ConfiguredCommand configuredCommand in commands)
+ {
+ AbstractCommand command = null;
+ switch (configuredCommand.Type)
+ {
+ case "CustomCommand":
+ command = new CustomCommand(publisher, configuredCommand.Command, configuredCommand.Name, configuredCommand.Id);
+ break;
+ default:
+ Log.Logger.Error("unsupported command type in config");
+ break;
+ }
+ if (command != null)
+ {
+ this.ConfiguredCommands.Add(command);
+ }
+ }
+ }
+
public async Task GetMqttClientOptionsAsync()
{
ConfiguredMqttBroker configuredBroker = await ReadMqttSettingsAsync();
@@ -173,7 +220,7 @@ namespace hass_workstation_service.Data
return configuredBroker;
}
- public async void WriteSettingsAsync()
+ public async void WriteSensorSettingsAsync()
{
while (this.SensorsSettingsFileLocked)
{
@@ -210,11 +257,44 @@ namespace hass_workstation_service.Data
this.SensorsSettingsFileLocked = false;
}
+ public async void WriteCommandSettingsAsync()
+ {
+ while (this.CommandSettingsFileLocked)
+ {
+ await Task.Delay(500);
+ }
+ this.CommandSettingsFileLocked = true;
+ List configuredCommandsToSave = new List();
+ using (FileStream stream = new FileStream(Path.Combine(path, "configured-commands.json"), FileMode.Open))
+ {
+ stream.SetLength(0);
+ Log.Logger.Information($"writing configured commands to: {stream.Name}");
+ foreach (AbstractCommand command in this.ConfiguredCommands)
+ {
+ if (command is CustomCommand customcommand)
+ {
+ configuredCommandsToSave.Add(new ConfiguredCommand() { Id = customcommand.Id, Name = customcommand.Name, Type = customcommand.GetType().Name, Command = customcommand.Command });
+ }
+ }
+
+ await JsonSerializer.SerializeAsync(stream, configuredCommandsToSave);
+ stream.Close();
+ }
+ this.CommandSettingsFileLocked = false;
+ }
+
public void AddConfiguredSensor(AbstractSensor sensor)
{
this.ConfiguredSensors.Add(sensor);
sensor.PublishAutoDiscoveryConfigAsync();
- WriteSettingsAsync();
+ WriteSensorSettingsAsync();
+ }
+
+ public void AddConfiguredCommand(AbstractCommand command)
+ {
+ this.ConfiguredCommands.Add(command);
+ command.PublishAutoDiscoveryConfigAsync();
+ WriteCommandSettingsAsync();
}
public async void DeleteConfiguredSensor(Guid id)
@@ -224,7 +304,7 @@ namespace hass_workstation_service.Data
{
await sensorToRemove.UnPublishAutoDiscoveryConfigAsync();
this.ConfiguredSensors.Remove(sensorToRemove);
- WriteSettingsAsync();
+ WriteSensorSettingsAsync();
}
else
{
@@ -233,10 +313,26 @@ namespace hass_workstation_service.Data
}
+ public async void DeleteConfiguredCommand(Guid id)
+ {
+ var sensorToRemove = this.ConfiguredCommands.FirstOrDefault(s => s.Id == id);
+ if (sensorToRemove != null)
+ {
+ await sensorToRemove.UnPublishAutoDiscoveryConfigAsync();
+ this.ConfiguredCommands.Remove(sensorToRemove);
+ WriteSensorSettingsAsync();
+ }
+ else
+ {
+ Log.Logger.Warning($"command with id {id} not found");
+ }
+
+ }
+
public void AddConfiguredSensors(List sensors)
{
sensors.ForEach((sensor) => this.ConfiguredSensors.Add(sensor));
- WriteSettingsAsync();
+ WriteSensorSettingsAsync();
}
///
diff --git a/hass-workstation-service/Data/ConfiguredCommand.cs b/hass-workstation-service/Data/ConfiguredCommand.cs
new file mode 100644
index 0000000..d5d6b48
--- /dev/null
+++ b/hass-workstation-service/Data/ConfiguredCommand.cs
@@ -0,0 +1,13 @@
+using hass_workstation_service.Domain.Sensors;
+using System;
+
+namespace hass_workstation_service.Data
+{
+ public class ConfiguredCommand
+ {
+ public string Type { get; set; }
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public string Command { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Data/IConfigurationService.cs b/hass-workstation-service/Data/IConfigurationService.cs
index 3c63154..acbea17 100644
--- a/hass-workstation-service/Data/IConfigurationService.cs
+++ b/hass-workstation-service/Data/IConfigurationService.cs
@@ -1,5 +1,6 @@
using hass_workstation_service.Communication;
using hass_workstation_service.Communication.InterProcesCommunication.Models;
+using hass_workstation_service.Domain.Commands;
using hass_workstation_service.Domain.Sensors;
using MQTTnet.Client.Options;
using System;
@@ -13,16 +14,21 @@ namespace hass_workstation_service.Data
{
ICollection ConfiguredSensors { get; }
Action MqqtConfigChangedHandler { get; set; }
+ ICollection ConfiguredCommands { get; }
+ void AddConfiguredCommand(AbstractCommand command);
void AddConfiguredSensor(AbstractSensor sensor);
void AddConfiguredSensors(List sensors);
Task GetMqttClientOptionsAsync();
void ReadSensorSettings(MqttPublisher publisher);
void WriteMqttBrokerSettingsAsync(MqttSettings settings);
- void WriteSettingsAsync();
+ void WriteSensorSettingsAsync();
Task GetMqttBrokerSettings();
void EnableAutoStart(bool enable);
bool IsAutoStartEnabled();
void DeleteConfiguredSensor(Guid id);
+ void DeleteConfiguredCommand(Guid id);
+ void WriteCommandSettingsAsync();
+ void ReadCommandSettings(MqttPublisher publisher);
}
}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/AbstractDiscoverable.cs b/hass-workstation-service/Domain/AbstractDiscoverable.cs
new file mode 100644
index 0000000..7c388f7
--- /dev/null
+++ b/hass-workstation-service/Domain/AbstractDiscoverable.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain
+{
+ public abstract class AbstractDiscoverable
+ {
+ public abstract string Domain { get; }
+ }
+}
diff --git a/hass-workstation-service/Domain/Commands/AbstractCommand.cs b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
new file mode 100644
index 0000000..7e29b98
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Threading.Tasks;
+using hass_workstation_service.Communication;
+using MQTTnet;
+
+namespace hass_workstation_service.Domain.Commands
+{
+
+ public abstract class AbstractCommand : AbstractDiscoverable
+ {
+ public Guid Id { get; protected set; }
+ public string Name { get; protected set; }
+ ///
+ /// The update interval in seconds. It checks state only if the interval has passed.
+ ///
+ public int UpdateInterval { get; protected set; }
+ public DateTime? LastUpdated { get; protected set; }
+ public string PreviousPublishedState { get; protected set; }
+ public MqttPublisher Publisher { get; protected set; }
+ public override string Domain { get => "switch"; }
+ public AbstractCommand(MqttPublisher publisher, string name, Guid id = default(Guid))
+ {
+ if (id == Guid.Empty)
+ {
+ this.Id = Guid.NewGuid();
+ }
+ else
+ {
+ this.Id = id;
+ }
+ this.Name = name;
+ this.Publisher = publisher;
+ publisher.Subscribe(this);
+
+ }
+ protected CommandDiscoveryConfigModel _autoDiscoveryConfigModel;
+ protected CommandDiscoveryConfigModel SetAutoDiscoveryConfigModel(CommandDiscoveryConfigModel config)
+ {
+ this._autoDiscoveryConfigModel = config;
+ return config;
+ }
+
+ public abstract CommandDiscoveryConfigModel GetAutoDiscoveryConfig();
+ public abstract string GetState();
+
+ public async Task PublishStateAsync()
+ {
+ if (LastUpdated.HasValue && LastUpdated.Value.AddSeconds(this.UpdateInterval) > DateTime.UtcNow)
+ {
+ // dont't even check the state if the update interval hasn't passed
+ return;
+ }
+ string state = this.GetState();
+ if (this.PreviousPublishedState == state)
+ {
+ // don't publish the state if it hasn't changed
+ return;
+ }
+ var message = new MqttApplicationMessageBuilder()
+ .WithTopic(this.GetAutoDiscoveryConfig().State_topic)
+ .WithPayload(state)
+ .WithExactlyOnceQoS()
+ .WithRetainFlag()
+ .Build();
+ await Publisher.Publish(message);
+ this.PreviousPublishedState = state;
+ this.LastUpdated = DateTime.UtcNow;
+ }
+ public async void PublishAutoDiscoveryConfigAsync()
+ {
+ await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig(), this.Domain);
+ }
+ public async Task UnPublishAutoDiscoveryConfigAsync()
+ {
+ await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig(), this.Domain, true);
+ }
+ public abstract void Execute();
+ }
+}
\ No newline at end of file
diff --git a/hass-workstation-service/Domain/Commands/CustomCommand.cs b/hass-workstation-service/Domain/Commands/CustomCommand.cs
new file mode 100644
index 0000000..aff15ec
--- /dev/null
+++ b/hass-workstation-service/Domain/Commands/CustomCommand.cs
@@ -0,0 +1,48 @@
+using hass_workstation_service.Communication;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hass_workstation_service.Domain.Commands
+{
+ public class CustomCommand : AbstractCommand
+ {
+ public string Command { get; protected set; }
+ public CustomCommand(MqttPublisher publisher, string command, string name = "Custom", Guid id = default(Guid)) : base(publisher, name ?? "Custom", id)
+ {
+ this.Command = command;
+ }
+
+ public override void Execute()
+ {
+ System.Diagnostics.Process process = new System.Diagnostics.Process();
+ System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
+ startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
+ startInfo.CreateNoWindow = true;
+ startInfo.FileName = "cmd.exe";
+ startInfo.Arguments = $"/C {this.Command}";
+ process.StartInfo = startInfo;
+ process.Start();
+ }
+
+ public override CommandDiscoveryConfigModel GetAutoDiscoveryConfig()
+ {
+ return new CommandDiscoveryConfigModel()
+ {
+ Name = this.Name,
+ Unique_id = this.Id.ToString(),
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Command_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/set",
+ Device = this.Publisher.DeviceConfigModel,
+ Expire_after = 60
+ };
+ }
+
+ public override string GetState()
+ {
+ return "off";
+ }
+ }
+}
diff --git a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
index 1b095bc..8c5cd50 100644
--- a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
@@ -6,7 +6,7 @@ using MQTTnet;
namespace hass_workstation_service.Domain.Sensors
{
- public abstract class AbstractSensor
+ public abstract class AbstractSensor : AbstractDiscoverable
{
public Guid Id { get; protected set; }
public string Name { get; protected set; }
@@ -17,6 +17,7 @@ namespace hass_workstation_service.Domain.Sensors
public DateTime? LastUpdated { get; protected set; }
public string PreviousPublishedState { get; protected set; }
public MqttPublisher Publisher { get; protected set; }
+ public override string Domain { get => "sensor"; }
public AbstractSensor(MqttPublisher publisher, string name, int updateInterval = 10, Guid id = default(Guid))
{
if (id == Guid.Empty)
@@ -32,14 +33,14 @@ namespace hass_workstation_service.Domain.Sensors
this.UpdateInterval = updateInterval;
}
- protected AutoDiscoveryConfigModel _autoDiscoveryConfigModel;
- protected AutoDiscoveryConfigModel SetAutoDiscoveryConfigModel(AutoDiscoveryConfigModel config)
+ protected SensorDiscoveryConfigModel _autoDiscoveryConfigModel;
+ protected SensorDiscoveryConfigModel SetAutoDiscoveryConfigModel(SensorDiscoveryConfigModel config)
{
this._autoDiscoveryConfigModel = config;
return config;
}
- public abstract AutoDiscoveryConfigModel GetAutoDiscoveryConfig();
+ public abstract SensorDiscoveryConfigModel GetAutoDiscoveryConfig();
public abstract string GetState();
public async Task PublishStateAsync()
@@ -67,11 +68,11 @@ namespace hass_workstation_service.Domain.Sensors
}
public async void PublishAutoDiscoveryConfigAsync()
{
- await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig());
+ await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig(), this.Domain);
}
public async Task UnPublishAutoDiscoveryConfigAsync()
{
- await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig(), true);
+ await this.Publisher.AnnounceAutoDiscoveryConfig(this.GetAutoDiscoveryConfig(), this.Domain, true);
}
}
diff --git a/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs b/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
index d431f14..79cdcb3 100644
--- a/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/ActiveWindowSensor.cs
@@ -9,15 +9,17 @@ namespace hass_workstation_service.Domain.Sensors
public class ActiveWindowSensor : AbstractSensor
{
public ActiveWindowSensor(MqttPublisher publisher, int? updateInterval = null, string name = "ActiveWindow", Guid id = default(Guid)) : base(publisher, name ?? "ActiveWindow", updateInterval ?? 10, id) { }
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:window-maximize",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs b/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
index 7000943..21710fa 100644
--- a/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/CPULoadSensor.cs
@@ -17,16 +17,18 @@ namespace hass_workstation_service.Domain.Sensors
{
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:chart-areaspline",
- Unit_of_measurement = "%"
+ Unit_of_measurement = "%",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs b/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
index 921a96d..5512d02 100644
--- a/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/CurrentClockSpeedSensor.cs
@@ -9,16 +9,18 @@ namespace hass_workstation_service.Domain.Sensors
{
public CurrentClockSpeedSensor(MqttPublisher publisher, int? updateInterval = null, string name = "CurrentClockSpeed", Guid id = default(Guid)) : base(publisher, "SELECT CurrentClockSpeed FROM Win32_Processor", updateInterval ?? 10, name ?? "CurrentClockSpeed", id) { }
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:speedometer",
- Unit_of_measurement = "MHz"
+ Unit_of_measurement = "MHz",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
}
diff --git a/hass-workstation-service/Domain/Sensors/DummySensor.cs b/hass-workstation-service/Domain/Sensors/DummySensor.cs
index 5960104..b63cee1 100644
--- a/hass-workstation-service/Domain/Sensors/DummySensor.cs
+++ b/hass-workstation-service/Domain/Sensors/DummySensor.cs
@@ -13,14 +13,16 @@ namespace hass_workstation_service.Domain.Sensors
this._random = new Random();
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state"
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs b/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
index 4a81342..07ef844 100644
--- a/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/LastActiveSensor.cs
@@ -9,15 +9,17 @@ namespace hass_workstation_service.Domain.Sensors
public LastActiveSensor(MqttPublisher publisher, int? updateInterval = 10, string name = "LastActive", Guid id = default) : base(publisher, name ?? "LastActive", updateInterval ?? 10, id){}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
- Icon = "mdi:clock-time-three-outline"
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ Icon = "mdi:clock-time-three-outline",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/LastBootSensor.cs b/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
index b9bcbd6..bda0e89 100644
--- a/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/LastBootSensor.cs
@@ -13,15 +13,17 @@ namespace hass_workstation_service.Domain.Sensors
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
- Icon = "mdi:clock-time-three-outline"
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ Icon = "mdi:clock-time-three-outline",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
index d905475..998369f 100644
--- a/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/MemoryUsageSensor.cs
@@ -34,16 +34,18 @@ namespace hass_workstation_service.Domain.Sensors
}
return "";
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:memory",
- Unit_of_measurement = "%"
+ Unit_of_measurement = "%",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
}
diff --git a/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs b/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
index e85f854..7e78826 100644
--- a/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/MicrophoneActiveSensor.cs
@@ -21,15 +21,17 @@ namespace hass_workstation_service.Domain.Sensors
}
else return "unsupported";
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:microphone",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
index 7cf7989..a852bf0 100644
--- a/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/NamedWindowSensor.cs
@@ -16,15 +16,17 @@ namespace hass_workstation_service.Domain.Sensors
this.WindowName = windowName;
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:window-maximize",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
index dc20f38..02efbc5 100644
--- a/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/SessionStateSensor.cs
@@ -35,15 +35,17 @@ namespace hass_workstation_service.Domain.Sensors
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()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:lock",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs b/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
index e98e1d9..e566be6 100644
--- a/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/UserNotificationStateSensor.cs
@@ -9,15 +9,17 @@ namespace hass_workstation_service.Domain.Sensors
{
public UserNotificationStateSensor(MqttPublisher publisher, int? updateInterval = null, string name = "NotificationState", Guid id = default(Guid)) : base(publisher, name ?? "NotificationState", updateInterval ?? 10, id) { }
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:laptop",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs b/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
index ed44784..1c2ff62 100644
--- a/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
+++ b/hass-workstation-service/Domain/Sensors/WMIQuerySensor.cs
@@ -20,14 +20,16 @@ namespace hass_workstation_service.Domain.Sensors
_objectQuery = new ObjectQuery(this.Query);
_searcher = new ManagementObjectSearcher(query);
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs b/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
index c690c03..fc08ba3 100644
--- a/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/WebcamActiveSensor.cs
@@ -23,15 +23,17 @@ namespace hass_workstation_service.Domain.Sensors
return "unsupported";
}
}
- public override AutoDiscoveryConfigModel GetAutoDiscoveryConfig()
+ public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
{
- return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new AutoDiscoveryConfigModel()
+ return this._autoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel()
{
Name = this.Name,
Unique_id = this.Id.ToString(),
Device = this.Publisher.DeviceConfigModel,
- State_topic = $"homeassistant/sensor/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
+ State_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/{this.Name}/state",
Icon = "mdi:webcam",
+ Availability_topic = $"homeassistant/{this.Domain}/{Publisher.DeviceConfigModel.Name}/availability",
+ Expire_after = 60
});
}
diff --git a/hass-workstation-service/Worker.cs b/hass-workstation-service/Worker.cs
index 540225b..e91a9ca 100644
--- a/hass-workstation-service/Worker.cs
+++ b/hass-workstation-service/Worker.cs
@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using hass_workstation_service.Communication;
using hass_workstation_service.Data;
+using hass_workstation_service.Domain.Commands;
using hass_workstation_service.Domain.Sensors;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -30,6 +31,7 @@ namespace hass_workstation_service
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
+ _configurationService.ReadCommandSettings(_mqttPublisher);
_configurationService.ReadSensorSettings(_mqttPublisher);
while (!_mqttPublisher.IsConnected)
@@ -40,11 +42,17 @@ namespace hass_workstation_service
_logger.LogInformation("Connected. Sending auto discovery messages.");
List sensors = _configurationService.ConfiguredSensors.ToList();
-
+ List commands = _configurationService.ConfiguredCommands.ToList();
+ _mqttPublisher.AnnounceAvailability("sensor");
+ _mqttPublisher.AnnounceAvailability("switch");
foreach (AbstractSensor sensor in sensors)
{
sensor.PublishAutoDiscoveryConfigAsync();
}
+ foreach (AbstractCommand command in commands)
+ {
+ command.PublishAutoDiscoveryConfigAsync();
+ }
while (!stoppingToken.IsCancellationRequested)
{
sensors = _configurationService.ConfiguredSensors.ToList();
@@ -60,7 +68,7 @@ namespace hass_workstation_service
{
Log.Logger.Warning("Sensor failed: " + sensor.Name, ex);
}
-
+
}
// announce autodiscovery every 30 seconds
if (_mqttPublisher.LastConfigAnnounce < DateTime.UtcNow.AddSeconds(-30))
@@ -69,9 +77,24 @@ namespace hass_workstation_service
{
sensor.PublishAutoDiscoveryConfigAsync();
}
+ foreach (AbstractCommand command in commands)
+ {
+ command.PublishAutoDiscoveryConfigAsync();
+ }
+ _mqttPublisher.AnnounceAvailability("sensor");
+ _mqttPublisher.AnnounceAvailability("switch");
}
await Task.Delay(1000, stoppingToken);
}
+
}
+
+ public override async Task StopAsync(CancellationToken stoppingToken)
+ {
+ _mqttPublisher.AnnounceAvailability("sensor", true);
+ _mqttPublisher.AnnounceAvailability("switch", true);
+ await _mqttPublisher.DisconnectAsync();
+ }
+
}
}