Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
sleevezipper committed Jan 17, 2021
2 parents fd30b25 + 85e53d0 commit 7684d65
Show file tree
Hide file tree
Showing 46 changed files with 1,323 additions and 119 deletions.
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ It will try to futher accomplish this goal in the future by:

## Screenshots

![The settings screen](https://i.imgur.com/WpCZaDR.png)
![The settings screen](https://i.imgur.com/RBQx807.png)

![The resulting sensors in Home Assistant](https://i.imgur.com/Kka8VOi.png)
![The resulting sensors and commands in Home Assistant](https://i.imgur.com/jXRU2cu.png)

## Installation

Expand All @@ -34,6 +34,10 @@ If you don't want to use the installer, you can find the standalone version rele

If you used the installer, the app checks for updates on startup. If an update is available you will be prompted to install. If you use the standalone, just delete all files from the previous install and unpack the zip to the same location as before.

## Need help?

Find us on us on [Discord](https://discord.gg/VraYT2N3wd).

## Sensors

The application provides several sensors. Sensors can be configured with a name and this name will be used in the MQTT topic like this: `homeassistant/sensor/{DeviceName}/{Name}/state`. Sensors will expose themselves through [MQTT discovery](https://www.home-assistant.io/docs/mqtt/discovery/) and will automatically appear in Home assistant or any other platform that supports this type of configuration.
Expand Down Expand Up @@ -132,3 +136,29 @@ 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. For each command, a switch will be available in Home Assistant. Turning on the switch fires the command on the client and it will turn the switch off when it's done. Turning it off will cancel thje running command.

### ShutdownCommand

This command shuts down the computer immediately. It runs `shutdown /s`.

### RestartCommand

This command restarts the computer immediately. It runs `shutdown /r`.

### LogOffCommand

This command logs off the current user. It runs `shutdown /l`.

### 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.|
|shutdown /s /t 300|Shuts the PC down after 5 minutes (300 seconds).|
|C:\path\to\your\batchfile.bat|Run the specified batch file.|
9 changes: 9 additions & 0 deletions UserInterface/UserInterface.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@
<ProjectReference Include="..\hass-workstation-service\hass-workstation-service.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\AddCommandDialog.axaml.cs">
<DependentUpon>AddCommandDialog.axaml</DependentUpon>
</Compile>
<Compile Update="Views\AppInfo.axaml.cs">
<DependentUpon>AppInfo.axaml</DependentUpon>
</Compile>
<Compile Update="Views\BackgroundServiceSettings.axaml.cs">
<DependentUpon>BackgroundServiceSettings.axaml</DependentUpon>
</Compile>
<Compile Update="Views\CommandSettings.axaml.cs">
<DependentUpon>CommandSettings.axaml</DependentUpon>
</Compile>
<Compile Update="Views\SensorSettings.axaml.cs">
<DependentUpon>SensorSettings.axaml</DependentUpon>
</Compile>
Expand Down
31 changes: 31 additions & 0 deletions UserInterface/ViewModels/AddCommandViewModel.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
25 changes: 25 additions & 0 deletions UserInterface/ViewModels/CommandSettingsViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Text;

namespace UserInterface.ViewModels
{
public class CommandSettingsViewModel : ViewModelBase
{
private ICollection<CommandViewModel> configuredCommands;

public ICollection<CommandViewModel> 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; }
}
}
26 changes: 26 additions & 0 deletions UserInterface/Views/AddCommandDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UserInterface.Views.AddCommandDialog"
SizeToContent="WidthAndHeight"
Title="Add sensor">
<StackPanel Margin="40" MinWidth="200">
<ContentControl Margin="0 20 0 10">Sensor type</ContentControl>

<ComboBox x:Name="ComboBox" SelectionChanged="ComboBoxClosed" SelectedItem="{Binding SelectedType}" MinHeight="27"></ComboBox>
<TextBlock Margin="0 10 0 10" MaxWidth="300" TextWrapping="Wrap" TextAlignment="Left" Text="{Binding Description}"></TextBlock>
<Button IsVisible="{Binding MoreInfoLink, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Click="OpenInfo" Margin="0 10 0 10">Click for more information.</Button>
<ContentControl Margin="0 20 0 10">Name</ContentControl>
<TextBox Text="{Binding Name}" HorizontalAlignment="Left" MinWidth="150"/>



<TextBlock Text="{Binding UpdateInterval, StringFormat= Update every {0} seconds}" HorizontalAlignment="Left" MinWidth="150"/>
<ContentControl IsVisible="{Binding ShowCommandInput}" Margin="0 20 0 10">Command</ContentControl>
<TextBox IsVisible="{Binding ShowCommandInput}" Text="{Binding Command}" Watermark="Rundll32.exe user32.dll,LockWorkStation" HorizontalAlignment="Left" MinWidth="300"/>
<Button IsVisible="{Binding ShowCommandInput}" Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Test">Test</Button>
<Button Width="75" HorizontalAlignment="Right" Margin="0 40 0 10" Click="Save">Save</Button>
</StackPanel>
</Window>
112 changes: 112 additions & 0 deletions UserInterface/Views/AddCommandDialog.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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<ServiceContractInterfaces> 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>("ComboBox");
this.comboBox.Items = Enum.GetValues(typeof(AvailableCommands)).Cast<AvailableCommands>().OrderBy(v => v.ToString());
this.comboBox.SelectedIndex = 0;

// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("addCommand", pipeName: "pipeinternal")
.BuildServiceProvider();

// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();

// 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;
case AvailableCommands.ShutdownCommand:
item.Description = "This command shuts down the PC immediately. ";
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#shutdowncommand";
item.ShowCommandInput = false;
break;
case AvailableCommands.RestartCommand:
item.Description = "This command restarts the PC immediately. ";
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#restartcommand";
item.ShowCommandInput = false;
break;
case AvailableCommands.LogOffCommand:
item.Description = "This command logs the current user off immediately. ";
item.MoreInfoLink = "https://github.com/sleevezipper/hass-workstation-service#logoffcommand";
item.ShowCommandInput = false;
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);
}
}
}
17 changes: 17 additions & 0 deletions UserInterface/Views/AppInfo.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="UserInterface.Views.AppInfo">
<StackPanel Margin="30">
<StackPanel Margin="0 0 0 20" HorizontalAlignment="Left">
<ContentControl FontSize="18" FontWeight="Bold" >Info</ContentControl>
<TextBlock Text="Need some help?" Margin="0 0 0 20"></TextBlock >
<StackPanel Margin="0 0 0 20" HorizontalAlignment="Left" Orientation="Horizontal">
<Button Width="75" HorizontalAlignment="Right" Margin="10 10" Click="Github">Github</Button>
<Button Width="75" HorizontalAlignment="Left" Margin="10 10" Click="Discord">Discord</Button>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
91 changes: 91 additions & 0 deletions UserInterface/Views/AppInfo.axaml.cs
Original file line number Diff line number Diff line change
@@ -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<ServiceContractInterfaces> client;

public AppInfo()
{
this.InitializeComponent();
// register IPC clients
ServiceProvider serviceProvider = new ServiceCollection()
.AddNamedPipeIpcClient<ServiceContractInterfaces>("broker", pipeName: "pipeinternal")
.BuildServiceProvider();

// resolve IPC client factory
IIpcClientFactory<ServiceContractInterfaces> clientFactory = serviceProvider
.GetRequiredService<IIpcClientFactory<ServiceContractInterfaces>>();

// 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);
}
}
}
Loading

0 comments on commit 7684d65

Please sign in to comment.