Compare commits

...

23 Commits

Author SHA1 Message Date
sleevezipper d7097dd6d7
Merge pull request #156 from Inrego/master
2 years ago
Sleevezipper 06d132c570 some fixes for new settings
2 years ago
sleevezipper 77cf49fcb3
Merge pull request #151 from prestomation/master
2 years ago
Rene Scott Simonsen d31856c9e7 Changed LastActive precision to 1 second.
2 years ago
sleevezipper b39b65c599
Merge pull request #152 from DeftNerd/patch-1
2 years ago
Adam Brown 87f403c8ae
Update WMIQuery.md
2 years ago
Preston Tamkin e145cc774c Add support for Client Certificates for MQTT and disabling Retained messages
2 years ago
Sleevezipper d86513c772 Merge branch 'develop'
2 years ago
sleevezipper c54d545595
Merge pull request #130 from SecretiveShell/patch-3
2 years ago
sleevezipper 85f30504ba
Merge pull request #134 from SecretiveShell/patch-5
2 years ago
sleevezipper 9199c13618
Update README.md
2 years ago
sleevezipper 036a9f3472
Update README.md
2 years ago
sleevezipper 712e4c52ab
Merge pull request #141 from Deadolus/master
2 years ago
Deadolus 9cd742428a
Fixing Webcam/Microphone Sensor mixup in Readme
3 years ago
TerminalMan 091abf4a25
added another video
3 years ago
TerminalMan 8eb2073043
Added a few more articles to the "in the press" section. Some of them are slightly outdated, but I hope they are useful.
3 years ago
TerminalMan 0b7d61e79f
Fix FAQ link
3 years ago
sleevezipper 25b3869ebe
Merge pull request #120 from SecretiveShell/inThePress
3 years ago
Sleevezipper 242e72601f Merge branch 'develop'
3 years ago
sleevezipper 9634eba96c
Merge pull request #118 from SecretiveShell/patch-2
3 years ago
TerminalMan a9922fe482
Update README.md
3 years ago
TerminalMan 3f49f1113d
Update README.md
3 years ago
TerminalMan f5cbc2d2d7
Create FAQ.md
3 years ago

@ -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
<details>
<summary>View some screenshots</summary>
![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.
</details>
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

@ -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)

