diff --git a/README.md b/README.md
index 093c726..d964599 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,25 @@ It will try to futher accomplish this goal in the future by:
- Being platform independent
-## Screenshots
+## Hass Workstation Service in the press
+
+
+View some screenshots
![The settings screen](https://i.imgur.com/RBQx807.png)
![The resulting sensors and commands in Home Assistant](https://i.imgur.com/jXRU2cu.png)
-Not convinced yet? Check out [this excellent video](https://youtu.be/D5A7le79R5M) by GeekToolkit on YouTube.
+
+
+If you would prefer a video, look at :
+ - [How to Control a PC from Home Assistant](https://youtu.be/D5A7le79R5M) by GeekToolkit on YouTube.
+ - [Mit Home-Assistant den Computer AN und AUS schalten!](https://www.youtube.com/watch?v=oDJHGEcV84A) by Fabsenet on YouTube.
+ - [The Butter, What?! show's review](https://youtu.be/wBTKfwkV-vs?t=376) by Pat and Brian on youtube.
+
+Or a written article :
+ - [How to Setup HASS Workstation Service in Home Assistant](https://smarthomepursuits.com/how-to-setup-hass-workstation-service-in-home-assistant/) by Danny @ smarthomepursuits.com
+ - [Control your Windows PC with HASS Workstation Service](https://home-assistant-guide.com/2021/01/18/control-your-windows-pc-with-hass-workstation-service/) by home-assistant-guide.com
## Installation
@@ -35,7 +47,7 @@ If you don't want to use the installer, standalone is what you need. Make sure t
As a prerequisite, make sure you have an MQTT username and password available. Using Home Assistant in combination with the Mosquitto broker add-on and integration? You can both use a Home Assistant account and a local account. From a security perspective, we recommend a local account as this only provides access to the MQTT Broker and not to your Home Assistant instance.
Now that you are all set, make sure to run the `hass-workstation-service.exe` executable first. This executable is responsible for setting up the sensors and talking with your MQTT Broker. To configure the service, start the `UserInterface.exe` executable.
-Add your `hostname` or `IP address`, `port`, `username` and `password` and click on Save. In case you use the Mosquitto add-in, provide port `8883` and check `Use TLS`. The application will mention "All good" when configured correctly.
+Add your `hostname` or `IP address`, `port`, `username` and `password` and click on Save. In case you use the Mosquitto add-in, provide port `8883` and check `Use TLS`. If you don't want to use TLS the default port is `1883`. The application will mention "All good" when configured correctly.
### Updating
@@ -43,7 +55,7 @@ If you used the installer, the app checks for updates on startup. If an update i
## Need help?
-Find us on [Discord](https://discord.gg/VraYT2N3wd).
+Find us on [Discord](https://discord.gg/VraYT2N3wd), or check out the [frequently asked questions](https://github.com/sleevezipper/hass-workstation-service/blob/master/documentation/FAQ.md#frequently-asked-questions).
## Development
@@ -67,8 +79,8 @@ Here is a list of the most commonly used sensors with the full documentation [he
|sensor|use|
|---|---|
|ActiveWindow|Exposes the currently selected window|
-|WebcamActive|Exposes the microphone state|
-|MicrophoneActive|Exposes the webcam state|
+|WebcamActive|Exposes the webcam state|
+|MicrophoneActive|Exposes the microphone state|
## Commands
diff --git a/UserInterface/ViewModels/BrokerSettingsViewModel.cs b/UserInterface/ViewModels/BrokerSettingsViewModel.cs
index 2811cb3..5c999d6 100644
--- a/UserInterface/ViewModels/BrokerSettingsViewModel.cs
+++ b/UserInterface/ViewModels/BrokerSettingsViewModel.cs
@@ -16,6 +16,9 @@ namespace UserInterface.ViewModels
private bool isConnected;
private int? port;
private bool useTLS;
+ private bool retainLWT = true;
+ private string rootCaPath;
+ private string clientCertPath;
public bool IsConnected { get => isConnected; set => this.RaiseAndSetIfChanged(ref isConnected, value); }
public string Message { get => message; set => this.RaiseAndSetIfChanged(ref message, value); }
@@ -29,6 +32,13 @@ namespace UserInterface.ViewModels
public bool UseTLS { get => useTLS; set => this.RaiseAndSetIfChanged(ref useTLS, value); }
+ public bool RetainLWT { get => retainLWT; set => this.RaiseAndSetIfChanged(ref retainLWT, value); }
+
+ public string RootCAPath { get => rootCaPath; set => this.RaiseAndSetIfChanged(ref rootCaPath, value); }
+
+ public string ClientCertPath { get => clientCertPath; set => this.RaiseAndSetIfChanged(ref clientCertPath, value); }
+
+
public void Update(MqttSettings settings)
{
this.Host = settings.Host;
@@ -36,6 +46,9 @@ namespace UserInterface.ViewModels
this.Password = settings.Password;
this.Port = settings.Port;
this.UseTLS = settings.UseTLS;
+ this.RetainLWT = settings.RetainLWT;
+ this.RootCAPath = settings.RootCAPath;
+ this.ClientCertPath = settings.ClientCertPath;
}
public void UpdateStatus(MqqtClientStatus status)
diff --git a/UserInterface/Views/BrokerSettings.axaml b/UserInterface/Views/BrokerSettings.axaml
index b23b798..92673df 100644
--- a/UserInterface/Views/BrokerSettings.axaml
+++ b/UserInterface/Views/BrokerSettings.axaml
@@ -19,12 +19,69 @@
Use TLS
+
+
+
+ Retain LastWillAndTestament
+
+ (What's this?)
+
+
+
+[Experimental]
+If set, sets Retain on the Last Will and Testament message.
+Only turn this off if you use a broker that does not support this(e.g. AWS IoT Core)
+Defaults to True
+
+
+
+
+
+
+
Username
+
Password
-
+
+
+
+ Root Cert Path (.pem/.crt)
+
+ (What's this?)
+
+
+
+ [Experimental]
+ If set, use this certificate in the TLS configuration for the MQTT connection.
+ This will be a pem or crt file provided by your broker.
+
+
+
+
+
+
+
+
+ Client Cert Path (.pfx)
+
+ (What's this?)
+
+
+
+ [Experimental]
+ If set, use this certificate in the TLS configuration for the MQTT connection.
+ This should be the private key .pfx file for a device created in your broker corresponding to this Windows PC.
+
+
+
+
+
+
+
+
diff --git a/UserInterface/Views/BrokerSettings.axaml.cs b/UserInterface/Views/BrokerSettings.axaml.cs
index e870a80..746b44d 100644
--- a/UserInterface/Views/BrokerSettings.axaml.cs
+++ b/UserInterface/Views/BrokerSettings.axaml.cs
@@ -50,7 +50,7 @@ namespace UserInterface.Views
ICollection results;
if (model.IsValid(model, out results))
{
- var result = this.client.InvokeAsync(x => x.WriteMqttBrokerSettingsAsync(new MqttSettings() { Host = model.Host, Username = model.Username, Password = model.Password ?? "", Port = model.Port, UseTLS = model.UseTLS }));
+ var result = this.client.InvokeAsync(x => x.WriteMqttBrokerSettingsAsync(new MqttSettings() { Host = model.Host, Username = model.Username, Password = model.Password ?? "", Port = model.Port, UseTLS = model.UseTLS, RootCAPath = model.RootCAPath, ClientCertPath = model.ClientCertPath, RetainLWT = model.RetainLWT }));
}
}
diff --git a/UserInterface/Views/GeneralSettings.axaml b/UserInterface/Views/GeneralSettings.axaml
index 4ca197d..a2c723e 100644
--- a/UserInterface/Views/GeneralSettings.axaml
+++ b/UserInterface/Views/GeneralSettings.axaml
@@ -8,18 +8,18 @@
SettingsName prefix
- (What's this?)
-
-
-
-[Experimental]
-This allows you to set a name which will be used to prefix all sensor- and command names. For example:
-If a sensor is called "ActiveWindow" and the name prefix is set to "laptop", the sensor will be named "laptop-ActiveWindow" and its entityId will be "laptop_activewindow".
-
+ (What's this?)
+
+
+
+ [Experimental]
+ This allows you to set a name which will be used to prefix all sensor- and command names. For example:
+ If a sensor is called "ActiveWindow" and the name prefix is set to "laptop", the sensor will be named "laptop-ActiveWindow" and its entityId will be "laptop_activewindow".
+
-
-
-
+
+
+
diff --git a/documentation/FAQ.md b/documentation/FAQ.md
new file mode 100644
index 0000000..d7984e3
--- /dev/null
+++ b/documentation/FAQ.md
@@ -0,0 +1,25 @@
+# Frequently asked questions
+
+There are some common problems people encounter with hass workstations service, so if you run into a problem you should search this list. Most browsers have a search function you can access by pressing `CTRL` and `F` simultaniously.
+
+If you cannot solve your problem still, join the [discord server](https://discord.gg/VraYT2N3wd).
+
+### Where are config files located?
+
+You can find the configuration files inside of `%appdata%\Hass Workstation Service` on windows.
+
+|file|usage|
+| --- | --- |
+|configured-commands.json|stores all data about commands, including their properties|
+|configured-sensors.json|stores information about sensors, including their properties|
+|mqttbroker.json|stores data about your MQTT broker, dont share online|
+
+
+### Are there any client logs?
+
+Check the logs folder, its stored in the same place as config files.
+
+
+### I cannot find documentation on a new feature?
+
+You can submit a pull request with new documentation, ask on the [discord server](https://discord.gg/VraYT2N3wd), or check the [develop branch](https://github.com/sleevezipper/hass-workstation-service/tree/develop) for updated documentation.
diff --git a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
index 86f7065..5423707 100644
--- a/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
+++ b/hass-workstation-service/Communication/InterProcesCommunication/ServiceContractModels.cs
@@ -11,6 +11,10 @@ namespace hass_workstation_service.Communication.InterProcesCommunication.Models
public string Password { get; set; }
public int? Port { get; set; }
public bool UseTLS { get; set; }
+
+ public bool RetainLWT { get; set; }
+ public string RootCAPath { get; set; }
+ public string ClientCertPath { get; set; }
}
public class MqqtClientStatus
diff --git a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
index 7449d67..0cacde5 100644
--- a/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
+++ b/hass-workstation-service/Communication/MQTT/MqttPublisher.cs
@@ -116,7 +116,7 @@ namespace hass_workstation_service.Communication
var message = new MqttApplicationMessageBuilder()
.WithTopic($"homeassistant/{discoverable.Domain}/{this.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(discoverable.GetAutoDiscoveryConfig().NamePrefix, discoverable.ObjectId)}/config")
.WithPayload(clearConfig ? "" : JsonSerializer.Serialize(discoverable.GetAutoDiscoveryConfig(), discoverable.GetAutoDiscoveryConfig().GetType(), options))
- .WithRetainFlag()
+ //.WithRetainFlag()
.Build();
await this.Publish(message);
// if clearconfig is true, also remove previous state messages
@@ -125,7 +125,7 @@ namespace hass_workstation_service.Communication
var stateMessage = new MqttApplicationMessageBuilder()
.WithTopic($"homeassistant/{discoverable.Domain}/{this.DeviceConfigModel.Name}/{DiscoveryConfigModel.GetNameWithPrefix(discoverable.GetAutoDiscoveryConfig().NamePrefix, discoverable.ObjectId)}/state")
.WithPayload("")
- .WithRetainFlag()
+ // .WithRetainFlag()
.Build();
await this.Publish(stateMessage);
}
diff --git a/hass-workstation-service/Data/ConfigurationService.cs b/hass-workstation-service/Data/ConfigurationService.cs
index f00bb3c..c3090f0 100644
--- a/hass-workstation-service/Data/ConfigurationService.cs
+++ b/hass-workstation-service/Data/ConfigurationService.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
+using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Threading.Tasks;
using hass_workstation_service.Communication;
@@ -20,6 +21,7 @@ using MQTTnet.Client.Options;
using MQTTnet.Extensions.ManagedClient;
using Serilog;
+
namespace hass_workstation_service.Data
{
public class ConfigurationService : IConfigurationService
@@ -310,22 +312,54 @@ namespace hass_workstation_service.Data
if (configuredBroker != null && configuredBroker.Host != null)
{
- var mqttClientOptions = new MqttClientOptionsBuilder()
+
+ var mqttClientOptionsBuilder = new MqttClientOptionsBuilder()
.WithTcpServer(configuredBroker.Host, configuredBroker.Port)
- .WithTls(new MqttClientOptionsBuilderTlsParameters()
- {
- UseTls = configuredBroker.UseTLS,
- AllowUntrustedCertificates = true,
- SslProtocol = configuredBroker.UseTLS ? System.Security.Authentication.SslProtocols.Tls12 : System.Security.Authentication.SslProtocols.None
- })
.WithCredentials(configuredBroker.Username, configuredBroker.Password.ToString())
- .WithKeepAlivePeriod(TimeSpan.FromSeconds(30))
- .WithWillMessage(new MqttApplicationMessageBuilder()
- .WithRetainFlag()
+ .WithKeepAlivePeriod(TimeSpan.FromSeconds(30));
+
+
+ /* Start LWT */
+ var lwtMessage = new MqttApplicationMessageBuilder()
.WithTopic($"homeassistant/sensor/{_deviceConfigModel.Name}/availability")
- .WithPayload("offline")
- .Build())
- .Build();
+ .WithPayload("offline");
+ if (configuredBroker.RetainLWT) {
+ lwtMessage.WithRetainFlag();
+ }
+
+ mqttClientOptionsBuilder.WithWillMessage(lwtMessage.Build());
+ /* End LWT */
+
+
+ /* Start TLS/Certificate configuration */
+
+ var tlsParameters = new MqttClientOptionsBuilderTlsParameters()
+ {
+ UseTls = configuredBroker.UseTLS,
+ AllowUntrustedCertificates = true,
+ SslProtocol = configuredBroker.UseTLS ? System.Security.Authentication.SslProtocols.Tls12 : System.Security.Authentication.SslProtocols.None
+ };
+
+ var certs = new List();
+
+ if (configuredBroker.RootCAPath != null) {
+ certs.Add(new X509Certificate2(configuredBroker.RootCAPath));
+ }
+
+ if (configuredBroker.ClientCertPath != null)
+ {
+ certs.Add(new X509Certificate2(configuredBroker.ClientCertPath));
+ }
+ if (certs.Count > 0) {
+ // IF certs are configured, let's add them here
+ tlsParameters.Certificates = certs;
+ }
+ mqttClientOptionsBuilder.WithTls(tlsParameters);
+
+ /* End TLS/Certificate Configuration */
+
+
+ var mqttClientOptions = mqttClientOptionsBuilder.Build();
return new ManagedMqttClientOptionsBuilder().WithClientOptions(mqttClientOptions).Build();
}
else
@@ -544,7 +578,10 @@ namespace hass_workstation_service.Data
Username = settings.Username,
Password = settings.Password ?? "",
Port = settings.Port ?? 1883,
- UseTLS = settings.UseTLS
+ UseTLS = settings.UseTLS,
+ RetainLWT = settings.RetainLWT,
+ RootCAPath = settings.RootCAPath,
+ ClientCertPath = settings.ClientCertPath
};
await JsonSerializer.SerializeAsync(stream, configuredBroker);
@@ -563,7 +600,10 @@ namespace hass_workstation_service.Data
Username = broker?.Username,
Password = broker?.Password,
Port = broker?.Port,
- UseTLS = broker?.UseTLS ?? false
+ UseTLS = broker?.UseTLS ?? false,
+ RetainLWT = broker?.RetainLWT ?? true,
+ RootCAPath = broker?.RootCAPath,
+ ClientCertPath = broker?.RootCAPath
};
}
diff --git a/hass-workstation-service/Data/ConfiguredMqttBroker.cs b/hass-workstation-service/Data/ConfiguredMqttBroker.cs
index f188322..d4a73d1 100644
--- a/hass-workstation-service/Data/ConfiguredMqttBroker.cs
+++ b/hass-workstation-service/Data/ConfiguredMqttBroker.cs
@@ -8,11 +8,33 @@ namespace hass_workstation_service.Data
private string username;
private string password;
private int? port;
+
+ private string rootCAPath;
+ private string clientCertPath;
public string Host { get; set; }
public int Port { get => port ?? 1883; set => port = value; }
public bool UseTLS { get; set; }
+ // Before this option, Retains was the default, so let's keep that here to not break backwards compatibility
+ public bool RetainLWT { get; set; } = true;
+
+ public string RootCAPath {
+ get
+ {
+ if (rootCAPath!= null) return rootCAPath;
+ return "";
+ }
+ set => rootCAPath = value;
+ }
+ public string ClientCertPath {
+ get
+ {
+ if (clientCertPath != null) return clientCertPath;
+ return "";
+ }
+ set => clientCertPath = value;
+ }
public string Username
{
diff --git a/hass-workstation-service/Domain/Commands/AbstractCommand.cs b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
index 94df49d..4e42b86 100644
--- a/hass-workstation-service/Domain/Commands/AbstractCommand.cs
+++ b/hass-workstation-service/Domain/Commands/AbstractCommand.cs
@@ -40,8 +40,8 @@ namespace hass_workstation_service.Domain.Commands
var message = new MqttApplicationMessageBuilder()
.WithTopic(GetAutoDiscoveryConfig().State_topic)
.WithPayload(state)
- .WithExactlyOnceQoS()
- .WithRetainFlag()
+ //.WithExactlyOnceQoS()
+ //.WithRetainFlag()
.Build();
await Publisher.Publish(message);
PreviousPublishedState = state;
diff --git a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
index 73904bf..4aa8f7c 100644
--- a/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
+++ b/hass-workstation-service/Domain/Sensors/AbstractSensor.cs
@@ -40,8 +40,8 @@ namespace hass_workstation_service.Domain.Sensors
var message = new MqttApplicationMessageBuilder()
.WithTopic(GetAutoDiscoveryConfig().State_topic)
.WithPayload(state)
- .WithExactlyOnceQoS()
- .WithRetainFlag()
+ //.WithExactlyOnceQoS()
+ //.WithRetainFlag()
.Build();
await Publisher.Publish(message);
PreviousPublishedState = state;
diff --git a/hass-workstation-service/hass-workstation-service.csproj b/hass-workstation-service/hass-workstation-service.csproj
index 30c7c44..77d0cd7 100644
--- a/hass-workstation-service/hass-workstation-service.csproj
+++ b/hass-workstation-service/hass-workstation-service.csproj
@@ -43,7 +43,7 @@
Always
- Always
+ Never