Skip to content

Commit

Permalink
Port WPF to Blazor WebView (#537)
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacrlevin committed Jan 20, 2022
1 parent ff937bf commit cd90832
Show file tree
Hide file tree
Showing 126 changed files with 1,868 additions and 6,398 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/Desktop_Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,6 @@ jobs:

- name: Build Appx Package
run: |
#dotnet restore .\src\DesktopClient\PresenceLight\PresenceLight.csproj
#msbuild /t:restore .\src\DesktopClient\PresenceLight.Package\PresenceLight.Package.wapproj
msbuild '.\src\DesktopClient\PresenceLight.Package\PresenceLight.Package.wapproj' /p:VersionNumber=${{ env.GitBuildVersionSimple }} `
/p:ChannelName=${{ matrix.ChannelName }} /p:configuration='${{ env.BuildConfiguration }}' /p:IncludeSymbols=true `
/p:AppxPackageDir="${{ github.workspace }}\${{ matrix.ChannelName }}\\"
Expand Down
28 changes: 14 additions & 14 deletions .github/workflows/Web_Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -404,17 +404,17 @@ jobs:
- name: Update Docker Files
run: |
$dockerFileLatest = Get-Content -path "${{ github.workspace }}/src/DockerFiles/Dockerfile" -Raw
$dockerFileLatest = Get-Content -path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile" -Raw
$dockerFileLatest = $dockerFileLatest -replace '{VERSION}', "${{ env.VERSION }} "
$dockerFileLatest | Set-Content -Path "${{ github.workspace }}/src/DockerFiles/Dockerfile"
$dockerFileLatest | Set-Content -Path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile"
$dockerFile32 = Get-Content -path "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm32" -Raw
$dockerFile32 = Get-Content -path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm32" -Raw
$dockerFile32 = $dockerFile32 -replace '{VERSION}', "${{ env.VERSION }} "
$dockerFile32 | Set-Content -Path "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm32"
$dockerFile32 | Set-Content -Path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm32"
$dockerFile64 = Get-Content -path "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm64" -Raw
$dockerFile64 = Get-Content -path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm64" -Raw
$dockerFile64 = $dockerFile64 -replace '{VERSION}', "${{ env.VERSION }} "
$dockerFile64 | Set-Content -Path "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm64"
$dockerFile64 | Set-Content -Path "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm64"
shell: pwsh
if: ${{ success() && github.event_name != 'pull_request' }}

Expand All @@ -426,7 +426,7 @@ jobs:
USERNAME: isaacrlevin
REGISTRY: "ghcr.io"
PASSWORD: ${{ secrets.GH_PERSONAL_TOKEN }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile"
IMAGE_NAME: "isaacrlevin/presencelight"
TAG_NAME: "${{ env.VERSION }}"
LATEST: "true"
Expand All @@ -440,7 +440,7 @@ jobs:
USERNAME: isaacrlevin
REGISTRY: "ghcr.io"
PASSWORD: ${{ secrets.GH_PERSONAL_TOKEN }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm32"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm32"
IMAGE_NAME: "isaacrlevin/presencelight"
TAG_NAME: "debian-arm32"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -453,7 +453,7 @@ jobs:
USERNAME: isaacrlevin
REGISTRY: "ghcr.io"
PASSWORD: ${{ secrets.GH_PERSONAL_TOKEN }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm64"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm64"
IMAGE_NAME: "isaacrlevin/presencelight"
TAG_NAME: "debian-arm64"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -465,7 +465,7 @@ jobs:
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm32"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm32"
IMAGE_NAME: "isaaclevin/presencelight"
TAG_NAME: "${{ env.VERSION }}-debian-arm32"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -477,7 +477,7 @@ jobs:
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm32"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm32"
IMAGE_NAME: "isaaclevin/presencelight"
TAG_NAME: "debian-arm32"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -489,7 +489,7 @@ jobs:
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm64"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm64"
IMAGE_NAME: "isaaclevin/presencelight"
TAG_NAME: "${{ env.VERSION }}-debian-arm64"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -501,7 +501,7 @@ jobs:
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile.debian-arm64"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile.debian-arm64"
IMAGE_NAME: "isaaclevin/presencelight"
TAG_NAME: "debian-arm64"
BUILD_PATH: "${{ github.workspace }}/src/"
Expand All @@ -513,7 +513,7 @@ jobs:
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERFILE: "${{ github.workspace }}/src/DockerFiles/Dockerfile"
DOCKERFILE: "${{ github.workspace }}/src/PresenceLight.Web/Dockerfile"
IMAGE_NAME: "isaaclevin/presencelight"
TAG_NAME: "${{ env.VERSION }}"
LATEST: "true"
Expand Down
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

| Application Type | Platforms | Readme
|--- | ---- | ---- |
| Desktop (.NET 5) | Windows 10 (min Version 1803) | [Desktop Readme](https://github.com/isaacrlevin/PresenceLight/blob/main/desktop-README.md)
| Web (ASP.NET 5) | Windows, Linux (Debian, AMD x64, ARM, ARM x64), | [Web Readme](https://github.com/isaacrlevin/PresenceLight/blob/main/web-README.md)
| Desktop (.NET 6) | Windows 10 (min Version 1803) | [Desktop Readme](https://github.com/isaacrlevin/PresenceLight/blob/main/desktop-README.md)
| Web (ASP.NET 6) | Windows, MacOS, Linux (Debian, AMD x64, ARM, ARM x64), | [Web Readme](https://github.com/isaacrlevin/PresenceLight/blob/main/web-README.md)
## What is PresenceLight?

[PresenceLight](https://isaacl.dev/presence-light) is a solution to broadcast your various statuses to various kinds of smart lights. Some statuses you can broadcast are: your availability in Microsoft Teams, your current Windows 10 theme, and a theme or color of your choosing. There are other solutions that do something similar to sending Teams Availability to a light, but they require a tethered solution (plugging a light into a computer via USB). What PresenceLight does is leverage the [Presence Api](https://docs.microsoft.com/graph/api/presence-get), which is available in [Microsoft Graph](https://docs.microsoft.com/graph/overview), allowing to retrieve your presence without having to be tethered. This could potentially allow someone to update the light bulb from a remote machine they do not use.
[PresenceLight](https://isaacl.dev/presence-light) is a solution to broadcast your various statuses to various kinds of smart lights. Some statuses you can broadcast are: your availability in Microsoft Teams or color of your choosing. There are other solutions that do something similar to sending Teams Availability to a light, but they require a tethered solution (plugging a light into a computer via USB). What PresenceLight does is leverage the [Presence Api](https://docs.microsoft.com/graph/api/presence-get), which is available in [Microsoft Graph](https://docs.microsoft.com/graph/overview), allowing to retrieve your presence without having to be tethered. This could potentially allow someone to update the light bulb from a remote machine they do not use.

#### [Blog Post](https://isaacl.dev/presence-light)

Expand All @@ -31,8 +31,8 @@
| Philips Hue (Local and Remote)
| LIFX |
| Yeelight |
| Any light which can be controlled via a GET or POST call to a web API |
| Philips Wiz |
| Any light which can be controlled via a GET or POST call to a web API |

## Configure Hardware
- [Configure Hardware](https://github.com/isaacrlevin/PresenceLight/wiki/Configure-Hardware)
Expand All @@ -49,8 +49,14 @@ I welcome all contributions here, as I am no expert in WPF/MSIX things.
Presence Light would not be possible without the amazing work from the contributors to the following third party libraries!

- [Q42.HueApi](https://github.com/Q42/Q42.HueApi)
- [AppInsights.WindowsDesktop](https://github.com/novotnyllc/AppInsights.WindowsDesktop)
- [IdentityModel.OidcClient](https://github.com/IdentityModel/IdentityModel.OidcClient)
- [wpftoolkit](https://github.com/xceedsoftware/wpftoolkit)
- [OSVersionHelper](https://github.com/onovotny/OSVersionHelper)
- [WpfAnimatedGif](https://github.com/XamlAnimatedGif/WpfAnimatedGif)
- [OpenWiz](https://github.com/UselessMnemonic/OpenWiz)
- [YeelightAPI](https://github.dev/roddone/YeelightAPI)
- [LifxCloud](https://github.com/isaacrlevin/LifxCloudClient)
- [MediatR](https://github.com/jbogard/MediatR)
- [Polly](https://github.com/App-vNext/Polly)
- [Serilog](https://github.com/serilog/serilog)
- [Blazored.Modal](https://github.com/Blazored/Modal)
- [Blazorise](https://github.com/Megabit/Blazorise)
- [BlazorPro.Spinkit](https://github.com/EdCharbeneau/BlazorPro.Spinkit)
- [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
- [IdentityModel.OidcClient](https://github.com/IdentityModel/IdentityModel.OidcClient)
20 changes: 10 additions & 10 deletions desktop-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ In order for the desktop app to work, you need to be running Windows 10, version

After you have followed the above steps (enable side-loading and installing app), you will have be welcomed with a message like this

![Configured](https://github.com/isaacrlevin/PresenceLight/raw/main/static/configured.png)
![Configured](static/configured.png)

PresenceLight obtains your Microsoft Teams Availability using a multi-tenant Azure Active Directory Application, meaning you will need to "grant" access to your Presence the first time you use the app. Clicking sign-in will prompt you for a login with your Microsoft 365 credentials, and finally when authenticated, you will be shown your Graph profile image and your presence. If you are curious about what is required to do this on your own tenant, read [Configure an Azure Active Directory Application](https://github.com/isaacrlevin/PresenceLight/wiki/Configure-an-Azure-Active-Directory-Application)

![Profile Image](https://github.com/isaacrlevin/PresenceLight/raw/main/static/profile.png)
![Profile Image](static/profile.png)

The application "polls" the Presence Api at a configured value, whican you can set bewteen 1 and 5 seconds on the Settings page. This means that the light and app will update based on your Teams presence with a slight delay.

Expand All @@ -51,15 +51,15 @@ You can only do one of these at a time, so if you for instance are syncing with

One of the features of PresenceLight is that you can minimize the app to the icon tray. When you open the app, you will see an icon similar to this.

![white Image](https://github.com/isaacrlevin/PresenceLight/raw/main/static/light-icon.png)
![white Image](static/light-icon.png)

This icon will represent your presence color. There are two "kinds" of icons: Transparent, and White. Here is the transparent icon

![Settings 1](https://github.com/isaacrlevin/PresenceLight/raw/main/static/trans-icon.png)
![Settings 1](static/trans-icon.png)

You can change the icon type in the settings pane.

![Settings 2](https://github.com/isaacrlevin/PresenceLight/raw/main/static/settings1.png)
![Settings 2](static/settings1.png)

After you change and save, the icon will update in the icon tray.

Expand All @@ -70,25 +70,25 @@ To connect PresenceLight to Philips Hue, you can do it 1 of 2 ways
- Obtain the IP Address of your Philips Hue Bridge (if you have it)
- Ask PresenceLight to find it for you (may no work in certain network configurations)

![Hue Settings](https://github.com/isaacrlevin/PresenceLight/raw/main/static/hue-settings.png)
![Hue Settings](static/hue-settings.png)

Once you have the IP of the bridge, you will need to register a developer account and get an Api Key. This is easily done by clicking the "Register Bridge" button. Clicking the button will popup a window asking you to press the sync button on the bridge, this is needed to register PresenceLight to the bridge.

![Sync Button](https://github.com/isaacrlevin/PresenceLight/raw/main/static/sync-button.png)
![Sync Button](static/sync-button.png)

When PresenceLight is configured, you will see a dropdown of Hue Bulbs connected to the bridge for you to set your presence to.

![Registered Bridge](https://github.com/isaacrlevin/PresenceLight/raw/main/static/registered-bridge.png)
![Registered Bridge](static/registered-bridge.png)

## Wire up LIFX

To connect PresenceLight to LIFX colored bulbs, you need to obtain a LIFX Developer Token. When you first arrive at the LIFX tab, you will see a message like this if you try to get Lights or Groups

![LIFX Unconfigured](https://github.com/isaacrlevin/PresenceLight/raw/main/static/lifx-unconfigured.png)
![LIFX Unconfigured](static/lifx-unconfigured.png)

After entering an obtained token, you will be able to get a list of either individual lights or groups of lights, selecting one of the options and saving gives you a message like this

![LIFX Configured](https://github.com/isaacrlevin/PresenceLight/raw/main/static/lifx-configured.png)
![LIFX Configured](static/lifx-configured.png)

## [Wire-up Custom API](https://github.com/isaacrlevin/presencelight/wiki/Wire-up-Custom-API)

Expand Down
27 changes: 0 additions & 27 deletions src/.dockerignore

This file was deleted.

79 changes: 49 additions & 30 deletions src/DesktopClient/PresenceLight/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@
using System.Threading;
using System.Windows;

using Blazored.Modal;

using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;

using MediatR;

using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using PresenceLight.Core;
using PresenceLight.Graph;
using PresenceLight.Razor;
using PresenceLight.Razor.Services;
using PresenceLight.Services;
using PresenceLight.Telemetry;

Expand All @@ -34,7 +41,6 @@ public partial class App : System.Windows.Application

public static IConfiguration? StaticConfig { get; private set; }


public App()
{

Expand All @@ -55,7 +61,6 @@ private void OnStartup(object sender, StartupEventArgs e)
{
Trace.WriteLine($"## Warning Notify ##: {ex}");
Log.Error(ex, "Stopped program because of exception");
OnCriticalFontLoadFailure();
}
}
else
Expand Down Expand Up @@ -107,6 +112,40 @@ private void ContinueStartup()
Log.Debug("Starting PresenceLight");

IServiceCollection services = new ServiceCollection();

services.AddBlazoredModal();
services.AddBlazorise(options =>
{
options.ChangeTextOnKeyPress = true;
}).AddBootstrapProviders()
.AddFontAwesomeIcons();


services.AddMediatR(typeof(App),
typeof(BaseConfig));



services.AddHttpClient();

services.AddHttpContextAccessor();

services.Configure<BaseConfig>(Configuration);
services.AddSingleton(Configuration);
services.AddOptions();
services.AddSingleton<AppState>();
services.AddSingleton<AppInfo, AppInfo>();
services.AddSingleton<ITelemetryInitializer, AppVersionTelemetryInitializer>();
services.AddPresenceServices();
services.AddBlazoredModal();

services.AddBlazorise(options =>
{
options.ChangeTextOnKeyPress = true;
}).AddBootstrapProviders()
.AddFontAwesomeIcons();

services.AddBlazorWebView();
services.AddOptions();
services.AddLogging(logging =>
{
Expand All @@ -117,16 +156,19 @@ private void ContinueStartup()
services.AddMediatR(typeof(App),
typeof(PresenceLight.Core.BaseConfig));

services.Configure<BaseConfig>(Configuration);
services.Configure<AADSettings>(Configuration.GetSection("AADSettings"));


var userAuthService = new UserAuthService(Configuration);
services.AddSingleton(userAuthService);


services.Configure<TelemetryConfiguration>(
(o) =>
{
o.InstrumentationKey = Configuration["ApplicationInsights:InstrumentationKey"];
o.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
o.TelemetryInitializers.Add(new AppVersionTelemetryInitializer());
o.TelemetryInitializers.Add(new EnvironmentTelemetryInitializer());
//o.TelemetryInitializers.Add(AppVersionTelemetryInitializer);
});
services.AddApplicationInsightsTelemetryWorkerService(options =>
{
Expand All @@ -139,7 +181,6 @@ private void ContinueStartup()
services.AddPresenceServices();

services.AddSingleton<LIFXOAuthHelper, LIFXOAuthHelper>();
services.AddSingleton<ThisAppInfo, ThisAppInfo>();
services.AddSingleton<MainWindow>();

if (Convert.ToBoolean(Configuration["IsAppPackaged"], CultureInfo.InvariantCulture))
Expand Down Expand Up @@ -173,27 +214,5 @@ private static bool IsCriticalFontLoadFailure(Exception ex)
return ex.StackTrace.Contains("MS.Internal.Text.TextInterface.FontFamily.GetFirstMatchingFont", StringComparison.OrdinalIgnoreCase) ||
ex.StackTrace.Contains("MS.Internal.Text.Line.Format", StringComparison.OrdinalIgnoreCase);
}

private static void OnCriticalFontLoadFailure()
{
Trace.WriteLine($"App OnCriticalFontLoadFailure");

new Thread(() =>
{
if (MessageBox.Show(
"PresenceLight Is Already Running",
"PresenceLight Could Note Start",
MessageBoxButton.OKCancel,
MessageBoxImage.Error,
MessageBoxResult.OK) == MessageBoxResult.OK)
{
Trace.WriteLine($"App OnCriticalFontLoadFailure OK");
}
Environment.Exit(0);
}).Start();

// Stop execution because callbacks to the UI thread will likely cause another cascading font error.
new AutoResetEvent(false).WaitOne();
}
}
}
Loading

0 comments on commit cd90832

Please sign in to comment.