@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="UserInterface.Views.BrokerSettings">
<StackPanel Margin="30" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility="Auto">
<StackPanel Margin="30" HorizontalAlignment="Left" MinWidth="250">
<ContentControl FontSize="18" FontWeight="Bold">MQTT Broker</ContentControl>
<TextBlock IsVisible="{Binding IsConnected}" Foreground="Green" Text="{Binding Message}"></TextBlock >
<TextBlock IsVisible="{Binding !IsConnected}" Foreground="Red" Text="{Binding Message}"></TextBlock >
@ -25,6 +25,65 @@
<TextBox Text="{Binding Username}" MinWidth="150"/>
<ContentControl Margin="0 20 0 10">Password</ContentControl>
<TextBox Text="{Binding Password}" MinWidth="150" PasswordChar="•"/>
<Expander Header="Advanced">
<StackPanel>
<StackPanel>
<StackPanel Margin="0 20 0 10" HorizontalAlignment="Left" Orientation="Horizontal">
<ContentControl>Retain LastWillAndTestament</ContentControl>
<TextBlock Cursor="Help" Margin="5 0 0 0" VerticalAlignment="Bottom" TextDecorations="Underline">
(What's this?)
<ToolTip.Tip>
<StackPanel>
<TextBlock>
[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
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
<CheckBox IsChecked="{Binding RetainLWT}" HorizontalAlignment="Left" Margin="0 0 0 0"/>
</StackPanel>
<StackPanel Margin="0 20 0 10" HorizontalAlignment="Left" Orientation="Horizontal">
<ContentControl>Root Cert Path (.pem/.crt)</ContentControl>
<TextBlock Cursor="Help" Margin="5 0 0 0" VerticalAlignment="Bottom" TextDecorations="Underline">
(What's this?)
<ToolTip.Tip>
<StackPanel>
<TextBlock>
[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.
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
<TextBox Text="{Binding RootCAPath}" MinWidth="150"/>
<StackPanel Margin="0 20 0 10" HorizontalAlignment="Left" Orientation="Horizontal">
<ContentControl>Client Cert Path (.pfx)</ContentControl>
<TextBlock Cursor="Help" Margin="5 0 0 0" VerticalAlignment="Bottom" TextDecorations="Underline">
(What's this?)
<ToolTip.Tip>
<StackPanel>
<TextBlock>
[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.
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
<TextBox Text="{Binding ClientCertPath}" MinWidth="150"/>
</StackPanel>
</Expander>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Configure">Save</Button>
</StackPanel>
</UserControl>

@ -50,7 +50,7 @@ namespace UserInterface.Views
ICollection<ValidationResult> 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 }));
}
}

@ -4,22 +4,22 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="UserInterface.Views.GeneralSettingsView">
<StackPanel Margin="30" HorizontalAlignment="Left">
<StackPanel Margin="30" HorizontalAlignment="Left" MinWidth="250">
<ContentControl FontSize="18" FontWeight="Bold">Settings</ContentControl>
<StackPanel Margin="0 20 0 10" HorizontalAlignment="Left" Orientation="Horizontal">
<ContentControl>Name prefix</ContentControl>
<TextBlock Cursor="Help" Margin="5 0 0 0" VerticalAlignment="Bottom" TextDecorations="Underline">(What's this?)
<ToolTip.Tip>
<StackPanel>
<TextBlock>
<TextBlock Cursor="Help" Margin="5 0 0 0" VerticalAlignment="Bottom" TextDecorations="Underline">(What's this?)
<ToolTip.Tip>
<StackPanel>
<TextBlock>
[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".
</TextBlock>
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
<TextBox Text="{Binding NamePrefix}" HorizontalAlignment="Left" Width="100"/>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Configure">Save</Button>

@ -15,14 +15,13 @@
<vm:MainWindowViewModel/>
</Design.DataContext>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid ColumnDefinitions="Auto, *, Auto" Margin="10">
<Grid.RowDefinitions>
<RowDefinition MinHeight="500"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<views:BrokerSettings Grid.Column="0" Grid.Row="0" Margin="10" Grid.RowSpan="2" MinHeight="500" Background="#2D2D30"/>
<views:GeneralSettingsView Grid.Column="0" Grid.Row="1" Margin="10" Grid.RowSpan="2" Background="#2D2D30"/>
<Grid ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, *" Margin="10">
<ScrollViewer Grid.Column="0" Grid.Row="0" Margin="10" Grid.RowSpan="2" >
<StackPanel>
<views:BrokerSettings Background="#2D2D30"/>
<views:GeneralSettingsView Margin="0 20 0 0" Background="#2D2D30"/>
</StackPanel>
</ScrollViewer>
<views:SensorSettings Grid.Column="1" Grid.Row="0" Margin="10" Background="#2D2D30"/>
<views:CommandSettings Grid.Column="1" Grid.Row="1" Margin="10" Background="#2D2D30"/>
<views:BackgroundServiceSettings Grid.Column="2" Grid.Row="0" Margin="10" Background="#2D2D30"/>

@ -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.

@ -7,15 +7,72 @@ To use the WMI query sensor, you should create a WMI query and paste it in the b
```sql
SELECT CurrentClockSpeed FROM Win32_Processor
```
which results in `4008` for my PC. Because this query retuens a single value (CPU frequency in MHz), it can be used with the current WMI query sensor implementation.
which results in `4008` for my PC. Because this query returns a single value (CPU frequency in MHz), it can be used with the current WMI query sensor implementation.
The command ```sql SELECT * FROM Win32_Processor``` cannot be used because it returns `|64|9|To Be Filled By O.E.M.|3| ... |GAME-PC-2016|8|1|False|False|`, and the current WMI query implementation only supports commands that return a single value.
You can use [WMI Explorer](https://github.com/vinaypamnani/wmie2/tree/v2.0.0.2) to construct a query, or alternatively look at the user submited sensors below:
You can use [WMI Explorer](https://github.com/vinaypamnani/wmie2/releases) to construct a query, or alternatively look at the user submitted sensors below
---
## User Submitted Sensor Examples
### User Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Current User | `SELECT UserName FROM Win32_ComputerSystem` | `Admin` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @grizzlyjere
### Network Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Get IPv4 Address | `SELECT IPv4Address FROM MSFT_NetIPAddress WHERE AddressFamily=2 AND AddressState=4 AND (PrefixOrigin=1 OR PrefixOrigin=3 OR PrefixOrigin=4)` | `192.168.1.101` | [:link:](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh872425\(v=vs.85\)) | @deftnerd |
| Link Speed | `SELECT Speed FROM MSFT_NetIPAddress WHERE AddressFamily=2 AND AddressState=4 AND (PrefixOrigin=1 OR PrefixOrigin=3 OR PrefixOrigin=4)` | `780000000` (In bps. Divide by 1,000,000 to get Mbps) | [:link:](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh872425\(v=vs.85\)) | @deftnerd |
| Manufacturer | `SELECT DNSHostName FROM Win32_ComputerSystem` | `mycomputer` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
### Hardware Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Manufacturer | `SELECT Manufacturer FROM Win32_ComputerSystem` | `Dell Inc.` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Model | `SELECT Model FROM Win32_ComputerSystem` | `OptiPlex 7010` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| System is a VM | `SELECT HypervisorPresent FROM Win32_ComputerSystem` | `0` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Physical Processors | `SELECT NumberOfProcessors FROM Win32_ComputerSystem` | `1` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Logical Processors | `SELECT NumberOfLogicalProcessors FROM Win32_ComputerSystem` | `8` (Processors * Cores * Threads) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| System Type | `SELECT PCSystemType FROM Win32_ComputerSystem` | `1` (0=Unspecified, 1=Desktop, 2=Laptop, 3=Workstation, etc) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Processor Architecture | `SELECT SystemType FROM Win32_ComputerSystem` | `64-bit Intel PC` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
### Power & Thermal Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Capable of Power Management | `SELECT PowerManagementSupported FROM Win32_ComputerSystem` | `1` (0=Unsupported, 1=Supported) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Power Management State | `SELECT PowerState FROM Win32_ComputerSystem` | `1` (0=Unknown, 1=Full Power, 2=Low Power Mode, 3=Standby, etc) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Power Supply State | `SELECT PowerSupplyState FROM Win32_ComputerSystem` | `3` (1=Other, 2=Unknown, 3=Safe, 4=Warning, 5=Critical, etc) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Enclosure Thermal State | `SELECT ThermalState FROM Win32_ComputerSystem` | `3` (1=Other, 2=Unknown, 3=Safe, 4=Warning, 5=Critical, etc) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem) | @deftnerd |
| Battery Status | `SELECT BatteryStatus FROM Win32_Battery` | `2` (1=Discharging, 2=On AC, 3=Fully Charged, 4=Low, 5=Critical, 6=Charging, etc) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| % Charge Remaining | `SELECT EstimatedChargeRemaining FROM Win32_Battery` | `80` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| Remaining Battery Minutes | `SELECT EstimatedRunTime FROM Win32_Battery` | `142` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| Max Minutes of Battery | `SELECT ExpectedLife FROM Win32_Battery` | `230` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| Minutes to Full Charge | `SELECT TimeToFullCharge FROM Win32_Battery` | `14` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| Max Minutes to Recharge | `SELECT MaxRechargeTime FROM Win32_Battery` | `65` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
| Time on Battery in Seconds | `SELECT TimeOnBattery FROM Win32_Battery` | `3640` (0=plugged in) | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery) | @deftnerd |
### Windows System Assessment Tool Results
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Overall score | `SELECT CPUScore FROM Win32_WinSAT` | `5.8` | [:link:](https://docs.microsoft.com/en-us/windows/win32/winsat/win32-winsat) | @deftnerd |
| CPU Score | `SELECT CPUScore FROM Win32_WinSAT` | `9.1` | [:link:](https://docs.microsoft.com/en-us/windows/win32/winsat/win32-winsat) | @deftnerd |
| Disk Score | `SELECT DiskScore FROM Win32_WinSAT` | `7.75` | [:link:](https://docs.microsoft.com/en-us/windows/win32/winsat/win32-winsat) | @deftnerd |
| Graphics Score | `SELECT GraphicsScore FROM Win32_WinSAT` | `5.8` | [:link:](https://docs.microsoft.com/en-us/windows/win32/winsat/win32-winsat) | @deftnerd |
| Memory Score | `SELECT MemoryScore FROM Win32_WinSAT` | `9.1` | [:link:](https://docs.microsoft.com/en-us/windows/win32/winsat/win32-winsat) | @deftnerd |
### Software Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| AntiSpyware Product | `SELECT displayName FROM AntiSpywareProduct` | `Windows Defender` | | @deftnerd |
| AntiVirus Product | `SELECT displayName FROM AntiVirusProduct` | `Windows Defender` | | @deftnerd |
### Process Information
| Data | Query | Example Response | Info | Contributor |
| --- | --- | --- | :---: | --- |
| Is program _x_ running? | `SELECT ProcessId FROM Win32_Process WHERE Name='notepad.exe'` | `1842` | [:link:](https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process) | @lafferlaffer |
|Query|Explanation|Submitted by|
|---|---|---|
|`SELECT username FROM Win32_ComputerSystem`|Shows the current user|@grizzlyjere|
|`Select * from Win32_Process Where Name = 'notepad.exe'`|Shows if the defined process is running|@lafferlaffer|

@ -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

@ -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);
}

@ -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<X509Certificate>();
if (!string.IsNullOrEmpty(configuredBroker.RootCAPath)) {
certs.Add(new X509Certificate2(configuredBroker.RootCAPath));
}
if (!string.IsNullOrEmpty(configuredBroker.ClientCertPath))
{
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
};
}

@ -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
{

@ -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;

@ -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;

@ -6,7 +6,7 @@ namespace hass_workstation_service.Domain.Sensors
{
public class LastActiveSensor : AbstractSensor
{
private DateTime _lastActive = DateTime.MinValue;
public LastActiveSensor(MqttPublisher publisher, int? updateInterval = 10, string name = "LastActive", Guid id = default) : base(publisher, name ?? "LastActive", updateInterval ?? 10, id){}
public override SensorDiscoveryConfigModel GetAutoDiscoveryConfig()
@ -25,7 +25,12 @@ namespace hass_workstation_service.Domain.Sensors
public override string GetState()
{
return GetLastInputTime().ToString("o", System.Globalization.CultureInfo.InvariantCulture);
var lastInput = GetLastInputTime();
if ((_lastActive - lastInput).Duration().TotalSeconds > 1)
{
_lastActive = lastInput;
}
return _lastActive.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
}

@ -43,7 +43,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="UserInterface.pdb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>

Loading…
Cancel
Save