diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b020d2e..b8ac2660 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,7 +70,7 @@ jobs: #configuration: [Debug, Release] configuration: [Release] - runs-on: windows-latest # For a list of available runner types, refer to + runs-on: windows-2022 # For a list of available runner types, refer to # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on env: @@ -87,9 +87,9 @@ jobs: # Install the .NET Core workload - name: Install .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4.2.0 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild #- name: Setup MSBuild.exe @@ -112,24 +112,42 @@ jobs: dotnet restore "src\\HASS.Agent\\HASS.Agent\\HASS.Agent.csproj" dotnet restore "src\\HASS.Agent\\HASS.Agent.Satellite.Service\\HASS.Agent.Satellite.Service.csproj" - - name: Build & Publish HASS.Agent + - name: Build & Publish HASS.Agent x64 working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent\\HASS.Agent" - run: "dotnet publish -c Release -f net6.0-windows10.0.19041.0 -o bin\\Publish-x64\\Release\\ --no-self-contained -r win-x64 -p:Platform=x64" + run: "dotnet publish -c Release -f net8.0-windows10.0.22621.0 -o bin\\Publish-x64\\Release\\ --no-self-contained -r win-x64 -p:Platform=x64" - - name: Build & Publish HASS.Agent.Satellite.Service + - name: Build & Publish HASS.Agent.Satellite.Service x64 working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent\\HASS.Agent.Satellite.Service" - run: "dotnet publish -c Release -f net6.0-windows10.0.19041.0 -o bin\\Publish-x64\\Release\\ --no-self-contained -r win-x64 -p:Platform=x64" + run: "dotnet publish -c Release -f net8.0-windows10.0.22621.0 -o bin\\Publish-x64\\Release\\ --no-self-contained -r win-x64 -p:Platform=x64" - - name: Compile InnoSetup Installer - Satellite Service + - name: Compile InnoSetup Installer - Satellite Service x64 working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" run: | & "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript-Service.iss - - name: Compile InnoSetup Installer - Client + - name: Compile InnoSetup Installer - Client x64 working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" run: | & "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript.iss + + - name: Build & Publish HASS.Agent x86 + working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent\\HASS.Agent" + run: "dotnet publish -c Release -f net8.0-windows10.0.22621.0 -o bin\\Publish-x86\\Release\\ --no-self-contained -r win-x86 -p:Platform=x86" + + - name: Build & Publish HASS.Agent.Satellite.Service x86 + working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent\\HASS.Agent.Satellite.Service" + run: "dotnet publish -c Release -f net8.0-windows10.0.22621.0 -o bin\\Publish-x86\\Release\\ --no-self-contained -r win-x86 -p:Platform=x86" + - name: Compile InnoSetup Installer - Satellite Service x86 + working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" + run: | + & "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript-Service-x86.iss + + - name: Compile InnoSetup Installer - Client x86 + working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" + run: | + & "${Env:ProgramFiles(x86)}\\Inno Setup 6\\iscc.exe" InstallerScript-x86.iss + - name: Decode the pfx working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" run: | @@ -137,42 +155,68 @@ jobs: $certificatePath = [IO.Path]::GetFullPath(".\\HASS.Agent.Installer.pfx") [IO.File]::WriteAllBytes($certificatePath, $pfxBytes) - - name: Sign the installer + - name: Sign the x64 installer working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" run: | $certificatePassword = ConvertTo-SecureString "${{ secrets.BASE64_ENCODED_PFX_PASSWORD }}" -AsPlainText -Force $certificate = Get-PfxCertificate -FilePath ".\\HASS.Agent.Installer.pfx" -Password $certificatePassword Set-AuthenticodeSignature -FilePath ".\\bin\\HASS.Agent.Installer.exe" -Certificate $certificate -HashAlgorithm SHA256 -TimestampServer http://timestamp.digicert.com + + - name: Sign the x86 installer + working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" + run: | + $certificatePassword = ConvertTo-SecureString "${{ secrets.BASE64_ENCODED_PFX_PASSWORD }}" -AsPlainText -Force + $certificate = Get-PfxCertificate -FilePath ".\\HASS.Agent.Installer.pfx" -Password $certificatePassword + Set-AuthenticodeSignature -FilePath ".\\bin\\HASS.Agent.Installer.x86.exe" -Certificate $certificate -HashAlgorithm SHA256 -TimestampServer http://timestamp.digicert.com - name: Remove the pfx working-directory: "D:\\a\\HASS.Agent\\HASS.Agent\\src\\HASS.Agent.Installer" run: Remove-Item -path ".\\HASS.Agent.Installer.pfx" - #Upload project artifacts: https://github.com/marketplace/actions/upload-a-build-artifact - - name: Upload build artifacts (HASS Agent) + # Upload x64 project artifacts: https://github.com/marketplace/actions/upload-a-build-artifact + - name: Upload build artifacts x64 (HASS Agent) uses: actions/upload-artifact@v4.6.0 with: name: HASS.Agent path: "src\\HASS.Agent\\HASS.Agent\\bin\\Publish-x64\\Release" - - name: Upload build artifacts (HASS Agent Satellite Service) + - name: Upload build artifacts x64 (HASS Agent Satellite Service) uses: actions/upload-artifact@v4.6.0 with: name: HASS.Agent.Satellite.Service path: "src\\HASS.Agent\\HASS.Agent.Satellite.Service\\bin\\Publish-x64\\Release" - - name: Upload build artifacts (HASS Agent Installer) + - name: Upload build artifacts x64 (HASS Agent Installer) uses: actions/upload-artifact@v4.6.0 with: name: HASS.Agent.Installer path: "src\\HASS.Agent.Installer\\bin\\HASS.Agent.Installer.exe" + # Upload x86 project artifacts + - name: Upload build artifacts x86 (HASS Agent) + uses: actions/upload-artifact@v4.6.0 + with: + name: HASS.Agent.x86 + path: "src\\HASS.Agent\\HASS.Agent\\bin\\Publish-x86\\Release" + + - name: Upload build artifacts x86 (HASS Agent Satellite Service) + uses: actions/upload-artifact@v4.6.0 + with: + name: HASS.Agent.Satellite.Service.x86 + path: "src\\HASS.Agent\\HASS.Agent.Satellite.Service\\bin\\Publish-x86\\Release" + + - name: Upload build artifacts x86 (HASS Agent Installer) + uses: actions/upload-artifact@v4.6.0 + with: + name: HASS.Agent.Installer.x86 + path: "src\\HASS.Agent.Installer\\bin\\HASS.Agent.Installer.x86.exe" + #- name: Debugging artifact paths # run: | # Set-Location src\HASS.Agent # Get-Childitem - #draft a release, https://github.com/actions/create-release + # Draft a release, https://github.com/actions/create-release publish: runs-on: ubuntu-latest needs: [build] @@ -184,43 +228,62 @@ jobs: mkdir HASS.Agent mkdir HASS.Agent.Satellite.Service mkdir HASS.Agent.Installer + mkdir HASS.Agent.x86 + mkdir HASS.Agent.Satellite.Service.x86 + mkdir HASS.Agent.Installer.x86 ls -la - - name: Download Build Artifacts (HASS Agent) + # x64 download + - name: Download Build Artifacts x64 (HASS Agent) uses: actions/download-artifact@v4.1.8 with: name: HASS.Agent path: HASS.Agent - - name: Download Build Artifacts (HASS Agent Satellite Service) + - name: Download Build Artifacts x64 (HASS Agent Satellite Service) uses: actions/download-artifact@v4.1.8 with: name: HASS.Agent.Satellite.Service path: HASS.Agent.Satellite.Service - - name: Download Build Artifacts (HASS Agent Installer) + - name: Download Build Artifacts x64 (HASS Agent Installer) uses: actions/download-artifact@v4.1.8 with: name: HASS.Agent.Installer path: HASS.Agent.Installer + + # x86 download + - name: Download Build Artifacts x86 (HASS Agent) + uses: actions/download-artifact@v4.1.8 + with: + name: HASS.Agent.x86 + path: HASS.Agent.x86 + + - name: Download Build Artifacts x86 (HASS Agent Satellite Service) + uses: actions/download-artifact@v4.1.8 + with: + name: HASS.Agent.Satellite.Service.x86 + path: HASS.Agent.Satellite.Service.x86 + + - name: Download Build Artifacts x86 (HASS Agent Installer) + uses: actions/download-artifact@v4.1.8 + with: + name: HASS.Agent.Installer.x86 + path: HASS.Agent.Installer.x86 - name: Compress HASS Agent directories for manual install run: | - cd HASS.Agent ls -la - zip -r HASS.Agent.zip ./ - cd ../ - cd HASS.Agent.Satellite.Service + zip -r HASS.Agent.Standalone.zip HASS.Agent HASS.Agent.Satellite.Service HASS.Agent.x86 HASS.Agent.Satellite.Service.x86 ls -la - zip -r HASS.Agent.Satellite.Service.zip ./ - name: Create Draft Release and Upload Build Artifacts - uses: ncipollo/release-action@v1.10.0 + uses: ncipollo/release-action@v1.15.0 with: name: ${{ github.event.inputs.releasetitle }} draft: true prerelease: ${{ github.event.inputs.prerelease }} tag: ${{ github.event.inputs.releasetag }} artifacts: - HASS.Agent/HASS.Agent.zip,HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.zip,HASS.Agent.Installer/HASS.Agent.Installer.exe + HASS.Agent.Installer/HASS.Agent.Installer.exe,HASS.Agent.Installer.x86/HASS.Agent.Installer.x86.exe,HASS.Agent.Standalone.zip diff --git a/README.md b/README.md index 9dea2c20..4fdb9a15 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/hass-agent/HASS.Agent)](https://github.com/hass-agent/HASS.Agent/releases/) [![license](https://img.shields.io/badge/license-MIT-blue)](#license) [![OS - Windows](https://img.shields.io/badge/OS-Windows-blue?logo=windows&logoColor=white)](https://www.microsoft.com/ "Go to Microsoft homepage") -[![dotnet](https://img.shields.io/badge/.NET-6.0-blue)](https://img.shields.io/badge/.NET-6.0-blue) +[![dotnet](https://img.shields.io/badge/.NET-8.0-blue)](https://img.shields.io/badge/.NET-8.0-blue) ![GitHub all releases](https://img.shields.io/github/downloads/hass-agent/HASS.Agent/total?color=blue) ![GitHub latest](https://img.shields.io/github/downloads/hass-agent/HASS.Agent/latest/total?color=blue) [![Discord](https://img.shields.io/badge/dynamic/json?color=blue&label=Discord&logo=discord&logoColor=white&query=presence_count&suffix=%20Online&url=https://discord.com/api/guilds/1173033284519862392/widget.json)](https://discord.com/invite/JfZj98xqJr) - - HASS.Agent logo + + HASS.Agent logo + # HASS.Agent -HASS.Agent is a Windows-based client (*companion*) application for [Home Assistant](https://www.home-assistant.io), developed in .NET 6. +HASS.Agent is a Windows-based client (*companion*) application for [Home Assistant](https://www.home-assistant.io), developed in .NET 8. Click [here](https://github.com/hass-agent/HASS.Agent/releases/latest/download/HASS.Agent.Installer.exe) to download the latest installer. @@ -52,6 +53,14 @@ The original HASS.Agent has been created by [Sam](https://github.com/LAB02-Admin Unfortunately due to some time constraints, they're not able to provide the constant support and feature updates. That's where we step in - trying to keep HASS.Agent bug free (dreams need to be big right?) and to introduce new features here and there! +#### Note on the organization and project name +*"Why this project is named the same as original HASS.Agent, it's confusing"* + +Yes, I agree, you're right. The initial idea after Sam's disappearance was to continue the work and offer it to Sam once they're back. +Well, now that's more than unlikely since it's been quite a long time we last spoke with Sam. + +Knowing what we do now, we've probably made a different decision but doing it now it'll only create more confusion. We have a full rewrite project going and it has been already decided the name is going to be altered. + ---- ### Functionality @@ -72,7 +81,7 @@ Summary of the core functions: * **WebView**: quickly show any website, anywhere - no browser required, for instance a HA dashboard. -* **Satellite Service**: use the service to collect sensordata and execute commands, even when you're not logged in (**not all commands/sensors are available for Satellite Service**) +* **Satellite Service**: use the service to collect sensor data and execute commands, even when you're not logged in (**not all commands/sensors are available for Satellite Service**) * All entities are dynamically acquired from your Home Assistant instance. @@ -88,8 +97,8 @@ Notification examples: ![image](https://user-images.githubusercontent.com/81011038/199956334-642def7d-4cb4-46f3-a73b-25c76e5bd02c.png) -![Text-based toast notification](https://raw.githubusercontent.com/LAB02-Research/HASS.Agent/main/images/hass_agent_toast_text.png) ![261428315-fa66e0cf-bd41-49d6-956c-864eec4bcc70](https://github.com/amadeo-alex/HASS.Agent/assets/68441479/c7e35fb1-59ea-4077-983e-916735dd3901) +![Text-based toast notification](https://raw.githubusercontent.com/LAB02-Research/HASS.Agent/main/images/hass_agent_toast_text.png) WebView example, showing a dashboard when right-clicking the tray icon: @@ -124,11 +133,11 @@ You'll be guided through the configuration options during onboarding: ### Installation -Installing HASS.Agent is easy; just [download the latest installer](https://github.com/hass-agent/HASS.Agent/releases/latest/download/HASS.Agent.Installer.exe), run it and you're done! The installer is signed by us and won't download or do weird stuff - it just places everything where it should, and launches with the right parameter. (optionally installing .NET6) +Installing HASS.Agent is easy; just [download the latest installer](https://github.com/hass-agent/HASS.Agent/releases/latest/download/HASS.Agent.Installer.exe), run it and you're done! The installer is signed by us and won't download or do weird stuff - it just places everything where it should, and launches with the right parameter. (optionally installing .NET8) After installing, the onboarding process will help you get everything configured, step by step. If you want an introduction into HASS.Agent, be sure to read the [introduction docs](https://www.hass-agent.io/latest/getting-started/). -Original HASS.Agent documentation is available [here](https://hassagent.readthedocs.io/en/latest/introduction/) - please bear in mind however that it may not represent state of things present in this version. +Original HASS.Agent documentation is available [here](https://www.hass-agent.io/latest/getting-started/#introduction) - please bear in mind however that it may not represent state of things present in this version. [Click here to download the latest installer](https://github.com/hass-agent/HASS.Agent/releases/latest/download/HASS.Agent.Installer.exe) @@ -158,7 +167,7 @@ If you want to help with the development of HASS.Agent, check out the [Helping O ### Articles -### Original HASS.Agent +### Original HASS.Agent by Sam/LAB02 Research Liam Alexander Colman from [Home Assistant Guide](https://home-assistant-guide.com) was kind enough to write an article about HASS.Agent: [Integrate Home Assistant with Windows using HASS.Agent](https://home-assistant-guide.com/2022/04/20/integrate-home-assistant-with-windows-using-hass-agent/). The website's full of useful articles, worth having a look :) @@ -166,11 +175,15 @@ Liam Alexander Colman from [Home Assistant Guide](https://home-assistant-guide.c ### What it's not -A Linux/macOS client! +A Linux/macOS client (at least yet)! + +Without getting into much of the details, it's not as easy as you think. + +With HASS.Agent "2.X" version it's basically impossible. We are thinking about cross-platform support for the "v3 rewrite" but as of now it's only a hopeful wish. -This question comes up a lot, understandably. However it's currently focussed on being a Windows-based client. Even though .NET 6 allows for Linux/macOS development, it's not as easy as pressing a button. The interface would have to be redesigned from the ground up, sensors and commands would need multiple codebases for each OS, testing would take way more time, every OS handles notifications differently, etc. +You can try the [official companion app](https://apps.apple.com/us/app/home-assistant/id1099568401) for macOS, or [IoPC](https://github.com/maksimkurb/IoPC) which runs on Linux. -You can use the [official companion app](https://apps.apple.com/us/app/home-assistant/id1099568401) for macOS, or [IoPC](https://github.com/maksimkurb/IoPC) which runs on Linux. Note: We haven't tested either. +#### Note: We haven't tested either and we do not track the development efforts. ---- @@ -180,6 +193,21 @@ The best way to help out is to test as much as you can (or even join the beta pr Same goes for sharing ideas for new (or improved) functionality! If you want, you can [join on Discord](https://discord.com/invite/JfZj98xqJr) to discuss your ideas. +#### Feature PR Submissions + +While all feature PR submissions are welcome, please note that their merging is at ***sole discretion*** of the main developers. + +When designing the new feature please take into account: +- **how they fit with the codebase/codestyle** (yes, we are aware the codebase has it's flaws - hence the rewrite efforts) +- **is the feature agnostic enough and will benefit all users** +- **the smaller the PR the better the chance of it being merged** + +#### AI Usage + +Fully "vibe-coded" submissions will most likely be rejected. + +While personal opinions on AI usage and it's impact can vary person-to-person, we'd rather put the effort writing code than reviewing fully AI generated and copy-pasted code. + ---- ### Credits and Licensing @@ -208,12 +236,14 @@ Everything on the HASS.Agent platform is released under the [MIT license](https: ### Legacy -HASS.Agent is a .NET 6 application. If for some reason you can't install .NET 6, you can use the last .NET Framework 4.8 version: +HASS.Agent is a .NET 8 application. If for some reason you can't install .NET 8, you can use the last .NET Framework 4.8 version also developed by Sam: [v2022.3.8](https://github.com/LAB02-Research/HASS.Agent/releases/tag/v2022.3.8) -It's pretty feature complete if you just want commands, sensors, quickactions and notifications. +Per it's release time it was pretty feature complete if you just want commands, sensors, quickactions and notifications. You'll need to have .NET Framework 4.8 installed on your PC, which you can [download here](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net48-web-installer). -If you find any bugs, feel free to [create a ticket](https://github.com/LAB02-Research/HASS.Agent/issues) and I'll try to patch it. +#### Please note +- .NET Framework version is considered legacy and unsupported +- All issues/requests regarding the .NET Framework version of HASS.Agent will be closed \ No newline at end of file diff --git a/assets/logo_128.png b/assets/logo_128.png new file mode 100644 index 00000000..37e6f839 Binary files /dev/null and b/assets/logo_128.png differ diff --git a/assets/new_quickaction_example.png b/assets/new_quickaction_example.png new file mode 100644 index 00000000..14a9078f Binary files /dev/null and b/assets/new_quickaction_example.png differ diff --git a/assets/new_sensor_example.png b/assets/new_sensor_example.png new file mode 100644 index 00000000..5761a8eb Binary files /dev/null and b/assets/new_sensor_example.png differ diff --git a/assets/notification_example_1.png b/assets/notification_example_1.png new file mode 100644 index 00000000..6855dd79 Binary files /dev/null and b/assets/notification_example_1.png differ diff --git a/assets/notification_example_2.png b/assets/notification_example_2.png new file mode 100644 index 00000000..1d7ea76c Binary files /dev/null and b/assets/notification_example_2.png differ diff --git a/assets/notification_example_3.png b/assets/notification_example_3.png new file mode 100644 index 00000000..595dc326 Binary files /dev/null and b/assets/notification_example_3.png differ diff --git a/assets/quickactions_example.png b/assets/quickactions_example.png new file mode 100644 index 00000000..5bc34ae1 Binary files /dev/null and b/assets/quickactions_example.png differ diff --git a/assets/satellite_config_example.png b/assets/satellite_config_example.png new file mode 100644 index 00000000..4ed80a18 Binary files /dev/null and b/assets/satellite_config_example.png differ diff --git a/assets/sensors_example.png b/assets/sensors_example.png new file mode 100644 index 00000000..1fb4dbad Binary files /dev/null and b/assets/sensors_example.png differ diff --git a/assets/webview_example.png b/assets/webview_example.png new file mode 100644 index 00000000..01d49ab9 Binary files /dev/null and b/assets/webview_example.png differ diff --git a/src/HASS.Agent.Installer/BeforeInstallNotice.rtf b/src/HASS.Agent.Installer/BeforeInstallNotice.rtf index 6499df36..0e67a650 100644 --- a/src/HASS.Agent.Installer/BeforeInstallNotice.rtf +++ b/src/HASS.Agent.Installer/BeforeInstallNotice.rtf @@ -1,5 +1,5 @@ {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang2057\deflangfe2057\themelang1045\themelangfe0\themelangcs1025{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 020b0604030504040204}Tahoma;} +{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 00000000000000000000}Tahoma;} {\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} @@ -43,8 +43,8 @@ \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf19 \sbasedon10 \sunhideused \styrsid490208 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpat0\chcbpat21 \sbasedon10 \ssemihidden \sunhideused \styrsid490208 Unresolved Mention;}}}} -{\*\rsidtbl \rsid227266\rsid490208\rsid1446401\rsid2324250\rsid3874449\rsid4072052\rsid5209844\rsid5534268\rsid7356386\rsid7820567\rsid12408085\rsid16263522}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1 -\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Amadeo}{\operator Amadeo}{\creatim\yr2023\mo11\dy27\hr16\min53}{\revtim\yr2024\mo9\dy27\hr20\min54}{\version10}{\edmins6}{\nofpages1}{\nofwords136}{\nofchars776}{\nofcharsws911}{\vern81}} +{\*\rsidtbl \rsid227266\rsid490208\rsid1010640\rsid1446401\rsid2324250\rsid3874449\rsid4072052\rsid5209844\rsid5534268\rsid7356386\rsid7820567\rsid12408085\rsid16263522}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0 +\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Amadeo}{\operator Amadeo}{\creatim\yr2023\mo11\dy27\hr16\min53}{\revtim\yr2025\mo5\dy11\hr20\min29}{\version11}{\edmins6}{\nofpages1}{\nofwords112}{\nofchars640}{\nofcharsws751}{\vern81}} {\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1 \noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1 @@ -57,19 +57,16 @@ \f31506\fs22\lang2057\langfe1033\cgrid\langnp2057\langfenp1033 {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid227266 \line }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 Welcome to HASS.Agent Installer! \par This version is maintained by the }{\field{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid2324250 HYPERLINK "https://github.com/hass-agent/" }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid2324250 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b56000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f0068006100730073002d006100670065006e0074002f000000795881f43b1d7f48af2c825dc485276300000000 -a5ab000300}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid2324250 HASS.Agent Team}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\rtlch\fcs1 -\af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 and is a fork of the }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 HYPERLINK "https://github.com/LAB02-Research/HASS.Agent" }{\rtlch\fcs1 -\af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 {\*\datafield +a5ab00030000}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid2324250 HASS.Agent Team}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj { +\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 and is a fork of the }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 HYPERLINK "https://github.com/LAB02-Research/HASS.Agent" }{ +\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b72000000680074007400700073003a002f002f006700690074006800750062002e0063006f006d002f004c0041004200300032002d00520065007300650061007200630068002f0048004100530053002e0041006700 -65006e0074000000795881f43b1d7f48af2c825dc485276300000000a5ab00030064d96621af0000}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid4072052 original}}}\sectd \ltrsect +65006e0074000000795881f43b1d7f48af2c825dc485276300000000a5ab00030064d96621af000000}}}{\fldrslt {\rtlch\fcs1 \af43 \ltrch\fcs0 \cs15\f43\fs16\ul\cf19\insrsid490208\charrsid4072052 original}}}\sectd \ltrsect \linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid490208\charrsid4072052 version by LAB02-Research. \par The intention of this version is to provide bug-fix and feature updated until the development of original version is resumed. \par We've tried our best to maintain compatibility with the original version but some breaking changes are unavoidable. \par The installer has an option to migrate current installation configuration for seamless transition, please remember that some settings (like MQTT configuration) may need - to be changed if you'd like to run two instances of HASS.Agent - forked and original one.}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid12408085 -\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid5534268\charrsid1446401 Please note:}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid5534268 the }{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid5534268\charrsid1446401 migration}{\rtlch\fcs1 \af43 -\ltrch\fcs0 \f43\fs16\insrsid5534268 option should be used }{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid5534268\charrsid5534268 only once}{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid5534268 . }{\rtlch\fcs1 \af43 \ltrch\fcs0 -\f43\fs16\insrsid5534268 Using it more than once will once again try to copy the configuration from the original HASS.Agent.}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid5534268\charrsid5534268 + to be changed if you'd like to run two instances of HASS.Agent - forked and original one.}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid5534268\charrsid5534268 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 @@ -214,8 +211,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000503f -ad9e0e11db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000b09a +eb9fa2c2db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/src/HASS.Agent.Installer/DotNet8Notice.rtf b/src/HASS.Agent.Installer/DotNet8Notice.rtf new file mode 100644 index 00000000..271cf46f --- /dev/null +++ b/src/HASS.Agent.Installer/DotNet8Notice.rtf @@ -0,0 +1,226 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang2057\deflangfe2057\themelang2057\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f2\fbidi \fmodern\fcharset238\fprq1{\*\panose 02070309020205020404}Courier New;}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;} +{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f37\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f42\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Segoe UI Emoji;} +{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 00000000000000000000}Tahoma;}{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;} +{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;} +{\fbiminor\f31507\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f46\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f66\fbidi \fmodern\fcharset0\fprq1 Courier New;}{\f65\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;} +{\f67\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f68\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f69\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);} +{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f386\fbidi \froman\fcharset0\fprq2 Cambria Math;}{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} +{\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} +{\f416\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\f415\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f417\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f418\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} +{\f419\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f420\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f421\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f422\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} +{\f476\fbidi \fswiss\fcharset0\fprq2 Tahoma;}{\f475\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f477\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}{\f478\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;} +{\f479\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f480\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f481\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f482\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);} +{\f483\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;} +{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} +{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} +{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);} +{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} +{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0; +\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128; +\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 } +\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 +\fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext11 \ssemihidden \sunhideused +Normal Table;}}{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 +\fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext11 \ssemihidden \sunhideused +Normal Table;}}}}{\*\listtable{\list\listtemplateid651192760\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\leveltemplateid134807553\'01{\uc1\u-3913 ?};}{\levelnumbers;} +\f3\fbias0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807555\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\lin1440 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807553\'01{\uc1\u-3913 ?};}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807555\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace360\levelindent0{\leveltext\leveltemplateid134807553\'01{\uc1\u-3913 ?};}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0 +{\leveltext\leveltemplateid134807555\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557 +\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\lin6480 }{\listname ;}\listid1037851123}}{\*\listoverridetable{\listoverride\listid1037851123\listoverridecount0\ls1}}{\*\rsidtbl \rsid4395212\rsid14366970\rsid15153285\rsid16274723}{\mmathPr +\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Amadeo}{\creatim\yr2025\mo5\dy11\hr19\min54}{\revtim\yr2025\mo5\dy11\hr20\min25}{\version4}{\edmins2}{\nofpages1} +{\nofwords47}{\nofchars268}{\nofcharsws314}{\vern81}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot14366970 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj +{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}} +{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid16274723 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 {\rtlch\fcs1 \af43 +\ltrch\fcs0 \f43\fs16\insrsid16274723 +\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16274723 \hich\af43\dbch\af31505\loch\f43 Starting with version }{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid16274723\charrsid16274723 \hich\af43\dbch\af31505\loch\f43 2.2.0}{\rtlch\fcs1 \af43 +\ltrch\fcs0 \f43\fs16\insrsid16274723 \hich\af43\dbch\af31505\loch\f43 , \hich\af43\dbch\af31505\loch\f43 HASS.Agent no longer uses .NET6 but instead ut\hich\af43\dbch\af31505\loch\f43 ilize\hich\af43\dbch\af31505\loch\f43 s }{\rtlch\fcs1 \af43 +\ltrch\fcs0 \b\f43\fs16\insrsid16274723\charrsid4395212 \hich\af43\dbch\af31505\loch\f43 .NET8}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16274723 . +\par \hich\af43\dbch\af31505\loch\f43 If you do not have \hich\af43\dbch\af31505\loch\f43 .NET8 libraries already installed \hich\af43\dbch\af31505\loch\f43 b\hich\af43\dbch\af31505\loch\f43 y another software, \hich\af43\dbch\af31505\loch\f43 installer will +\hich\af43\dbch\af31505\loch\f43 download and install it fo\hich\af43\dbch\af31505\loch\f43 r you. +\par \hich\af43\dbch\af31505\loch\f43 There \hich\af43\dbch\af31505\loch\f43 is n\hich\af43\dbch\af31505\loch\f43 o special action required from your side\hich\af43\dbch\af31505\loch\f43 , \hich\af43\dbch\af31505\loch\f43 +we just wanted to provide an additional \loch\af43\dbch\af31505\hich\f43 \'93\hich\af43\dbch\af31505\loch\f43 FYI\loch\af43\dbch\af31505\hich\f43 \'94\hich\af43\dbch\af31505\loch\f43 }{\rtlch\fcs1 \af42 \ltrch\fcs0 +\fs16\loch\af42\hich\af42\dbch\af42\insrsid16274723\charrsid16274723 \loch\af42\dbch\af42\hich\f42 \u-10179\'3f\u-8694\'3f}{\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid16274723\charrsid16274723 +\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid16274723\charrsid11156998 +\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid15153285\charrsid16274723 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210041daf6e5a6070000e6200000160000007468656d652f7468656d652f +7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f222b524cfda33f660c90e7bac914add65557789aed28cc56208de532e81c026e49085 +bded21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c9f3810326cc0886eed2efbdfad57bafde7baabaffd9ab983a1738e584253db77eafe63a3899b13949 +c29efb7c3aae745c870b94cc116509eeb91bccddcf1efcfa57f7d19188708c1d904ff811eab99110aba36a95cf6018f17b6c8513f86ec1d21809784dc3ea3c45 +97a037a6d546add6aac68824ae93a018d49e32b1b974566849b0583b4f170b32c3ee83ed24230a332582cb81194d27720a9c4b9e29195d64beac4b20dff080a6 +ce05a23d17a69db3cb297e255c87222ee08b9e5b537f6ef5c1fd2a3aca85a83820abc98dd55f2e970bcc970d35671a9e17937a9eefb5fa857e05a0621f376a8f +5aa356a14f01d06c060bceb8983adb8dc0cbb11a287bb4e81eb687cdba81d7f437f738f77df931f00a94e9f7f6f0e371005634f00a94e1fd3dbc3fe80e86a67e +05caf0ad3d7cbbd61f7a6d43bf02459424cb3d74cd6f3583ed6a0bc882d1632bbceb7be37623575ea2201a8a2093532c5822ae08b918bd64e91870124f912089 +23362bbc403388ed0051729e12e7848411c4df0a258cc370ad511bd79af05f7e3cf5a41c8b8e30d2a4253d20c4f786242d87cf52b2123df711687535c8bb9f7f +7efbe6c7b76f7e7afbd5576fdffc3d9f5ba932e48e5112ea72bf7cf787ff7cf35be7df3f7cfbcbd77fcca6dec5731dfffe6fbf7bff8f7f7e483dacb834c5bb3f +7dfffec7efdffdf9f7fffaebd716edfd149debf0298931779ee04be7198b618116fef83cbd99c434424497e82721470992b358f48f4464a09f6c104516dc009b +767c9142c6b1011fae5f1a842751ba16c4a2f171141bc053c6e880a5562b3c967369669eae93d03e79bad671cf10bab0cd1da0c4f0f268bd828c4b6c2a83081b +34cf284a040a71828523bf634b8c2dabfb8210c3aea7649632ce16c2f982380344ac26999273239a4aa16312835f363682e06fc336a72f9c01a3b6550ff18589 +84bd81a885fc1453c38c0fd15aa0d8a6728a62aa1bfc0489c84672b249673a6ec405783ac49439a339e6dc26f33485f56a4e7f0c69c6eef653ba894d642ac8d2 +a6f30431a623876c1944285ed9b01392443af673be841045ce191336f8293377887c073fa0e4a0bb5f402fa04f707536780e1956972803447eb34e2dbe7c8899 +11bf930d5d206c4b35fd3436526c3f25d6e818ac4323b44f30a6e812cd31769e7f6e6130602bc3e625e947116495636c0bac47c88c55f99e608e1dd5e3ece7c9 +13c28d909de0901de073bad9493c1b94c4283da4f909785db7f9084a5d6c0b80a774b6d4814f087486102f56a33ce5a0430bee835acf22641430f9ceedf1ba49 +0dff5d678fc1be7c69d0b8c6be04197c631948ecbacc076d3345d498a00c9829822ec3966e41c4707f29228bab125b5be516e6a62ddd004d92d1f4c424b9b203 +dae97dfcff5def031dc6bbbf7c63d96c1fa7dfb12b3692d50d3b9d43c9e478a7bf3984dbed6a0296cec9a7dfd40cd13a39c35047f633d65d4f73d7d3b8fff73d +cda1fd7cd7c91cea37ee3a19173a8cbb4e263f5cf9389d4cd9bc405f230f3cb2f31e75fa135f75f8b320944ec486e213aece7f38fcac998f61508aabe3505c9c +09ae227894d50ee63170618a948c9332f11b22a249845670485477a59290e7aa43eeac1887b323356cd52df1741d9fb27976f459afcb63ceacc07224caf19a5f +8cc37995c8d0ad76799c57a8576c4375faba2520656f42429bcc24d1b490686f07a591d4592f18cd4242adeca3b0e85a5874a4faadabf65800b5c22bf0bbdb81 +5feb3dd7f7400484e0580e7af4b9f453e6eaad7795333fa6a70f19d38800e8b3b711507aba2bb91e5c9e5c5d166ad7f0b441420b379384b28ceaf37804bf86f3 +e894a3d7a171535f774b971af4a429d47c105a258d76e7432c6eeb6b90dbcd0d34d133054d9ccb9edb6afa103233b4eab90b383b86c77805b1c3e54f2f4443b8 +96998934dbf0b7c92cab948b21e25166709574b26c101381538792b8e7cae5176ea089ca218a5bbd0109e19325d785b4f2a99103a79b4ec68b059e09ddedda88 +b474f60a193ecb15d66f95f8edc15292adc1dd93687ee99cd375fa0c4188f9edba34e09c70b842a867d69c13b81a2b1259197f3b85294fbbfadd948aa16c1cd1 +5584f28aa227f30cae52794147bd1536d0def23583413593e485f03c94055637aa514d8baa9171385875af169296d3926659338dac22aba63d8b19336ccbc08e +2d6f57e435565b13434ed32b7c96ba77536e779beb76fa84a24a80c10bfb59aaee350a8246ad9ccca02619efa76199b3f351b3766c177805b5eb14092debb7b6 +6a77ec56d408eb743078abca0f72bb510b438b6d7ba92cadaed4f5db6e76fe1292c7109add35155cb9122eb153040dd144f52459da802df24ae45b039e9c754a +7aee9735bfef050d3fa8d43afea8e235bd5aa5e3f79b95beef37eb23bf5e1b0e1aafa1b08828aefbd975fe18ee31e826bfd457e37b17fbf1f6aae6de8cc555a6 +aeeaab8ab8bad8af378c8bfdec2adf99ca1b7bd7219074be6c35c6dd6677d0aa749bfd71c51b0e3a956ed01a5486ada03d1c0f03bfd31dbf769d0b05f6facdc0 +6b8d3a95563d082a5eab26e977ba95b6d768f4bd76bf33f2faaff33606569ea58fdc16605ec5ebc17f010000ffff0300504b0304140006000800000021000dd1 +909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277 +086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b +64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996 +509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff +0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7 +c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000 +001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210041 +daf6e5a6070000e62000001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600080000 +0021000dd1909fb60000001b0100002700000000000000000000000000b00a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ab0b00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; +\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; +\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; +\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; +\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; +\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000060fa +e211a2c2db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/src/HASS.Agent.Installer/InstallerScript-Service-x86.iss b/src/HASS.Agent.Installer/InstallerScript-Service-x86.iss new file mode 100644 index 00000000..d34a5ec6 --- /dev/null +++ b/src/HASS.Agent.Installer/InstallerScript-Service-x86.iss @@ -0,0 +1,135 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! +; Modified to suit HASS.Agent requirements :) + +; InnoDependencyInstaller +; Thanks to https://github.com/DomGries/InnoDependencyInstaller for the amazing work! +#define public Dependency_Path_NetCoreCheck "dependencies\" +#include "CodeDependencies.iss" + +; Standard installation constants +#define MyAppName "HASS.Agent Satellite Service" +#define MyAppVersion "2.2.1" +#define MyAppPublisher "HASS.Agent Team" +#define MyAppURL "https://hass-agent.io" +#define MyAppExeName "HASS.Agent.Satellite.Service.exe" +#define ServiceName "hass.agent.svc" +#define ServiceDisplayName "HASS.Agent - Satellite Service" +#define ServiceDescription "Satellite service for HASS.Agent: a Windows based Home Assistant client. This service processes commands and sensors without the requirement of a logged-in user." + +[Setup] +;SetupMutex=Global\HASS.Agent.Setup.Satellite.Mutex,HASS.Agent.Satellite.Setup.Mutex +AppMutex=Global\\HASS.Agent.Service.Mutex +AppId={{4004588E-F411-41C2-ABD8-A898B0A14B93} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={commonpf32}\{#MyAppName}\Service +DisableProgramGroupPage=yes +LicenseFile=..\..\LICENSE.md +InfoBeforeFile=.\BeforeInstallNotice-Service.rtf +InfoAfterFile=.\AfterInstallNotice-Service.rtf +PrivilegesRequired=admin +OutputDir=.\bin +OutputBaseFilename=HASS.Agent.Service.Installer.x86 +SetupIconFile=..\HASS.Agent\HASS.Agent.Shared\hassagent.ico +Compression=lzma +SolidCompression=yes +WizardStyle=modern +CloseApplications=yes +CloseApplicationsFilter=*.* +UninstallDisplayIcon={app}\{#MyAppExeName} +UninstallDisplayName={#MyAppName} {#MyAppVersion} + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +; NOTE: Don't use "Flags: ignoreversion" on any shared system files +[Files] +; Service files +Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; BeforeInstall: StopSatelliteService() + +[Run] +Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser + +[Registry] +Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: createvalueifdoesntexist uninsdeletevalue + +; Ensure LibreHardwareMonitor/WinRing0 files are removed if left due to any reason by previous installs +[InstallDelete] +Type: files; Name: "{app}\LibreHardwareMonitorLib.dll" +Type: files; Name: "{app}\HASS.Agent.Satellite.Service.sys" + +; Service registration and removal +[Run] +Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""\""{app}\{#MyAppExeName}""\"""; Flags: runhidden +Filename: "{sys}\sc.exe"; Parameters: "failure {#ServiceName} reset= 86400 actions= restart/60000/restart/60000//1000"; Flags: runhidden +Filename: "{sys}\sc.exe"; Parameters: "description {#ServiceName} ""{#ServiceDescription}"""; Flags: runhidden +Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} DisplayName= ""{#ServiceDisplayName}"""; Flags: runhidden +Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} start= auto"; Flags: runhidden +Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} binpath= ""\""{app}\{#MyAppExeName}""\"""; Flags: runhidden +[UninstallRun] +Filename: "{sys}\net.exe"; Parameters: "stop {#ServiceName}"; RunOnceId: StopService; Flags: runhidden +Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay1; Flags:runhidden +Filename: "{sys}\sc.exe"; Parameters: "delete {#ServiceName}" ; RunOnceId: DeleteService; Flags: runhidden +Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay2; Flags:runhidden + +[Code] +function InitializeSetup: Boolean; +begin + Dependency_ForceX86 := True; + Dependency_AddDotNet80Desktop; + Result := True; +end; + +procedure CurStepChanged(CurStep: TSetupStep); +var + ProgressPage: TOutputProgressWizardPage; + I, Step, Wait, ResultCode: Integer; +begin + if CurStep = ssInstall then + begin + //thanks to https://stackoverflow.com/a/39827761 + Wait := 5000; + Step := 100; + ProgressPage := + CreateOutputProgressPage( + WizardForm.PageNameLabel.Caption, + WizardForm.PageDescriptionLabel.Caption); + ProgressPage.SetText('Making sure the satellite service is stopped...', ''); + ProgressPage.SetProgress(0, Wait); + ProgressPage.Show; + + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + + try + for I := 0 to Wait div Step do + begin + ProgressPage.SetProgress(I * Step, Wait); + Sleep(Step); + end; + finally + ProgressPage.Hide; + ProgressPage.Free; + end; + + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + //Exec(ExpandConstant('{sys}\taskkill.exe'), '/f /im ' + '"' + ExpandConstant('{#MyAppExeName}') + '"', ExpandConstant('{sys}'), SW_HIDE, ewWaitUntilTerminated, ResultCode); + + end; +end; + +procedure StopSatelliteService(); +var + ResultCode: Integer; +begin + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + + //Ensure LibreHardwareMonitor/WinRing0 files are removed + DeleteFile(ExpandConstant('{app}\LibreHardwareMonitorLib.dll')); + DeleteFile(ExpandConstant('{app}\HASS.Agent.Satellite.Service.sys')); +end; \ No newline at end of file diff --git a/src/HASS.Agent.Installer/InstallerScript-Service.iss b/src/HASS.Agent.Installer/InstallerScript-Service.iss index 7270347e..f9c44adb 100644 --- a/src/HASS.Agent.Installer/InstallerScript-Service.iss +++ b/src/HASS.Agent.Installer/InstallerScript-Service.iss @@ -9,7 +9,7 @@ ; Standard installation constants #define MyAppName "HASS.Agent Satellite Service" -#define MyAppVersion "2.1.1" +#define MyAppVersion "2.2.1" #define MyAppPublisher "HASS.Agent Team" #define MyAppURL "https://hass-agent.io" #define MyAppExeName "HASS.Agent.Satellite.Service.exe" @@ -41,7 +41,7 @@ SetupIconFile=..\HASS.Agent\HASS.Agent.Shared\hassagent.ico Compression=lzma SolidCompression=yes WizardStyle=modern -CloseApplications=force +CloseApplications=yes CloseApplicationsFilter=*.* UninstallDisplayIcon={app}\{#MyAppExeName} UninstallDisplayName={#MyAppName} {#MyAppVersion} @@ -52,14 +52,19 @@ Name: "english"; MessagesFile: "compiler:Default.isl" ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Files] ; Service files -Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "..\HASS.Agent\HASS.Agent.Satellite.Service\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; BeforeInstall: StopSatelliteService() [Run] -Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser +Filename: "{sys}\sc.exe"; Parameters: "start {#ServiceName}"; Description: "Start Satellite Service"; Flags: postinstall runhidden runascurrentuser [Registry] Root: HKLM; Subkey: "SOFTWARE\HASSAgent\SatelliteService"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: createvalueifdoesntexist uninsdeletevalue +; Ensure LibreHardwareMonitor/WinRing0 files are removed if left due to any reason by previous installs +[InstallDelete] +Type: files; Name: "{app}\LibreHardwareMonitorLib.dll" +Type: files; Name: "{app}\HASS.Agent.Satellite.Service.sys" + ; Service registration and removal [Run] Filename: "{sys}\sc.exe"; Parameters: "create {#ServiceName} binpath= ""\""{app}\{#MyAppExeName}""\"""; Flags: runhidden @@ -69,7 +74,7 @@ Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} DisplayName= ""{#Se Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} start= auto"; Flags: runhidden Filename: "{sys}\sc.exe"; Parameters: "config {#ServiceName} binpath= ""\""{app}\{#MyAppExeName}""\"""; Flags: runhidden [UninstallRun] -Filename: "{sys}\sc.exe"; Parameters: "stop {#ServiceName}"; RunOnceId: StopService; Flags: runhidden +Filename: "{sys}\net.exe"; Parameters: "stop {#ServiceName}"; RunOnceId: StopService; Flags: runhidden Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay1; Flags:runhidden Filename: "{sys}\sc.exe"; Parameters: "delete {#ServiceName}" ; RunOnceId: DeleteService; Flags: runhidden Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay2; Flags:runhidden @@ -78,7 +83,7 @@ Filename: "{sys}\timeout.exe"; Parameters: "5"; RunOnceId: Delay2; Flags:runhidd function InitializeSetup: Boolean; begin Dependency_ForceX86 := False; - Dependency_AddDotNet60Desktop; + Dependency_AddDotNet80Desktop; Result := True; end; @@ -89,8 +94,6 @@ var begin if CurStep = ssInstall then begin - Exec(ExpandConstant('{sys}') + '\sc.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) - //thanks to https://stackoverflow.com/a/39827761 Wait := 5000; Step := 100; @@ -101,6 +104,9 @@ begin ProgressPage.SetText('Making sure the satellite service is stopped...', ''); ProgressPage.SetProgress(0, Wait); ProgressPage.Show; + + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + try for I := 0 to Wait div Step do begin @@ -111,5 +117,20 @@ begin ProgressPage.Hide; ProgressPage.Free; end; + + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + //Exec(ExpandConstant('{sys}\taskkill.exe'), '/f /im ' + '"' + ExpandConstant('{#MyAppExeName}') + '"', ExpandConstant('{sys}'), SW_HIDE, ewWaitUntilTerminated, ResultCode); + end; +end; + +procedure StopSatelliteService(); +var + ResultCode: Integer; +begin + Exec(ExpandConstant('{sys}') + '\net.exe', 'stop ' + ExpandConstant('{#ServiceName}'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) + + //Ensure LibreHardwareMonitor/WinRing0 files are removed + DeleteFile(ExpandConstant('{app}\LibreHardwareMonitorLib.dll')); + DeleteFile(ExpandConstant('{app}\HASS.Agent.Satellite.Service.sys')); end; \ No newline at end of file diff --git a/src/HASS.Agent.Installer/InstallerScript-x86.iss b/src/HASS.Agent.Installer/InstallerScript-x86.iss new file mode 100644 index 00000000..75927faa --- /dev/null +++ b/src/HASS.Agent.Installer/InstallerScript-x86.iss @@ -0,0 +1,145 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! +; Modified to suit HASS.Agent requirements :) + +; InnoDependencyInstaller +; Thanks to https://github.com/DomGries/InnoDependencyInstaller for the amazing work! +#define public Dependency_Path_NetCoreCheck "dependencies\" +#include "CodeDependencies.iss" + +; Standard installation constants +#define MyAppName "HASS.Agent" +#define MyAppVersion "2.2.1" +#define MyAppPublisher "HASS.Agent Team" +#define MyAppURL "https://hass-agent.io" +#define MyAppExeName "HASS.Agent.exe" + +#define MigrationNotice "MigrationNotice.rtf" +#define DotNet8Notice "DotNet8Notice.rtf" + +[Setup] +SetupMutex=Global\HASS.Agent.Setup.Mutex,HASS.Agent.Setup.Mutex +AppMutex=HASS.Agent.App.Mutex +AppId={{7BBED458-609B-4D13-AD9E-4FF219DF8644} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={localappdata}\{#MyAppName}\Client +DisableProgramGroupPage=yes +LicenseFile=..\..\LICENSE.md +InfoBeforeFile=.\BeforeInstallNotice.rtf +InfoAfterFile=.\AfterInstallNotice.rtf +; Uncomment the following line to run in non administrative install mode (install for current user only.) +PrivilegesRequired=lowest +OutputDir=.\bin +OutputBaseFilename=HASS.Agent.Installer.x86 +SetupIconFile=..\HASS.Agent\HASS.Agent.Shared\hassagent.ico +Compression=lzma +SolidCompression=yes +WizardStyle=modern +CloseApplications=force +CloseApplicationsFilter=*.* +UninstallDisplayIcon={app}\{#MyAppExeName} +UninstallDisplayName={#MyAppName} {#MyAppVersion} + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +; NOTE: Don't use "Flags: ignoreversion" on any shared system files +[Files] +; Client files +Source: "..\HASS.Agent\HASS.Agent\bin\Publish-x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "{#MigrationNotice}"; Flags: dontcopy +Source: "{#DotNet8Notice}"; Flags: dontcopy +; Service installer +Source: ".\bin\HASS.Agent.Service.Installer.x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion + +[Icons] +Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration - use only once (administrative permissions required)"; Flags: postinstall runascurrentuser unchecked +Filename: "{tmp}\HASS.Agent.Service.Installer.x86.exe"; Parameters: "{code:GetCmdLineParams}"; Description: "Install Satellite Service (administrative permissions required)"; Flags: postinstall runascurrentuser +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall skipifsilent nowait + +[Code] +var + OriginalCmdLine: String; + +function GetCmdLineParams(Param: String): String; +begin + Result := OriginalCmdLine; +end; + +procedure InitializeWizard; +var + AfterID: Integer; + MigrationNotice: AnsiString; + DotNet8Notice: AnsiString; +begin + OriginalCmdLine := GetCmdTail; + + AfterID := wpSelectTasks; + + ExtractTemporaryFile('{#MigrationNotice}'); + LoadStringFromFile(ExpandConstant('{tmp}\{#MigrationNotice}'), MigrationNotice); + AfterID := CreateOutputMsgMemoPage(AfterID, 'Configuration migration', 'Please read carefully before proceeding.', 'Ignoring below message might cause you to loose your configuration.' , MigrationNotice).ID + + ExtractTemporaryFile('{#DotNet8Notice}'); + LoadStringFromFile(ExpandConstant('{tmp}\{#DotNet8Notice}'), DotNet8Notice); + AfterID := CreateOutputMsgMemoPage(AfterID, '.NET 8', 'New .NET version required with this HASS.Agent version.', '' , DotNet8Notice).ID +end; + +function InitializeSetup: Boolean; +begin + Dependency_ForceX86 := True; + Dependency_AddDotNet80Desktop; + Result := True; +end; + +function NextButtonClick(CurPageID: Integer): Boolean; +var + Msg: string; +begin + if CurPageID = wpFinished then + begin + if WizardForm.RunList.Checked[0] then + begin; + Msg := 'Migration requires original HASS.Agent and Satellite Service to be stopped, do you wish to proceed?'; + Result := (MsgBox(Msg, mbConfirmation, MB_YESNO) = IDYES) + end + else + begin + Result := True; + end + end + else + begin + Result := True; + end +end; + +procedure CurUninstallStepChanged (CurUninstallStep: TUninstallStep); +var + mres : integer; + serviceUninstallerPath : String; + ResultCode : integer; +begin + case CurUninstallStep of + usPostUninstall: + begin + mres := MsgBox('Do you want to uninstall the Satellite Service? (administrative permissions required)', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) + if mres = IDYES then + RegQueryStringValue(HKLM, 'SOFTWARE\HASSAgent\SatelliteService', 'InstallPath', serviceUninstallerPath); + Exec(serviceUninstallerPath + '\unins000.exe', '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode); + end; + end; +end; diff --git a/src/HASS.Agent.Installer/InstallerScript.iss b/src/HASS.Agent.Installer/InstallerScript.iss index 1dee32b1..335f0338 100644 --- a/src/HASS.Agent.Installer/InstallerScript.iss +++ b/src/HASS.Agent.Installer/InstallerScript.iss @@ -9,11 +9,14 @@ ; Standard installation constants #define MyAppName "HASS.Agent" -#define MyAppVersion "2.1.1" +#define MyAppVersion "2.2.1" #define MyAppPublisher "HASS.Agent Team" #define MyAppURL "https://hass-agent.io" #define MyAppExeName "HASS.Agent.exe" +#define MigrationNotice "MigrationNotice.rtf" +#define DotNet8Notice "DotNet8Notice.rtf" + [Setup] ArchitecturesInstallIn64BitMode=x64 SetupMutex=Global\HASS.Agent.Setup.Mutex,HASS.Agent.Setup.Mutex @@ -54,6 +57,8 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ [Files] ; Client files Source: "..\HASS.Agent\HASS.Agent\bin\Publish-x64\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "{#MigrationNotice}"; Flags: dontcopy +Source: "{#DotNet8Notice}"; Flags: dontcopy ; Service installer Source: ".\bin\HASS.Agent.Service.Installer.exe"; DestDir: "{tmp}"; Flags: ignoreversion @@ -61,16 +66,48 @@ Source: ".\bin\HASS.Agent.Service.Installer.exe"; DestDir: "{tmp}"; Flags: ignor Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon +; Ensure LibreHardwareMonitor/WinRing0 files are removed +[InstallDelete] +Type: files; Name: "{app}\LibreHardwareMonitorLib.dll" +Type: files; Name: "{app}\HASS.Agent.sys" + [Run] -Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration - use only once (administrative permissions required)"; Flags: postinstall skipifsilent runascurrentuser unchecked -Filename: "{tmp}\HASS.Agent.Service.Installer.exe"; Description: "Install Satellite Service (administrative permissions required)"; Flags: postinstall runascurrentuser +Filename: "{app}\{#MyAppExeName}"; Parameters: "compat_migrate"; Description: "Try to migrate configuration - use only once (administrative permissions required)"; Flags: postinstall runascurrentuser unchecked +Filename: "{tmp}\HASS.Agent.Service.Installer.exe"; Parameters: "{code:GetCmdLineParams}"; Description: "Install Satellite Service (administrative permissions required)"; Flags: postinstall runascurrentuser Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: postinstall skipifsilent nowait [Code] +var + OriginalCmdLine: String; + +function GetCmdLineParams(Param: String): String; +begin + Result := OriginalCmdLine; +end; + +procedure InitializeWizard; +var + AfterID: Integer; + MigrationNotice: AnsiString; + DotNet8Notice: AnsiString; +begin + OriginalCmdLine := GetCmdTail; + + AfterID := wpSelectTasks; + + ExtractTemporaryFile('{#MigrationNotice}'); + LoadStringFromFile(ExpandConstant('{tmp}\{#MigrationNotice}'), MigrationNotice); + AfterID := CreateOutputMsgMemoPage(AfterID, 'Configuration migration', 'Please read carefully before proceeding.', 'Ignoring below message might cause you to loose your configuration.' , MigrationNotice).ID + + ExtractTemporaryFile('{#DotNet8Notice}'); + LoadStringFromFile(ExpandConstant('{tmp}\{#DotNet8Notice}'), DotNet8Notice); + AfterID := CreateOutputMsgMemoPage(AfterID, '.NET 8', 'New .NET version required with this HASS.Agent version.', '' , DotNet8Notice).ID +end; + function InitializeSetup: Boolean; begin Dependency_ForceX86 := False; - Dependency_AddDotNet60Desktop; + Dependency_AddDotNet80Desktop; Result := True; end; diff --git a/src/HASS.Agent.Installer/MigrationNotice.rtf b/src/HASS.Agent.Installer/MigrationNotice.rtf new file mode 100644 index 00000000..c5e8ac71 --- /dev/null +++ b/src/HASS.Agent.Installer/MigrationNotice.rtf @@ -0,0 +1,231 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang2057\deflangfe2057\themelang2057\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f2\fbidi \fmodern\fcharset238\fprq1{\*\panose 02070309020205020404}Courier New;}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;} +{\f34\fbidi \froman\fcharset238\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f37\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f43\fbidi \fswiss\fcharset238\fprq2{\*\panose 00000000000000000000}Tahoma;} +{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f46\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f66\fbidi \fmodern\fcharset0\fprq1 Courier New;} +{\f65\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f67\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f68\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f69\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} +{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f386\fbidi \froman\fcharset0\fprq2 Cambria Math;} +{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f416\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\f415\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f417\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;} +{\f418\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f419\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f420\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f421\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\f422\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\f476\fbidi \fswiss\fcharset0\fprq2 Tahoma;}{\f475\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f477\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;} +{\f478\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;}{\f479\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f480\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f481\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;} +{\f482\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f483\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;} +{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} +{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} +{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);} +{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} +{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0; +\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128; +\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;\red5\green99\blue193;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 +\ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext11 \ssemihidden \sunhideused +Normal Table;}{\*\cs15 \additive \ul\cf19 \sunhideused \styrsid11156998 Hyperlink;}}{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 +\ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 \snext11 \ssemihidden \sunhideused +Normal Table;}{\*\cs15 \additive \ul\cf19 \sunhideused \styrsid11156998 Hyperlink;}}}}{\*\listtable{\list\listtemplateid651192760\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0 +{\leveltext\leveltemplateid134807553\'01{\uc1\u-3913 ?};}{\levelnumbers;}\f3\fbias0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext +\leveltemplateid134807555\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557 +\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807553 +\'01{\uc1\u-3913 ?};}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807555\'01o;}{\levelnumbers;} +\f2\fbias0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 +\fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807553\'01{\uc1\u-3913 ?};}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\lin5040 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807555\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace360\levelindent0{\leveltext\leveltemplateid134807557\'01{\uc1\u-3929 ?};}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\lin6480 }{\listname ;}\listid1037851123}}{\*\listoverridetable +{\listoverride\listid1037851123\listoverridecount0\ls1}}{\*\rsidtbl \rsid11156998\rsid14366970\rsid15153285}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info +{\operator Amadeo}{\creatim\yr2025\mo5\dy11\hr19\min54}{\revtim\yr2025\mo5\dy11\hr20\min22}{\version3}{\edmins10}{\nofpages1}{\nofwords54}{\nofchars314}{\nofcharsws367}{\vern81}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} +\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot14366970 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sectrsid16263522\sftnbj +{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}} +{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid11156998 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang2057\langfe2057\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp2057\langfenp2057 {\rtlch\fcs1 \af43 +\ltrch\fcs0 \f43\fs16\insrsid11156998 +\par \hich\af43\dbch\af31505\loch\f43 After HASS.Agent i\hich\af43\dbch\af31505\loch\f43 s installed, you have an\hich\af43\dbch\af31505\loch\f43 option to migrate configuration f\hich\af43\dbch\af31505\loch\f43 rom the origi\hich\af43\dbch\af31505\loch\f43 +nal \loch\af43\dbch\af31505\hich\f43 \'93\hich\af43\dbch\af31505\loch\f43 LAB\hich\af43\dbch\af31505\loch\f43 02\hich\af43\dbch\af31505\loch\f43 \hich\af43\dbch\af31505\loch\f43 Resea\hich\af43\dbch\af31505\loch\f43 rch\loch\af43\dbch\af31505\hich\f43 +\'94\hich\af43\dbch\af31505\loch\f43 \hich\af43\dbch\af31505\loch\f43 installation. +\par }{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid11156998\charrsid11156998 \hich\af43\dbch\af31505\loch\f43 Unless you know what\hich\af43\dbch\af31505\loch\f43 you\loch\af43\dbch\af31505\hich\f43 \rquote \hich\af43\dbch\af31505\loch\f43 re doing, d +\hich\af43\dbch\af31505\loch\f43 o not use this option\hich\af43\dbch\af31505\loch\f43 : +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af43\afs22 \ltrch\fcs0 \f3\fs16\insrsid11156998 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls1\adjustright\rin0\lin720\itap0\pararsid11156998 {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid11156998 \hich\af43\dbch\af31505\loch\f43 M\hich\af43\dbch\af31505\loch\f43 ore +\hich\af43\dbch\af31505\loch\f43 than onc\hich\af43\dbch\af31505\loch\f43 e +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af43\afs22 \ltrch\fcs0 \f3\fs16\insrsid11156998 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}\hich\af43\dbch\af31505\loch\f43 If you\loch\af43\dbch\af31505\hich\f43 \rquote \hich\af43\dbch\af31505\loch\f43 ve +\hich\af43\dbch\af31505\loch\f43 nev\hich\af43\dbch\af31505\loch\f43 er\hich\af43\dbch\af31505\loch\f43 installed\hich\af43\dbch\af31505\loch\f43 \hich\af43\dbch\af31505\loch\f43 original\hich\af43\dbch\af31505\loch\f43 +\loch\af43\dbch\af31505\hich\f43 \'93\hich\af43\dbch\af31505\loch\f43 LAB02 \hich\af43\dbch\af31505\loch\f43 Research\loch\af43\dbch\af31505\hich\f43 \'94\hich\af43\dbch\af31505\loch\f43 HASS.Agent ver\hich\af43\dbch\af31505\loch\f43 sion +\par }\pard \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid11156998 {\rtlch\fcs1 \af43 \ltrch\fcs0 \f43\fs16\insrsid11156998 \hich\af43\dbch\af31505\loch\f43 +During migration, all current configuration is replaced with the \hich\af43\dbch\af31505\loch\f43 old one \loch\af43\dbch\af31505\hich\f43 \endash \hich\af43\dbch\af31505\loch\f43 }{\rtlch\fcs1 \af43 \ltrch\fcs0 +\b\f43\fs16\insrsid11156998\charrsid11156998 \hich\af43\dbch\af31505\loch\f43 you \hich\af43\dbch\af31505\loch\f43 have been warned.}{\rtlch\fcs1 \af43 \ltrch\fcs0 \b\f43\fs16\insrsid11156998\charrsid11156998 +\par }{\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid15153285\charrsid11156998 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210041daf6e5a6070000e6200000160000007468656d652f7468656d652f +7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f222b524cfda33f660c90e7bac914add65557789aed28cc56208de532e81c026e49085 +bded21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c9f3810326cc0886eed2efbdfad57bafde7baabaffd9ab983a1738e584253db77eafe63a3899b13949 +c29efb7c3aae745c870b94cc116509eeb91bccddcf1efcfa57f7d19188708c1d904ff811eab99110aba36a95cf6018f17b6c8513f86ec1d21809784dc3ea3c45 +97a037a6d546add6aac68824ae93a018d49e32b1b974566849b0583b4f170b32c3ee83ed24230a332582cb81194d27720a9c4b9e29195d64beac4b20dff080a6 +ce05a23d17a69db3cb297e255c87222ee08b9e5b537f6ef5c1fd2a3aca85a83820abc98dd55f2e970bcc970d35671a9e17937a9eefb5fa857e05a0621f376a8f +5aa356a14f01d06c060bceb8983adb8dc0cbb11a287bb4e81eb687cdba81d7f437f738f77df931f00a94e9f7f6f0e371005634f00a94e1fd3dbc3fe80e86a67e +05caf0ad3d7cbbd61f7a6d43bf02459424cb3d74cd6f3583ed6a0bc882d1632bbceb7be37623575ea2201a8a2093532c5822ae08b918bd64e91870124f912089 +23362bbc403388ed0051729e12e7848411c4df0a258cc370ad511bd79af05f7e3cf5a41c8b8e30d2a4253d20c4f786242d87cf52b2123df711687535c8bb9f7f +7efbe6c7b76f7e7afbd5576fdffc3d9f5ba932e48e5112ea72bf7cf787ff7cf35be7df3f7cfbcbd77fcca6dec5731dfffe6fbf7bff8f7f7e483dacb834c5bb3f +7dfffec7efdffdf9f7fffaebd716edfd149debf0298931779ee04be7198b618116fef83cbd99c434424497e82721470992b358f48f4464a09f6c104516dc009b +767c9142c6b1011fae5f1a842751ba16c4a2f171141bc053c6e880a5562b3c967369669eae93d03e79bad671cf10bab0cd1da0c4f0f268bd828c4b6c2a83081b +34cf284a040a71828523bf634b8c2dabfb8210c3aea7649632ce16c2f982380344ac26999273239a4aa16312835f363682e06fc336a72f9c01a3b6550ff18589 +84bd81a885fc1453c38c0fd15aa0d8a6728a62aa1bfc0489c84672b249673a6ec405783ac49439a339e6dc26f33485f56a4e7f0c69c6eef653ba894d642ac8d2 +a6f30431a623876c1944285ed9b01392443af673be841045ce191336f8293377887c073fa0e4a0bb5f402fa04f707536780e1956972803447eb34e2dbe7c8899 +11bf930d5d206c4b35fd3436526c3f25d6e818ac4323b44f30a6e812cd31769e7f6e6130602bc3e625e947116495636c0bac47c88c55f99e608e1dd5e3ece7c9 +13c28d909de0901de073bad9493c1b94c4283da4f909785db7f9084a5d6c0b80a774b6d4814f087486102f56a33ce5a0430bee835acf22641430f9ceedf1ba49 +0dff5d678fc1be7c69d0b8c6be04197c631948ecbacc076d3345d498a00c9829822ec3966e41c4707f29228bab125b5be516e6a62ddd004d92d1f4c424b9b203 +dae97dfcff5def031dc6bbbf7c63d96c1fa7dfb12b3692d50d3b9d43c9e478a7bf3984dbed6a0296cec9a7dfd40cd13a39c35047f633d65d4f73d7d3b8fff73d +cda1fd7cd7c91cea37ee3a19173a8cbb4e263f5cf9389d4cd9bc405f230f3cb2f31e75fa135f75f8b320944ec486e213aece7f38fcac998f61508aabe3505c9c +09ae227894d50ee63170618a948c9332f11b22a249845670485477a59290e7aa43eeac1887b323356cd52df1741d9fb27976f459afcb63ceacc07224caf19a5f +8cc37995c8d0ad76799c57a8576c4375faba2520656f42429bcc24d1b490686f07a591d4592f18cd4242adeca3b0e85a5874a4faadabf65800b5c22bf0bbdb81 +5feb3dd7f7400484e0580e7af4b9f453e6eaad7795333fa6a70f19d38800e8b3b711507aba2bb91e5c9e5c5d166ad7f0b441420b379384b28ceaf37804bf86f3 +e894a3d7a171535f774b971af4a429d47c105a258d76e7432c6eeb6b90dbcd0d34d133054d9ccb9edb6afa103233b4eab90b383b86c77805b1c3e54f2f4443b8 +96998934dbf0b7c92cab948b21e25166709574b26c101381538792b8e7cae5176ea089ca218a5bbd0109e19325d785b4f2a99103a79b4ec68b059e09ddedda88 +b474f60a193ecb15d66f95f8edc15292adc1dd93687ee99cd375fa0c4188f9edba34e09c70b842a867d69c13b81a2b1259197f3b85294fbbfadd948aa16c1cd1 +5584f28aa227f30cae52794147bd1536d0def23583413593e485f03c94055637aa514d8baa9171385875af169296d3926659338dac22aba63d8b19336ccbc08e +2d6f57e435565b13434ed32b7c96ba77536e779beb76fa84a24a80c10bfb59aaee350a8246ad9ccca02619efa76199b3f351b3766c177805b5eb14092debb7b6 +6a77ec56d408eb743078abca0f72bb510b438b6d7ba92cadaed4f5db6e76fe1292c7109add35155cb9122eb153040dd144f52459da802df24ae45b039e9c754a +7aee9735bfef050d3fa8d43afea8e235bd5aa5e3f79b95beef37eb23bf5e1b0e1aafa1b08828aefbd975fe18ee31e826bfd457e37b17fbf1f6aae6de8cc555a6 +aeeaab8ab8bad8af378c8bfdec2adf99ca1b7bd7219074be6c35c6dd6677d0aa749bfd71c51b0e3a956ed01a5486ada03d1c0f03bfd31dbf769d0b05f6facdc0 +6b8d3a95563d082a5eab26e977ba95b6d768f4bd76bf33f2faaff33606569ea58fdc16605ec5ebc17f010000ffff0300504b0304140006000800000021000dd1 +909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277 +086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b +64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996 +509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff +0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7 +c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000 +001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210041 +daf6e5a6070000e62000001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600080000 +0021000dd1909fb60000001b0100002700000000000000000000000000b00a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ab0b00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; +\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; +\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; +\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; +\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; +\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e50000000000000000000000006023 +a8b5a1c2db01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/src/HASS.Agent.sln b/src/HASS.Agent.sln index b2da0434..4317c9ab 100644 --- a/src/HASS.Agent.sln +++ b/src/HASS.Agent.sln @@ -19,34 +19,48 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x64.ActiveCfg = Debug|x64 - {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x64.Build.0 = Debug|x64 + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x64.ActiveCfg = Debug|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x64.Build.0 = Debug|Any CPU {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x86.ActiveCfg = Debug|x86 {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Debug|x86.Build.0 = Debug|x86 - {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x64.ActiveCfg = Release|x64 - {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x64.Build.0 = Release|x64 + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|Any CPU.Build.0 = Release|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x64.ActiveCfg = Release|Any CPU + {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x64.Build.0 = Release|Any CPU {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x86.ActiveCfg = Release|x86 {6CB1FA4F-4798-4939-B8DF-7E908FD23242}.Release|x86.Build.0 = Release|x86 - {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x64.ActiveCfg = Debug|x64 - {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x64.Build.0 = Debug|x64 + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x64.ActiveCfg = Debug|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x64.Build.0 = Debug|Any CPU {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x86.ActiveCfg = Debug|x86 {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Debug|x86.Build.0 = Debug|x86 - {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x64.ActiveCfg = Release|x64 - {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x64.Build.0 = Release|x64 + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|Any CPU.Build.0 = Release|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x64.ActiveCfg = Release|Any CPU + {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x64.Build.0 = Release|Any CPU {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x86.ActiveCfg = Release|x86 {3EE6337D-63D6-4AE0-9B8C-788F7598D076}.Release|x86.Build.0 = Release|x86 - {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x64.ActiveCfg = Debug|x64 - {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x64.Build.0 = Debug|x64 + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x64.ActiveCfg = Debug|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x64.Build.0 = Debug|Any CPU {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x86.ActiveCfg = Debug|x86 {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Debug|x86.Build.0 = Debug|x86 - {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x64.ActiveCfg = Release|x64 - {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x64.Build.0 = Release|x64 + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|Any CPU.Build.0 = Release|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x64.ActiveCfg = Release|Any CPU + {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x64.Build.0 = Release|Any CPU {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x86.ActiveCfg = Release|x86 {C4138592-B05C-4B3D-B55C-A1337E97CDDD}.Release|x86.Build.0 = Release|x86 EndGlobalSection @@ -54,7 +68,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - VisualSVNWorkingCopyRoot = . SolutionGuid = {C17243F4-9756-47EF-9AA8-86C64B7582A6} + VisualSVNWorkingCopyRoot = . EndGlobalSection EndGlobal diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs index 875eb8e5..c27a3b67 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Commands/CommandsManager.cs @@ -50,7 +50,7 @@ internal static async void Initialize() /// Unpublishes all commands /// /// - internal static async Task UnpublishAllCommands() + internal static async Task UnpublishAllCommands(bool migration = false) { try { @@ -60,7 +60,7 @@ internal static async Task UnpublishAllCommands() var count = 0; foreach (var command in Variables.Commands) { - await command.UnPublishAutoDiscoveryConfigAsync(); + await command.UnPublishAutoDiscoveryConfigAsync(migration); await Variables.MqttManager.UnsubscribeAsync(command); command.ClearAutoDiscoveryConfig(); count++; @@ -76,6 +76,25 @@ internal static async Task UnpublishAllCommands() { Log.Fatal(ex, "[COMMANDSMANAGER] Error while unpublishing: {err}", ex.Message); } + + _discoveryPublished = false; + } + + /// + /// Publishes all commands + /// + /// + internal static async Task ForcePublishAllCommands() + { + if (!CommandsPresent()) + return; + + foreach (var command in Variables.Commands) + { + await command.PublishAutoDiscoveryConfigAsync(); + } + + _discoveryPublished = true; } /// diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs index a57b6c07..70fe5591 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Extensions/RpcExtensions.cs @@ -182,7 +182,8 @@ public static ServiceMqttSettings ConvertToServiceMqttSettings(this RpcServiceMq MqttUseRetainFlag = rpcServiceMqttSettings.MqttUseRetainFlag, MqttRootCertificate = rpcServiceMqttSettings.MqttRootCertificate, MqttClientCertificate = rpcServiceMqttSettings.MqttClientCertificate, - MqttClientId = rpcServiceMqttSettings.MqttClientId + MqttClientId = rpcServiceMqttSettings.MqttClientId, + MqttUseWebSocket = rpcServiceMqttSettings.MqttUseWebSocket }; return serviceMqttSettings; @@ -207,7 +208,8 @@ public static RpcServiceMqttSettings ConvertToRpcServiceMqttSettings(this Servic MqttUseRetainFlag = serviceMqttSettings.MqttUseRetainFlag, MqttRootCertificate = serviceMqttSettings.MqttRootCertificate, MqttClientCertificate = serviceMqttSettings.MqttClientCertificate, - MqttClientId = serviceMqttSettings.MqttClientId + MqttClientId = serviceMqttSettings.MqttClientId, + MqttUseWebSocket = serviceMqttSettings.MqttUseWebSocket }; return rpcServiceMqttSettings; diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Functions/HelperFunctions.cs index c969eecb..61e1b19c 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Functions/HelperFunctions.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Functions/HelperFunctions.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using HASS.Agent.Satellite.Service.Commands; using HASS.Agent.Satellite.Service.Sensors; +using HASS.Agent.Shared.Managers.Audio; using Serilog; namespace HASS.Agent.Satellite.Service.Functions @@ -59,6 +60,9 @@ internal static async Task ShutdownAsync(TimeSpan waitBeforeClosing) // log our demise Log.Information("[SYSTEM] Service shutting down"); + //stop audio manager + AudioManager.Shutdown(); + // stop mqtt await Variables.MqttManager.AnnounceAvailabilityAsync(true); Variables.MqttManager.Disconnect(); diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj index 2be6c92a..4464d5b6 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj @@ -1,14 +1,14 @@ - net6.0-windows10.0.19041.0 + net8.0-windows10.0.22621.0 true enable enable dotnet-HASSAgentSatelliteService-6E4FA50A-3AC9-4E66-8671-9FAB92372154 - x64 - x64;x86 - 2.1.1 + anycpu + x64;x86;AnyCPU + 2.2.1 HASS.Agent Team HASS.Agent Satellite Service HASS.Agent.Satellite.Service @@ -17,9 +17,9 @@ https://github.com/hass-agent/HASS.Agent https://github.com/hass-agent/HASS.Agent hass.png - 2.1.1 + 2.2.1 hass.ico - 2.1.1 + 2.2.1 10.0.17763.0 false @@ -29,26 +29,26 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - - - - - - + + + + + + + - + diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/MQTT/MqttManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/MQTT/MqttManager.cs index 6758dd7f..fc54d1e1 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/MQTT/MqttManager.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/MQTT/MqttManager.cs @@ -126,7 +126,7 @@ private async Task OnMqttDisconnected(MqttClientDisconnectedEventArgs arg) if (IsConnected()) { _isReady = true; - + return; } @@ -357,7 +357,7 @@ public async Task PublishAsync(MqttApplicationMessage message) /// /// /// - public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false) + public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false, bool migration = false) { if (!IsConnected()) return; @@ -394,7 +394,8 @@ public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discover if (clearConfig) { - messageBuilder.WithPayload(Array.Empty()); + var payload = migration ? Encoding.UTF8.GetBytes("{\"migrate_discovery\": true }") : Array.Empty(); + messageBuilder.WithPayload(payload); } else { @@ -601,13 +602,22 @@ public async Task UnsubscribeAsync(AbstractCommand command) var clientOptionsBuilder = new MqttClientOptionsBuilder() .WithClientId(Variables.ServiceMqttSettings.MqttClientId) - .WithTcpServer(Variables.ServiceMqttSettings.MqttAddress, Variables.ServiceMqttSettings.MqttPort) .WithCleanSession() .WithWillTopic($"{Variables.ServiceMqttSettings.MqttDiscoveryPrefix}/sensor/{Variables.DeviceConfig.Name}/availability") .WithWillPayload("offline") .WithWillRetain(Variables.ServiceMqttSettings.MqttUseRetainFlag) .WithKeepAlivePeriod(TimeSpan.FromSeconds(15)); + if (Variables.ServiceMqttSettings.MqttUseWebSocket) + { + clientOptionsBuilder.WithWebSocketServer(o => o.WithUri($"{Variables.ServiceMqttSettings.MqttAddress}:{Variables.ServiceMqttSettings.MqttPort}")); + Log.Information("[MQTT] Using WebSocket for the connection"); + } + else + { + clientOptionsBuilder.WithTcpServer(Variables.ServiceMqttSettings.MqttAddress, Variables.ServiceMqttSettings.MqttPort); + } + if (!string.IsNullOrEmpty(Variables.ServiceMqttSettings.MqttUsername)) clientOptionsBuilder.WithCredentials(Variables.ServiceMqttSettings.MqttUsername, Variables.ServiceMqttSettings.MqttPassword); diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto index a21c9157..f261aa30 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/RPC/Protos/hassagentsatellite.proto @@ -120,6 +120,7 @@ message RpcServiceMqttSettings { string mqttRootCertificate = 9; string mqttClientCertificate = 10; string mqttClientId = 11; + bool mqttUseWebSocket = 12; } message RpcConfiguredServerSensor { diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs index 867fd2ae..ea2430fb 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Sensors/SensorsManager.cs @@ -49,7 +49,7 @@ internal static async void Initialize() /// Unpublishes all single- and multivalue sensors /// /// - internal static async Task UnpublishAllSensors() + internal static async Task UnpublishAllSensors(bool migration = false) { try { @@ -61,7 +61,7 @@ internal static async Task UnpublishAllSensors() { foreach (var sensor in Variables.SingleValueSensors) { - await sensor.UnPublishAutoDiscoveryConfigAsync(); + await sensor.UnPublishAutoDiscoveryConfigAsync(migration); sensor.ClearAutoDiscoveryConfig(); singleCount++; } @@ -71,7 +71,7 @@ internal static async Task UnpublishAllSensors() { foreach (var sensor in Variables.MultiValueSensors) { - await sensor.UnPublishAutoDiscoveryConfigAsync(); + await sensor.UnPublishAutoDiscoveryConfigAsync(migration); sensor.ClearAutoDiscoveryConfig(); multiCount++; } @@ -87,6 +87,32 @@ internal static async Task UnpublishAllSensors() { Log.Fatal(ex, "[SENSORSMANAGER] Error while unpublishing: {err}", ex.Message); } + + _discoveryPublished = false; + } + + /// + /// Publishes all single- and multivalue sensors + /// + /// + internal static async Task ForcePublishAllSensors() + { + if (SingleValueSensorsPresent()) + { + foreach (var sensor in Variables.SingleValueSensors) + { + await sensor.PublishAutoDiscoveryConfigAsync(); + } + } + if (MultiValueSensorsPresent()) + { + foreach (var sensor in Variables.MultiValueSensors) + { + await sensor.PublishAutoDiscoveryConfigAsync(); + } + } + + _discoveryPublished = true; } /// diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/SettingsManager.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/SettingsManager.cs index 139cd5c4..26bc4b67 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/SettingsManager.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/SettingsManager.cs @@ -101,7 +101,7 @@ private static bool LoadServiceSettings() // set shared config AgentSharedBase.SetDeviceName(Variables.ServiceSettings.DeviceName); AgentSharedBase.SetCustomExecutorBinary(Variables.ServiceSettings.CustomExecutorBinary); - + // done Log.Information("[SETTINGS] Configuration loaded"); return true; @@ -192,7 +192,7 @@ private static bool StoreInitialMqttSettings(bool storeSettings = true) try { Log.Information("[SETTINGS] No MQTT config found, storing default settings"); - + // default settings Variables.ServiceMqttSettings = new ServiceMqttSettings(); @@ -225,7 +225,7 @@ internal static bool Store() // store app settings var ok = StoreServiceSettings(); if (!ok) allGood = false; - + // store commands ok = StoredCommands.Store(); if (!ok) allGood = false; @@ -289,10 +289,10 @@ internal static bool ProcessReceivedServiceMqttSettings(ServiceMqttSettings serv return true; } } - + // bind received settings Variables.ServiceMqttSettings = serviceMqttSettings; - + // store var stored = StoreServiceSettings(); if (stored) Log.Information("[SETTINGS] Received MQTT settings stored"); @@ -313,19 +313,19 @@ internal static bool ProcessReceivedServiceMqttSettings(ServiceMqttSettings serv /// /// Changes the device's name internally and with HA /// - /// + /// [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")] - internal static async void ProcessNameChange(string deviceName) + internal static async void ProcessNameChange(string newDeviceName) { Variables.ServiceSettings ??= new ServiceSettings(); - if (deviceName == Variables.ServiceSettings.DeviceName) + if (newDeviceName == Variables.ServiceSettings.DeviceName) { Log.Information("[SETTINGS] Name change requested, but name is same as before, ignoring"); return; } - Log.Information("[SETTINGS] Processing name change to: {name}", deviceName); + Log.Information("[SETTINGS] Processing name change to: {name}", newDeviceName); Log.Information("[SETTINGS] Previous name: {name}", Variables.ServiceSettings.DeviceName); // make sure the managers stop publishing @@ -335,7 +335,27 @@ internal static async void ProcessNameChange(string deviceName) // give the managers some time to stop await Task.Delay(250); - // unpublish all entities + // signal migration to HA MQTT integration + await SensorsManager.UnpublishAllSensors(migration: true); + await CommandsManager.UnpublishAllCommands(migration: true); + + await Task.Delay(250); + + // mock new device name and publish new discovery messages + if (Variables.DeviceConfig != null) //Note(Amadeo): ugly, should be cleaned up but doesn't make sense due to full rewrite + { + Variables.DeviceConfig.Name = newDeviceName; + } + await SensorsManager.ForcePublishAllSensors(); + await CommandsManager.ForcePublishAllCommands(); + + await Task.Delay(250); + + // restore previous device name and clear migration messages + if (Variables.DeviceConfig != null) //Note(Amadeo): ugly, should be cleaned up but doesn't make sense due to full rewrite + { + Variables.DeviceConfig.Name = Variables.ServiceSettings.DeviceName; + } await SensorsManager.UnpublishAllSensors(); await CommandsManager.UnpublishAllCommands(); @@ -346,7 +366,7 @@ internal static async void ProcessNameChange(string deviceName) await Task.Delay(250); // set the name - Variables.ServiceSettings.DeviceName = deviceName; + Variables.ServiceSettings.DeviceName = newDeviceName; // store var stored = StoreServiceSettings(); @@ -354,7 +374,7 @@ internal static async void ProcessNameChange(string deviceName) else Log.Error("[SETTINGS] Errors while storing new name"); // config shared functions - AgentSharedBase.SetDeviceName(deviceName); + AgentSharedBase.SetDeviceName(newDeviceName); // restart mqtt Variables.MqttManager.ReloadConfiguration(); diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredCommands.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredCommands.cs index 3ff0d54e..de838048 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredCommands.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredCommands.cs @@ -1,4 +1,4 @@ -using HASS.Agent.Shared.Enums; +using HASS.Agent.Shared.Enums; using HASS.Agent.Shared.Models.Config; using HASS.Agent.Shared.HomeAssistant.Commands; using HASS.Agent.Shared.HomeAssistant.Commands.CustomCommands; @@ -56,7 +56,10 @@ internal static async Task LoadAsync() // convert to abstract commands await Task.Run(delegate { - foreach (var abstractCommand in configuredCommands.Select(ConvertConfiguredToAbstract)) Variables.Commands.Add(abstractCommand!); + foreach (var abstractCommand in configuredCommands.Select(ConvertConfiguredToAbstract)) + { + if (abstractCommand != null) Variables.Commands.Add(abstractCommand); + } }); // all good @@ -141,6 +144,9 @@ await Task.Run(delegate case CommandType.MonitorSleepCommand: abstractCommand = new MonitorSleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; + case CommandType.MonitorSleepPowerPlanCommand: + abstractCommand = new MonitorSleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); + break; case CommandType.MonitorWakeCommand: abstractCommand = new MonitorWakeCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs index 398b3a9d..556c8086 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Settings/StoredSensors.cs @@ -1,4 +1,4 @@ -using HASS.Agent.Shared.Enums; +using HASS.Agent.Shared.Enums; using HASS.Agent.Shared.Models.Config; using HASS.Agent.Satellite.Service.Extensions; using HASS.Agent.Shared.HomeAssistant.Sensors; @@ -9,7 +9,6 @@ using HASS.Agent.Shared.Models.HomeAssistant; using Newtonsoft.Json; using Serilog; -using LibreHardwareMonitor.Hardware; using SensorType = HASS.Agent.Shared.Enums.SensorType; namespace HASS.Agent.Satellite.Service.Settings @@ -62,8 +61,16 @@ await Task.Run(delegate { foreach (var sensor in configuredSensors) { - if (sensor.IsSingleValue()) Variables.SingleValueSensors.Add(ConvertConfiguredToAbstractSingleValue(sensor)!); - else Variables.MultiValueSensors.Add(ConvertConfiguredToAbstractMultiValue(sensor)!); + if (sensor.IsSingleValue()) + { + var abstractSensor = ConvertConfiguredToAbstractSingleValue(sensor); + if (abstractSensor != null) Variables.SingleValueSensors.Add(abstractSensor); + } + else + { + var abstractSensor = ConvertConfiguredToAbstractMultiValue(sensor); + if (abstractSensor != null) Variables.MultiValueSensors.Add(abstractSensor); + } } }); @@ -110,6 +117,9 @@ await Task.Run(delegate case SensorType.NamedWindowSensor: abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings); break; + case SensorType.NamedActiveWindowSensor: + abstractSensor = new NamedActiveWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings); + break; case SensorType.LastActiveSensor: abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings); break; @@ -259,6 +269,22 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract }; } + case NamedActiveWindowSensor namedActiveWindowSensor: + { + _ = Enum.TryParse(namedActiveWindowSensor.GetType().Name, out var type); + return new ConfiguredSensor + { + Id = Guid.Parse(namedActiveWindowSensor.Id), + EntityName = namedActiveWindowSensor.EntityName, + Name = namedActiveWindowSensor.Name, + Type = type, + UpdateInterval = namedActiveWindowSensor.UpdateIntervalSeconds, + IgnoreAvailability = namedActiveWindowSensor.IgnoreAvailability, + WindowName = namedActiveWindowSensor.WindowName, + AdvancedSettings = namedActiveWindowSensor.AdvancedSettings + }; + } + case PerformanceCounterSensor performanceCounterSensor: { _ = Enum.TryParse(performanceCounterSensor.GetType().Name, out var type); diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/Worker.cs b/src/HASS.Agent/HASS.Agent.Satellite.Service/Worker.cs index 11743d4c..d5cf887a 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/Worker.cs +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/Worker.cs @@ -1,12 +1,15 @@ -using HASS.Agent.Shared; +using System.Diagnostics; +using System.Security.Principal; using HASS.Agent.Satellite.Service.Commands; using HASS.Agent.Satellite.Service.Functions; using HASS.Agent.Satellite.Service.Managers; using HASS.Agent.Satellite.Service.RPC; using HASS.Agent.Satellite.Service.Sensors; using HASS.Agent.Satellite.Service.Settings; -using Serilog; +using HASS.Agent.Shared; using HASS.Agent.Shared.Managers; +using HASS.Agent.Shared.Managers.Audio; +using Serilog; namespace HASS.Agent.Satellite.Service { @@ -40,7 +43,11 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _log.LogInformation("[WORKER] Startup completed, commencing execution .."); - HardwareManager.Initialize(); + var runningUser = WindowsIdentity.GetCurrent(); + if(runningUser.User?.Value != "S-1-5-18") + { + _log.LogWarning("[WORKER] Service is not running as 'System' user but rather '{user}', this may cause permission issues!", runningUser.Name); + } // load stored settings (if any) var launched = await SettingsManager.LoadAsync(); @@ -59,6 +66,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) // initialize the RPC server _ = Task.Run(RpcManager.Initialize, stoppingToken); + // initialize the audio manager + _ = Task.Run(AudioManager.Initialize, stoppingToken); + // initialize the mqtt manager _ = Task.Run(Variables.MqttManager.Initialize, stoppingToken); @@ -99,8 +109,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } finally { - HardwareManager.Shutdown(); - // stop the application _hostApplicationLifetime.StopApplication(); diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs index a25ca5b1..ca6549f7 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/CommandType.cs @@ -65,6 +65,10 @@ public enum CommandType [EnumMember(Value = "MonitorSleepCommand")] MonitorSleepCommand, + [LocalizedDescription("CommandType_MonitorSleepPowerPlanCommand", typeof(Languages))] + [EnumMember(Value = "MonitorSleepPowerPlanCommand")] + MonitorSleepPowerPlanCommand, + [LocalizedDescription("CommandType_MonitorWakeCommand", typeof(Languages))] [EnumMember(Value = "MonitorWakeCommand")] MonitorWakeCommand, @@ -127,6 +131,10 @@ public enum CommandType [LocalizedDescription("CommandType_RadioCommand", typeof(Languages))] [EnumMember(Value = "RadioCommand")] - RadioCommand + RadioCommand, + + [LocalizedDescription("CommandType_WinformsSleepCommand", typeof(Languages))] + [EnumMember(Value = "WinformsSleepCommand")] + WinformsSleepCommand } } \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs index 4b453b34..8bd710ca 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/HassDomain.cs @@ -62,6 +62,16 @@ public enum HassDomain [LocalizedDescription("HassDomain_Button", typeof(Languages))] [Category("button")] [EnumMember(Value = "Button")] - Button + Button, + + [LocalizedDescription("HassDomain_InputButton", typeof(Languages))] + [Category("input_button")] + [EnumMember(Value = "InputButton")] + InputButton, + + [LocalizedDescription("HassDomain_Fan", typeof(Languages))] + [Category("fan")] + [EnumMember(Value = "Fan")] + Fan } } \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs index 65348afa..d6fab954 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Enums/SensorType.cs @@ -19,6 +19,10 @@ public enum SensorType [EnumMember(Value = "ActiveDesktopSensor")] ActiveDesktopSensor, + [LocalizedDescription("SensorType_AccentColorSensor", typeof(Languages))] + [EnumMember(Value = "AccentColorSensor")] + AccentColorSensor, + [LocalizedDescription("SensorType_AudioSensors", typeof(Languages))] [EnumMember(Value = "AudioSensors")] AudioSensors, @@ -107,6 +111,10 @@ public enum SensorType [EnumMember(Value = "NamedWindowSensor")] NamedWindowSensor, + [LocalizedDescription("SensorType_NamedActiveWindowSensor", typeof(Languages))] + [EnumMember(Value = "NamedActiveWindowSensor")] + NamedActiveWindowSensor, + [LocalizedDescription("SensorType_NetworkSensors", typeof(Languages))] [EnumMember(Value = "NetworkSensors")] NetworkSensors, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj index 90565d05..31336c84 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj +++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj @@ -1,8 +1,8 @@  - net6.0-windows10.0.19041.0 - x64;x86 + net8.0-windows10.0.22621.0 + x64;x86;AnyCPU HASS.Agent.Shared HASS.Agent.Shared HASS.Agent Team @@ -10,9 +10,9 @@ Shared functions and models for the HASS.Agent platform. https://github.com/hass-agent/HASS.Agent https://github.com/hass-agent/HASS.Agent - 2.1.1 - 2.1.1 - 2.1.1 + 2.2.1 + 2.2.1 + 2.2.1 logo_128.png True hassagent.ico @@ -32,21 +32,21 @@ - - + - + - - - - - - - - + + + + + + + + + diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/CustomCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/CustomCommand.cs index 8436a446..76a4152e 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/CustomCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/CustomCommand.cs @@ -74,7 +74,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return new CommandDiscoveryConfigModel() + return new CommandDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommand.cs index 48d4cee7..a7511e17 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommand.cs @@ -44,7 +44,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return new CommandDiscoveryConfigModel() + return new CommandDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/MonitorSleepPowerPlanCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/MonitorSleepPowerPlanCommand.cs new file mode 100644 index 00000000..d71bc152 --- /dev/null +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/MonitorSleepPowerPlanCommand.cs @@ -0,0 +1,96 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Input; +using HASS.Agent.Shared.Enums; +using HASS.Agent.Shared.Functions; +using Serilog; +using Vanara.PInvoke; +using static Vanara.PInvoke.PowrProf; + +namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands +{ + /// + /// Command to put all monitors to sleep + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class MonitorSleepPowerPlanCommand : InternalCommand + { + private static readonly HKEY s_key = new HKEY(); + + private const string DefaultName = "monitorsleep-pp"; + + public MonitorSleepPowerPlanCommand(string entityName = DefaultName, string name = DefaultName, CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(entityName ?? DefaultName, name ?? null, string.Empty, entityType, id) + { + State = "OFF"; + } + + public override void TurnOn() + { + State = "ON"; + + try + { + var win32Error = PowerGetActiveScheme(out var activeScheme); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("cannot get active power scheme"); + + var schemes = PowerEnumerate(null, null); + var removalError = false; + foreach (var scheme in schemes) + { + var name = PowerReadFriendlyName(scheme); + if (name.EndsWith(" - HASS.Agent Monitor Sleep")) + { + win32Error = PowerDeleteScheme(s_key, scheme); + if (win32Error != Win32Error.ERROR_SUCCESS) + removalError = true; + } + } + + if (removalError) + Log.Warning("[MONITORSLEEP-PP] [{name}] Error occurred while trying to remove HASS.Agent temp power plan/s", EntityName, EntityName); + + + win32Error = PowerDuplicateScheme(s_key, activeScheme, out var duplicateScheme); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("cannot duplicate current power scheme"); + + var duplicatedSchemeGuid = duplicateScheme.ToStructure(); + var newDuplicatedName = PowerReadFriendlyName(activeScheme) + " - HASS.Agent Monitor Sleep"; + win32Error = PowerWriteFriendlyName(duplicatedSchemeGuid, null, null, newDuplicatedName); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("error setting new name to the duplicated power plan"); + + win32Error = PowerWriteACValueIndex(s_key, duplicatedSchemeGuid, GUID_VIDEO_SUBGROUP, GUID_VIDEO_POWERDOWN_TIMEOUT, 1); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("error changing the power plan timeout value"); + + win32Error = PowerSetActiveScheme(s_key, duplicatedSchemeGuid); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("error activating the temporary power plan"); + + Thread.Sleep(1500); //TODO(Amadeo): ugly... + + win32Error = PowerSetActiveScheme(s_key, activeScheme); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("error activating the original power plan"); + + win32Error = PowerDeleteScheme(s_key, duplicatedSchemeGuid); + if (win32Error != Win32Error.ERROR_SUCCESS) + throw new Exception("error removing the duplicated power plan"); + } + catch (Exception ex) + { + Log.Error("[MONITORSLEEP-PP] [{name}] Error activating the command: {msg}", EntityName, ex.Message); + } + finally + { + State = "OFF"; + } + } + } +} diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs index cdb39cdc..a1ada486 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs @@ -1,7 +1,6 @@ using HASS.Agent.Shared.Enums; using HASS.Agent.Shared.Managers; using HASS.Agent.Shared.Managers.Audio; -using HidSharp; using Newtonsoft.Json; using Serilog; using System; diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/KeyCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/KeyCommand.cs index 592eeb19..08871f22 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/KeyCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/KeyCommand.cs @@ -19,7 +19,7 @@ public class KeyCommand : AbstractCommand private const string DefaultName = "key"; public string State { get; protected set; } - + public VirtualKeyShort KeyCode { get; set; } public KeyCommand(VirtualKeyShort keyCode, string entityName = DefaultName, string name = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(entityName ?? DefaultName, name ?? null, entityType, id) @@ -35,7 +35,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return new CommandDiscoveryConfigModel + return new CommandDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, @@ -52,31 +52,59 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() public override void TurnOff() { - // - } - - public override void TurnOn() - { - State = "ON"; - - var inputs = new INPUT[2]; + var inputs = new INPUT[1]; inputs[0].type = InputType.INPUT_KEYBOARD; inputs[0].U.ki.wVk = KeyCode; - - inputs[1].type = InputType.INPUT_KEYBOARD; - inputs[1].U.ki.wVk = KeyCode; - inputs[1].U.ki.dwFlags = KEYEVENTF.KEYUP; + inputs[0].U.ki.dwFlags = KEYEVENTF.KEYUP; var ret = SendInput((uint)inputs.Length, inputs, INPUT.Size); if (ret != inputs.Length) { var error = Marshal.GetLastWin32Error(); - Log.Error($"[{DefaultName}] Error simulating key press for {KeyCode}: {error}"); + Log.Error($"[{DefaultName}] Error lifting key {KeyCode}: {error}"); } State = "OFF"; } + public override void TurnOn() + { + State = "ON"; + + if (EntityType == CommandEntityType.Switch) + { + var inputs = new INPUT[1]; + inputs[0].type = InputType.INPUT_KEYBOARD; + inputs[0].U.ki.wVk = KeyCode; + + var ret = SendInput((uint)inputs.Length, inputs, INPUT.Size); + if (ret != inputs.Length) + { + var error = Marshal.GetLastWin32Error(); + Log.Error($"[{DefaultName}] Error pressing down key {KeyCode}: {error}"); + } + } + else + { + var inputs = new INPUT[2]; + inputs[0].type = InputType.INPUT_KEYBOARD; + inputs[0].U.ki.wVk = KeyCode; + + inputs[1].type = InputType.INPUT_KEYBOARD; + inputs[1].U.ki.wVk = KeyCode; + inputs[1].U.ki.dwFlags = KEYEVENTF.KEYUP; + + var ret = SendInput((uint)inputs.Length, inputs, INPUT.Size); + if (ret != inputs.Length) + { + var error = Marshal.GetLastWin32Error(); + Log.Error($"[{DefaultName}] Error simulating key press for {KeyCode}: {error}"); + } + + State = "OFF"; + } + } + public override void TurnOnWithAction(string action) { // diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs index 3d56a286..9689f761 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/MultipleKeysCommand.cs @@ -33,7 +33,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return new CommandDiscoveryConfigModel + return new CommandDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/PowershellCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/PowershellCommand.cs index e3179b49..b004bda5 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/PowershellCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/PowershellCommand.cs @@ -73,7 +73,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return new CommandDiscoveryConfigModel() + return new CommandDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs index 4c6ecb97..87988493 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/AudioSensors.cs @@ -9,7 +9,6 @@ using HASS.Agent.Shared.Managers.Audio; using HASS.Agent.Shared.Models.HomeAssistant; using HASS.Agent.Shared.Models.Internal; -using HidSharp; using Newtonsoft.Json; using Serilog; diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeBoolSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeBoolSensor.cs index 93781b0d..9fd8d778 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeBoolSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeBoolSensor.cs @@ -45,7 +45,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs index b0fbc741..0f4460fb 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeDoubleSensor.cs @@ -50,7 +50,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs index 0a9ec4e6..a9d04b2c 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeIntSensor.cs @@ -49,7 +49,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeStringSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeStringSensor.cs index be61b13b..3a173218 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeStringSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/MultiValue/DataTypes/DataTypeStringSensor.cs @@ -49,7 +49,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs index 46b689e3..c44a21fa 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveWindowSensor.cs @@ -27,7 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs index 659db848..94bac4a5 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs @@ -23,7 +23,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs index 45afb306..939eb97c 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/DummySensor.cs @@ -18,7 +18,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs index e4b4e92a..00f54708 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuLoadSensor.cs @@ -1,8 +1,9 @@ -using System.Globalization; +using System.Diagnostics; +using System.Globalization; using System.Linq; +using System.Threading; using HASS.Agent.Shared.Managers; using HASS.Agent.Shared.Models.HomeAssistant; -using LibreHardwareMonitor.Hardware; namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue; @@ -11,54 +12,61 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue; /// public class GpuLoadSensor : AbstractSingleValueSensor { - private const string DefaultName = "gpuload"; - private readonly IHardware _gpu; + private const string DefaultName = "gpuload"; - public GpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) - { - _gpu = HardwareManager.Hardware.FirstOrDefault( - h => h.HardwareType == HardwareType.GpuAmd || - h.HardwareType == HardwareType.GpuNvidia || - h.HardwareType == HardwareType.GpuIntel - ); - } + public GpuLoadSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) + { - public override DiscoveryConfigModel GetAutoDiscoveryConfig() - { - if (Variables.MqttManager == null) - return null; + } - var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); - if (deviceConfig == null) - return null; + public override DiscoveryConfigModel GetAutoDiscoveryConfig() + { + if (Variables.MqttManager == null) + return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() - { - EntityName = EntityName, - Name = Name, - Unique_id = Id, - Device = deviceConfig, - State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state", - Unit_of_measurement = "%", + var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); + if (deviceConfig == null) + return null; + + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) + { + EntityName = EntityName, + Name = Name, + Unique_id = Id, + Device = deviceConfig, + State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state", + Unit_of_measurement = "%", State_class = "measurement", Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability" - }); - } - - public override string GetState() - { - if (_gpu == null) - return null; + }); + } - _gpu.Update(); + public override string GetState() + { + return GetGPUUsage().ToString("#.##", CultureInfo.InvariantCulture); + } - var sensor = _gpu.Sensors.FirstOrDefault(s => s.SensorType == SensorType.Load); + public override string GetAttributes() => string.Empty; - if (sensor?.Value == null) - return null; + public float GetGPUUsage() + { + try + { + var category = new PerformanceCounterCategory("GPU Engine"); + var gpuCounters = category.GetInstanceNames() + .Where(name => name.EndsWith("engtype_3D")) + .SelectMany(name => category.GetCounters(name)) + .Where(counter => counter.CounterName.Equals("Utilization Percentage")) + .ToList(); - return sensor.Value.HasValue ? sensor.Value.Value.ToString("#.##", CultureInfo.InvariantCulture) : null; - } + gpuCounters.ForEach(x => { _ = x.NextValue(); }); + Thread.Sleep(10); //TODO(Amadeo): fix this - public override string GetAttributes() => string.Empty; + return gpuCounters.Sum(x => x.NextValue()); + } + catch + { + return 0; + } + } } diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs index 8204c3d9..747c070c 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/GpuTemperatureSensor.cs @@ -2,7 +2,6 @@ using System.Linq; using HASS.Agent.Shared.Managers; using HASS.Agent.Shared.Models.HomeAssistant; -using LibreHardwareMonitor.Hardware; namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue { @@ -12,15 +11,15 @@ namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue public class GpuTemperatureSensor : AbstractSingleValueSensor { private const string DefaultName = "gputemperature"; - private readonly IHardware _gpu; + //private readonly IHardware _gpu; public GpuTemperatureSensor(int? updateInterval = null, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) { - _gpu = HardwareManager.Hardware.FirstOrDefault( +/* _gpu = HardwareManager.Hardware.FirstOrDefault( h => h.HardwareType == HardwareType.GpuAmd || h.HardwareType == HardwareType.GpuNvidia || h.HardwareType == HardwareType.GpuIntel - ); + );*/ } public override DiscoveryConfigModel GetAutoDiscoveryConfig() @@ -30,7 +29,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, @@ -46,7 +45,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() public override string GetState() { - if (_gpu == null) +/* if (_gpu == null) return null; _gpu.Update(); @@ -56,7 +55,8 @@ public override string GetState() if (sensor?.Value == null) return null; - return sensor.Value.HasValue ? sensor.Value.Value.ToString("#.##", CultureInfo.InvariantCulture) : null; + return sensor.Value.HasValue ? sensor.Value.Value.ToString("#.##", CultureInfo.InvariantCulture) : null;*/ + return "0"; } public override string GetAttributes() => string.Empty; diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs index bd67b9d6..5d7de85e 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastActiveSensor.cs @@ -38,7 +38,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, @@ -86,12 +86,12 @@ private static DateTime GetLastInputTime() lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo); lastInputInfo.dwTime = 0; - var envTicks = Environment.TickCount; + var envTicks = Environment.TickCount64; if (!GetLastInputInfo(ref lastInputInfo)) return DateTime.Now; - var lastInputTick = Convert.ToDouble(lastInputInfo.dwTime); + var lastInputTick = (long)lastInputInfo.dwTime; var idleTime = envTicks - lastInputTick; return idleTime > 0 ? DateTime.Now - TimeSpan.FromMilliseconds(idleTime) : DateTime.Now; diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs index c8e6429c..3a4ae1a0 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastBootSensor.cs @@ -22,7 +22,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs index 482c0fad..f1572a0d 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LastSystemStateChangeSensor.cs @@ -19,7 +19,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs index ba14ba5b..c35010b2 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUserSensor.cs @@ -19,7 +19,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs index e10c958e..4f7e2a61 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/LoggedUsersSensor.cs @@ -27,7 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs index 5d630ef6..453ceb22 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneActiveSensor.cs @@ -25,7 +25,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs index c7b89615..aab53dc6 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/MicrophoneProcessSensor.cs @@ -39,7 +39,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() return null; } - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedActiveWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedActiveWindowSensor.cs new file mode 100644 index 00000000..29bedee7 --- /dev/null +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedActiveWindowSensor.cs @@ -0,0 +1,71 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using HASS.Agent.Shared.Functions; +using HASS.Agent.Shared.Models.HomeAssistant; + +namespace HASS.Agent.Shared.HomeAssistant.Sensors.GeneralSensors.SingleValue +{ + /// + /// Sensor indicating whether the currently focused window contains the configured name + /// + public class NamedActiveWindowSensor : AbstractSingleValueSensor + { + private const string DefaultName = "namedactivewindow"; + public string WindowName { get; protected set; } + + public NamedActiveWindowSensor(string windowName, string entityName = DefaultName, string name = DefaultName, int? updateInterval = 10, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 30, id, advancedSettings: advancedSettings) + { + Domain = "binary_sensor"; + WindowName = windowName; + } + + public override DiscoveryConfigModel GetAutoDiscoveryConfig() + { + if (Variables.MqttManager == null) return null; + + var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); + if (deviceConfig == null) return null; + + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) + { + EntityName = EntityName, + Name = Name, + Unique_id = Id, + Device = deviceConfig, + State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state", + Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/sensor/{deviceConfig.Name}/availability" + }); + } + + public override string GetState() + { + var windowHandle = GetForegroundWindow(); + var windowTitle = GetWindowTitle(windowHandle); + + return windowTitle.ToUpper().Contains(WindowName.ToUpper()) ? "ON" : "OFF"; + } + + public override string GetAttributes() => string.Empty; + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern int GetWindowTextLength(IntPtr hWnd); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder builder, int count); + + private static string GetWindowTitle(IntPtr windowHandle) + { + var titleLength = GetWindowTextLength(windowHandle) + 1; + var builder = new StringBuilder(titleLength); + var windowTitle = GetWindowText(windowHandle, builder, titleLength) > 0 ? builder.ToString() : string.Empty; + + return windowTitle.Length > 255 ? windowTitle[..255] : windowTitle; //Note(Amadeo): to make sure we don't exceed HA limitation of 255 payload length + } + } +} diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs index b3f494c7..e2363c89 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/NamedWindowSensor.cs @@ -25,7 +25,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs index 09d51781..eceb1534 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ProcessActiveSensor.cs @@ -29,7 +29,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs index fa2dd172..fd73dbd1 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ScreenshotSensor.cs @@ -42,7 +42,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new CameraSensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new CameraSensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs index f3655606..ebe8b77f 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/ServiceStateSensor.cs @@ -24,7 +24,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs index 9f698e0e..17d52e5c 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/SessionStateSensor.cs @@ -19,7 +19,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs index ee66f883..1713fe8f 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/UserNotificationStateSensor.cs @@ -20,7 +20,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs index 372f8a1c..eb9ae233 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamActiveSensor.cs @@ -30,7 +30,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs index 8dbfbccd..fe5a4a5f 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WebcamProcessSensor.cs @@ -34,7 +34,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var model = new SensorDiscoveryConfigModel() + var model = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs index b07eccc6..2c5d234a 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/WindowStateSensor.cs @@ -27,7 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs index 245c143e..0a05af95 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerfCounterSensors/SingleValue/CpuLoadSensor.cs @@ -18,9 +18,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - var asd = ObjectId; - - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs index cde16828..a52ee5c0 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PerformanceCounterSensor.cs @@ -47,7 +47,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs index 832f7e66..4d6d7578 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/PowershellSensor.cs @@ -34,7 +34,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs index 3c2fb43d..80382cd3 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiQuerySensor.cs @@ -58,7 +58,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs index 23770359..f34c9fa2 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/CurrentClockSpeedSensor.cs @@ -27,7 +27,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs index eaf79098..83212595 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/WmiSensors/SingleValue/MemoryUsageSensor.cs @@ -20,7 +20,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs index 12e13d7f..aa404f60 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs @@ -10,7 +10,6 @@ using HASS.Agent.Shared.Managers.Audio.Internal; using Serilog; using NAudio.CoreAudioApi.Interfaces; -using HidSharp; using Microsoft.VisualBasic.ApplicationServices; namespace HASS.Agent.Shared.Managers.Audio; @@ -25,6 +24,9 @@ public static class AudioManager private static readonly Dictionary _applicationNameCache = new(); + private static bool _noDefaultInputLogged = false; + private static bool _noDefaultOutputLogged = false; + private static void InitializeDevices() { _enumerator = new MMDeviceEnumerator(); @@ -208,6 +210,18 @@ public static void CleanupDevices() Log.Debug("[AUDIOMGR] cleanup completed"); } + private static MMDevice GetDefaultMultimediaDevice(DataFlow dataFlow) + { + MMDevice device = null; + try + { + device = _enumerator.GetDefaultAudioEndpoint(dataFlow, Role.Multimedia); + } + catch { } + + return device; + } + public static List GetDevices() { var audioDevices = new List(); @@ -217,11 +231,29 @@ public static List GetDevices() try { - using var defaultInputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Multimedia); - using var defaultOutputDevice = _enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + using var defaultInputDevice = GetDefaultMultimediaDevice(DataFlow.Capture); + if (defaultInputDevice == null && !_noDefaultInputLogged) + { + Log.Information("[AUDIOMGR] no default input device detected"); + _noDefaultInputLogged = true; + } + else if (defaultInputDevice != null) + { + _noDefaultInputLogged = false; + } + + using var defaultOutputDevice = GetDefaultMultimediaDevice(DataFlow.Render); + if (defaultOutputDevice == null && !_noDefaultOutputLogged) + { + Log.Information("[AUDIOMGR] no default output device detected"); + _noDefaultOutputLogged = true; + }else if (defaultOutputDevice != null) + { + _noDefaultOutputLogged = false; + } - var defaultInputDeviceId = defaultInputDevice.ID; - var defaultOutputDeviceId = defaultOutputDevice.ID; + var defaultInputDeviceId = defaultInputDevice != null ? defaultInputDevice.ID : string.Empty; + var defaultOutputDeviceId = defaultOutputDevice != null ? defaultOutputDevice.ID : string.Empty; foreach (var (deviceId, deviceName) in _devices) { diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/HardwareManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/HardwareManager.cs deleted file mode 100644 index a692a598..00000000 --- a/src/HASS.Agent/HASS.Agent.Shared/Managers/HardwareManager.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LibreHardwareMonitor.Hardware; - -namespace HASS.Agent.Shared.Managers; -public static class HardwareManager -{ - private static Computer s_computer; - public static void Initialize() - { - //Note(Amadeo): for "performance" reasons only GPU is selected below, enable additional ones if required by new sensors/commands - s_computer = new Computer() - { - IsCpuEnabled = false, - IsGpuEnabled = true, - IsMemoryEnabled = false, - IsMotherboardEnabled = false, - IsControllerEnabled = false, - IsNetworkEnabled = false, - IsStorageEnabled = false, - }; - - s_computer.Open(); - } - - public static IList Hardware => s_computer.Hardware; - - public static void Shutdown() => s_computer?.Close(); -} diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/Config/Service/ServiceMqttSettings.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/Config/Service/ServiceMqttSettings.cs index 9b4ee401..dfbb3254 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Models/Config/Service/ServiceMqttSettings.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Models/Config/Service/ServiceMqttSettings.cs @@ -9,6 +9,7 @@ public ServiceMqttSettings() public string MqttAddress { get; set; } = "homeassistant.local"; public int MqttPort { get; set; } = 1883; + public bool MqttUseWebSocket { get; set; } = false; public bool MqttUseTls { get; set; } public bool MqttAllowUntrustedCertificates { get; set; } = true; public string MqttUsername { get; set; } = string.Empty; diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractCommand.cs index 5dc55fa7..5514d7cf 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractCommand.cs @@ -83,10 +83,12 @@ public async Task PublishAutoDiscoveryConfigAsync() await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain); } - public async Task UnPublishAutoDiscoveryConfigAsync() + public async Task UnPublishAutoDiscoveryConfigAsync(bool migration = false) { - if (Variables.MqttManager == null) return; - await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true); + if (Variables.MqttManager == null) + return; + + await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true, migration); } public abstract void TurnOn(); diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractMultiValueSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractMultiValueSensor.cs index 6a243d38..49f231ed 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractMultiValueSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractMultiValueSensor.cs @@ -68,9 +68,9 @@ public async Task PublishAutoDiscoveryConfigAsync() foreach (var sensor in Sensors) await sensor.Value.PublishAutoDiscoveryConfigAsync(); } - public async Task UnPublishAutoDiscoveryConfigAsync() + public async Task UnPublishAutoDiscoveryConfigAsync(bool migration = false) { - foreach (var sensor in Sensors) await sensor.Value.UnPublishAutoDiscoveryConfigAsync(); + foreach (var sensor in Sensors) await sensor.Value.UnPublishAutoDiscoveryConfigAsync(migration); } } } diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs index e3d3f267..9a941e36 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/AbstractSingleValueSensor.cs @@ -160,11 +160,11 @@ public async Task PublishAutoDiscoveryConfigAsync() await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain); } - public async Task UnPublishAutoDiscoveryConfigAsync() + public async Task UnPublishAutoDiscoveryConfigAsync(bool migration = false) { if (Variables.MqttManager == null) return; - await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true); + await Variables.MqttManager.AnnounceAutoDiscoveryConfigAsync(this, Domain, true, migration); } } diff --git a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs index 8d2cd9f5..454bd77b 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Models/HomeAssistant/DiscoveryConfigModel.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; +using Newtonsoft.Json; namespace HASS.Agent.Shared.Models.HomeAssistant { @@ -12,9 +12,12 @@ namespace HASS.Agent.Shared.Models.HomeAssistant public abstract class DiscoveryConfigModel { /// - /// (Optional) The MQTT topic subscribed to receive availability (online/offline) updates. + /// Domain of the entity, used to create default object id property. /// /// + [Newtonsoft.Json.JsonIgnore] + public string Domain { get; set; } + public string Availability_topic { get; set; } /// @@ -41,6 +44,16 @@ public abstract class DiscoveryConfigModel /// /// public string State_topic { get; set; } + + protected DiscoveryConfigModel(string domain) + { + if (string.IsNullOrWhiteSpace(domain)) + { + throw new ArgumentException("domain cannot be null or empty string/whitespace"); + } + + Domain = domain; + } } [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -133,9 +146,16 @@ public string Object_id return $"{Device.Name}_{EntityName}"; } - set { _objectId = value; } + + set => _objectId = value; } + //TODO(Amadeo): move Object_id logic here once it's fully deprecated in HA (or even better, finish v3 rewrite and abandon this monstrosity of a code...) + /// + /// Default object id parameter required starting with HA 2025.10 + /// + public string Default_entity_id => $"{Domain}.{Object_id}"; + /// /// (Optional) Defines the units of measurement of the sensor, if any. /// @@ -147,6 +167,8 @@ public string Object_id /// /// public string Value_template { get; set; } + + public SensorDiscoveryConfigModel(string domain) :base(domain) { } } [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -161,6 +183,8 @@ public class CameraSensorDiscoveryConfigModel : SensorDiscoveryConfigModel /// (Optional) The encoding of the image payloads received. /// public string Image_encoding { get; set; } + + public CameraSensorDiscoveryConfigModel(string domain) : base(domain) { } } [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -259,14 +283,23 @@ public string Object_id return $"{Device.Name}_{EntityName}"; } - set { _objectId = value; } + + set => _objectId = value; } + //TODO(Amadeo): move Object_id logic here once it's fully deprecated in HA (or even better, finish v3 rewrite and abandon this monstrosity of a code...) + /// + /// Default object id parameter required starting with HA 2025.10 + /// + public string Default_entity_id => $"{Domain}.{Object_id}"; + /// /// (Optional) Defines a template to extract the value. /// /// public string Value_template { get; set; } + + public CommandDiscoveryConfigModel(string domain) : base(domain) { } } /// diff --git a/src/HASS.Agent/HASS.Agent.Shared/Mqtt/IMqttManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Mqtt/IMqttManager.cs index d41673e7..17bfc4b1 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Mqtt/IMqttManager.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Mqtt/IMqttManager.cs @@ -17,7 +17,7 @@ public interface IMqttManager void Initialize(); void CreateDeviceConfigModel(); Task PublishAsync(MqttApplicationMessage message); - Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false); + Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false, bool migration = false); MqttStatus GetStatus(); Task AnnounceAvailabilityAsync(bool offline = false); Task ClearDeviceConfigAsync(); diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs index 931858fa..fba85dfc 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.Designer.cs @@ -495,6 +495,16 @@ internal static string CommandsManager_MonitorSleepCommandDescription { } } + /// + /// Looks up a localized string similar to Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. + ///Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep.. + /// + internal static string CommandsManager_MonitorSleepPowerPlanCommandDescription { + get { + return ResourceManager.GetString("CommandsManager_MonitorSleepPowerPlanCommandDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tries to wake up all monitors by simulating a 'arrow up' keypress.. /// @@ -607,6 +617,17 @@ internal static string CommandsManager_WebViewCommandDescription { } } + /// + /// Looks up a localized string similar to Puts the machine to sleep using WinForms API. + /// + ///Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration.. + /// + internal static string CommandsManager_WinformsSleepCommandDescription { + get { + return ResourceManager.GetString("CommandsManager_WinformsSleepCommandDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Configure Command &Parameters. /// @@ -1212,6 +1233,15 @@ internal static string CommandType_MonitorSleepCommand { } } + /// + /// Looks up a localized string similar to MonitorSleepPowerPlan. + /// + internal static string CommandType_MonitorSleepPowerPlanCommand { + get { + return ResourceManager.GetString("CommandType_MonitorSleepPowerPlanCommand", resourceCulture); + } + } + /// /// Looks up a localized string similar to MonitorWake. /// @@ -1356,6 +1386,15 @@ internal static string CommandType_WebViewCommand { } } + /// + /// Looks up a localized string similar to WinformsSleep. + /// + internal static string CommandType_WinformsSleepCommand { + get { + return ResourceManager.GetString("CommandType_WinformsSleepCommand", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connecting... /// @@ -3515,6 +3554,15 @@ internal static string HassDomain_Cover { } } + /// + /// Looks up a localized string similar to Fan. + /// + internal static string HassDomain_Fan { + get { + return ResourceManager.GetString("HassDomain_Fan", resourceCulture); + } + } + /// /// Looks up a localized string similar to HASS.Agent Commands. /// @@ -3533,6 +3581,15 @@ internal static string HassDomain_InputBoolean { } } + /// + /// Looks up a localized string similar to InputButton. + /// + internal static string HassDomain_InputButton { + get { + return ResourceManager.GetString("HassDomain_InputButton", resourceCulture); + } + } + /// /// Looks up a localized string similar to Light. /// @@ -4410,7 +4467,7 @@ internal static string OnboardingDone_LblInfo2 { } /// - /// Looks up a localized string similar to There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + /// Looks up a localized string similar to There's a lot more to tinker with, so make sure you take a look at the Configuration Window! /// /// ///Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -4423,7 +4480,8 @@ internal static string OnboardingDone_LblInfo3 { } /// - /// Looks up a localized string similar to Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated!. + /// Looks up a localized string similar to Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) + ///Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day.. /// internal static string OnboardingDone_LblInfo6 { get { @@ -5713,7 +5771,10 @@ internal static string SensorsManager_GpuLoadSensorDescription { } /// - /// Looks up a localized string similar to Provides the current temperature of the first GPU.. + /// Looks up a localized string similar to NOTE: This is a non-functioning sensor. + /// + ///Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. + ///Please see documentation for alternative options.. /// internal static string SensorsManager_GpuTemperatureSensorDescription { get { @@ -5816,6 +5877,15 @@ internal static string SensorsManager_MonitorPowerStateSensorDescription { } } + /// + /// Looks up a localized string similar to Provides an ON/OFF value based on whether the focused window name contains configured string.. + /// + internal static string SensorsManager_NamedActiveWindowSensorDescription { + get { + return ResourceManager.GetString("SensorsManager_NamedActiveWindowSensorDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Provides an ON/OFF value based on whether the window is currently open (doesn't have to be active).. /// @@ -6510,6 +6580,15 @@ internal static string SensorsMod_WmiTestFailed { } } + /// + /// Looks up a localized string similar to AccentColor. + /// + internal static string SensorType_AccentColorSensor { + get { + return ResourceManager.GetString("SensorType_AccentColorSensor", resourceCulture); + } + } + /// /// Looks up a localized string similar to ActiveDesktop. /// @@ -6726,6 +6805,15 @@ internal static string SensorType_MonitorPowerStateSensor { } } + /// + /// Looks up a localized string similar to NamedActiveWindow. + /// + internal static string SensorType_NamedActiveWindowSensor { + get { + return ResourceManager.GetString("SensorType_NamedActiveWindowSensor", resourceCulture); + } + } + /// /// Looks up a localized string similar to NamedWindow. /// diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx index c5179acd..163a3652 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.de.resx @@ -2174,7 +2174,10 @@ Nimmt derzeit die Lautstärke deines Standardgeräts. Liefert die aktuelle Auslastung der ersten GPU in Prozent. - Liefert die aktuelle Temperatur der ersten GPU. + HINWEIS: Dieser Sensor funktioniert nicht. + +Aufgrund von Sicherheitsbedenken bezüglich Libre Hardware Monitor (Bibliothek, die HASS.Agent Zugriff auf GPU-Temperaturdaten ermöglicht) wurde dieser Sensor aus Gründen der Abwärtskompatibilität beibehalten und gibt immer 0 zurück. +Weitere Optionen finden Sie in der Dokumentation. Stellt einen datetime-Wert bereit, der den letzten Moment enthält, in dem der Benutzer eine Eingabe gemacht hat. @@ -3213,7 +3216,8 @@ Sind Sie sicher, dass Sie diesen Schlüssel trotzdem verwenden möchten? Bist Du sicher, dass Du es so verwenden möchtest? - Die Entwicklung und Wartung dieses Tools (und alles, was es umgibt) nimmt viel Zeit in Anspruch. Wie die meisten Entwickler verwende ich Koffein – wenn Du es also entbehren kannst, ist eine Tasse Kaffee immer sehr willkommen! + Derzeit nehmen wir (das HASS.Agent-Team, das die Abspaltung betreut) keinerlei Spenden an. :) +Sie können aber gerne dem ursprünglichen Autor von HASS.Agent – ​​Sam – eine Spende zukommen lassen! Wo auch immer er sich gerade befindet, eine Tasse Kaffee könnte ihm den Tag verschönern. Tipp: Andere Spendenmethoden sind im Über-Fenster verfügbar. @@ -3376,4 +3380,31 @@ Willst Du den Runtime Installer herunterladen? Presse + + Stellt einen EIN/AUS-Wert bereit, basierend darauf, ob der fokussierte Fenstername eine konfigurierte Zeichenfolge enthält. + + + BenanntesAktivesFenster + + + Akzentfarbe + + + Lüfter + + + Versetzt den Rechner mithilfe der WinForms-API in den Ruhezustand. + +Hinweis: Aufgrund des „Modern Sleep“-Modus können sich die Ruhezustandsbefehle je nach Geräte-OEM und Betriebssystemkonfiguration unterschiedlich verhalten. + + + WinformsSleep + + + Versetzt alle Monitore in den Energiesparmodus (niedriger Stromverbrauch) mithilfe einer alternativen Methode zur Änderung des Energiesparplans. +Sollte bei neuen Systemen mit „modernem S0ix-Schlafmodus“ eine bessere Benutzererfahrung bieten und das System nicht in den Ruhezustand versetzen. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx index f881bf80..ab3ac347 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.en.resx @@ -524,7 +524,7 @@ The certificate of the downloaded file will get checked before running,you will HASS.Agent GitHub page - There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + There's a lot more to tinker with, so make sure you take a look at the Configuration Window! Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -2054,7 +2054,10 @@ Currently takes the volume of your default device. Provides the current load of the first GPU as a percentage. - Provides the current temperature of the first GPU. + NOTE: This is a non-functioning sensor. + +Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. +Please see documentation for alternative options. Provides a datetime value containing the last moment the user provided any input. @@ -3089,7 +3092,8 @@ Are you sure you want to use this key anyway? Are you sure you want to use this URI anyway? - Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated! + Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) +Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day. Tip: Other donation methods are available on the About Window. @@ -3252,4 +3256,34 @@ Do you want to download the runtime installer? Button + + NamedActiveWindow + + + Provides an ON/OFF value based on whether the focused window name contains configured string. + + + AccentColor + + + InputButton + + + Fan + + + Puts the machine to sleep using WinForms API. + +Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration. + + + WinformsSleep + + + MonitorSleepPowerPlan + + + Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. +Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx index 2ea0d91a..eba8c949 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.es.resx @@ -2059,7 +2059,10 @@ Actualmente toma el volumen de su dispositivo predeterminado. Proporciona la carga actual de la primera GPU como porcentaje. - Proporciona la temperatura actual de la primera GPU. + NOTA: Este sensor no funciona. + +Debido a problemas de seguridad relacionados con Libre Hardware Monitor (biblioteca que permite a HASS.Agent acceder a los datos de temperatura de la GPU), este sensor se mantiene por compatibilidad con versiones anteriores y siempre devolverá 0. +Consulte la documentación para ver opciones alternativas. Proporciona un valor de fecha y hora que contiene el último momento en que el usuario proporcionó una entrada. @@ -3089,7 +3092,8 @@ Debería contener tres secciones (separadas por dos puntos). ¿Está seguro de que quiere usarlo así? - Desarrollar y mantener esta herramienta (y todo lo que la rodea) requiere mucho tiempo. Al igual que la mayoría de los desarrolladores, funciono a base de cafeína, así que, si puede dedicarle un poco de tiempo, ¡una taza de café es siempre muy apreciada! + Actualmente, nosotros (el equipo de HASS.Agent que mantiene esta versión) no aceptamos donaciones de ningún tipo :) +Sin embargo, no duden en donar al autor original de HASS.Agent, ¡Sam! Esté donde esté, una taza de café podría alegrarle el día. Sugerencia: hay otros métodos de donación disponibles en la ventana Acerca de. @@ -3252,4 +3256,31 @@ Oculta, Maximizada, Minimizada, Normal y Desconocida. Prensa + + Proporciona un valor de ENCENDIDO/APAGADO según si el nombre de la ventana enfocada contiene una cadena configurada. + + + Ventana activa con nombre + + + Color de acento + + + Admirador + + + Pone la máquina en suspensión mediante la API de WinForms. + +Nota: Debido al modo de suspensión moderno, los comandos de suspensión pueden comportarse de forma diferente según el fabricante del dispositivo y la configuración del sistema operativo. + + + WinformsSleep + + + Pone todos los monitores en modo de suspensión (bajo consumo de energía) utilizando un método alternativo de modificación del plan de energía. +Esto debería proporcionar una mejor experiencia con los nuevos sistemas con "suspensión moderna S0ix" y evitar que el sistema entre en modo de suspensión completa. + + + Plan de energía para el sueño del monitor + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx index 27c3ec38..2860c004 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.fr.resx @@ -2089,7 +2089,10 @@ Indique le volume de votre appareil par défaut. Fournit la charge actuelle du premier GPU sous forme de pourcentage. - Fournit la température actuelle du premier GPU. + REMARQUE : Ce capteur est non fonctionnel. + +Pour des raisons de sécurité concernant Libre Hardware Monitor (bibliothèque permettant à HASS.Agent d'accéder aux données de température du GPU), ce capteur est conservé pour des raisons de rétrocompatibilité et renvoie toujours 0. +Veuillez consulter la documentation pour connaître les autres options. Fournit la date et l'heure de la dernière utilisation d'un périphérique par l'utilisateur. @@ -3122,7 +3125,8 @@ Etes-vous sûr de vouloir l'utiliser comme ça ? Etes-vous sûr de vouloir l'utiliser ainsi ? - Développer et maintenir cet outil (et tout ce qui l'entoure) prend beaucoup de temps. Comme la plupart des développeurs, je fonctionne à la caféine - donc si vous pouvez vous le permettre, une tasse de café est toujours très appréciée ! + Actuellement, nous (l'équipe HASS.Agent qui maintient cette version dérivée) n'acceptons aucune donation, sous quelque forme que ce soit. +N'hésitez cependant pas à faire un don à l'auteur original de HASS.Agent, Sam ! Où qu'il soit actuellement, une tasse de café pourrait lui faire plaisir. Astuce : d'autres méthodes de dons sont disponibles dans la fenêtre À propos. @@ -3285,4 +3289,31 @@ Do you want to download the runtime installer? Presse + + Fournit une valeur ON/OFF selon que le nom de la fenêtre focalisée contient ou non une chaîne configurée. + + + Fenêtre active nommée + + + AccentColor + + + Ventilateur + + + Met la machine en veille à l'aide de l'API WinForms. + +Remarque : en raison de la fonction « Modern Sleep », les commandes de mise en veille peuvent se comporter différemment selon le fabricant de l'appareil et la configuration du système d'exploitation. + + + WinformsSleep + + + Met tous les moniteurs en mode veille (basse consommation) en utilisant une approche alternative de modification du plan d'alimentation. +Cela devrait offrir une meilleure expérience avec les nouveaux systèmes dotés du mode veille moderne « S0ix » et éviter de mettre le système en veille complète. + + + Surveillance du mode veille/alimentation + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx index 3a9871c4..962d4cb6 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.nl.resx @@ -2070,7 +2070,10 @@ Pakt momenteel het volume van je standaardapparaat. Geeft de huidige belasting van de eerste GPU als een percentage. - Geeft de huidige temperatuur van de eerste GPU. + OPMERKING: Dit is een niet-functionerende sensor. + +Vanwege beveiligingsproblemen met Libre Hardware Monitor (bibliotheek die HASS.Agent toegang geeft tot GPU-temperatuurgegevens) wordt deze sensor om redenen van achterwaartse compatibiliteit niet gebruikt en retourneert altijd 0. +Raadpleeg de documentatie voor alternatieve opties. Geeft een datetime waarde met het laatste moment dat de gebruiker invoer geleverd heeft. @@ -3111,7 +3114,8 @@ Weet je zeker dat je 'm zo wilt gebruiken? Weet je zeker dat je 'm zo wilt gebruiken? - Het ontwikkelen en onderhouden van deze tool (en alles wat erbij komt kijken, zoals support, deze vertaling en de documentatie) neemt een hoop tijd in beslag. Zoals de meeste ontwikkelaars draai ik op caffeïne - dus als je 't kunt missen, wordt een kop koffie altijd erg gewaardeerd! + Momenteel accepteren wij (het HASS.Agent-team dat de fork beheert) geen donaties in welke vorm dan ook :) +Voel je echter vrij om te doneren aan de oorspronkelijke auteur van HASS.Agent - Sam! Waar ze zich ook bevinden, een kopje koffie kan hun dag misschien opvrolijken. Tip: andere donatie methodes zijn beschikbaar in het Over scherm. @@ -3274,4 +3278,31 @@ Wil je de runtime installatie downloaden? Pers + + Geeft een AAN/UIT-waarde op basis van het al dan niet bevatten van de focusvensternaam van een geconfigureerde tekenreeks. + + + SchermActiefNaam + + + Accentkleur + + + Fan + + + Zet de machine in slaapstand met behulp van de WinForms API. + +Opmerking: vanwege "Modern Sleep" kunnen de slaapopdrachten zich anders gedragen, afhankelijk van de OEM van het apparaat en de configuratie van het besturingssysteem. + + + WinformsSleep + + + Zet alle monitoren in de slaapstand (laag energieverbruik) met behulp van een alternatieve aanpak voor het aanpassen van het energiebeheerschema. +Zou een betere ervaring moeten bieden met nieuwe systemen met een "moderne S0ix-slaapstand" en het systeem niet in de slaapstand zetten. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx index 6947c51d..dcf1b18a 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pl.resx @@ -2163,7 +2163,10 @@ Obecnie zwraca głośność domyślnego urządzenia. Zwraca obciążenie pierwszego GPU w procentach. - Zwraca temperaturę pierwszego GPU. + UWAGA: Ten czujnik nie działa. + +Ze względu na obawy dotyczące bezpieczeństwa Libre Hardware Monitor (biblioteki umożliwiającej HASS.Agent dostęp do danych o temperaturze GPU), ten czujnik został pozostawiony ze względu na kompatybilność wsteczną i zawsze będzie zwracał wartość 0. +Informacje o alternatywnych opcjach znajdują się w dokumentacji. Zwraca czas (datetime value) w którym użytkownik dokonał jakiejkolwiek czynności. @@ -3199,7 +3202,8 @@ Czy jesteś pewien, że chcesz użyć tego klucza? Czy jesteś pewien, że chcesz użyć tego linku URL? - Tworzenie i utrzymanie aplikacji (i wszystko dookoła, jak wsparcie i dokumentacja) zajmuje sporo czasu. Jak większość deweloperów moim paliwem jest kofeina, więc jeżeli możesz postawić mi kubek kawy będę bardzo wdzięczny! + Obecnie my (zespół HASS.Agent, odpowiedzialny za utrzymywanie tej wersji) nie przyjmujemy żadnych darowizn :) +Prosimy jednak o wsparcie finansowe oryginalnego autora HASS.Agent – ​​Sama! Gdziekolwiek się teraz znajduje, filiżanka kawy z pewnością umili mu dzień. Wskazówka: Inne sposoby wsparcia dostępne są w zakładce O. @@ -3362,4 +3366,31 @@ Czy chcesz pobrać plik instalacyjny? Naciśnij + + Zapewnia wartość WŁ./WYŁ. w zależności od tego, czy nazwa wybranego okna zawiera skonfigurowany ciąg znaków. + + + NazwaAktywnegoOkna + + + Kolor akcentu + + + Wiatrak + + + Uśpij maszynę za pomocą interfejsu API WinForms. + +Uwaga: ze względu na „Modern Sleep” polecenia uśpienia mogą zachowywać się inaczej w zależności od producenta OEM urządzenia i konfiguracji systemu operacyjnego. + + + WinformsSleep + + + Przełącza wszystkie monitory w tryb uśpienia (niskiego poboru mocy) za pomocą alternatywnej metody modyfikacji planu zasilania. +Powinno to zapewnić lepsze działanie w przypadku nowych systemów z „nowoczesnym trybem uśpienia S0ix” i zapobiec przejściu systemu w stan uśpienia. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx index a7b67ecb..2e6f62f1 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.pt-br.resx @@ -1730,7 +1730,8 @@ Se você encontrar algum problema, crie um ticket na página do GitHub. Fuzzy - Desenvolver e manter essa ferramenta (e tudo o que a envolve, como suporte e documentação) leva muito tempo. Como a maioria dos desenvolvedores, eu amo café - então se você puder me doar um cafezinho serei muito agradecido! + Atualmente, nós (a equipa do HASS.Agent que mantém o fork) não aceitamos donativos de qualquer forma :) +No entanto, sinta-se à vontade para doar ao autor original do HASS.Agent - Sam! Onde quer que ele esteja, uma chávena de café pode alegrar o seu dia. Dica: outros tipos de doação estão disponíveis na janela Sobre. @@ -2215,7 +2216,10 @@ Dependendo da sua versão do Windows, isso pode ser encontrado no novo painel de Fornece a carga atual da GPU como uma porcentagem. - Fornece a temperatura atual da GPU. + NOTA: Este sensor não está a funcionar. + +Devido a questões de segurança relacionadas com o Libre Hardware Monitor (biblioteca que permite ao HASS.Agent aceder aos dados de temperatura da GPU), este sensor foi mantido por motivos de compatibilidade com versões anteriores e irá sempre retornar 0. +Consulte a documentação para obter opções alternativas. Fornece um valor de data e hora contendo o último momento em que o usuário forneceu qualquer entrada. @@ -3299,4 +3303,31 @@ Deseja baixar o Microsoft WebView2 runtime? Imprensa + + Fornece um valor ON/OFF com base na presença ou não de uma string configurada no nome da janela em foco. + + + JanelaAtiva Nomeada + + + Cor de destaque + + + + + + Coloca a máquina em modo de espera utilizando a API do WinForms. + +Nota: devido ao "Modern Sleep", os comandos de espera podem comportar-se de forma diferente, dependendo do fabricante do dispositivo e da configuração do sistema operativo. + + + WinformsSleep + + + Coloca todos os monitores em modo de suspensão (baixo consumo de energia) utilizando uma abordagem alternativa de modificação do plano de energia. +Deve proporcionar uma melhor experiência com novos sistemas com "suspensão S0ix moderna" e não colocar o sistema em modo de suspensão. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx index 8fc95014..2b24e3c1 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.resx @@ -1147,7 +1147,10 @@ Currently takes the volume of your default device. Provides the current load of the first GPU as a percentage. - Provides the current temperature of the first GPU. + NOTE: This is a non-functioning sensor. + +Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. +Please see documentation for alternative options. Provides a datetime value containing the last moment the user provided any input. @@ -3057,10 +3060,11 @@ Tip: if you're using the HA addon, you can probably use the preset address - jus Enable &Notifications - Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated! + Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) +Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day. - There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + There's a lot more to tinker with, so make sure you take a look at the Configuration Window! Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -3235,4 +3239,34 @@ Do you want to download the runtime installer? Button + + NamedActiveWindow + + + Provides an ON/OFF value based on whether the focused window name contains configured string. + + + AccentColor + + + InputButton + + + Fan + + + Puts the machine to sleep using WinForms API. + +Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration. + + + WinformsSleep + + + MonitorSleepPowerPlan + + + Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. +Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx index 6f5d7c6e..9098b171 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.ru.resx @@ -2113,7 +2113,10 @@ Home Assistant version: {0} Показывает текущую загрузку первого графического процессора в процентах. - Показывает текущую температуру первого графического процессора. + ПРИМЕЧАНИЕ: Это неработающий датчик. + +Из-за проблем безопасности, связанных с Libre Hardware Monitor (библиотекой, позволяющей HASS.Agent получать доступ к данным о температуре графического процессора), этот датчик оставлен в целях обратной совместимости и всегда будет возвращать 0. +Альтернативные варианты см. в документации. Предоставляет значение даты и времени, содержащее последний момент, когда пользователь предоставил какие-либо входные данные. @@ -3157,7 +3160,8 @@ Home Assistant. Вы уверены, что все равно хотите использовать этот URI? - Разработка и обслуживание этого инструмента (и всего, что его окружает) отнимает много времени. Как и большинство разработчиков, я работаю на кофеине - так что, если вы можете поделиться им, чашка кофе всегда очень ценится! + В настоящее время мы (команда HASS.Agent, поддерживающая форк) не принимаем пожертвования в какой-либо форме :) +Однако, пожалуйста, не стесняйтесь сделать пожертвование оригинальному автору HASS.Agent — Сэму! Где бы он сейчас ни находился, чашка кофе может скрасить его день. Совет: Другие способы пожертвования доступны в окне 'О программе'. @@ -3320,4 +3324,31 @@ Home Assistant. Нажимать + + Предоставляет значение ВКЛ/ВЫКЛ в зависимости от того, содержит ли имя окна в фокусе настроенную строку. + + + ИменованноеАктивноеОкно + + + Акцентный цвет + + + Вентилятор + + + Переводит устройство в спящий режим с помощью API WinForms. + +Примечание: из-за «Modern Sleep» команды перехода в спящий режим могут работать по-разному в зависимости от производителя устройства и конфигурации ОС. + + + WinformsSleep + + + Переводит все мониторы в спящий режим (режим пониженного энергопотребления), используя альтернативный подход к изменению схемы управления питанием. +Это должно обеспечить лучшую работу на новых системах с «современным спящим режимом S0ix» и предотвратить переход системы в спящий режим. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx index 1edd421c..345c8167 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.sl.resx @@ -1788,7 +1788,8 @@ Hvala, ker uporabljate HASS.Agent. Upam, da vam bo koristil :-) Fuzzy - Razvoj in vzdrževanje tega dodatka (in vsega, kar spada zraven, kot je podpora, navodila) vzame veliko časa. Kot večina razvijalcev tudi jaz delam na kofein - zato bi bil zelo hvaležen kake skodelice kave, če jo lahko pogrešate! + Trenutno (ekipa HASS.Agent, ki vzdržuje forum) ne sprejema donacij v nobeni obliki :) +Vendar pa lahko brez zadržkov donirate izvirnemu avtorju HASS.Agent - Samu! Kjerkoli že je, mu lahko skodelica kave polepša dan. Namig: ostale možnosti donacij so na voljo v zavihku "Vizitka". @@ -2292,7 +2293,10 @@ Glede na verzijo Windows-ov se to lahko nahaja v Nadzorna plošča --> Sistem Zagotavlja trenutno obremenitev prvega GPU-ja v odstotkih. - Zagotavlja trenutno temperaturo prvega GPU-ja. + OPOMBA: To je nedelujoč senzor. + +Zaradi varnostnih pomislekov glede Libre Hardware Monitor (knjižnica, ki omogoča HASS.Agent dostop do podatkov o temperaturi GPU) je ta senzor ostal zaradi združljivosti s prejšnjimi različicami in bo vedno vrnil vrednost 0. +Za alternativne možnosti glejte dokumentacijo. Zagotavlja vrednost datuma in časa, ki vsebuje zadnji trenutek, ko je uporabnik vnesel kakršen koli vnos. @@ -3401,4 +3405,31 @@ Ali želite prenesti runtime installer? Pritisnite + + Zagotavlja vrednost VKLOP/IZKLOP glede na to, ali ime okna v fokusu vsebuje konfiguriran niz. + + + PoimenovanoAktivnoOkno + + + Barva poudarka + + + Ventilator + + + Preklopi računalnik v stanje mirovanja z uporabo WinForms API-ja. + +Opomba: Zaradi »modernega mirovanja« se lahko ukazi za mirovanje obnašajo drugače, odvisno od proizvajalca originalne opreme naprave in konfiguracije operacijskega sistema. + + + WinformsSleep + + + Preklopi vse monitorje v način mirovanja (nizka poraba energije) z uporabo alternativnega pristopa k spreminjanju načrta porabe energije. +Omogoča boljšo izkušnjo z novimi sistemi s »sodobnim načinom mirovanja S0ix« in ne preklopi sistema v stanje mirovanja. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx index 28956a1c..4a877f6b 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx +++ b/src/HASS.Agent/HASS.Agent.Shared/Resources/Localization/Languages.tr.resx @@ -1846,7 +1846,10 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku Yüzde olarak ilk GPU'nun mevcut yükünü sağlar. - İlk GPU'nun mevcut sıcaklığını sağlar. + NOT: Bu, çalışmayan bir sensördür. + +Libre Donanım İzleyicisi (HASS.Agent'ın GPU sıcaklık verilerine erişmesine izin veren kütüphane) ile ilgili güvenlik endişeleri nedeniyle, bu sensör geriye dönük uyumluluk nedeniyle bırakılmıştır ve her zaman 0 döndürecektir. +Alternatif seçenekler için lütfen belgelere bakın. Kullanıcının herhangi bir girdi sağladığı son anı içeren bir tarih saat değeri sağlar. @@ -2728,7 +2731,8 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku Sağladığınız URI geçerli görünmüyor, geçerli bir URI aşağıdakilerden biri gibi görünebilir: - http://homeassistant.local:8123 - http://192.168.0.1:8123 Kullanmak istediğinizden emin misiniz? yine de bu URI? - Bu aracı (ve onu çevreleyen her şeyi) geliştirmek ve sürdürmek çok zaman alır. Çoğu geliştirici gibi, ben de kafein kullanıyorum - bu yüzden eğer onu ayırabilirseniz, bir fincan kahve her zaman çok değerlidir! + Şu anda (çatalın bakımını üstlenen HASS.Agent Ekibi) hiçbir şekilde bağış kabul etmiyoruz :) +Ancak lütfen HASS.Agent'ın orijinal yazarı Sam'e bağış yapmaktan çekinmeyin! Şu anda nerede olurlarsa olsunlar, bir fincan kahve günlerini güzelleştirebilir. İpucu: Hakkında Penceresinde başka bağış yöntemleri de mevcuttur. @@ -2859,4 +2863,31 @@ Lütfen aracınız için credentialları sağlayın, HA Mosquitto eklentisini ku Basmak + + Odaklanan pencere adının yapılandırılmış dizeyi içerip içermediğine bağlı olarak bir AÇIK/KAPALI değeri sağlar. + + + AdlandırılmışAktifPencere + + + Vurgu Rengi + + + Fan + + + WinForms API'sini kullanarak makineyi uyku moduna geçirir. + +Not: "Modern Uyku" nedeniyle uyku komutları, cihazın OEM ve işletim sistemi yapılandırmasına bağlı olarak farklı davranabilir. + + + WinformsSleep + + + Güç planı değişikliği gibi alternatif bir yaklaşım kullanarak tüm monitörleri uyku moduna (düşük güç) alır. +"Modern S0ix uyku" özelliğine sahip yeni sistemlerde daha iyi bir deneyim sağlamalı ve sistemi uyku moduna almamalıdır. + + + MonitörUykuGüçPlanı + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/API/ApiEndpoints.cs b/src/HASS.Agent/HASS.Agent/API/ApiEndpoints.cs index ab28e889..6e7387cc 100644 --- a/src/HASS.Agent/HASS.Agent/API/ApiEndpoints.cs +++ b/src/HASS.Agent/HASS.Agent/API/ApiEndpoints.cs @@ -2,10 +2,12 @@ using Grapevine; using HASS.Agent.Enums; using HASS.Agent.Extensions; +using HASS.Agent.Functions; using HASS.Agent.Managers; using HASS.Agent.Media; using HASS.Agent.Models.HomeAssistant; using HASS.Agent.MQTT; +using HASS.Agent.Shared.Models.HomeAssistant; using Newtonsoft.Json; using Serilog; using HttpMethod = System.Net.Http.HttpMethod; @@ -24,6 +26,21 @@ public class ApiEndpoints : ApiDeserialization /// public static async Task DeviceInfoRoute(IHttpContext context) { + if (Variables.DeviceConfig == null) + { + var name = HelperFunctions.GetConfiguredDeviceName(); + Log.Information("[LOCALAPI] Device configuration missing, creating and identifying as device: {name}", name); + + Variables.DeviceConfig = new DeviceConfigModel + { + Name = name, + Identifiers = "hass.agent-" + name, + Manufacturer = "HASS.Agent Team", + Model = Environment.OSVersion.ToString(), + Sw_version = Variables.Version + }; + } + context.Response.ContentType = "application/json"; await context.Response.SendResponseAsync(JsonConvert.SerializeObject(new { @@ -36,7 +53,7 @@ await context.Response.SendResponseAsync(JsonConvert.SerializeObject(new } }, MqttManager.JsonSerializerSettings)); } - + /// /// Notification route, handles all incoming notifications on '/notify' /// diff --git a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs index f9a47fd9..dc7e4200 100644 --- a/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs +++ b/src/HASS.Agent/HASS.Agent/Commands/CommandsManager.cs @@ -58,20 +58,37 @@ internal static async void Initialize() /// Unpublishes all commands /// /// - internal static async Task UnpublishAllCommands() + internal static async Task UnpublishAllCommands(bool migration = false) { if (!CommandsPresent()) return; foreach (var command in Variables.Commands) { - await command.UnPublishAutoDiscoveryConfigAsync(); + await command.UnPublishAutoDiscoveryConfigAsync(migration); await Variables.MqttManager.UnsubscribeAsync(command); } _discoveryPublished = false; } + /// + /// Publishes all commands + /// + /// + internal static async Task ForcePublishAllCommands() + { + if (!CommandsPresent()) + return; + + foreach (var command in Variables.Commands) + { + await command.PublishAutoDiscoveryConfigAsync(); + } + + _discoveryPublished = false; + } + /// /// Generates new ID's for all commands /// @@ -416,6 +433,14 @@ internal static void LoadCommandInfo() // ================================= + commandInfoCard = new CommandInfoCard(CommandType.MonitorSleepPowerPlanCommand, + Languages.CommandsManager_MonitorSleepPowerPlanCommandDescription, + true, false, false); + + CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard); + + // ================================= + commandInfoCard = new CommandInfoCard(CommandType.MonitorWakeCommand, Languages.CommandsManager_MonitorWakeCommandDescription, true, false, false); @@ -448,6 +473,14 @@ internal static void LoadCommandInfo() // ================================= + commandInfoCard = new CommandInfoCard(CommandType.RadioCommand, + Languages.CommandsManager_RadioCommandDescription, + true, false, true); + + CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard); + + // ================================= + commandInfoCard = new CommandInfoCard(CommandType.RestartCommand, Languages.CommandsManager_RestartCommandDescription, true, true, false); @@ -520,24 +553,24 @@ internal static void LoadCommandInfo() // ================================= - commandInfoCard = new CommandInfoCard(CommandType.WebViewCommand, - Languages.CommandsManager_WebViewCommandDescription, - true, false, true); + commandInfoCard = new CommandInfoCard(CommandType.TrayWebViewCommand, + Languages.CommandsManager_TrayWebViewCommandDescription, + true, false, false); CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard); // ================================= - commandInfoCard = new CommandInfoCard(CommandType.TrayWebViewCommand, - Languages.CommandsManager_TrayWebViewCommandDescription, + commandInfoCard = new CommandInfoCard(CommandType.WinformsSleepCommand, + Languages.CommandsManager_WinformsSleepCommandDescription, true, false, false); CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard); // ================================= - commandInfoCard = new CommandInfoCard(CommandType.RadioCommand, - Languages.CommandsManager_RadioCommandDescription, + commandInfoCard = new CommandInfoCard(CommandType.WebViewCommand, + Languages.CommandsManager_WebViewCommandDescription, true, false, true); CommandInfoCards.Add(commandInfoCard.CommandType, commandInfoCard); diff --git a/src/HASS.Agent/HASS.Agent/Compatibility/MigrateCompatibilityTask.cs b/src/HASS.Agent/HASS.Agent/Compatibility/MigrateCompatibilityTask.cs index f299a454..0c291874 100644 --- a/src/HASS.Agent/HASS.Agent/Compatibility/MigrateCompatibilityTask.cs +++ b/src/HASS.Agent/HASS.Agent/Compatibility/MigrateCompatibilityTask.cs @@ -255,6 +255,38 @@ private void MigrateCommands(string commandsFile) Log.Information("[COMPATTASK] Commands configuration migrated"); } + + private void CreateForkConfigBackup() + { + var backupFolderName = $"migrationBackup_{DateTime.Now.ToString("ddMMyy_HHmmss")}"; + Log.Information("[COMPATTASK] Creating backup of existing forked version configuration (client) into {bf}", backupFolderName); + + var destination = Path.Combine(Variables.ConfigPath, backupFolderName); + Directory.CreateDirectory(destination); + + var currentConfigFiles = new DirectoryInfo(Variables.ConfigPath).GetFiles(); + foreach (var file in currentConfigFiles) + { + Log.Information("[COMPATTASK] Backing up {fn}", file.FullName); + file.CopyTo(Path.Combine(destination, file.Name), true); + } + + Log.Information("[COMPATTASK] Creating backup of existing forked version configuration (service) into {bf}", backupFolderName); + + var serviceConfigPath = Path.Combine(ServiceManager.GetInstallPath(), "config"); + destination = Path.Combine(serviceConfigPath, backupFolderName); + Directory.CreateDirectory(destination); + + currentConfigFiles = new DirectoryInfo(serviceConfigPath).GetFiles(); + foreach (var file in currentConfigFiles) + { + Log.Information("[COMPATTASK] Backing up {fn}", file.FullName); + file.CopyTo(Path.Combine(destination, file.Name), true); + } + + Log.Information("[COMPATTASK] Creating backup of existing forked version configuration completed"); + } + public async Task<(bool, string)> Perform() { try @@ -265,6 +297,8 @@ private void MigrateCommands(string commandsFile) StopOriginalInstances(); + CreateForkConfigBackup(); + MigrateServiceConfig(); MigrateClientConfig(); MigrateRegistryConfig(); diff --git a/src/HASS.Agent/HASS.Agent/Compatibility/NameCompatibilityTask.cs b/src/HASS.Agent/HASS.Agent/Compatibility/NameCompatibilityTask.cs index ef3fd03b..0fa21df6 100644 --- a/src/HASS.Agent/HASS.Agent/Compatibility/NameCompatibilityTask.cs +++ b/src/HASS.Agent/HASS.Agent/Compatibility/NameCompatibilityTask.cs @@ -10,8 +10,6 @@ using HASS.Agent.Shared.Functions; using HASS.Agent.Shared.Models.Config; using HASS.Agent.Shared.Models.HomeAssistant; -using HidSharp.Utility; -using LibreHardwareMonitor.Hardware; using Octokit; using Serilog; using System; diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs index 670ddf22..d404552a 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.Designer.cs @@ -37,6 +37,7 @@ private void InitializeComponent() this.TbMqttRootCertificate = new System.Windows.Forms.TextBox(); this.LblRootCert = new System.Windows.Forms.Label(); this.CbUseRetainFlag = new System.Windows.Forms.CheckBox(); + this.CbUseWebSocket = new System.Windows.Forms.CheckBox(); this.CbAllowUntrustedCertificates = new System.Windows.Forms.CheckBox(); this.BtnMqttClearConfig = new Syncfusion.WinForms.Controls.SfButton(); this.LblTip1 = new System.Windows.Forms.Label(); @@ -138,13 +139,27 @@ private void InitializeComponent() this.CbUseRetainFlag.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton; this.CbUseRetainFlag.AutoSize = true; this.CbUseRetainFlag.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.CbUseRetainFlag.Location = new System.Drawing.Point(412, 451); + this.CbUseRetainFlag.Location = new System.Drawing.Point(412, 431); this.CbUseRetainFlag.Name = "CbUseRetainFlag"; this.CbUseRetainFlag.Size = new System.Drawing.Size(114, 23); this.CbUseRetainFlag.TabIndex = 10; this.CbUseRetainFlag.Text = Languages.ConfigMqtt_CbUseRetainFlag; this.CbUseRetainFlag.UseVisualStyleBackColor = true; // + // CbUseWebSocketFlag + // + this.CbUseWebSocket.AccessibleDescription = "Use WebSocket for MQTT connection instead of direct one."; + this.CbUseWebSocket.AccessibleName = "WebSocket flag"; + this.CbUseWebSocket.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton; + this.CbUseWebSocket.AutoSize = true; + this.CbUseWebSocket.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.CbUseWebSocket.Location = new System.Drawing.Point(412, 469); + this.CbUseWebSocket.Name = "CbUseWebSocketFlag"; + this.CbUseWebSocket.Size = new System.Drawing.Size(114, 23); + this.CbUseWebSocket.TabIndex = 57; + this.CbUseWebSocket.Text = Languages.ConfigMqtt_CbUseWebSocket; + this.CbUseWebSocket.UseVisualStyleBackColor = true; + // // CbAllowUntrustedCertificates // this.CbAllowUntrustedCertificates.AccessibleDescription = "Enable allowing untrusted certificates when connecting."; @@ -500,6 +515,7 @@ private void InitializeComponent() this.Controls.Add(TbMqttRootCertificate); this.Controls.Add(LblRootCert); this.Controls.Add(CbUseRetainFlag); + this.Controls.Add(CbUseWebSocket); this.Controls.Add(CbAllowUntrustedCertificates); this.Controls.Add(BtnMqttClearConfig); this.Controls.Add(LblTip1); @@ -540,6 +556,7 @@ private void InitializeComponent() internal System.Windows.Forms.TextBox TbMqttClientCertificate; internal System.Windows.Forms.TextBox TbMqttRootCertificate; internal System.Windows.Forms.CheckBox CbUseRetainFlag; + internal System.Windows.Forms.CheckBox CbUseWebSocket; internal System.Windows.Forms.CheckBox CbAllowUntrustedCertificates; internal Syncfusion.WinForms.Controls.SfButton BtnMqttClearConfig; internal System.Windows.Forms.TextBox TbMqttDiscoveryPrefix; diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs index 83354e26..cf85f27d 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigMqtt.cs @@ -47,6 +47,7 @@ private void BtnMqttClearConfig_Click(object sender, EventArgs e) TbMqttClientCertificate.Text = string.Empty; CbAllowUntrustedCertificates.CheckState = CheckState.Checked; CbUseRetainFlag.CheckState = CheckState.Checked; + CbUseWebSocket.CheckState = CheckState.Unchecked; } private void ConfigMqtt_Load(object sender, EventArgs e) diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs index 8fec92e9..3ef31079 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.Designer.cs @@ -1,4 +1,5 @@ using HASS.Agent.Resources.Localization; +using Syncfusion.Windows.Forms.Tools; namespace HASS.Agent.Controls.Configuration { @@ -39,14 +40,16 @@ private void InitializeComponent() LblX = new Label(); LblWebViewSize = new Label(); BtnShowWebViewPreview = new Syncfusion.WinForms.Controls.SfButton(); - NumWebViewWidth = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt(); - NumWebViewHeight = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt(); + NumWebViewWidth = new NumericUpDownExt(); + NumWebViewHeight = new NumericUpDownExt(); BtnWebViewReset = new Syncfusion.WinForms.Controls.SfButton(); CbWebViewKeepLoaded = new CheckBox(); LblInfo2 = new Label(); CbWebViewShowMenuOnLeftClick = new CheckBox(); + NumWebViewScreen = new ComboBoxAdv(); ((System.ComponentModel.ISupportInitialize)NumWebViewWidth).BeginInit(); ((System.ComponentModel.ISupportInitialize)NumWebViewHeight).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NumWebViewScreen).BeginInit(); SuspendLayout(); // // LblInfo1 @@ -142,7 +145,7 @@ private void InitializeComponent() LblX.AccessibleRole = AccessibleRole.StaticText; LblX.AutoSize = true; LblX.Font = new Font("Segoe UI", 10F); - LblX.Location = new Point(184, 350); + LblX.Location = new Point(176, 350); LblX.Name = "LblX"; LblX.Size = new Size(17, 19); LblX.TabIndex = 53; @@ -170,9 +173,9 @@ private void InitializeComponent() BtnShowWebViewPreview.Enabled = false; BtnShowWebViewPreview.Font = new Font("Segoe UI", 10F); BtnShowWebViewPreview.ForeColor = Color.FromArgb(241, 241, 241); - BtnShowWebViewPreview.Location = new Point(405, 348); + BtnShowWebViewPreview.Location = new Point(494, 348); BtnShowWebViewPreview.Name = "BtnShowWebViewPreview"; - BtnShowWebViewPreview.Size = new Size(206, 26); + BtnShowWebViewPreview.Size = new Size(117, 26); BtnShowWebViewPreview.Style.BackColor = Color.FromArgb(63, 63, 70); BtnShowWebViewPreview.Style.FocusedBackColor = Color.FromArgb(63, 63, 70); BtnShowWebViewPreview.Style.FocusedForeColor = Color.FromArgb(241, 241, 241); @@ -222,7 +225,7 @@ private void InitializeComponent() NumWebViewHeight.Enabled = false; NumWebViewHeight.Font = new Font("Segoe UI", 10F); NumWebViewHeight.ForeColor = Color.FromArgb(241, 241, 241); - NumWebViewHeight.Location = new Point(218, 348); + NumWebViewHeight.Location = new Point(197, 348); NumWebViewHeight.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); NumWebViewHeight.MaxLength = 10; NumWebViewHeight.MetroColor = SystemColors.WindowFrame; @@ -243,7 +246,7 @@ private void InitializeComponent() BtnWebViewReset.Font = new Font("Segoe UI", 10F); BtnWebViewReset.ForeColor = Color.FromArgb(241, 241, 241); BtnWebViewReset.ImageSize = new Size(24, 24); - BtnWebViewReset.Location = new Point(317, 348); + BtnWebViewReset.Location = new Point(289, 348); BtnWebViewReset.Name = "BtnWebViewReset"; BtnWebViewReset.Size = new Size(51, 26); BtnWebViewReset.Style.BackColor = Color.FromArgb(63, 63, 70); @@ -305,9 +308,25 @@ private void InitializeComponent() CbWebViewShowMenuOnLeftClick.Text = Languages.ConfigTrayIcon_CbWebViewShowMenuOnLeftClick; CbWebViewShowMenuOnLeftClick.UseVisualStyleBackColor = true; // - // ConfigTrayIcon - // - AccessibleDescription = "Panel containing the tray icon configuration."; + // NumWebViewScreen + // + NumWebViewScreen.AccessibleDescription = "Dropdown containing the screens to choose for tray web view display."; + NumWebViewScreen.AccessibleName = "Display on which to show the tray web view"; + NumWebViewScreen.BeforeTouchSize = new Size(142, 23); + NumWebViewScreen.Font = new Font("Segoe UI", 10F); + NumWebViewScreen.Location = new Point(346, 348); + NumWebViewScreen.Name = "NumWebViewScreen"; + NumWebViewScreen.Size = new Size(142, 23); + NumWebViewScreen.TabIndex = 78; + NumWebViewScreen.Text = Languages.ConfigTrayIcon_NumWebViewScreen; + NumWebViewScreen.SelectedValueChanged += domainUpDown1_SelectedItemChanged; + NumWebViewScreen.BackColor = Color.FromArgb(63, 63, 70); + NumWebViewScreen.Border3DStyle = Border3DStyle.Flat; + NumWebViewScreen.ForeColor = Color.FromArgb(241, 241, 241); + // + // ConfigTrayIcon + // + AccessibleDescription = "Panel containing the tray icon configuration."; AccessibleName = "Tray icon"; AccessibleRole = AccessibleRole.Pane; AutoScaleDimensions = new SizeF(96F, 96F); @@ -328,6 +347,7 @@ private void InitializeComponent() Controls.Add(LblInfo1); Controls.Add(CbDefaultMenu); Controls.Add(CbUseModernIcon); + Controls.Add(NumWebViewScreen); ForeColor = Color.FromArgb(241, 241, 241); Margin = new Padding(4); Name = "ConfigTrayIcon"; @@ -335,25 +355,27 @@ private void InitializeComponent() Load += ConfigTrayIcon_Load; ((System.ComponentModel.ISupportInitialize)NumWebViewWidth).EndInit(); ((System.ComponentModel.ISupportInitialize)NumWebViewHeight).EndInit(); + ((System.ComponentModel.ISupportInitialize)NumWebViewScreen).EndInit(); ResumeLayout(false); PerformLayout(); } #endregion - private System.Windows.Forms.Label LblInfo1; - internal System.Windows.Forms.CheckBox CbUseModernIcon; - internal System.Windows.Forms.CheckBox CbDefaultMenu; + private Label LblInfo1; + internal CheckBox CbUseModernIcon; + internal CheckBox CbDefaultMenu; internal CheckBox CbShowWebView; internal TextBox TbWebViewUrl; internal Syncfusion.WinForms.Controls.SfButton BtnShowWebViewPreview; internal Label LblWebViewUrl; internal Label LblX; internal Label LblWebViewSize; - internal Syncfusion.Windows.Forms.Tools.NumericUpDownExt NumWebViewWidth; - internal Syncfusion.Windows.Forms.Tools.NumericUpDownExt NumWebViewHeight; + internal NumericUpDownExt NumWebViewWidth; + internal NumericUpDownExt NumWebViewHeight; internal Syncfusion.WinForms.Controls.SfButton BtnWebViewReset; internal CheckBox CbWebViewKeepLoaded; internal Label LblInfo2; internal CheckBox CbWebViewShowMenuOnLeftClick; + internal ComboBoxAdv NumWebViewScreen; } } diff --git a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.cs b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.cs index 88575ef8..9757ebfb 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Configuration/ConfigTrayIcon.cs @@ -1,11 +1,13 @@ -using System.Diagnostics; -using HASS.Agent.Functions; +using HASS.Agent.Functions; using HASS.Agent.Models.Internal; +using Syncfusion.Windows.Forms.Tools; namespace HASS.Agent.Controls.Configuration { public partial class ConfigTrayIcon : UserControl { + internal int SelectedScreen { get; set; } + public ConfigTrayIcon() { InitializeComponent(); @@ -14,6 +16,33 @@ public ConfigTrayIcon() private void ConfigTrayIcon_Load(object sender, EventArgs e) { if (string.IsNullOrEmpty(TbWebViewUrl.Text)) TbWebViewUrl.Text = Variables.AppSettings.HassUri; + InitMultiScreenConfig(); + } + + private void InitMultiScreenConfig() + { + if (Variables.AppSettings.TrayIconWebViewScreen == -1) + { + HelperFunctions.InitMultiScreenConfig(); + } + + if (Screen.AllScreens.Length == 1) + { + NumWebViewScreen.Visible = true; + NumWebViewScreen.Items.Add("Single Screen Mode"); + NumWebViewScreen.Enabled = false; + } + else + { + foreach (var display in Screen.AllScreens) + { + var label = display.Primary ? $"{display.DeviceName} (Primary)" : display.DeviceName; + NumWebViewScreen.Items.Add(label); + } + } + + NumWebViewScreen.SelectedIndex = Variables.AppSettings.TrayIconWebViewScreen; + SelectedScreen = Variables.AppSettings.TrayIconWebViewScreen; } private void CbDefaultMenu_CheckedChanged(object sender, EventArgs e) @@ -45,8 +74,7 @@ private void BtnShowWebViewPreview_Click(object sender, EventArgs e) IsTrayIconWebView = true, IsTrayIconPreview = true }; - - HelperFunctions.LaunchTrayIconWebView(webView); + HelperFunctions.LaunchTrayIconWebView(webView, NumWebViewScreen.SelectedIndex); } private void BtnWebViewReset_Click(object sender, EventArgs e) @@ -54,5 +82,10 @@ private void BtnWebViewReset_Click(object sender, EventArgs e) NumWebViewWidth.Value = 700; NumWebViewHeight.Value = 560; } + + private void domainUpDown1_SelectedItemChanged(object sender, EventArgs e) + { + SelectedScreen = ((ComboBoxAdv)sender).SelectedIndex; + } } } diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.de.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.de.resx index deb06fac..d216376e 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.de.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.de.resx @@ -1,14 +1,23 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Um zu erfahren, welche Entitäten Sie konfiguriert haben, und um schnelle Aktionen zu senden, verwendet HASS.Agent die API von Home Assistant. + +Bitte geben Sie einen langlebigen Zugriffstoken und die Adresse Ihrer Home Assistant-Instanz an. +Sie erhalten einen Token in Home Assistant, indem Sie unten links auf Ihr Profilbild klicken, oben auf den Sicherheits-Tab wechseln und zum unteren Seitenrand navigieren, bis die Schaltfläche „TOKEN ERSTELLEN“ angezeigt wird. + +Bitte beachten Sie, dass Sie für die Benachrichtigungsfunktion einen Administrator-Konto-Token angeben müssen. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.en.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.en.resx index deb06fac..bf2328a9 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.en.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.en.resx @@ -1,14 +1,26 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + To learn which entities you have configured and to send quick actions, HASS.Agent uses +Home Assistant's API. + +Please provide a long-lived access token and the address of your Home Assistant instance. +You can get a token in Home Assistant by clicking your profile picture at the bottom-left +switch to the security tab at the top and navigating to the bottom of the page until you +see the 'CREATE TOKEN' button. + +Please note, that for actionable notification functionality you need to provide admin account token. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.es.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.es.resx index deb06fac..617c2683 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.es.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.es.resx @@ -1,14 +1,23 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Para saber qué entidades has configurado y enviar acciones rápidas, HASS.Agent utiliza la API de Home Assistant. + +Proporciona un token de acceso de larga duración y la dirección de tu instancia de Home Assistant. +Puedes obtener un token en Home Assistant haciendo clic en tu foto de perfil en la esquina inferior izquierda, cambiando a la pestaña de seguridad en la parte superior y navegando hasta el final de la página hasta que veas el botón "CREAR TOKEN". + +Ten en cuenta que para la función de notificaciones procesables, debes proporcionar el token de la cuenta de administrador. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.fr.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.fr.resx index deb06fac..3417cf4d 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.fr.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.fr.resx @@ -1,14 +1,23 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pour connaître les entités que vous avez configurées et envoyer des actions rapides, HASS.Agent utilise l'API de Home Assistant. + +Veuillez fournir un jeton d'accès longue durée et l'adresse de votre instance Home Assistant. +Vous pouvez obtenir un jeton dans Home Assistant en cliquant sur votre photo de profil en bas à gauche, en passant à l'onglet Sécurité en haut et en naviguant vers le bas de la page jusqu'à ce que le bouton « CRÉER UN JETON » apparaisse. + +Veuillez noter que pour bénéficier de la fonctionnalité de notification, vous devez fournir un jeton de compte administrateur. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.nl.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.nl.resx index deb06fac..ad0df29e 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.nl.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.nl.resx @@ -1,14 +1,26 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Om te zien welke entiteiten je hebt geconfigureerd en om snelle acties te versturen, gebruikt HASS.Agent de API van +Home Assistant. + +Geef een toegangstoken met een lange levensduur en het adres van je Home Assistant-instantie op. +Je kunt een token in Home Assistant verkrijgen door linksonder op je profielfoto te klikken, +naar het tabblad Beveiliging bovenaan te gaan en naar de onderkant van de pagina te navigeren totdat je +de knop 'TOKEN AANMAKEN' ziet. + +Houd er rekening mee dat je voor actieve meldingen een beheerdersaccounttoken moet opgeven. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pl.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pl.resx index deb06fac..cecb80b2 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pl.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pl.resx @@ -1,14 +1,26 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Aby dowiedzieć się, które jednostki zostały skonfigurowane i wysłać szybkie akcje, HASS.Agent korzysta z +API Home Assistant. + +Podaj długoterminowy token dostępu i adres swojej instancji Home Assistant. +Możesz uzyskać token w Home Assistant, klikając swoje zdjęcie profilowe w lewym dolnym rogu, +przechodząc do zakładki bezpieczeństwa u góry i przechodząc na dół strony, aż zobaczysz +przycisk „UTWÓRZ TOKEN”. + +Pamiętaj, że aby korzystać z funkcji powiadomień z możliwością wykonania akcji, musisz podać token konta administratora. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pt-br.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pt-br.resx index deb06fac..d728b209 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pt-br.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.pt-br.resx @@ -1,14 +1,26 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Para saber quais as entidades que configurou e enviar ações rápidas, o HASS.Agent utiliza +a API do Home Assistant. + +Forneça um token de acesso de longa duração e o endereço da sua instância do Home Assistant. +Pode obter um token no Home Assistant clicando na sua fotografia de perfil no canto inferior esquerdo, +mude para o separador de segurança na parte superior e navegue até ao final da página até +ver o botão "CRIAR TOKEN". + +Note que, para a funcionalidade de notificação acionável, precisa de fornecer o token da conta de administrador. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.resx index f9ee09c4..d99bf07c 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.resx @@ -63,8 +63,9 @@ Home Assistant's API. Please provide a long-lived access token and the address of your Home Assistant instance. You can get a token in Home Assistant by clicking your profile picture at the bottom-left -and navigating to the bottom of the page until you see the 'CREATE TOKEN' button. +switch to the security tab at the top and navigating to the bottom of the page until you +see the 'CREATE TOKEN' button. Please note, that for actionable notification functionality you need to provide admin account token. - \ No newline at end of file + diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.ru.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.ru.resx index deb06fac..a163bd96 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.ru.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.ru.resx @@ -1,14 +1,25 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Чтобы узнать, какие сущности вы настроили, и отправлять быстрые действия, HASS.Agent использует +API Home Assistant. + +Укажите долгосрочный токен доступа и адрес вашего экземпляра Home Assistant. +Вы можете получить токен в Home Assistant, нажав на изображение своего профиля в левом нижнем углу, +переключившись на вкладку «Безопасность» в верхней части страницы и перейдя вниз, пока не увидите кнопку «СОЗДАТЬ ТОКЕН». + +Обратите внимание, что для работы функции уведомлений с действиями вам необходимо предоставить токен учетной записи администратора. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.sl.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.sl.resx index deb06fac..5d0df84d 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.sl.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.sl.resx @@ -1,14 +1,26 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Če želite izvedeti, katere entitete ste konfigurirali, in poslati hitra dejanja, HASS.Agent uporablja +API Home Assistant. + +Navedite žeton za dolgotrajen dostop in naslov vašega primerka Home Assistant. +Žeton v Home Assistant lahko dobite tako, da kliknete svojo profilno sliko spodaj levo, +preklopite na zavihek varnost na vrhu in se pomaknete na dno strani, dokler ne +zagledate gumba »USTVARITE ŽETON«. + +Upoštevajte, da morate za funkcionalnost obvestil, ki omogočajo ukrepanje, navesti žeton skrbniškega računa. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.tr.resx b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.tr.resx index deb06fac..795c1a10 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.tr.resx +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-3-API.tr.resx @@ -1,14 +1,23 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hangi varlıkları yapılandırdığınızı öğrenmek ve hızlı eylemler göndermek için HASS.Agent, Home Assistant API'sini kullanır. + +Lütfen uzun ömürlü bir erişim belirteci ve Home Assistant örneğinizin adresini sağlayın. +Home Assistant'ta, sol alt köşedeki profil resminize tıklayarak, üstteki güvenlik sekmesine geçerek ve 'JETON OLUŞTUR' düğmesini görene kadar sayfanın en altına giderek bir belirteç alabilirsiniz. + +Eylemsel bildirim işlevi için yönetici hesabı belirteci sağlamanız gerektiğini lütfen unutmayın. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.Designer.cs index 286871ea..fa3859e2 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.Designer.cs @@ -48,6 +48,8 @@ private void InitializeComponent() this.NumMqttPort = new Syncfusion.Windows.Forms.Tools.NumericUpDownExt(); this.PbShow = new System.Windows.Forms.PictureBox(); this.CbEnableMqtt = new System.Windows.Forms.CheckBox(); + this.CbUseWebSocket = new System.Windows.Forms.CheckBox(); + this.BtnTest = new Syncfusion.WinForms.Controls.SfButton(); ((System.ComponentModel.ISupportInitialize)(this.PbHassAgentLogo)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NumMqttPort)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.PbShow)).BeginInit(); @@ -126,6 +128,20 @@ private void InitializeComponent() this.CbMqttTls.UseVisualStyleBackColor = true; this.CbMqttTls.CheckedChanged += new System.EventHandler(this.CbMqttTls_CheckedChanged); // + // CbUseWebSocketFlag + // + this.CbUseWebSocket.AccessibleDescription = "Use WebSocket for MQTT connection instead of direct one."; + this.CbUseWebSocket.AccessibleName = "WebSocket flag"; + this.CbUseWebSocket.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton; + this.CbUseWebSocket.AutoSize = true; + this.CbUseWebSocket.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.CbUseWebSocket.Location = new System.Drawing.Point(371, 148); + this.CbUseWebSocket.Name = "CbUseWebSocketFlag"; + this.CbUseWebSocket.Size = new System.Drawing.Size(114, 23); + this.CbUseWebSocket.TabIndex = 104; + this.CbUseWebSocket.Text = Languages.ConfigMqtt_CbUseWebSocket; + this.CbUseWebSocket.UseVisualStyleBackColor = true; + // // LblPassword // this.LblPassword.AccessibleDescription = "Password textbox description."; @@ -306,6 +322,29 @@ private void InitializeComponent() this.CbEnableMqtt.Text = Languages.OnboardingMqtt_CbEnableMqtt; this.CbEnableMqtt.UseVisualStyleBackColor = true; // + // BtnTest + // + BtnTest.AccessibleDescription = "Perform a test connection with your MQTT instance."; + BtnTest.AccessibleName = "Test connection"; + BtnTest.AccessibleRole = AccessibleRole.PushButton; + BtnTest.BackColor = Color.FromArgb(63, 63, 70); + BtnTest.Font = new Font("Segoe UI", 10F, FontStyle.Regular, GraphicsUnit.Point); + BtnTest.ForeColor = Color.FromArgb(241, 241, 241); + BtnTest.Location = new Point(558, 349); + BtnTest.Name = "BtnTest"; + BtnTest.Size = new Size(180, 23); + BtnTest.Style.BackColor = Color.FromArgb(63, 63, 70); + BtnTest.Style.FocusedBackColor = Color.FromArgb(63, 63, 70); + BtnTest.Style.FocusedForeColor = Color.FromArgb(241, 241, 241); + BtnTest.Style.ForeColor = Color.FromArgb(241, 241, 241); + BtnTest.Style.HoverBackColor = Color.FromArgb(63, 63, 70); + BtnTest.Style.HoverForeColor = Color.FromArgb(241, 241, 241); + BtnTest.Style.PressedForeColor = Color.Black; + BtnTest.TabIndex = 103; + BtnTest.Text = Languages.OnboardingApi_BtnTest; + BtnTest.UseVisualStyleBackColor = false; + BtnTest.Click += BtnTest_Click; + // // OnboardingMqtt // this.AccessibleDescription = "Panel containing the onboarding MQTT configuration."; @@ -325,12 +364,14 @@ private void InitializeComponent() this.Controls.Add(this.TbMqttUsername); this.Controls.Add(this.TbMqttAddress); this.Controls.Add(this.CbMqttTls); + this.Controls.Add(this.CbUseWebSocket); this.Controls.Add(this.LblPassword); this.Controls.Add(this.LblUsername); this.Controls.Add(this.LblPort); this.Controls.Add(this.LblIpAdress); this.Controls.Add(this.LblInfo1); this.Controls.Add(this.PbHassAgentLogo); + this.Controls.Add(this.BtnTest); this.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241))))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "OnboardingMqtt"; @@ -350,6 +391,7 @@ private void InitializeComponent() private System.Windows.Forms.TextBox TbMqttUsername; private System.Windows.Forms.TextBox TbMqttAddress; private System.Windows.Forms.CheckBox CbMqttTls; + private System.Windows.Forms.CheckBox CbUseWebSocket; private System.Windows.Forms.Label LblPassword; private System.Windows.Forms.Label LblUsername; private System.Windows.Forms.Label LblPort; @@ -359,7 +401,8 @@ private void InitializeComponent() private System.Windows.Forms.Label LblDiscoveryPrefix; private System.Windows.Forms.Label LblTip1; private System.Windows.Forms.Label LblTip2; - internal Syncfusion.Windows.Forms.Tools.NumericUpDownExt NumMqttPort; + private Syncfusion.WinForms.Controls.SfButton BtnTest; + private Syncfusion.Windows.Forms.Tools.NumericUpDownExt NumMqttPort; private PictureBox PbShow; internal CheckBox CbEnableMqtt; } diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.cs b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.cs index a2dd465b..849c7bc0 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-4-MQTT.cs @@ -1,4 +1,10 @@ -using Serilog; +using System.Windows.Forms; +using HASS.Agent.HomeAssistant; +using HASS.Agent.MQTT; +using HASS.Agent.Resources.Localization; +using HASS.Agent.Shared.Functions; +using Serilog; +using Syncfusion.Windows.Forms; namespace HASS.Agent.Controls.Onboarding { @@ -27,12 +33,18 @@ private void OnboardingMqtt_Load(object sender, EventArgs e) Log.Error("[MQTT] Unable to parse URI {uri}: {msg}", Variables.AppSettings.HassUri, ex.Message); } } - + // if the above process failed somewhere, just enter the entire address (if any) - if (string.IsNullOrEmpty(TbMqttAddress.Text)) TbMqttAddress.Text = Variables.AppSettings.MqttAddress; + if (string.IsNullOrEmpty(TbMqttAddress.Text)) + { + TbMqttAddress.Text = Variables.AppSettings.MqttAddress; + } // optionally set default port - if (Variables.AppSettings.MqttPort < 1) Variables.AppSettings.MqttPort = 1883; + if (Variables.AppSettings.MqttPort < 1) + { + Variables.AppSettings.MqttPort = 1883; + } NumMqttPort.Value = Variables.AppSettings.MqttPort; CbMqttTls.Checked = Variables.AppSettings.MqttUseTls; @@ -55,15 +67,62 @@ internal bool Store() Variables.AppSettings.MqttPassword = TbMqttPassword.Text; Variables.AppSettings.MqttDiscoveryPrefix = TbMqttDiscoveryPrefix.Text; Variables.AppSettings.MqttEnabled = CbEnableMqtt.CheckState == CheckState.Checked; + return true; } private void CbMqttTls_CheckedChanged(object sender, EventArgs e) { - if (_initializing) return; + if (_initializing) + { + return; + } + NumMqttPort.Value = CbMqttTls.Checked ? 8883 : 1883; } private void PbShow_Click(object sender, EventArgs e) => TbMqttPassword.UseSystemPasswordChar = !TbMqttPassword.UseSystemPasswordChar; + + private async void BtnTest_Click(object sender, EventArgs e) + { + var address = TbMqttAddress.Text.Trim(); + var port = (int)NumMqttPort.Value; + var username = TbMqttUsername.Text.Trim(); + var password = TbMqttPassword.Text.Trim(); + var useTls = CbMqttTls.Checked; + var useWebSocket = CbUseWebSocket.Checked; + + CbEnableMqtt.Enabled = false; + CbUseWebSocket.Enabled = false; + CbMqttTls.Enabled = false; + TbMqttAddress.Enabled = false; + NumMqttPort.Enabled = false; + TbMqttUsername.Enabled = false; + TbMqttPassword.Enabled = false; + TbMqttDiscoveryPrefix.Enabled = false; + BtnTest.Enabled = false; + BtnTest.Text = Languages.OnboardingApi_BtnTest_Testing; + + var result = await MqttManager.TestConnection(address, port, useTls, useWebSocket, username, password); + if (!result) + { + MessageBoxAdv.Show(this, Languages.OnboardingMqtt_BtnTest_MessageError, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + else + { + MessageBoxAdv.Show(this, Languages.OnboardingMqtt_BtnTest_MessageOk, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + CbEnableMqtt.Enabled = true; + CbUseWebSocket.Enabled = true; + CbMqttTls.Enabled = true; + TbMqttAddress.Enabled = true; + NumMqttPort.Enabled = true; + TbMqttUsername.Enabled = true; + TbMqttPassword.Enabled = true; + TbMqttDiscoveryPrefix.Enabled = true; + BtnTest.Enabled = true; + BtnTest.Text = Languages.OnboardingApi_BtnTest; + } } } diff --git a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-6-HotKey.cs b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-6-HotKey.cs index 2d1e6d25..a8c556f7 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-6-HotKey.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Onboarding/Onboarding-6-HotKey.cs @@ -1,12 +1,13 @@ using HASS.Agent.Functions; -using WK.Libraries.HotkeyListenerNS; +using System.Windows.Forms; namespace HASS.Agent.Controls.Onboarding { public partial class OnboardingHotKey : UserControl { - private readonly HotkeySelector _hotkeySelector = new(); - + private Keys _key = Keys.None; + private Keys _modifiers = Keys.None; + public OnboardingHotKey() { InitializeComponent(); @@ -14,41 +15,51 @@ public OnboardingHotKey() private void OnboardingHotKey_Load(object sender, EventArgs e) { - // config quick actions hotkey selector - _hotkeySelector.Enable(TbQuickActionsHotkey); - - // if nothing set, load default - if (string.IsNullOrEmpty(Variables.AppSettings.QuickActionsHotKey)) LoadDefault(); - // if set to empty, show empty - else if (Variables.AppSettings.QuickActionsHotKey == _hotkeySelector.EmptyHotkeyText) TbQuickActionsHotkey.Text = _hotkeySelector.EmptyHotkeyText; - // show set value - else LoadSetValue(); - } + TbQuickActionsHotkey.ReadOnly = true; + TbQuickActionsHotkey.KeyDown += TbQuickActionsHotkey_KeyDown; - private void LoadDefault() - { - if (!HelperFunctions.InputLanguageCheckDiffers(out var knownToCollide, out var warning)) + if (string.IsNullOrEmpty(Variables.AppSettings.QuickActionsHotKey)) { - TbQuickActionsHotkey.Text = "Control, Alt + Q"; - LblLanguageWarning.Visible = false; - return; + // if nothing set, load default + LoadDefault(); } - - if (knownToCollide) + else if (Variables.AppSettings.QuickActionsHotKey == string.Empty) { - // the system's input language collides with our hotkey, let the user know and set empty key - LblLanguageWarning.Text = warning; - TbQuickActionsHotkey.Text = _hotkeySelector.EmptyHotkeyText; - return; + // if set to empty, show empty + TbQuickActionsHotkey.Text = string.Empty; + } + else + { + // show set value + LoadSetValue(); } + } + + private void LoadDefault() + { + /* if (!HelperFunctions.InputLanguageCheckDiffers(out var knownToCollide, out var warning)) + { + TbQuickActionsHotkey.Text = "Shift, Control + Q"; + LblLanguageWarning.Visible = false; + return; + } + + if (knownToCollide) + { + // the system's input language collides with our hotkey, let the user know and set empty key + LblLanguageWarning.Text = warning; + TbQuickActionsHotkey.Text = _hotkeySelector.EmptyHotkeyText; + return; + }*/ + //Amadeo(Note): above was commented out when we changed default hotkey to ctrl+shift+q, leaving this here because reasons // the system's input language is unknown, we're presetting the default but warn the user // deprecated, we're not doing this anymore - //TbQuickActionsHotkey.Text = "Control, Alt + Q"; + //TbQuickActionsHotkey.Text = "Shift, Control + Q"; //LblLanguageWarning.ForeColor = Color.DarkOrange; //LblLanguageWarning.Text = warning; - TbQuickActionsHotkey.Text = "Control, Alt + Q"; + TbQuickActionsHotkey.Text = "Shift, Control + Q"; LblLanguageWarning.Visible = false; } @@ -56,24 +67,75 @@ private void LoadSetValue() { TbQuickActionsHotkey.Text = Variables.AppSettings.QuickActionsHotKey; - if (!HelperFunctions.InputLanguageCheckDiffers(out var knownToCollide, out var warning)) return; + if (!HelperFunctions.InputLanguageCheckDiffers(out var knownToCollide, out var warning)) + return; // the system's input language is unknown or collides with our hotkey, let the user know if it's set to default - if (Variables.AppSettings.QuickActionsHotKey != "Control, Alt + Q") return; + if (Variables.AppSettings.QuickActionsHotKey != "Shift, Control + Q") + return; - if (knownToCollide) LblLanguageWarning.Text = warning; + if (knownToCollide) + LblLanguageWarning.Text = warning; } internal bool Store() { Variables.AppSettings.QuickActionsHotKey = TbQuickActionsHotkey.Text; - _hotkeySelector.Dispose(); + TbQuickActionsHotkey.KeyDown -= TbQuickActionsHotkey_KeyDown; return true; } private void BtnClear_Click(object sender, EventArgs e) { - TbQuickActionsHotkey.Text = _hotkeySelector.EmptyHotkeyText; + TbQuickActionsHotkey.Text = string.Empty; + } + + private void TbQuickActionsHotkey_KeyDown(object sender, KeyEventArgs e) + { + e.SuppressKeyPress = true; + + var key = e.KeyCode; + + if (key is Keys.LControlKey or Keys.RControlKey + or Keys.LShiftKey or Keys.RShiftKey + or Keys.LWin or Keys.RWin + or Keys.Alt) + { + key = Keys.None; + } + + if (key == Keys.Escape) + { + _key = Keys.None; + _modifiers = Keys.None; + TbQuickActionsHotkey.Text = string.Empty; + + return; + } + + _key = key; + TbQuickActionsHotkey.Text = FormatHotkey(_key, e.Modifiers); + } + + private string FormatHotkey(Keys key, Keys modifiers) + { + var parts = new List(); + if ((modifiers & Keys.Shift) != 0) + { + parts.Add(nameof(Keys.Shift)); + } + + if ((modifiers & Keys.Control) != 0) + { + parts.Add(nameof(Keys.Control)); + } + + if ((modifiers & Keys.Alt) != 0) + { + parts.Add(nameof(Keys.Alt)); + } + + return parts.Count > 0 ? string.Join(", ", parts) + " + " + key : key.ToString(); } } } diff --git a/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMQTT.Designer.cs b/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMQTT.Designer.cs index f69435c0..554f8d96 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMQTT.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMQTT.Designer.cs @@ -40,6 +40,7 @@ private void InitializeComponent() this.TbMqttRootCertificate = new System.Windows.Forms.TextBox(); this.LblRootCert = new System.Windows.Forms.Label(); this.CbUseRetainFlag = new System.Windows.Forms.CheckBox(); + this.CbUseWebSocket = new System.Windows.Forms.CheckBox(); this.CbAllowUntrustedCertificates = new System.Windows.Forms.CheckBox(); this.BtnMqttClearConfig = new Syncfusion.WinForms.Controls.SfButton(); this.LblTip1 = new System.Windows.Forms.Label(); @@ -206,7 +207,7 @@ private void InitializeComponent() // // CbUseRetainFlag // - this.CbUseRetainFlag.AccessibleDescription = "Enable using the retain flag for messages."; + this.CbUseRetainFlag.AccessibleDescription = "Use WebSocket for MQTT connection instead of direct one."; this.CbUseRetainFlag.AccessibleName = "Retain flag"; this.CbUseRetainFlag.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton; this.CbUseRetainFlag.AutoSize = true; @@ -218,6 +219,20 @@ private void InitializeComponent() this.CbUseRetainFlag.Text = global::HASS.Agent.Resources.Localization.Languages.ServiceMqtt_CbUseRetainFlag; this.CbUseRetainFlag.UseVisualStyleBackColor = true; // + // CbUseWebSocket + // + this.CbUseWebSocket.AccessibleDescription = "Enable using the retain flag for messages."; + this.CbUseWebSocket.AccessibleName = "WebSocket flag"; + this.CbUseWebSocket.AccessibleRole = System.Windows.Forms.AccessibleRole.CheckButton; + this.CbUseWebSocket.AutoSize = true; + this.CbUseWebSocket.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.CbUseWebSocket.Location = new System.Drawing.Point(506, 393); + this.CbUseWebSocket.Name = "CbUseWebSocket"; + this.CbUseWebSocket.Size = new System.Drawing.Size(114, 23); + this.CbUseWebSocket.TabIndex = 14; + this.CbUseWebSocket.Text = global::HASS.Agent.Resources.Localization.Languages.ServiceMqtt_CbUseWebSocket; + this.CbUseWebSocket.UseVisualStyleBackColor = true; + // // CbAllowUntrustedCertificates // this.CbAllowUntrustedCertificates.AccessibleDescription = "Enable allowing untrusted certificates when connecting."; @@ -546,6 +561,7 @@ private void InitializeComponent() this.Controls.Add(this.TbMqttRootCertificate); this.Controls.Add(this.LblRootCert); this.Controls.Add(this.CbUseRetainFlag); + this.Controls.Add(this.CbUseWebSocket); this.Controls.Add(this.CbAllowUntrustedCertificates); this.Controls.Add(this.BtnMqttClearConfig); this.Controls.Add(this.LblTip1); @@ -584,6 +600,7 @@ private void InitializeComponent() internal TextBox TbMqttRootCertificate; private Label LblRootCert; internal CheckBox CbUseRetainFlag; + internal CheckBox CbUseWebSocket; internal CheckBox CbAllowUntrustedCertificates; internal Syncfusion.WinForms.Controls.SfButton BtnMqttClearConfig; private Label LblTip1; diff --git a/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMqtt.cs b/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMqtt.cs index 5b034dc7..af677bec 100644 --- a/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMqtt.cs +++ b/src/HASS.Agent/HASS.Agent/Controls/Service/ServiceMqtt.cs @@ -125,6 +125,7 @@ public void SetConfig(ServiceMqttSettings mqttSettings) TbMqttClientCertificate.Text = mqttSettings.MqttClientCertificate; CbAllowUntrustedCertificates.Checked = mqttSettings.MqttAllowUntrustedCertificates; CbUseRetainFlag.Checked = mqttSettings.MqttUseRetainFlag; + CbUseWebSocket.Checked = mqttSettings.MqttUseWebSocket; } /// @@ -145,6 +146,7 @@ private void BtnCopy_Click(object sender, EventArgs e) TbMqttClientCertificate.Text = Variables.AppSettings.MqttClientCertificate; CbAllowUntrustedCertificates.Checked = Variables.AppSettings.MqttAllowUntrustedCertificates; CbUseRetainFlag.Checked = Variables.AppSettings.MqttUseRetainFlag; + CbUseWebSocket.Checked = Variables.AppSettings.MqttUseWebSocket; } /// @@ -165,6 +167,7 @@ private void BtnMqttClearConfig_Click(object sender, EventArgs e) TbMqttClientCertificate.Text = string.Empty; CbAllowUntrustedCertificates.Checked = true; CbUseRetainFlag.Checked = true; + CbUseWebSocket.Checked = false; } private async void BtnStore_Click(object sender, EventArgs e) @@ -188,7 +191,8 @@ private async void BtnStore_Click(object sender, EventArgs e) MqttRootCertificate = TbMqttRootCertificate.Text, MqttClientCertificate = TbMqttClientCertificate.Text, MqttAllowUntrustedCertificates = CbAllowUntrustedCertificates.Checked, - MqttUseRetainFlag = CbUseRetainFlag.Checked + MqttUseRetainFlag = CbUseRetainFlag.Checked, + MqttUseWebSocket = CbUseWebSocket.Checked }; // store diff --git a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs index d36c4879..caab3545 100644 --- a/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs +++ b/src/HASS.Agent/HASS.Agent/Extensions/RpcExtensions.cs @@ -183,7 +183,8 @@ public static ServiceMqttSettings ConvertToServiceMqttSettings(this RpcServiceMq MqttUseRetainFlag = rpcServiceMqttSettings.MqttUseRetainFlag, MqttRootCertificate = rpcServiceMqttSettings.MqttRootCertificate, MqttClientCertificate = rpcServiceMqttSettings.MqttClientCertificate, - MqttClientId = rpcServiceMqttSettings.MqttClientId + MqttClientId = rpcServiceMqttSettings.MqttClientId, + MqttUseWebSocket = rpcServiceMqttSettings.MqttUseWebSocket }; return serviceMqttSettings; @@ -208,7 +209,8 @@ public static RpcServiceMqttSettings ConvertToRpcServiceMqttSettings(this Servic MqttUseRetainFlag = serviceMqttSettings.MqttUseRetainFlag, MqttRootCertificate = serviceMqttSettings.MqttRootCertificate, MqttClientCertificate = serviceMqttSettings.MqttClientCertificate, - MqttClientId = serviceMqttSettings.MqttClientId + MqttClientId = serviceMqttSettings.MqttClientId, + MqttUseWebSocket = serviceMqttSettings.MqttUseWebSocket }; return rpcServiceMqttSettings; diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandConfig/WebViewCommandConfig.Designer.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandConfig/WebViewCommandConfig.Designer.cs index 718b80ee..cba67b64 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandConfig/WebViewCommandConfig.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandConfig/WebViewCommandConfig.Designer.cs @@ -243,11 +243,8 @@ private void InitializeComponent() this.NumLocationX.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.NumLocationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241))))); this.NumLocationX.Location = new System.Drawing.Point(34, 160); - this.NumLocationX.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); + this.NumLocationX.Maximum = new decimal(Int32.MaxValue); + this.NumLocationX.Minimum = new decimal(Int32.MinValue); this.NumLocationX.MaxLength = 10; this.NumLocationX.MetroColor = System.Drawing.SystemColors.WindowFrame; this.NumLocationX.Name = "NumLocationX"; @@ -269,11 +266,8 @@ private void InitializeComponent() this.NumLocationY.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.NumLocationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(241)))), ((int)(((byte)(241))))); this.NumLocationY.Location = new System.Drawing.Point(149, 160); - this.NumLocationY.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); + this.NumLocationY.Maximum = new decimal(Int32.MaxValue); + this.NumLocationY.Minimum = new decimal(Int32.MinValue); this.NumLocationY.MaxLength = 10; this.NumLocationY.MetroColor = System.Drawing.SystemColors.WindowFrame; this.NumLocationY.Name = "NumLocationY"; diff --git a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs index a14d80bc..de248bda 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/Commands/CommandsMod.cs @@ -28,7 +28,9 @@ public partial class CommandsMod : MetroForm private bool _interfaceLockedWrongType; private bool _loading = true; - private readonly Dictionary _commandEntityTypes = new(); + private bool _escapeLock = true; + + private readonly Dictionary _commandEntityTypes = new(); private readonly Dictionary _radioDevices = new(); public CommandsMod(ConfiguredCommand command, bool serviceMode = false, string serviceDeviceName = "") @@ -977,11 +979,21 @@ private void LblIntegrityInfo_Click(object sender, EventArgs e) private void CommandsMod_KeyUp(object sender, KeyEventArgs e) { - if (e.KeyCode != Keys.Escape) - return; + if (e.KeyCode != Keys.Escape) + { + _escapeLock = true; + return; + } - Close(); - } + if (_escapeLock) + { + _escapeLock = false; + } + else + { + Close(); + } + } private void CommandsMod_Layout(object sender, LayoutEventArgs e) { diff --git a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs index 2f83e22a..cf1f6baa 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/Configuration.cs @@ -8,16 +8,15 @@ using HASS.Agent.Settings; using HASS.Agent.Shared; using HASS.Agent.Shared.Functions; -using WK.Libraries.HotkeyListenerNS; using Task = System.Threading.Tasks.Task; using ConfigSatelliteService = HASS.Agent.Controls.Configuration.ConfigService; +using HASS.Agent.MQTT; namespace HASS.Agent.Forms { public partial class Configuration : MetroForm { - private readonly HotkeySelector _hotkeySelector = new(); - private readonly Hotkey _previousHotkey = Variables.QuickActionsHotKey; + private readonly string _previousHotkey = Variables.QuickActionsHotKey; private readonly string _previousDeviceName = Variables.AppSettings.DeviceName; private readonly int _previousLocalApiPort = Variables.AppSettings.LocalApiPort; @@ -40,6 +39,9 @@ public partial class Configuration : MetroForm private bool _initializing = true; + private Keys _key = Keys.None; + private Keys _modifiers = Keys.None; + public Configuration() { InitializeComponent(); @@ -51,7 +53,7 @@ private void Configuration_Load(object sender, EventArgs e) KeyPreview = true; // suspend global hotkeys - Variables.HotKeyListener.Suspend(); + Variables.HotKeyListener.IsEnabled = false; // load controls TabGeneral.Controls.Add(_general); @@ -78,18 +80,21 @@ private void Configuration_Load(object sender, EventArgs e) LoadSettings(); // config quick actions hotkey selector - if (Variables.QuickActionsHotKey != null) _hotkeySelector.Enable(_hotKey.TbQuickActionsHotkey, Variables.QuickActionsHotKey); - else _hotkeySelector.Enable(_hotKey.TbQuickActionsHotkey); + _hotKey.TbQuickActionsHotkey.ReadOnly = true; + _hotKey.TbQuickActionsHotkey.KeyDown += TbQuickActionsHotkey_KeyDown; + if (Variables.QuickActionsHotKey != null) + { + _hotKey.TbQuickActionsHotkey.Text = Variables.QuickActionsHotKey; + } } private void Configuration_FormClosing(object sender, FormClosingEventArgs e) { // resume global hotkeys - Variables.HotKeyListener.Resume(); + Variables.HotKeyListener.IsEnabled = true; - // remove hotkey selector - _hotkeySelector?.Disable(_hotKey.TbQuickActionsHotkey); - _hotkeySelector?.Dispose(); + // clean hotkey selector + _hotKey.TbQuickActionsHotkey.KeyDown -= TbQuickActionsHotkey_KeyDown; // dispose controls _general.Dispose(); @@ -109,14 +114,62 @@ private void Configuration_FormClosing(object sender, FormClosingEventArgs e) _nfc.Dispose(); } + private void TbQuickActionsHotkey_KeyDown(object sender, KeyEventArgs e) + { + e.SuppressKeyPress = true; + + var key = e.KeyCode; + + if (key is Keys.LControlKey or Keys.RControlKey + or Keys.LShiftKey or Keys.RShiftKey + or Keys.LWin or Keys.RWin + or Keys.Alt) + { + key = Keys.None; + } + + if (key == Keys.Escape) + { + _key = Keys.None; + _modifiers = Keys.None; + _hotKey.TbQuickActionsHotkey.Text = string.Empty; + + return; + } + + _key = key; + _hotKey.TbQuickActionsHotkey.Text = FormatHotkey(_key, e.Modifiers); + } + + private string FormatHotkey(Keys key, Keys modifiers) + { + var parts = new List(); + if ((modifiers & Keys.Shift) != 0) + { + parts.Add(nameof(Keys.Shift)); + } + + if ((modifiers & Keys.Control) != 0) + { + parts.Add(nameof(Keys.Control)); + } + + if ((modifiers & Keys.Alt) != 0) + { + parts.Add(nameof(Keys.Alt)); + } + + return parts.Count > 0 ? string.Join(", ", parts) + " + " + key : key.ToString(); + } + private void BindEvents() { // hass _homeAssistantApi.CbHassAutoClientCertificate.CheckedChanged += CbHassAutoClientCertificate_CheckedChanged; - + // mqtt _mqtt.CbMqttTls.CheckedChanged += CbMqttTls_CheckedChanged; - + // hotkey _hotKey.BtnClearHotKey.Click += BtnClearHotKey_Click; } @@ -147,7 +200,8 @@ private async void ProcessChanges() BtnStore.Text = Languages.Configuration_BtnStore_Busy; // optionally sanitize device name - if (_general.CbEnableDeviceNameSanitation.Checked) _general.TbDeviceName.Text = SharedHelperFunctions.GetSafeValue(_general.TbDeviceName.Text); + if (_general.CbEnableDeviceNameSanitation.Checked) + _general.TbDeviceName.Text = SharedHelperFunctions.GetSafeValue(_general.TbDeviceName.Text); // store settings await StoreSettingsAsync(); @@ -171,7 +225,21 @@ private async void ProcessChanges() // give the managers some time to stop await Task.Delay(250); - // unpublish all entities + // signal migration to HA MQTT integration + await SensorsManager.UnpublishAllSensors(migration: true); + await CommandsManager.UnpublishAllCommands(migration: true); + + await Task.Delay(250); + + // mock new device name and publish new discovery messages + Variables.DeviceConfig.Name = Variables.AppSettings.DeviceName; + await SensorsManager.ForcePublishAllSensors(); + await CommandsManager.ForcePublishAllCommands(); + + await Task.Delay(250); + + // restore previous device name and clear migration messages + Variables.DeviceConfig.Name = _previousDeviceName; await SensorsManager.UnpublishAllSensors(); await CommandsManager.UnpublishAllCommands(); @@ -183,28 +251,32 @@ private async void ProcessChanges() // disconnect mqtt so we don't get announced again await Task.Run(Variables.MqttManager.Disconnect); - + forceRestart = true; } // reserve the new local api's port if it's changed if (Variables.AppSettings.LocalApiPort != _previousLocalApiPort) { - MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox2, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox2, Variables.MessageBoxTitle, + MessageBoxButtons.OK, MessageBoxIcon.Information); // try to reserve elevated if (!ApiManager.ExecuteElevatedPortReservation()) { // failed, copy the command onto the clipboard - Clipboard.SetText($"netsh http add urlacl url=http://+:{Variables.AppSettings.LocalApiPort}/ user=\"{SharedHelperFunctions.EveryoneLocalizedAccountName()}\""); + Clipboard.SetText( + $"netsh http add urlacl url=http://+:{Variables.AppSettings.LocalApiPort}/ user=\"{SharedHelperFunctions.EveryoneLocalizedAccountName()}\""); // notify the user - MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox3, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox3, + Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); } else { // notify the user - MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox4, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox4, + Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); // we need to restart, so go ahead, otherwise it's starting to look like popup-spam .. forceRestart = true; @@ -215,17 +287,22 @@ private async void ProcessChanges() { // prepare the restart without asking var restartPrepared = HelperFunctions.Restart(); - if (!restartPrepared) MessageBoxAdv.Show(this, Languages.Configuration_MessageBox_RestartManually, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + if (!restartPrepared) + MessageBoxAdv.Show(this, Languages.Configuration_MessageBox_RestartManually, + Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); } else { // ask the user if they want to restart - var question = MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox5, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + var question = MessageBoxAdv.Show(this, Languages.Configuration_ProcessChanges_MessageBox5, + Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (question == DialogResult.Yes) { // prepare the restart var restartPrepared = HelperFunctions.Restart(); - if (!restartPrepared) MessageBoxAdv.Show(this, Languages.Configuration_MessageBox_RestartManually, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + if (!restartPrepared) + MessageBoxAdv.Show(this, Languages.Configuration_MessageBox_RestartManually, + Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); } } @@ -245,7 +322,8 @@ private bool CheckValues() { if (!SharedHelperFunctions.CheckHomeAssistantApiToken(hassApi)) { - var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); + var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox1, + Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (q != DialogResult.Yes) return false; } } @@ -256,7 +334,8 @@ private bool CheckValues() { if (!SharedHelperFunctions.CheckHomeAssistantUri(hassUri)) { - var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox2, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); + var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox2, + Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (q != DialogResult.Yes) return false; } } @@ -267,7 +346,8 @@ private bool CheckValues() { if (!SharedHelperFunctions.CheckMqttBrokerUri(mqttUri)) { - var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox3, Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); + var q = MessageBoxAdv.Show(this, Languages.Configuration_CheckValues_MessageBox3, + Variables.MessageBoxTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (q != DialogResult.Yes) return false; } } @@ -283,28 +363,42 @@ private void LoadSettings() { // general _general.TbDeviceName.Text = Variables.AppSettings.DeviceName; - _general.CbEnableDeviceNameSanitation.CheckState = Variables.AppSettings.SanitizeName ? CheckState.Checked : CheckState.Unchecked; + _general.CbEnableDeviceNameSanitation.CheckState = + Variables.AppSettings.SanitizeName ? CheckState.Checked : CheckState.Unchecked; _general.NumDisconnectGrace.Value = Variables.AppSettings.DisconnectedGracePeriodSeconds; - _general.CbEnableStateNotifications.CheckState = Variables.AppSettings.EnableStateNotifications ? CheckState.Checked : CheckState.Unchecked; + _general.CbEnableStateNotifications.CheckState = Variables.AppSettings.EnableStateNotifications + ? CheckState.Checked + : CheckState.Unchecked; // startup settings Task.Run(_startup.DetermineStartOnLoginStatus); // local api _localApi.NumLocalApiPort.Value = Variables.AppSettings.LocalApiPort; - _localApi.CbLocalApiActive.CheckState = Variables.AppSettings.LocalApiEnabled ? CheckState.Checked : CheckState.Unchecked; + _localApi.CbLocalApiActive.CheckState = + Variables.AppSettings.LocalApiEnabled ? CheckState.Checked : CheckState.Unchecked; // notifications - _notifications.CbAcceptNotifications.CheckState = Variables.AppSettings.NotificationsEnabled ? CheckState.Checked : CheckState.Unchecked; - _notifications.CbNotificationsIgnoreImageCertErrors.CheckState = Variables.AppSettings.NotificationsIgnoreImageCertificateErrors ? CheckState.Checked : CheckState.Unchecked; - _notifications.CbNotificationsOpenActionUri.CheckState = Variables.AppSettings.NotificationsOpenActionUri ? CheckState.Checked : CheckState.Unchecked; + _notifications.CbAcceptNotifications.CheckState = Variables.AppSettings.NotificationsEnabled + ? CheckState.Checked + : CheckState.Unchecked; + _notifications.CbNotificationsIgnoreImageCertErrors.CheckState = + Variables.AppSettings.NotificationsIgnoreImageCertificateErrors + ? CheckState.Checked + : CheckState.Unchecked; + _notifications.CbNotificationsOpenActionUri.CheckState = Variables.AppSettings.NotificationsOpenActionUri + ? CheckState.Checked + : CheckState.Unchecked; // hass settings _homeAssistantApi.TbHassIp.Text = Variables.AppSettings.HassUri; _homeAssistantApi.TbHassApiToken.Text = Variables.AppSettings.HassToken; _homeAssistantApi.TbHassClientCertificate.Text = Variables.AppSettings.HassClientCertificate; - _homeAssistantApi.CbHassAutoClientCertificate.CheckState = Variables.AppSettings.HassAutoClientCertificate ? CheckState.Checked : CheckState.Unchecked; - _homeAssistantApi.CbHassAllowUntrustedCertificates.CheckState = Variables.AppSettings.HassAllowUntrustedCertificates ? CheckState.Checked : CheckState.Unchecked; + _homeAssistantApi.CbHassAutoClientCertificate.CheckState = Variables.AppSettings.HassAutoClientCertificate + ? CheckState.Checked + : CheckState.Unchecked; + _homeAssistantApi.CbHassAllowUntrustedCertificates.CheckState = + Variables.AppSettings.HassAllowUntrustedCertificates ? CheckState.Checked : CheckState.Unchecked; if (Variables.AppSettings.HassAutoClientCertificate) { _homeAssistantApi.TbHassClientCertificate.Text = string.Empty; @@ -312,10 +406,13 @@ private void LoadSettings() } // hotkey - _hotKey.CbEnableQuickActionsHotkey.CheckState = Variables.AppSettings.QuickActionsHotKeyEnabled ? CheckState.Checked : CheckState.Unchecked; + _hotKey.CbEnableQuickActionsHotkey.CheckState = Variables.AppSettings.QuickActionsHotKeyEnabled + ? CheckState.Checked + : CheckState.Unchecked; // mqtt - _mqtt.CbEnableMqtt.CheckState = Variables.AppSettings.MqttEnabled ? CheckState.Checked : CheckState.Unchecked; + _mqtt.CbEnableMqtt.CheckState = + Variables.AppSettings.MqttEnabled ? CheckState.Checked : CheckState.Unchecked; _mqtt.TbMqttAddress.Text = Variables.AppSettings.MqttAddress; _mqtt.NumMqttPort.Value = Variables.AppSettings.MqttPort; _mqtt.CbMqttTls.CheckState = Variables.AppSettings.MqttUseTls ? CheckState.Checked : CheckState.Unchecked; @@ -325,14 +422,25 @@ private void LoadSettings() _mqtt.TbMqttClientId.Text = Variables.AppSettings.MqttClientId; _mqtt.TbMqttRootCertificate.Text = Variables.AppSettings.MqttRootCertificate; _mqtt.TbMqttClientCertificate.Text = Variables.AppSettings.MqttClientCertificate; - _mqtt.CbAllowUntrustedCertificates.CheckState = Variables.AppSettings.MqttAllowUntrustedCertificates ? CheckState.Checked : CheckState.Unchecked; - _mqtt.CbUseRetainFlag.CheckState = Variables.AppSettings.MqttUseRetainFlag ? CheckState.Checked : CheckState.Unchecked; - _mqtt.CbIgnoreGracePeriod.CheckState = Variables.AppSettings.MqttIgnoreGracePeriod ? CheckState.Checked : CheckState.Unchecked; + _mqtt.CbAllowUntrustedCertificates.CheckState = Variables.AppSettings.MqttAllowUntrustedCertificates + ? CheckState.Checked + : CheckState.Unchecked; + _mqtt.CbUseRetainFlag.CheckState = + Variables.AppSettings.MqttUseRetainFlag ? CheckState.Checked : CheckState.Unchecked; + _mqtt.CbUseWebSocket.CheckState = + Variables.AppSettings.MqttUseWebSocket ? CheckState.Checked : CheckState.Unchecked; + _mqtt.CbIgnoreGracePeriod.CheckState = Variables.AppSettings.MqttIgnoreGracePeriod + ? CheckState.Checked + : CheckState.Unchecked; // updates - _updates.CbUpdates.CheckState = Variables.AppSettings.CheckForUpdates ? CheckState.Checked : CheckState.Unchecked; - _updates.CbBetaUpdates.CheckState = Variables.AppSettings.ShowBetaUpdates ? CheckState.Checked : CheckState.Unchecked; - _updates.CbExecuteUpdater.CheckState = Variables.AppSettings.EnableExecuteUpdateInstaller ? CheckState.Checked : CheckState.Unchecked; + _updates.CbUpdates.CheckState = + Variables.AppSettings.CheckForUpdates ? CheckState.Checked : CheckState.Unchecked; + _updates.CbBetaUpdates.CheckState = + Variables.AppSettings.ShowBetaUpdates ? CheckState.Checked : CheckState.Unchecked; + _updates.CbExecuteUpdater.CheckState = Variables.AppSettings.EnableExecuteUpdateInstaller + ? CheckState.Checked + : CheckState.Unchecked; // cache _localStorage.TbImageCacheLocation.Text = Variables.ImageCachePath; @@ -343,7 +451,9 @@ private void LoadSettings() _localStorage.NumWebViewRetention.Value = Variables.AppSettings.WebViewCacheRetentionDays; // logging - _logging.CbExtendedLogging.CheckState = SettingsManager.GetExtendedLoggingSetting() ? CheckState.Checked : CheckState.Unchecked; + _logging.CbExtendedLogging.CheckState = SettingsManager.GetExtendedLoggingSetting() + ? CheckState.Checked + : CheckState.Unchecked; // external tools _externalTools.TbExternalBrowserName.Text = Variables.AppSettings.BrowserName; @@ -353,19 +463,31 @@ private void LoadSettings() _externalTools.TbExternalExecutorBinary.Text = Variables.AppSettings.CustomExecutorBinary; // mediaplayer - _mediaPlayer.CbEnableMediaPlayer.CheckState = Variables.AppSettings.MediaPlayerEnabled ? CheckState.Checked : CheckState.Unchecked; + _mediaPlayer.CbEnableMediaPlayer.CheckState = Variables.AppSettings.MediaPlayerEnabled + ? CheckState.Checked + : CheckState.Unchecked; // tray icon - _trayIcon.CbUseModernIcon.CheckState = Variables.AppSettings.TrayIconUseModern ? CheckState.Checked : CheckState.Unchecked; - _trayIcon.CbDefaultMenu.CheckState = Variables.AppSettings.TrayIconShowDefaultMenu ? CheckState.Checked : CheckState.Unchecked; - _trayIcon.CbShowWebView.CheckState = Variables.AppSettings.TrayIconShowWebView ? CheckState.Checked : CheckState.Unchecked; + _trayIcon.CbUseModernIcon.CheckState = + Variables.AppSettings.TrayIconUseModern ? CheckState.Checked : CheckState.Unchecked; + _trayIcon.CbDefaultMenu.CheckState = Variables.AppSettings.TrayIconShowDefaultMenu + ? CheckState.Checked + : CheckState.Unchecked; + _trayIcon.CbShowWebView.CheckState = + Variables.AppSettings.TrayIconShowWebView ? CheckState.Checked : CheckState.Unchecked; _trayIcon.NumWebViewWidth.Value = Variables.AppSettings.TrayIconWebViewWidth; _trayIcon.NumWebViewHeight.Value = Variables.AppSettings.TrayIconWebViewHeight; + _trayIcon.SelectedScreen = Variables.AppSettings.TrayIconWebViewScreen; _trayIcon.TbWebViewUrl.Text = Variables.AppSettings.TrayIconWebViewUrl; - _trayIcon.CbWebViewKeepLoaded.CheckState = Variables.AppSettings.TrayIconWebViewBackgroundLoading ? CheckState.Checked : CheckState.Unchecked; - _trayIcon.CbWebViewShowMenuOnLeftClick.CheckState = Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick ? CheckState.Checked : CheckState.Unchecked; + _trayIcon.CbWebViewKeepLoaded.CheckState = Variables.AppSettings.TrayIconWebViewBackgroundLoading + ? CheckState.Checked + : CheckState.Unchecked; + _trayIcon.CbWebViewShowMenuOnLeftClick.CheckState = Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick + ? CheckState.Checked + : CheckState.Unchecked; - _nfc.CbEnableNfc.CheckState = Variables.AppSettings.NfcScanningEnabled ? CheckState.Checked : CheckState.Unchecked; + _nfc.CbEnableNfc.CheckState = + Variables.AppSettings.NfcScanningEnabled ? CheckState.Checked : CheckState.Unchecked; // done _initializing = false; @@ -377,10 +499,13 @@ private void LoadSettings() private async Task StoreSettingsAsync() { // general - var deviceName = string.IsNullOrEmpty(_general.TbDeviceName.Text) ? SharedHelperFunctions.GetSafeDeviceName() : _general.TbDeviceName.Text; + var deviceName = string.IsNullOrEmpty(_general.TbDeviceName.Text) + ? SharedHelperFunctions.GetSafeDeviceName() + : _general.TbDeviceName.Text; Variables.AppSettings.DeviceName = deviceName; Variables.AppSettings.SanitizeName = _general.CbEnableDeviceNameSanitation.CheckState == CheckState.Checked; - Variables.AppSettings.EnableStateNotifications = _general.CbEnableStateNotifications.CheckState == CheckState.Checked; + Variables.AppSettings.EnableStateNotifications = + _general.CbEnableStateNotifications.CheckState == CheckState.Checked; var uiLanguage = Variables.SupportedUILanguages.Find(x => x.DisplayName == _general.CbLanguage.Text); Variables.AppSettings.InterfaceLanguage = uiLanguage?.Name ?? "en"; @@ -392,32 +517,38 @@ private async Task StoreSettingsAsync() Variables.AppSettings.LocalApiEnabled = _localApi.CbLocalApiActive.CheckState == CheckState.Checked; // notifications - Variables.AppSettings.NotificationsEnabled = _notifications.CbAcceptNotifications.CheckState == CheckState.Checked; - Variables.AppSettings.NotificationsIgnoreImageCertificateErrors = _notifications.CbNotificationsIgnoreImageCertErrors.CheckState == CheckState.Checked; - Variables.AppSettings.NotificationsOpenActionUri = _notifications.CbNotificationsOpenActionUri.CheckState == CheckState.Checked; + Variables.AppSettings.NotificationsEnabled = + _notifications.CbAcceptNotifications.CheckState == CheckState.Checked; + Variables.AppSettings.NotificationsIgnoreImageCertificateErrors = + _notifications.CbNotificationsIgnoreImageCertErrors.CheckState == CheckState.Checked; + Variables.AppSettings.NotificationsOpenActionUri = + _notifications.CbNotificationsOpenActionUri.CheckState == CheckState.Checked; // hass settings Variables.AppSettings.HassUri = _homeAssistantApi.TbHassIp.Text; Variables.AppSettings.HassToken = _homeAssistantApi.TbHassApiToken.Text; Variables.AppSettings.HassClientCertificate = _homeAssistantApi.TbHassClientCertificate.Text; - Variables.AppSettings.HassAutoClientCertificate = _homeAssistantApi.CbHassAutoClientCertificate.CheckState == CheckState.Checked; - Variables.AppSettings.HassAllowUntrustedCertificates = _homeAssistantApi.CbHassAllowUntrustedCertificates.CheckState == CheckState.Checked; + Variables.AppSettings.HassAutoClientCertificate = + _homeAssistantApi.CbHassAutoClientCertificate.CheckState == CheckState.Checked; + Variables.AppSettings.HassAllowUntrustedCertificates = + _homeAssistantApi.CbHassAllowUntrustedCertificates.CheckState == CheckState.Checked; // hotkey config - Variables.AppSettings.QuickActionsHotKeyEnabled = _hotKey.CbEnableQuickActionsHotkey.CheckState == CheckState.Checked; + Variables.AppSettings.QuickActionsHotKeyEnabled = + _hotKey.CbEnableQuickActionsHotkey.CheckState == CheckState.Checked; if (Variables.AppSettings.QuickActionsHotKeyEnabled) { // hotkey enabled, store and activate - Variables.QuickActionsHotKey = new Hotkey(_hotKey.TbQuickActionsHotkey.Text); + Variables.QuickActionsHotKey = _hotKey.TbQuickActionsHotkey.Text; Variables.AppSettings.QuickActionsHotKey = Variables.QuickActionsHotKey.ToString(); - Variables.HotKeyManager.QuickActionsHotKeyChanged(_previousHotkey); + Variables.InternalHotKeyManager.QuickActionsHotKeyChanged(_previousHotkey); } else { // hotkey disabled, remove and deactivate Variables.QuickActionsHotKey = null; Variables.AppSettings.QuickActionsHotKey = string.Empty; - Variables.HotKeyManager.QuickActionsHotKeyChanged(_previousHotkey, false); + Variables.InternalHotKeyManager.QuickActionsHotKeyChanged(_previousHotkey, false); } // mqtt @@ -431,8 +562,10 @@ private async Task StoreSettingsAsync() Variables.AppSettings.MqttClientId = _mqtt.TbMqttClientId.Text; Variables.AppSettings.MqttRootCertificate = _mqtt.TbMqttRootCertificate.Text; Variables.AppSettings.MqttClientCertificate = _mqtt.TbMqttClientCertificate.Text; - Variables.AppSettings.MqttAllowUntrustedCertificates = _mqtt.CbAllowUntrustedCertificates.CheckState == CheckState.Checked; + Variables.AppSettings.MqttAllowUntrustedCertificates = + _mqtt.CbAllowUntrustedCertificates.CheckState == CheckState.Checked; Variables.AppSettings.MqttUseRetainFlag = _mqtt.CbUseRetainFlag.CheckState == CheckState.Checked; + Variables.AppSettings.MqttUseWebSocket = _mqtt.CbUseWebSocket.CheckState == CheckState.Checked; Variables.AppSettings.MqttIgnoreGracePeriod = _mqtt.CbIgnoreGracePeriod.CheckState == CheckState.Checked; // mqtt -> service @@ -441,7 +574,8 @@ private async Task StoreSettingsAsync() // updates Variables.AppSettings.CheckForUpdates = _updates.CbUpdates.CheckState == CheckState.Checked; Variables.AppSettings.ShowBetaUpdates = _updates.CbBetaUpdates.CheckState == CheckState.Checked; - Variables.AppSettings.EnableExecuteUpdateInstaller = _updates.CbExecuteUpdater.CheckState == CheckState.Checked; + Variables.AppSettings.EnableExecuteUpdateInstaller = + _updates.CbExecuteUpdater.CheckState == CheckState.Checked; // cache Variables.AppSettings.ImageCacheRetentionDays = (int)_localStorage.NumImageRetention.Value; @@ -462,7 +596,8 @@ private async Task StoreSettingsAsync() AgentSharedBase.SetCustomExecutorBinary(Variables.AppSettings.CustomExecutorBinary); // mediaplayer - Variables.AppSettings.MediaPlayerEnabled = _mediaPlayer.CbEnableMediaPlayer.CheckState == CheckState.Checked; + Variables.AppSettings.MediaPlayerEnabled = + _mediaPlayer.CbEnableMediaPlayer.CheckState == CheckState.Checked; // tray icon Variables.AppSettings.TrayIconUseModern = _trayIcon.CbUseModernIcon.CheckState == CheckState.Checked; @@ -470,13 +605,18 @@ private async Task StoreSettingsAsync() Variables.AppSettings.TrayIconShowWebView = _trayIcon.CbShowWebView.CheckState == CheckState.Checked; Variables.AppSettings.TrayIconWebViewWidth = (int)_trayIcon.NumWebViewWidth.Value; Variables.AppSettings.TrayIconWebViewHeight = (int)_trayIcon.NumWebViewHeight.Value; + Variables.AppSettings.TrayIconWebViewScreen = _trayIcon.NumWebViewScreen.SelectedIndex; Variables.AppSettings.TrayIconWebViewUrl = _trayIcon.TbWebViewUrl.Text; - Variables.AppSettings.TrayIconWebViewBackgroundLoading = _trayIcon.CbWebViewKeepLoaded.CheckState == CheckState.Checked; - Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick = _trayIcon.CbWebViewShowMenuOnLeftClick.CheckState == CheckState.Checked; + Variables.AppSettings.TrayIconWebViewBackgroundLoading = + _trayIcon.CbWebViewKeepLoaded.CheckState == CheckState.Checked; + Variables.AppSettings.TrayIconWebViewShowMenuOnLeftClick = + _trayIcon.CbWebViewShowMenuOnLeftClick.CheckState == CheckState.Checked; // nfc Variables.AppSettings.NfcScanningEnabled = _nfc.CbEnableNfc.CheckState == CheckState.Checked; - Variables.AppSettings.NfcSelectedScanner = _nfc.CbNfcScanner.SelectedItem == null ? string.Empty : _nfc.CbNfcScanner.SelectedItem.ToString(); + Variables.AppSettings.NfcSelectedScanner = _nfc.CbNfcScanner.SelectedItem == null + ? string.Empty + : _nfc.CbNfcScanner.SelectedItem.ToString(); // save to file SettingsManager.StoreAppSettings(); @@ -513,7 +653,7 @@ private void Configuration_KeyUp(object sender, KeyEventArgs e) private void BtnClearHotKey_Click(object sender, EventArgs e) { - _hotKey.TbQuickActionsHotkey.Text = _hotkeySelector.EmptyHotkeyText; + _hotKey.TbQuickActionsHotkey.Text = string.Empty; } private void Configuration_ResizeEnd(object sender, EventArgs e) @@ -552,4 +692,4 @@ private void CbHassAutoClientCertificate_CheckedChanged(object sender, EventArgs private void BtnClose_Click(object sender, EventArgs e) => Close(); } -} +} \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Forms/Main.cs b/src/HASS.Agent/HASS.Agent/Forms/Main.cs index 84408a66..5bde6719 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/Main.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/Main.cs @@ -24,10 +24,12 @@ using Serilog; using Syncfusion.Windows.Forms; using WindowsDesktop; -using WK.Libraries.HotkeyListenerNS; using NativeMethods = HASS.Agent.Functions.NativeMethods; using QuickActionsConfig = HASS.Agent.Forms.QuickActions.QuickActionsConfig; using Task = System.Threading.Tasks.Task; +using Microsoft.Win32; +using NHotkey; +using NHotkey.WindowsForms; namespace HASS.Agent.Forms { @@ -65,6 +67,11 @@ private async void Main_Load(object sender, EventArgs e) AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } + if (SharedHelperFunctions.RunningElevated()) + { + Log.Warning("[MAIN] Running with elevated privileges, this might cause issues for example with notifications!"); + } + // catch all key presses KeyPreview = true; @@ -82,7 +89,7 @@ private async void Main_Load(object sender, EventArgs e) SetMqttStatus(ComponentStatus.Loading); // create a hotkey listener - Variables.HotKeyListener = new HotkeyListener(); + Variables.HotKeyListener = HotkeyManager.Current; // check for dpi scaling CheckDpiScalingFactor(); @@ -90,7 +97,6 @@ private async void Main_Load(object sender, EventArgs e) // core components initialization - required for loading the entities await RadioManager.Initialize(); await InternalDeviceSensorsManager.Initialize(); - InitializeHardwareManager(); InitializeVirtualDesktopManager(); await Task.Run(InitializeAudioManager); @@ -117,7 +123,10 @@ private async void Main_Load(object sender, EventArgs e) if (Variables.ShuttingDown) return; + // prepare the tray icon config + SystemEvents.DisplaySettingsChanged += (s, a) => RefreshTrayIcon(); ProcessTrayIcon(); + InitializeHotkeys(); var initTask = Task.Run(async () => @@ -166,7 +175,6 @@ await Task.WhenAll(hassApiTask, sensorTask, commandsTask, serviceTask, private void OnProcessExit(object sender, EventArgs e) { AudioManager.Shutdown(); - HardwareManager.Shutdown(); NotificationManager.Exit(); } @@ -250,13 +258,8 @@ private void CheckDpiScalingFactor() private void ProcessTrayIcon() { - if (Variables.AppSettings.TrayIconUseModern) - { - var icon = (Icon)new System.Resources.ResourceManager(typeof(Main)).GetObject("ModernNotifyIcon"); - if (icon != null) - NotifyIcon.Icon = icon; - } - + RefreshTrayIcon(); + // are we set to show the webview and keep it loaded? if (!Variables.AppSettings.TrayIconShowWebView) return; @@ -302,10 +305,10 @@ private static void CurrentDomain_UnhandledException(object sender, UnhandledExc private void InitializeHotkeys() { // prepare listener - Variables.HotKeyListener.HotkeyPressed += HotkeyListener_HotkeyPressed; + Variables.InternalHotKeyManager.HotkeyActivated += HotkeyListener_HotkeyPressed; // bind quick actions hotkey (if configured) - Variables.HotKeyManager.InitializeQuickActionsHotKeys(); + Variables.InternalHotKeyManager.InitializeQuickActionsHotKeys(); } /// @@ -315,10 +318,10 @@ private void InitializeHotkeys() /// private void HotkeyListener_HotkeyPressed(object sender, HotkeyEventArgs e) { - if (e.Hotkey == Variables.QuickActionsHotKey) + if (e.Name == Variables.QuickActionsHotKey) ShowQuickActions(); else - HotKeyManager.ProcessQuickActionHotKey(e.Hotkey.ToString()); + InternalHotKeyManager.ProcessQuickActionHotKey(e.Name); } /// @@ -329,14 +332,6 @@ private void InitializeVirtualDesktopManager() VirtualDesktopManager.Initialize(); } - /// - /// Initialized the Hardware Manager - /// - private void InitializeHardwareManager() - { - HardwareManager.Initialize(); - } - /// /// Initialized the Audio Manager /// @@ -400,6 +395,21 @@ internal void HideTrayIcon() })); } + internal void RefreshTrayIcon() + { + var iconResourceName = Variables.AppSettings.TrayIconUseModern ? "ModernNotifyIcon" : "NotifyIcon.Icon"; + var icon = (Icon)new System.Resources.ResourceManager(typeof(Main)).GetObject(iconResourceName); + if (icon != null) + { + Invoke(new MethodInvoker(delegate + { + NotifyIcon.Visible = false; + NotifyIcon.Icon = icon; + NotifyIcon.Visible = true; + })); + } + } + /// /// Show a messagebox on the UI thread /// @@ -833,11 +843,15 @@ private async void CheckForUpdate() BtnCheckForUpdate.Text = Languages.Main_Checking; var (isAvailable, version) = await UpdateManager.CheckIsUpdateAvailableAsync(); - if (!isAvailable) + if (!isAvailable && version != null) { var beta = Variables.Beta ? " [BETA]" : string.Empty; MessageBoxAdv.Show(this, string.Format(Languages.Main_CheckForUpdate_MessageBox1, Variables.Version, beta), Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); - + return; + } + else if(!isAvailable) + { + MessageBoxAdv.Show(this, Languages.Main_CheckForUpdateFailed_MessageBox1, Variables.MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } diff --git a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActions.cs b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActions.cs index 0c54706b..10da740c 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActions.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActions.cs @@ -108,6 +108,10 @@ private void BuildLayout() if (rows > 1) Height += 5 * (rows - 1); + var scalingFactor = HelperFunctions.GetScalingFactors(); + Width = (int)(Width * scalingFactor.dpiScalingFactor); + Height = (int)(Height * scalingFactor.dpiScalingFactor); + // add the quickactions as controls var currentColumn = 0; var currentRow = 0; diff --git a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsConfig.cs b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsConfig.cs index 9a8285c4..7c86317c 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsConfig.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsConfig.cs @@ -163,7 +163,7 @@ private void BtnStore_Click(object sender, EventArgs e) StoredQuickActions.Store(); // reload hotkey bindings - Variables.HotKeyManager.ReloadQuickActionsHotKeys(); + Variables.InternalHotKeyManager.ReloadQuickActionsHotKeys(); // done Close(); diff --git a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs index dcb776fb..3b4008e0 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/QuickActions/QuickActionsMod.cs @@ -6,17 +6,18 @@ using HASS.Agent.Shared.Enums; using HASS.Agent.Shared.Functions; using Syncfusion.Windows.Forms; -using WK.Libraries.HotkeyListenerNS; namespace HASS.Agent.Forms.QuickActions { public partial class QuickActionsMod : MetroForm { - private readonly HotkeySelector _hotkeySelector = new(); internal readonly QuickAction QuickAction; private readonly Dictionary _hassDomainEntityTypes = new(); private readonly Dictionary _hassActionEntityTypes = new(); + + private Keys _key = Keys.None; + private Keys _modifiers = Keys.None; public QuickActionsMod(QuickAction quickAction) { @@ -90,15 +91,17 @@ private async void QuickActionsMod_Load(object sender, EventArgs e) } LvDomain.EndUpdate(); + TbHotkey.ReadOnly = true; + TbHotkey.KeyDown += TbQuickActionsHotkey_KeyDown; + // load or new quickaction? if (QuickAction.Id == Guid.Empty) { // new quickaction Text = Languages.QuickActionsMod_Title_New; QuickAction.Id = Guid.NewGuid(); - - _hotkeySelector.Enable(TbHotkey); - TbHotkey.Text = _hotkeySelector.EmptyHotkeyText; + + TbHotkey.Text = string.Empty; LvDomain.Items[0].Selected = true; if (CbEntity.Items.Count > 0) CbEntity.SelectedIndex = 0; @@ -211,12 +214,7 @@ private void LoadQuickAction() if (!string.IsNullOrWhiteSpace(TbDescription.Text)) TbDescription.SelectionStart = TbDescription.Text.Length; // load the hotkey - if (!string.IsNullOrEmpty(QuickAction.HotKey)) _hotkeySelector.Enable(TbHotkey, new Hotkey(QuickAction.HotKey)); - else - { - _hotkeySelector.Enable(TbHotkey); - TbHotkey.Text = _hotkeySelector.EmptyHotkeyText; - } + TbHotkey.Text = !string.IsNullOrEmpty(QuickAction.HotKey) ? QuickAction.HotKey : string.Empty; } /// @@ -396,6 +394,22 @@ private void LoadEntityList() CbEntity.Items.Add(item); } break; + + case HassDomain.InputButton: + foreach (var item in HassApiManager.InputButtonList) + { + CbEntity.AutoCompleteCustomSource.Add(item); + CbEntity.Items.Add(item); + } + break; + + case HassDomain.Fan: + foreach (var item in HassApiManager.FanList) + { + CbEntity.AutoCompleteCustomSource.Add(item); + CbEntity.Items.Add(item); + } + break; } } @@ -410,16 +424,13 @@ private void CbEntity_SelectedIndexChanged(object sender, EventArgs e) private void QuickActionsMod_FormClosing(object sender, FormClosingEventArgs e) { - // stop and dispose selector - _hotkeySelector?.Disable(TbHotkey); - _hotkeySelector?.Dispose(); + // clean selector + TbHotkey.KeyDown -= TbQuickActionsHotkey_KeyDown; } private void TbHotkey_TextChanged(object sender, EventArgs e) { - if (string.IsNullOrWhiteSpace(TbHotkey.Text) - || TbHotkey.Text == _hotkeySelector?.EmptyHotkeyText - || TbHotkey.Text == _hotkeySelector?.InvalidHotkeyText) + if (string.IsNullOrWhiteSpace(TbHotkey.Text)) { CbEnableHotkey.CheckState = CheckState.Unchecked; return; @@ -465,5 +476,53 @@ private void QuickActionsMod_Layout(object sender, LayoutEventArgs e) // hide the pesky horizontal scrollbar ListViewTheme.ShowScrollBar(LvDomain.Handle, ListViewTheme.SB_HORZ, false); } + + private void TbQuickActionsHotkey_KeyDown(object sender, KeyEventArgs e) + { + e.SuppressKeyPress = true; + + var key = e.KeyCode; + + if (key is Keys.LControlKey or Keys.RControlKey + or Keys.LShiftKey or Keys.RShiftKey + or Keys.LWin or Keys.RWin + or Keys.Alt) + { + key = Keys.None; + } + + if (key == Keys.Escape) + { + _key = Keys.None; + _modifiers = Keys.None; + TbHotkey.Text = string.Empty; + + return; + } + + _key = key; + TbHotkey.Text = FormatHotkey(_key, e.Modifiers); + } + + private string FormatHotkey(Keys key, Keys modifiers) + { + var parts = new List(); + if ((modifiers & Keys.Shift) != 0) + { + parts.Add(nameof(Keys.Shift)); + } + + if ((modifiers & Keys.Control) != 0) + { + parts.Add(nameof(Keys.Control)); + } + + if ((modifiers & Keys.Alt) != 0) + { + parts.Add(nameof(Keys.Alt)); + } + + return parts.Count > 0 ? string.Join(", ", parts) + " + " + key : key.ToString(); + } } } diff --git a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs index e852dbbf..df3f172a 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/Sensors/SensorsMod.cs @@ -180,6 +180,7 @@ private void LoadSensor() switch (_selectedSensorType) { case SensorType.NamedWindowSensor: + case SensorType.NamedActiveWindowSensor: TbSetting1.Text = Sensor.WindowName; break; @@ -291,6 +292,7 @@ private bool SetType(bool setDefaultValues = true) // process the interface switch (sensorCard.SensorType) { + case SensorType.NamedActiveWindowSensor: case SensorType.NamedWindowSensor: SetWindowGui(); break; @@ -702,6 +704,7 @@ private void BtnStore_Click(object sender, EventArgs e) switch (sensorCard.SensorType) { case SensorType.NamedWindowSensor: + case SensorType.NamedActiveWindowSensor: var window = TbSetting1.Text.Trim(); if (string.IsNullOrEmpty(window)) { diff --git a/src/HASS.Agent/HASS.Agent/Forms/WebView.Designer.cs b/src/HASS.Agent/HASS.Agent/Forms/WebView.Designer.cs index c15a08b9..e99f42ff 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/WebView.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/WebView.Designer.cs @@ -1,6 +1,6 @@ -extern alias WV2; -using HASS.Agent.Resources.Localization; -using WV2::Microsoft.Web.WebView2.WinForms; +using HASS.Agent.Resources.Localization; +using Microsoft.Web.WebView2.WinForms; + namespace HASS.Agent.Forms { diff --git a/src/HASS.Agent/HASS.Agent/Forms/WebView.cs b/src/HASS.Agent/HASS.Agent/Forms/WebView.cs index 8e0ed48a..96a871e7 100644 --- a/src/HASS.Agent/HASS.Agent/Forms/WebView.cs +++ b/src/HASS.Agent/HASS.Agent/Forms/WebView.cs @@ -1,5 +1,4 @@ -extern alias WV2; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Syncfusion.Windows.Forms; using HASS.Agent.Functions; using HASS.Agent.Models.Internal; @@ -8,7 +7,8 @@ using Serilog; using System.Runtime.InteropServices; using Windows.Web.UI.Interop; -using WV2::Microsoft.Web.WebView2.Core; +using Microsoft.Web.WebView2.Core; + namespace HASS.Agent.Forms { diff --git a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs index b56eace8..30796038 100644 --- a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs +++ b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs @@ -1,13 +1,8 @@ -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.Diagnostics; using System.IO; -using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using HASS.Agent.API; @@ -27,11 +22,15 @@ using HASS.Agent.Shared.Managers; using Newtonsoft.Json.Serialization; using System.Windows; +using System.Security.Policy; namespace HASS.Agent.Functions { - internal static class HelperFunctions + internal static partial class HelperFunctions { + [GeneratedRegex("^https?://")] + private static partial Regex AbsoluteURLRegex(); + private static bool _shutdownCalled = false; /// @@ -173,8 +172,8 @@ internal static async Task ShutdownAsync(TimeSpan waitBeforeClosing) // stop hotkey Variables.MainForm?.Invoke(new MethodInvoker(delegate { - Variables.HotKeyListener?.RemoveAll(); - Variables.HotKeyListener?.Dispose(); + Variables.InternalHotKeyManager?.ReloadQuickActionsHotKeys(); + //Variables.HotKeyListener?.Dispose(); })); // stop bt listener @@ -365,18 +364,22 @@ internal static void OpenLocalFolder(string path) internal static Form GetForm(string formName) => System.Windows.Forms.Application.OpenForms.Cast
().FirstOrDefault(x => x.Name == formName); + internal static bool IsAbsoluteUrl(string url) => AbsoluteURLRegex().IsMatch(url); + /// /// Launches the url with the user's custom browser if provided, or the system's default /// /// /// - internal static void LaunchUrl(string url, bool incognito = false) + internal static void LaunchUrl(string url, bool incognito = false, bool explicitUrl = false) { + var targetUrl = explicitUrl ? url : StorageManager.GetElementUrl(url); + // did the user provide a browser? if (string.IsNullOrEmpty(Variables.AppSettings.BrowserBinary)) { // nope - using (_ = Process.Start(new ProcessStartInfo(url) { UseShellExecute = true })) { } + using (_ = Process.Start(new ProcessStartInfo(targetUrl) { UseShellExecute = true })) { } return; } @@ -385,7 +388,7 @@ internal static void LaunchUrl(string url, bool incognito = false) // yep, but not found Log.Warning("[BROWSER] User provided browser not found, using default: {bin}", Variables.AppSettings.BrowserBinary); - using (_ = Process.Start(new ProcessStartInfo(url) { UseShellExecute = true })) { } + using (_ = Process.Start(new ProcessStartInfo(targetUrl) { UseShellExecute = true })) { } return; } @@ -394,8 +397,8 @@ internal static void LaunchUrl(string url, bool incognito = false) var startupArgs = new ProcessStartInfo { FileName = Variables.AppSettings.BrowserBinary }; // if incgonito flag is set, use the incog. args (if set) - otherwise, just the url - if (incognito) startupArgs.Arguments = !string.IsNullOrEmpty(Variables.AppSettings.BrowserIncognitoArg) ? $"{Variables.AppSettings.BrowserIncognitoArg} {url}" : url; - else startupArgs.Arguments = url; + if (incognito) startupArgs.Arguments = !string.IsNullOrEmpty(Variables.AppSettings.BrowserIncognitoArg) ? $"{Variables.AppSettings.BrowserIncognitoArg} {targetUrl}" : targetUrl; + else startupArgs.Arguments = targetUrl; userBrowser.StartInfo = startupArgs; userBrowser.Start(); @@ -423,7 +426,7 @@ internal static void LaunchWebView(WebViewInfo webViewInfo, string url = "") /// /// Prepares and loads the tray icon's webview /// - internal static void PrepareTrayIconWebView() + internal static void PrepareTrayIconWebView(int screenIndex = 0) { // prepare the webview info var webViewInfo = new WebViewInfo @@ -434,8 +437,8 @@ internal static void PrepareTrayIconWebView() IsTrayIconWebView = true }; - var x = Screen.PrimaryScreen.WorkingArea.Width - webViewInfo.Width; - var y = Screen.PrimaryScreen.WorkingArea.Height - webViewInfo.Height; + var x = Screen.AllScreens[screenIndex].Bounds.Right - webViewInfo.Width; + var y = Screen.AllScreens[screenIndex].Bounds.Bottom - webViewInfo.Height; webViewInfo.X = x; webViewInfo.Y = y; @@ -471,20 +474,20 @@ internal static void LaunchTrayIconWebView() Width = Variables.AppSettings.TrayIconWebViewWidth, }; - LaunchTrayIconWebView(webView); + LaunchTrayIconWebView(webView, Variables.AppSettings.TrayIconWebViewScreen); } /// /// Shows a new webview form near the tray icon /// /// - internal static void LaunchTrayIconWebView(WebViewInfo webViewInfo) + internal static void LaunchTrayIconWebView(WebViewInfo webViewInfo, int screenIndex = 0) { // are we previewing? if (webViewInfo.IsTrayIconPreview) { // yep, show as configured - LaunchTrayIconCustomWebView(webViewInfo); + LaunchTrayIconCustomWebView(webViewInfo, screenIndex); // done return; @@ -494,17 +497,17 @@ internal static void LaunchTrayIconWebView(WebViewInfo webViewInfo) if (Variables.AppSettings.TrayIconWebViewBackgroundLoading) { // yep - LaunchTrayIconBackgroundLoadedWebView(); + LaunchTrayIconBackgroundLoadedWebView(screenIndex); // done return; } // show a new webview from within the UI thread - LaunchTrayIconCustomWebView(webViewInfo); + LaunchTrayIconCustomWebView(webViewInfo, screenIndex); } - private static void LaunchTrayIconBackgroundLoadedWebView() + private static void LaunchTrayIconBackgroundLoadedWebView(int screenIndex) { Variables.MainForm.Invoke(new MethodInvoker(delegate { @@ -517,12 +520,27 @@ private static void LaunchTrayIconBackgroundLoadedWebView() })); } - private static void LaunchTrayIconCustomWebView(WebViewInfo webViewInfo) + private static void LaunchTrayIconCustomWebView(WebViewInfo webViewInfo, int screenIndex = 0) { + if (screenIndex == -1) + { + InitMultiScreenConfig(); + screenIndex = Variables.AppSettings.TrayIconWebViewScreen; + } + Variables.MainForm.Invoke(new MethodInvoker(delegate { - var x = Screen.PrimaryScreen.WorkingArea.Width - webViewInfo.Width; - var y = Screen.PrimaryScreen.WorkingArea.Height - webViewInfo.Height; + var totalX = 0; + + foreach (var widthscreen in Screen.AllScreens) + { + totalX += widthscreen.Bounds.X; + } + + Screen targetScreen = Screen.AllScreens[screenIndex]; + + var x = targetScreen.Bounds.X + webViewInfo.Width; + var y = targetScreen.WorkingArea.Height - webViewInfo.Height; webViewInfo.X = x; webViewInfo.Y = y; @@ -533,12 +551,43 @@ private static void LaunchTrayIconCustomWebView(WebViewInfo webViewInfo) var webView = new WebView(webViewInfo); webView.Opacity = 0; + webView.AutoScaleMode = AutoScaleMode.Font; + webView.Show(); + + webView.Left = targetScreen.WorkingArea.Right - webView.Width; + webView.Top = targetScreen.WorkingArea.Bottom - webView.Height; Variables.TrayIconWebView = webView; - webView.Show(); + })); } + public static void InitMultiScreenConfig() + { + var primaryScreenIndex = 0; + var displays = Screen.AllScreens; + + if (Screen.AllScreens.Length == 1) + { + Variables.AppSettings.TrayIconWebViewScreen = 0; + } + else + { + for (var i = 0; i < displays.Length; i++) + { + var display = displays[i]; + + if (display.Primary) + { + primaryScreenIndex = i; + break; + } + } + } + + Variables.AppSettings.TrayIconWebViewScreen = primaryScreenIndex; + } + private static readonly Dictionary KnownOkInputLanguage = new() { { new IntPtr(-268367863), "United States-International" }, diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj index 75aa88d6..f3fc5f33 100644 --- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj +++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj @@ -1,18 +1,23 @@  + + False + False + + WinExe - net6.0-windows10.0.19041.0 + net8.0-windows10.0.22621.0 disable true true enable HASS.Agent hassagent.ico - x64 - x64;x86 + anycpu + x64;x86;AnyCPU full - 2.1.1 + 2.2.1 HASS.Agent Team HASS.Agent Team Windows-based client for Home Assistant. Provides notifications, quick actions, commands, sensors and more. @@ -22,16 +27,18 @@ https://github.com/hass-agent/HASS.Agent MIT app.manifest - 2.1.1 - 2.1.1 + 2.2.1 + 2.2.1 HASS.Agent None - win10-x64;win10-x86 true true false false false + 10.0.22621.38 + win-x64 + false @@ -48,49 +55,43 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - WV2 + + + + + + - + + - - - - - - + + + + + + + - + - - - Libraries\HADotNet.Core.dll - - - Libraries\HotkeyListener.dll - - - UserControl diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/CustomCommands/WinformsSleepCommand.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/CustomCommands/WinformsSleepCommand.cs new file mode 100644 index 00000000..a8804708 --- /dev/null +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/CustomCommands/WinformsSleepCommand.cs @@ -0,0 +1,29 @@ +using System; +using System.Diagnostics; +using System.IO; +using HASS.Agent.Shared.Enums; +using Serilog; + +namespace HASS.Agent.Shared.HomeAssistant.Commands.InternalCommands; + +/// +/// Activates provided Virtual Desktop +/// +public class WinformsSleepCommand : InternalCommand +{ + private const string DefaultName = "switchdesktop"; + + public WinformsSleepCommand(string entityName = DefaultName, string name = DefaultName, CommandEntityType entityType = CommandEntityType.Button, string id = default) : base(entityName ?? DefaultName, name ?? null, string.Empty, entityType, id) + { + State = "OFF"; + } + + public override void TurnOn() + { + State = "ON"; + + Application.SetSuspendState(PowerState.Suspend, false, false); + + State = "OFF"; + } +} diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/LaunchUrlCommand.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/LaunchUrlCommand.cs index b20b53d2..b8884225 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/LaunchUrlCommand.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/LaunchUrlCommand.cs @@ -39,7 +39,7 @@ public override void TurnOn() return; } - HelperFunctions.LaunchUrl(_url, _incognito); + HelperFunctions.LaunchUrl(_url, _incognito, true); State = "OFF"; } @@ -59,7 +59,7 @@ public override void TurnOnWithAction(string action) // prepare command var command = string.IsNullOrWhiteSpace(_url) ? action : $"{_url} {action}"; - HelperFunctions.LaunchUrl(command, _incognito); + HelperFunctions.LaunchUrl(command, _incognito, true); State = "OFF"; } diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/RadioCommand.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/RadioCommand.cs index 4c279dbd..34884234 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/RadioCommand.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Commands/InternalCommands/RadioCommand.cs @@ -3,6 +3,7 @@ using HASS.Agent.Shared.Enums; using HASS.Agent.Shared.HomeAssistant.Commands; using HASS.Agent.Shared.Models.HomeAssistant; +using Serilog; using Windows.Devices.Radios; namespace HASS.Agent.HomeAssistant.Commands.InternalCommands @@ -11,29 +12,50 @@ internal class RadioCommand : InternalCommand { private const string DefaultName = "radiocommand"; - private readonly Radio _radio; + private Radio _radio; public string RadioName { get; set; } internal RadioCommand(string radioName, string entityName = DefaultName, string name = DefaultName, CommandEntityType entityType = CommandEntityType.Switch, string id = default) : base(entityName ?? DefaultName, name ?? null, radioName, entityType, id) { RadioName = radioName; - _radio = RadioManager.AvailableRadio.First(r => r.Name == radioName); + _radio = RadioManager.AvailableRadio.FirstOrDefault(r => r.Name == radioName); + + if (_radio == null) + { + Log.Warning("[RADIOCOMMAND] [{name}] '{radioName}' not present, will retry to find on each command execution", EntityName, radioName); + } } public override void TurnOn() { - Task.Run(async () => { await _radio.SetStateAsync(RadioState.On); }); + if (_radio == null) + { + _radio = RadioManager.AvailableRadio.FirstOrDefault(r => r.Name == RadioName); + } + + if (_radio != null) + { + Task.Run(async () => { await _radio.SetStateAsync(RadioState.On); }); + } } public override void TurnOff() { - Task.Run(async () => { await _radio.SetStateAsync(RadioState.Off); }); + if (_radio == null) + { + _radio = RadioManager.AvailableRadio.FirstOrDefault(r => r.Name == RadioName); + } + + if (_radio != null) + { + Task.Run(async () => { await _radio.SetStateAsync(RadioState.Off); }); + } } public override string GetState() { - return _radio.State == RadioState.On ? "ON" : "OFF"; + return _radio != null ? (_radio.State == RadioState.On ? "ON" : "OFF") : "OFF"; } } } diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs index 36a49af8..27d07f4d 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/HassApiManager.cs @@ -44,6 +44,8 @@ internal static class HassApiManager internal static List ClimateList = new(); internal static List MediaPlayerList = new(); internal static List ButtonList = new(); + internal static List InputButtonList = new(); + internal static List FanList = new(); private static readonly string[] OnStates = { "on", "playing", "open", "opening" }; private static readonly string[] OffStates = { "off", "idle", "paused", "stopped", "closed", "closing" }; @@ -405,6 +407,8 @@ private static async Task LoadEntitiesAsync(bool clearCurrent = false) await LoadDomain("climate", ClimateList); await LoadDomain("media_player", MediaPlayerList); await LoadDomain("button", ButtonList); + await LoadDomain("input_button", InputButtonList); + await LoadDomain("fan", FanList); if (ManagerStatus != HassManagerStatus.Failed) return; diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs index a06e73e3..e726ddc9 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/MultiValue/PrintersSensors.cs @@ -143,7 +143,7 @@ private PrinterInfo GetPrinterInfo() var queueJob = new PrintJobInfo { - Nmae = job.Name, + Name = job.Name, Submitter = job.Submitter, Status = job.JobStatus.ToString(), NumberOfPages = job.NumberOfPages, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/AccentColorSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/AccentColorSensor.cs new file mode 100644 index 00000000..34e868f1 --- /dev/null +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/AccentColorSensor.cs @@ -0,0 +1,78 @@ +using HASS.Agent.Managers; +using HASS.Agent.Shared.Extensions; +using HASS.Agent.Shared.Models.HomeAssistant; +using Newtonsoft.Json; +using Windows.UI.ViewManagement; +using static HASS.Agent.Shared.Functions.Inputs; + +namespace HASS.Agent.HomeAssistant.Sensors.GeneralSensors.SingleValue +{ + /// + /// Sensor containing the current monitor power state + /// + public class AccentColorSensor : AbstractSingleValueSensor + { + private const string DefaultName = "accentcolor"; + + private readonly UISettings _uiSettings = new(); + + private string _attributesJson = string.Empty; + + public AccentColorSensor(int? updateInterval = 120, string entityName = DefaultName, string name = DefaultName, string id = default, string advancedSettings = default) : base(entityName ?? DefaultName, name ?? null, updateInterval ?? 120, id, true, advancedSettings) { } + + public override DiscoveryConfigModel GetAutoDiscoveryConfig() + { + if (Variables.MqttManager == null) return null; + + var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); + if (deviceConfig == null) return null; + + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) + { + EntityName = EntityName, + Name = Name, + Unique_id = Id, + Device = deviceConfig, + State_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/state", + Icon = "mdi:palette", + Availability_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/availability", + Json_attributes_topic = $"{Variables.MqttManager.MqttDiscoveryPrefix()}/{Domain}/{deviceConfig.Name}/{ObjectId}/attributes" + }); + } + + public override string GetState() + { + var accent = TryGetColor(UIColorType.Accent); + _attributesJson = JsonConvert.SerializeObject(new + { + accent, + background = TryGetColor(UIColorType.Background), + foreground = TryGetColor(UIColorType.Foreground), + accentDark3 = TryGetColor(UIColorType.AccentDark3), + accentDark2 = TryGetColor(UIColorType.AccentDark2), + accentDark1 = TryGetColor(UIColorType.AccentDark1), + accentLight3 = TryGetColor(UIColorType.AccentLight3), + accentLight2 = TryGetColor(UIColorType.AccentLight2), + accentLight1 = TryGetColor(UIColorType.AccentLight1), + complement = TryGetColor(UIColorType.Complement), + }); + + return accent; + } + + public override string GetAttributes() => _attributesJson; + + private string TryGetColor(UIColorType colorType) + { + var color = ""; + + try + { + color = _uiSettings.GetColorValue(colorType).ToString().Replace("#FF", "#"); + } + catch {} + + return color; + } + } +} diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs index 8bc87fae..4804b582 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/ActiveDesktopSensor.cs @@ -44,7 +44,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() return null; } - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs index 62d75631..dbf48c0b 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothDevicesSensor.cs @@ -26,7 +26,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs index c61a57b8..228e20d5 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/BluetoothLeDevicesSensor.cs @@ -29,7 +29,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs index 78bc6d83..09d45ac1 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/GeoLocationSensor.cs @@ -18,7 +18,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs index 34fb6fd0..4bb014c3 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/InternalDeviceSensor.cs @@ -34,7 +34,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() if (deviceConfig == null) return null; - var sensorDiscoveryConfigModel = new SensorDiscoveryConfigModel() + var sensorDiscoveryConfigModel = new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs index ae984599..f7dd6f5d 100644 --- a/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs +++ b/src/HASS.Agent/HASS.Agent/HomeAssistant/Sensors/GeneralSensors/SingleValue/MonitorPowerStateSensor.cs @@ -19,7 +19,7 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() var deviceConfig = Variables.MqttManager.GetDeviceConfigModel(); if (deviceConfig == null) return null; - return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel() + return AutoDiscoveryConfigModel ?? SetAutoDiscoveryConfigModel(new SensorDiscoveryConfigModel(Domain) { EntityName = EntityName, Name = Name, diff --git a/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.dll b/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.dll deleted file mode 100644 index 2e825960..00000000 Binary files a/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.dll and /dev/null differ diff --git a/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.xml b/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.xml deleted file mode 100644 index 4515185e..00000000 --- a/src/HASS.Agent/HASS.Agent/Libraries/HADotNet.Core.xml +++ /dev/null @@ -1,1729 +0,0 @@ - - - - HADotNet.Core - - - - - Represents the base client from which all other API clients derive. - - - - - Gets the static HttpClient instance. - - - - - Initializes a new instance. - - The preconfigured to communicate with a Home Assistant instance. - - - - Performs a GET request on the specified path. - - The type of data to deserialize and return. - The relative API endpoint path. - The deserialized data of type . - - - - Performs a POST request on the specified path. - - The type of object expected back. - The path to post to. - The body contents to serialize and include. - if the body should be interpereted as a pre-built JSON string, or if it should be serialized. - - - - - Performs a DELETE request on the specified path. - - The type of data to deserialize and return. - The relative API endpoint path. - The deserialized data of type . - - - - Performs a DELETE request on the specified path. - - The relative API endpoint path. - - - - Provides a factory which can instantiate API clients (useful in DI scenarios, for example). - - - - - Gets whether or not the Client Factory has been initialized. - - - - - Gets the instance configured for this ClientFactory. To reconfigure the HttpClient, call again. - - - - - Initializes the client factory with the specified and which are forwarded to clients instantiated from this factory. - - The Home Assistant base instance address (do not include /api/). - The Home Assistant long-lived access token. - - - - Initializes the client factory with the specified and which are forwarded to clients instantiated from this factory. - Allows specifying the to allow further configuring. - - - - - - - - Initializes the client factory with the specified and which are forwarded to clients instantiated from this factory. - - The Home Assistant base instance address (do not include /api/). - The Home Assistant long-lived access token. - - - - Initializes the client factory with the specified and which are forwarded to clients instantiated from this factory. - Allows specifying the to allow further configuring. - - - - - - - - Resets the Client Factory to its initial state (not initialized). - - - - - Retrieves a new instance of a client by type, preconfigured with the same as this (from the last time was called). - - The type of client to get. - Thrown if this is not initialized (call first). - A new instance of the specified type. - - - - Provides access to the automations API for working with automations. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Create the . - - The . - The . - - - - Read the . - - The automation id. - The . - - - - Update the . - - The . - The . - - - - Delete the . - - The automation id. - The . - - - - Provides access to the calendar API for retrieving information about calendar entries. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of current and future calendar items, from now until the specified . The maximum number of results is driven by the "max_results" configuration option in the calendar config. - - The full name of the calendar entity. If this paramter does not start with "calendar.", it will be prepended automatically. - Optional, defaults to 30. The number of days from the current point in time to retrieve calendar items for. - A representing the calendar items found. - - - - Retrieves a list of current and future calendar items, between the and parameters. The maximum number of results is driven by the "max_results" configuration option in the calendar config. - - The full name of the calendar entity. If this paramter does not start with "calendar.", it will be prepended automatically. - The start date/time to search. - The end date/time to search. - A representing the calendar items found. - - - - Provides access to the camera proxy API which allows fetching of the current image from a camera entity. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves the most recently available (still) image data from the specified . - - The camera entity ID to reteive the image for. - A byte array containing the still image, typically in JPEG format. - - - - Retrieves the most recently available (still) image data from the specified . - - The camera entity ID to reteive the image for. - true to include the prefix "data:image/jpg;base64,", false to omit. Defaults to true. - A web-friendly Base64-encoded still image, in JPEG format. - - - - Provides access to the configuration API for retrieving the current Home Assistant configuration. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves the current Home Assistant configuration object. - - A representing the current Home Assistant configuration. - - - - Performs a configuration check and returns the result. - - A containing the results of the check, and any errors that occurred. - - - - Provides access to the discovery API for retrieving the current Home Assistant instance information. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves the current Home Assistant discovery object. - - A representing the current Home Assistant instance information. - - - - Provides a wrapper around the States endpoint for retrieving entity info. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of all current entity names (that have state) in the format "domain.name". - - An of strings of all known entities (with state) at the time. - - - - Retrieves a list of entity names for a particular domain (that have state) in the format "domain.name". - - A domain name to filter the entity list to (e.g. "light"). - An of strings of all known entities (with state) at the time. - - - - Provides access to the error log API for retrieving the current error log messages. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of error log entries. - - An containing error log entries. - - - - Provides access to the event API for retrieving information about events and firing events. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of event types from the current Home Assistant instance. - - A list of representing the available event types. - - - - Fires an event of type on the event bus. - - An with a message on if the event fired successfully or not. - - - - Provides access to the history API for retrieving and querying for historical state information. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of ALL historical states for all entities for the past 1 day. WARNING: On larger HA installs, this can return 300+ entities, over 4 MB of data, and take 20+ seconds. - - A representing a 24-hour history snapshot for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified day ( + 24 hours). WARNING: On larger HA installs, this can return 300+ entities, over 4 MB of data, and take 20+ seconds. - - A representing a 24-hour history snapshot starting from for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified time range, from to . WARNING: On larger HA installs, for multiple days, this can return A LOT of data and potentially take a LONG time to return. Use with caution! - - A representing a 24-hour history snapshot, from to , for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified time range, from , for the specified . WARNING: On larger HA installs, for multiple days, this can return A LOT of data and potentially take a LONG time to return. Use with caution! - - A representing a 24-hour history snapshot, from , for the specified , for all entities. - - - - Retrieves a list of historical states for the specified for the specified time range, from to . - - The entity ID to filter on. - The earliest history entry to retrieve. - The most recent history entry to retrieve. - A of history snapshots for the specified , from to . - - - - Retrieves a list of historical states for the specified for the past 1 day. - - The entity ID to retrieve state history for. - A representing a 24-hour history snapshot for the specified . - - - - Provides access to the info API for retrieving information about Supervisor, Core and Host. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves Supervisor information. - - A representing Supervisor informatio. - - - - Retrieves Host information. - - A representing Host informatio. - - - - Retrieves Core information. - - A representing Host informatio. - - - - Provides access to the Logbook API for retrieving and querying for change events. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of ALL logbook states for all entities for the past 1 day. - - A representing a 24-hour history snapshot for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified day ( + 24 hours). WARNING: On larger HA installs, this can return 300+ entities, over 4 MB of data, and take 20+ seconds. - - A representing a 24-hour history snapshot starting from for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified time range, from to . WARNING: On larger HA installs, for multiple days, this can return A LOT of data and potentially take a LONG time to return. Use with caution! - - A representing a 24-hour history snapshot, from to , for all entities. - - - - Retrieves a list of ALL historical states for all entities for the specified time range, from , for the specified . WARNING: On larger HA installs, for multiple days, this can return A LOT of data and potentially take a LONG time to return. Use with caution! - - A representing a 24-hour history snapshot, from , for the specified , for all entities. - - - - Retrieves a list of historical states for the specified for the specified time range, from to . - - The entity ID to filter on. - The earliest history entry to retrieve. - The most recent history entry to retrieve. - A of history snapshots for the specified , from to . - - - - Retrieves a list of historical states for the specified for the past 1 day. - - The entity ID to retrieve state history for. - A representing a 24-hour history snapshot for the specified . - - - - Provides access to the root API call (located at /api/) to ensure the API is working normally. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves the API status message for the Home Assistant instance, to ensure it is running. - - A indicating the status of the connected instance. - - - - Provides access to the service API for retrieving information about services and calling services. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of current services, separated into service domains. - - A representing available services grouped by domain. - - - - Calls a service using the given , , and optionally, . - - The domain of the service (e.g. "light"). - The name of the service (e.g. "turn_on"). - Optional. An object representing the fields/parameters to pass to the service. Can be an anonymous type, or a Dictionary<string, object>. - - - - - Calls a service using the given fully-qualified , and optionally, . - - The fully-qualified service name (e.g. "light.turn_on"). - Optional. An object representing the fields/parameters to pass to the service. Can be an anonymous type, or a Dictionary<string, object>. - - - - - Calls a service using the given , , and optionally, . - - The domain of the service (e.g. "light"). - The name of the service (e.g. "turn_on"). - Optional. A JSON string representing the fields/parameters to pass to the service. Ensure the JSON is a well-formatted object. - - - - - Calls a service using the given fully-qualified , and optionally, . - - The fully-qualified service name (e.g. "light.turn_on"). - Optional. A JSON string representing the fields/parameters to pass to the service. Ensure the JSON is a well-formatted object. - - - - - Calls a service using the given fully-qualified and one or more . - - The fully-qualified service name (e.g. "light.turn_on"). - The entity IDs to pass to the service (using the entity_ids parameter). - - - - - Provides access to the states API for retrieving information about the current state of entities. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves a list of current entities and their states. - - A representing the current state. - - - - Retrieves the state of an entity by its ID. - - A representing the current state of the requested . - - - - Sets the state of an entity. If the entity does not exist, it will be created. - - The entity ID of the state to change. - The new state value. - Optional. The attributes to set. - A representing the updated state of the updated . - - - - Provides access to the info API for retrieving statistics about Supervisor and Core. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Retrieves Supervisor information. - - A representing Supervisor stats. - - - - Retrieves Core stats. - - A representing Core stats. - - - - Provides access to the template API for rendering Home Assistant templates. - - - - - Initializes a new instance of the . - - The preconfigured to communicate with a Home Assistant instance. - - - - Renders a template and returns the resulting output as a string. - - A string of the rendered template output. - - - - Represents a failed HTTP call to a Home Assistant endpoint. - - - - - Gets the status code for the HTTP response. - - - - - Gets the network description, if the error was at the network level. - - - - - Gets the original request path. - - - - - Gets the error response body. - - - - - Initializes a new HttpResponseException. - - - - - Initializes a new HttpResponseException. - - - - - Initializes a new HttpResponseException. - - - - - The exception that occurs when a Supervisor-only API call is made to a non-Supervisor environment. - - - - - Initializes a new instance of the SupervisorNotFoundException. - - - - - Initializes a new instance of the SupervisorNotFoundException. - - - - - Initializes a new instance of the SupervisorNotFoundException. - - - - - Represents Add-on object. - - - - - Gets or sets the description. - - - - - True if icon is available, otherwise False. - - - - - True if logo is available, otherwise False. - - - - - Gets or sets the name. - - - - - Gets or sets the repository. - - - - - Gets or sets the slug. - - - - - Gets or sets the state. - - - - - True if update is available, otherwise False. - - - - - Gets or sets the version. - - - - - Gets or sets the latest version. - - - - - Represents automation object. - - - - - Gets or sets the ID. - - - - - Gets or sets the alias. - - - - - Gets or sets the description. - - - - - Gets or sets the actions. - - - - - Gets or sets the conditions. - - - - - Gets or sets the triggers. - - - - - Represents the automation result. - - - - - Gets or sets the automation result. - - - - - Represents a calendar item from the Calendar API. - - - - - Gets or sets the Attendee list, if applicable. - - - - - Gets or sets the Created time of the event. - - - - - Gets or sets the creator of the event. - - - - - Gets or sets the end time of the event. - - - - - Gets or sets the eTag for this object. - - - - - Gets or sets the HTML link for this calendar item. - - - - - Gets or sets the iCal UID for this calendar item. - - - - - Gets or sets the unique ID of this calendar item. - - - - - Gets or sets the kind of item (e.g. "calendar#event"). - - - - - Gets or sets the location of the event. - - - - - Gets or sets the organizer of the event. - - - - - Gets or sets the original start time of the event. - - - - - Gets or sets the recurring ID, if this is a recurring item. - - - - - Gets or sets if there are any reminders for this event. - - - - - Gets or sets the sequence number of this event, if applicable. - - - - - Gets or sets the start time of the event. - - - - - Gets or sets the status of this event (e.g. "confirmed"). - - - - - Gets or sets the summary, or title, of this event. - - - - - Gets or sets the transparency of this event (e.g. "transparent"). - - - - - Gets or sets the timestamp when this item was last updated. - - - - - Represents a person (an organizer, an attendee, etc) for an event. - - - - - Gets or sets the display name for this person. - - - - - Gets or sets the person's e-mail address. - - - - - Gets or sets the response if this is an attendee person (e.g. "accepted"). - - - - - Gets or sets if this is the same person as the owner of this calendar (yourself). - - - - - Gets or sets if this is the organizer of the event, for attendees. - - - - - Represents a date/time with a timezone. - - - - - The date of the event, if the event is all-day. - - - - - The date/time of the event, in local time (with an offset). - - - - - The timezone of this date/time. Not always present. - - - - - Represents a reminder override notification. - - - - - The method of notification. - - - - - The number of minutes before the event starts. - - - - - Represents a set of reminders for an event. - - - - - Whether or not to use the account's default reminder period for this event. - - - - - A list of specific notifications or reminders for this event only. - - - - - Represents the Home Assistant configuration object. - - - - - Gets the errors that occurred. This value is if is valid. - - - - - Gets the result of the configuration check. Valid values are valid and invalid. - - - - - Gets a string representation of this object. - - - - - Represents the Home Assistant configuration object. - - - - - Gets or sets the list of components loaded, in the [domain] or [domain].[component] format. - - - - - Gets or sets the relative path to the config directory (usually "/config"). - - - - - Gets or sets the config source, or type of configuration file (usually "yaml"). - - - - - Gets or sets the elevation (in meters) of the current location. - - - - - Gets or sets the latitude of the current location. - - - - - Gets or sets the longitude of the current location. - - - - - Gets or sets the location's friendly name. - - - - - Gets or sets the time zone name (in "tz database" name format, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - - - - - Gets or sets information about the various measurement unit preferences. - - - - - Gets or sets the version of Home Assistant that is currently running (e.g. 0.96.1). - - - - - Gets or sets a list of relative paths that are approved to be exposed externally (e.g. /config/www). - - - - - Gets a string representation of this object. - - - - - Represents an entity state's context. - - - - - Gets or sets the ID of this context. - - - - - Gets or sets the Parent Context ID if this element is a child of another context, otherwise . - - - - - Gets or sets the User ID of this element, or for the default user or no user. - - - - - Gets a string representation of this object. - - - - - Represents Core info object. - - - - - Gets or sets the processor architecture. - - - - - Gets or sets the audio input. - - - - - Gets or sets the audio output. - - - - - True if booted, otherwise False. - - - - - Gets or sets the image. - - - - - Gets or sets the IP address. - - - - - Gets or sets the last_version. - - - - - Gets or sets the machine. - - - - - Gets or sets the port. - - - - - True if SSL is enabled, otherwise False. - - - - - True if update is available, otherwise False. - - - - - Gets or sets the version. - - - - - Gets or sets the latest version. - - - - - Gets or sets the wait boot timeout. - - - - - True if watchdog is enabled, otherwise False. - - - - - Represents a discovery info object, used to convey basic information about the HA instance. - - - - - Gets or sets the Base URL for the instance (e.g. http://192.168.0.2:8123). - - - - - Gets or sets the location name for this instance (e.g. "Home"). - - - - - Gets or sets whether or not a password is required to use the API. (should most always be "true"). - - - - - Gets or sets the current version of Home Assistant (e.g. 0.96.1). - - - - - Gets a string representation of this object. - - - - - Represents an error log entry. - - - - - Gets a Regex object representing a new log line. - - - - - Gets a Regex object representing an error line. - - - - - Gets a Regex object representing a warning line. - - - - - Gets the full, raw log text. - - - - - Gets the list of log entries. - - - - - Gets only log lines with a type of "ERROR". - - - - - Gets only log lines with a type of "WARNING". - - - - - Gets the most recent of entries, sorted newest first. - - The number of entries to retrieve. - A list of log entries of the specified , newest first. - - - - Initializes a new instance of the error log object with the specified data. - - The raw log data to parse. - - - - Parses the raw log text and splits each entry out based on whether or not the line starts with a date. - - The log text to parse. - A parsed list of log entries. - - - - Represents the result of an event firing. - - - - - Gets the resulting message from the event fire command. - - - - - Represents an event definition in Home Assistant. - - - - - Gets the global event string (*). - - - - - Gets the event's name. - - - - - Gets the listener count for this event. - - - - - Gets a string representation of this object. - - - - - Represents a list of one or more historical states for an entity. - - - - - Gets the EntityId for the state objects in this list. - - - - - Gets the earliest point in time represented by this history list. - - - - - Gets the most recent point in time represented by this history list. - - - - - Gets a string representation of this object. - - - - - Represents Host info object. - - - - - Gets or sets the chassis type. - - - - - Gets or sets the CPE string. - - - - - Gets or sets the deployment type (e.g. production). - - - - - Gets or sets the disk free, expressed in GB. - - - - - Gets or sets the disk total, expressed in GB. - - - - - Gets or sets the disk used, expressed in GB. - - - - - Gets or sets the feature list. - - - - - Gets or sets the hostname. - - - - - Gets or sets the kernel version. - - - - - Gets or sets the operating system. - - - - - Represents a logbook entry object. The only consistently available properties are , , and . - - - - - Gets or sets the entity ID for this entry. - - - - - Gets or sets the domain. - - - - - Gets or sets the context user ID associated with this entry. - - - - - Gets or sets the context entity ID associated with this entry. (For example, which automation triggered this change?) - - - - - Gets or sets the friendly name for the . This is sometimes the same as . - - - - - Gets or sets the type of associated change, for example, if an automation triggered this event, the value is 'automation_triggered'. - - - - - Gets or sets the domain associated with the context entity. For example, if an automation triggered this change, the value is 'automation'. - - - - - Gets or sets the name associated with the context entity (e.g. the name of the automation). This is sometimes the same as . - - - - - Gets or sets the new state that the entity transitioned to. - - - - - Gets or sets the source for this logbook entry (i.e. what triggered this change). - - - - - Gets or sets the message, a brief description of what happened. - - - - - Gets or sets the category name for this entry. - - - - - Gets or sets the timestmap when this entry occurred.. - - - - - Represents a basic message object returned from the server (e.g. an object with a "message" property). - - - - - Gets or sets the message for this message object. - - - - - Represents Home Assistant response. - - - - - - Gets or sets the result. - - - - - Gets or sets the data. - - - - - Represents a service domain definition in Home Assistant. - - - - - Gets the service domain's name. - - - - - Gets the list of services in this domain. - - - - - Gets a flat, fully-qualified list of services in this service domain. - - - - - Retrieves a service object from this domain by its name, or returns if the service does not exist. - - The service name to retrieve. - The , if the name exists in this domain, otherwise . - - - - If the specified exists, populates the with the - fully qualified service name (domain.service), and returns . Otherwise, if the service does not exist, - returns . - - The relative name of the service to look up in this service domain. - Upon successful match, will be set to the fullly qualified service name (domain.service). Otherwise, . - if a match was found, otherwise . - - - - Gets a string representation of this object. - - - - - Represents a signle field in a service call. - - - - - Gets or sets the description of this field. - - - - - Gets or sets the example text for this field (may be ). - - - - - Represents a signle service definition. - - - - - The description of the service object. - - - - - The fields/parameters that the service supports. - - - - - Represents a single entity's state. - - - - - Gets or sets the Entity ID that this state represents. - - - - - Gets or sets the string representation of the state that this entity is currently in. - - - - - Gets or sets the entity's current attributes and values. - - - - - Gets or sets the context for this entity's state. - - - - - Gets or sets the UTC date and time that this state was last changed. - - - - - Gets or sets the UTC date and time that this state was last updated. - - - - - Attempts to get the value of the specified attribute by , and cast the value to type . - - Thrown when the specified type cannot be cast to the attribute's current value. - The desired type to cast the attribute value to. - The name of the attribute to retrieve the value for. - The attribute's current value, cast to type . - - - - Gets a string representation of this entity's state. - - - - - Represents the stats object. - - - - - Gets or sets the number of bulk reads. - - - - - Gets or sets the number of bulk writes. - - - - - Gets or sets the CPU usage percent. - - - - - Gets or sets the memory limit. - - - - - Gets or sets the memory usage percent. - - - - - Gets or sets the memory usage. - - - - - Gets or sets the network rx. - - - - - Gets or sets the network tx. - - - - - Represents Supervisor info object. - - - - - Gets or sets the addons. - - - - - Gets or sets the addons repositories. - - - - - Gets or sets the processor architecture. - - - - - Gets or sets the processor channel. - - - - - True if debug, otherwise False. - - - - - True if debug block, otherwise False. - - - - - True if diagnostics is available, otherwise False. - - - - - True if healthy, otherwise False. - - - - - Gets or sets the IP address. - - - - - Gets or sets the logging. - - - - - True if supported, otherwise False. - - - - - Gets or sets the timezone. - - - - - True if update is available, otherwise False. - - - - - Gets or sets the version. - - - - - Gets or sets the latest version. - - - - - Gets or sets the wait boot timeout. - - - - - Represents the Unit System, part of the . - - - - - The length unit (e.g. mi, km). - - - - - The mass unit (e.g. lb, kg). - - - - - The pressure unit (e.g. psi, bar). - - - - - The temperature unit including degree symbol (e.g. °F, °C). - - - - - The volume unit (e.g. gal, L). - - - - - Deserializes the "Example" field into a string regardless of its type. - - - - - Always attempt to deserialize examples. - - - - - Read the JSON into a string. - - - - - Read-only, no writing. - - - - diff --git a/src/HASS.Agent/HASS.Agent/Libraries/HotkeyListener.dll b/src/HASS.Agent/HASS.Agent/Libraries/HotkeyListener.dll deleted file mode 100644 index 6907c33c..00000000 Binary files a/src/HASS.Agent/HASS.Agent/Libraries/HotkeyListener.dll and /dev/null differ diff --git a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs index 0431213b..43eae3a5 100644 --- a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs +++ b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs @@ -424,7 +424,7 @@ public async Task PublishAsync(MqttApplicationMessage message) /// /// /// - public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false) + public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discoverable, string domain, bool clearConfig = false, bool migration = false) { if (!Variables.AppSettings.MqttEnabled || !IsConnected()) return; @@ -442,7 +442,8 @@ public async Task AnnounceAutoDiscoveryConfigAsync(AbstractDiscoverable discover if (clearConfig) { - messageBuilder.WithPayload(Array.Empty()); + var payload = migration ? Encoding.UTF8.GetBytes("{\"migrate_discovery\": true }") : Array.Empty(); + messageBuilder.WithPayload(payload); } else { @@ -567,35 +568,35 @@ public async Task ClearDeviceConfigAsync() var messageBuilder = new MqttApplicationMessageBuilder() .WithTopic($"{Variables.AppSettings.MqttDiscoveryPrefix}/sensor/{Variables.DeviceConfig.Name}/availability") .WithPayload(Array.Empty()) - .WithRetainFlag(false); + .WithRetainFlag(true); await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build()); messageBuilder = new MqttApplicationMessageBuilder() .WithTopic($"hass.agent/devices/{Variables.DeviceConfig.Name}") .WithPayload(Array.Empty()) - .WithRetainFlag(false); + .WithRetainFlag(true); await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build()); messageBuilder = new MqttApplicationMessageBuilder() .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}") .WithPayload(Array.Empty()) - .WithRetainFlag(false); + .WithRetainFlag(true); await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build()); messageBuilder = new MqttApplicationMessageBuilder() .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}/thumbnail") .WithPayload(Array.Empty()) - .WithRetainFlag(false); + .WithRetainFlag(true); await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build()); messageBuilder = new MqttApplicationMessageBuilder() .WithTopic($"hass.agent/media_player/{Variables.DeviceConfig.Name}/state") .WithPayload(Array.Empty()) - .WithRetainFlag(false); + .WithRetainFlag(true); await _mqttClient.InternalClient.PublishAsync(messageBuilder.Build()); } @@ -710,13 +711,22 @@ private static ManagedMqttClientOptions GetOptions() var clientOptionsBuilder = new MqttClientOptionsBuilder() .WithClientId(Variables.AppSettings.MqttClientId) - .WithTcpServer(Variables.AppSettings.MqttAddress, Variables.AppSettings.MqttPort) .WithCleanSession() .WithWillTopic($"{Variables.AppSettings.MqttDiscoveryPrefix}/sensor/{Variables.DeviceConfig.Name}/availability") .WithWillPayload("offline") .WithWillRetain(Variables.AppSettings.MqttUseRetainFlag) .WithKeepAlivePeriod(TimeSpan.FromSeconds(15)); + if (Variables.AppSettings.MqttUseWebSocket) + { + clientOptionsBuilder.WithWebSocketServer(o => o.WithUri($"{Variables.AppSettings.MqttAddress}:{Variables.AppSettings.MqttPort}")); + Log.Information("[MQTT] Using WebSocket for the connection"); + } + else + { + clientOptionsBuilder.WithTcpServer(Variables.AppSettings.MqttAddress, Variables.AppSettings.MqttPort); + } + if (!string.IsNullOrEmpty(Variables.AppSettings.MqttUsername)) clientOptionsBuilder.WithCredentials(Variables.AppSettings.MqttUsername, Variables.AppSettings.MqttPassword); @@ -885,5 +895,66 @@ private static void HandleActionReceived(MqttApplicationMessage applicationMessa command.TurnOnWithAction(payload); } + + //Note(Amadeo): ideally this should use logic of the manager but yeah, we live with what we've got I guess + internal static async Task TestConnection(string address, int port, bool useTls, bool useWebSocket, string username, string password) + { + var mqttFactory = new MqttFactory(); + + using (var mqttClient = mqttFactory.CreateMqttClient()) + { + var clientOptionsBuilder = new MqttClientOptionsBuilder() + .WithClientId("hassAgentConnectionTest") + .WithCleanSession() + .WithKeepAlivePeriod(TimeSpan.FromSeconds(1)); + + if (useWebSocket) + { + clientOptionsBuilder.WithWebSocketServer(o => o.WithUri($"{address}:{port}")); + } + else + { + clientOptionsBuilder.WithTcpServer(address, port); + } + + if (!string.IsNullOrEmpty(username)) + { + clientOptionsBuilder.WithCredentials(username, password); + } + + var clientTlsOptions = new MqttClientTlsOptions() + { + UseTls = useTls, + AllowUntrustedCertificates = true, + SslProtocol = useTls ? SslProtocols.Tls12 : SslProtocols.None, + IgnoreCertificateChainErrors = true, + IgnoreCertificateRevocationErrors = true, + CertificateValidationHandler = delegate (MqttClientCertificateValidationEventArgs _) + { + return true; + } + }; + + clientOptionsBuilder.WithTlsOptions(clientTlsOptions); + var options = clientOptionsBuilder.Build(); + + try + { + using var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + await mqttClient.ConnectAsync(options, timeoutToken.Token); + + return mqttClient.IsConnected; + } + catch + { + return false; + } + finally + { + await mqttClient.DisconnectAsync(); + mqttClient.Dispose(); + } + } + } } } diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HumanPresenceSensor.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HumanPresenceSensor.cs new file mode 100644 index 00000000..9c51c8c4 --- /dev/null +++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/HumanPresenceSensor.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Sensors; + +namespace HASS.Agent.Managers.DeviceSensors +{ + internal class HumanPresenceSensor : IInternalDeviceSensor + { + public const string AttributeDistanceInMilimeters = "DistanceInMilimeters"; + public const string AttributeEngagement = "Engagement"; + + private Windows.Devices.Sensors.HumanPresenceSensor _humanPresenceSensor; + + public string MeasurementType { get; } = string.Empty; + public string UnitOfMeasurement { get; } = string.Empty; + + public bool Available => _humanPresenceSensor != null; + public InternalDeviceSensorType Type => InternalDeviceSensorType.ProximitySensor; + public string Measurement + { + get + { + if (!Available) + return null; + + var sensorReading = _humanPresenceSensor.GetCurrentReading(); + if (sensorReading == null) + return null; + + Attributes = new Dictionary() + { + [AttributeDistanceInMilimeters] = sensorReading.DistanceInMillimeters.ToString(), + [AttributeEngagement] = sensorReading.Engagement.ToString(), + }; + + return (sensorReading.Presence == HumanPresence.Present).ToString(); + } + } + + public bool IsNumeric { get; } = false; + + public Dictionary Attributes { get; private set; } + + public HumanPresenceSensor(Windows.Devices.Sensors.HumanPresenceSensor humanPresenceSensor) + { + _humanPresenceSensor = humanPresenceSensor; + } + + public void UpdateInternalSensor(Windows.Devices.Sensors.HumanPresenceSensor humanPresenceSensor) + { + _humanPresenceSensor = humanPresenceSensor; + } + } +} diff --git a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InternalDeviceSensorsManager.cs b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InternalDeviceSensorsManager.cs index 1e3e9e10..a9707ccf 100644 --- a/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InternalDeviceSensorsManager.cs +++ b/src/HASS.Agent/HASS.Agent/Managers/DeviceSensors/InternalDeviceSensorsManager.cs @@ -20,20 +20,51 @@ public static async Task Initialize() deviceWatcher = DeviceInformation.CreateWatcher(Windows.Devices.Sensors.ProximitySensor.GetDeviceSelector()); deviceWatcher.Added += OnProximitySensorAdded; - deviceSensors.Add(new AccelerometerSensor(Accelerometer.GetDefault())); - deviceSensors.Add(new ActivitySensor(await Windows.Devices.Sensors.ActivitySensor.GetDefaultAsync())); - deviceSensors.Add(new AltimeterSensor(Altimeter.GetDefault())); - deviceSensors.Add(new BarometerSensor(Barometer.GetDefault())); - deviceSensors.Add(new CompassSensor(Compass.GetDefault())); - deviceSensors.Add(new GyrometerSensor(Gyrometer.GetDefault())); - deviceSensors.Add(new HingeAngleSensor(await Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync())); - deviceSensors.Add(new InclinometerSensor(Inclinometer.GetDefault())); - deviceSensors.Add(new LightSensor(Windows.Devices.Sensors.LightSensor.GetDefault())); - deviceSensors.Add(new MagnetometerSensor(Magnetometer.GetDefault())); - deviceSensors.Add(new OrientationSensor(Windows.Devices.Sensors.OrientationSensor.GetDefault())); - deviceSensors.Add(new PedometerSensor(await Pedometer.GetDefaultAsync())); - deviceSensors.Add(new ProximitySensor(await GetDefaultProximitySensorAsync())); - deviceSensors.Add(new SimpleOrientationSensor(Windows.Devices.Sensors.SimpleOrientationSensor.GetDefault())); + //TODO(Amadeo): this is ugly + try { deviceSensors.Add(new AccelerometerSensor(Accelerometer.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Accelerometer not added"); } + + try { deviceSensors.Add(new ActivitySensor(await Windows.Devices.Sensors.ActivitySensor.GetDefaultAsync()));} + catch { Log.Debug("[INTERNALSENSORS] ActivitySensor not added"); } + + try { deviceSensors.Add(new AltimeterSensor(Altimeter.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Altimeter not added"); } + + try { deviceSensors.Add(new BarometerSensor(Barometer.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Barometer not added"); } + + try { deviceSensors.Add(new CompassSensor(Compass.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Compass not added"); } + + try { deviceSensors.Add(new GyrometerSensor(Gyrometer.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Gyrometer not added"); } + + try { deviceSensors.Add(new HingeAngleSensor(await Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync())); } + catch { Log.Debug("[INTERNALSENSORS] HingeAngleSensor not added"); } + + try { deviceSensors.Add(new HumanPresenceSensor(Windows.Devices.Sensors.HumanPresenceSensor.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] HumanPresenceSensor not added"); } + + try { deviceSensors.Add(new InclinometerSensor(Inclinometer.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Inclinometer not added"); } + + try { deviceSensors.Add(new LightSensor(Windows.Devices.Sensors.LightSensor.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] LightSensor not added"); } + + try { deviceSensors.Add(new MagnetometerSensor(Magnetometer.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] Magnetometer not added"); } + + try { deviceSensors.Add(new OrientationSensor(Windows.Devices.Sensors.OrientationSensor.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] OrientationSensor not added"); } + + try { deviceSensors.Add(new PedometerSensor(await Pedometer.GetDefaultAsync())); } + catch { Log.Debug("[INTERNALSENSORS] Pedometer not added"); } + + try { deviceSensors.Add(new ProximitySensor(await GetDefaultProximitySensorAsync())); } + catch { Log.Debug("[INTERNALSENSORS] ProximitySensor not added"); } + + try { deviceSensors.Add(new SimpleOrientationSensor(Windows.Devices.Sensors.SimpleOrientationSensor.GetDefault())); } + catch { Log.Debug("[INTERNALSENSORS] SimpleOrientationSensor not added"); } Log.Information("[INTERNALSENSORS] Ready"); } diff --git a/src/HASS.Agent/HASS.Agent/Managers/HotKeyManager.cs b/src/HASS.Agent/HASS.Agent/Managers/InternalHotKeyManager.cs similarity index 51% rename from src/HASS.Agent/HASS.Agent/Managers/HotKeyManager.cs rename to src/HASS.Agent/HASS.Agent/Managers/InternalHotKeyManager.cs index b2d9b2cb..9db0495c 100644 --- a/src/HASS.Agent/HASS.Agent/Managers/HotKeyManager.cs +++ b/src/HASS.Agent/HASS.Agent/Managers/InternalHotKeyManager.cs @@ -1,13 +1,15 @@ using HASS.Agent.Commands; using HASS.Agent.HomeAssistant; using HASS.Agent.Shared.Enums; +using NHotkey; using Serilog; -using WK.Libraries.HotkeyListenerNS; namespace HASS.Agent.Managers { - internal class HotKeyManager + internal class InternalHotKeyManager { + internal event EventHandler HotkeyActivated; + /// /// Initializes the quickaction hotkeys /// @@ -31,7 +33,7 @@ internal void ReloadQuickActionsHotKeys() Variables.MainForm?.BeginInvoke(new MethodInvoker(delegate { // remove all bindings - Variables.HotKeyListener?.RemoveAll(); + RemoveAllRegisteredHotkeys(); // reload InitializeQuickActionsHotKeys(); @@ -80,38 +82,56 @@ internal static void ProcessQuickActionHotKey(string hotkey) } } - private static void InitializeGlobalQuickActionsHotKey() + internal void RemoveAllRegisteredHotkeys() + { + foreach (var quickAction in Variables.QuickActions) + { + Variables.HotKeyListener?.Remove(quickAction.HotKey); + } + } + + private void InitializeGlobalQuickActionsHotKey() { Variables.MainForm?.BeginInvoke(new MethodInvoker(delegate { - // check if the quick action hotkey's active - if (!Variables.AppSettings.QuickActionsHotKeyEnabled) return; - - // check if it's configured - if (Variables.QuickActionsHotKey == null || Variables.QuickActionsHotKey.ToString() == "None") return; + // check if it's enabled and configured + if (!Variables.AppSettings.QuickActionsHotKeyEnabled || string.IsNullOrWhiteSpace(Variables.QuickActionsHotKey) || Variables.QuickActionsHotKey == "None") + { + return; + } // all good, bind - Variables.HotKeyListener?.Add(Variables.QuickActionsHotKey); - - Log.Information("[HOTKEY] Completed bind for global quickaction hotkey"); + var globalHotkey = HotkeyFromString(Variables.QuickActionsHotKey); + if (globalHotkey.Item1 != Keys.None) + { + Variables.HotKeyListener?.AddOrReplace(Variables.QuickActionsHotKey, globalHotkey.Item1 | globalHotkey.Item2, OnHotkeyActivated); + Log.Information("[HOTKEY] Completed bind for global quickaction hotkey"); + } + else + { + Log.Warning("[HOTKEY] Could not bind for global quickaction hotkey"); + } })); } - private static void InitializeIndividualQuickActionsHotKeys() + private void InitializeIndividualQuickActionsHotKeys() { Variables.MainForm?.BeginInvoke(new MethodInvoker(delegate { var count = 0; - foreach (var quickAcion in Variables.QuickActions.Where(x => x.HotKeyEnabled && !string.IsNullOrWhiteSpace(x.HotKey))) + foreach (var quickAcion in Variables.QuickActions.Where(x => + x.HotKeyEnabled && !string.IsNullOrWhiteSpace(x.HotKey))) { try { - Variables.HotKeyListener?.Add(new Hotkey(quickAcion.HotKey)); + var hotkey = HotkeyFromString(quickAcion.HotKey); + Variables.HotKeyListener?.AddOrReplace(quickAcion.HotKey, hotkey.Item1 | hotkey.Item2, OnHotkeyActivated); count++; } catch (Exception ex) { - Log.Fatal(ex, "[HOTKEYS] Unable to bind individual quickaction hotkey '{hotkey}': {msg}", quickAcion.HotKey, ex.Message); + Log.Fatal(ex, "[HOTKEYS] Unable to bind individual quickaction hotkey '{hotkey}': {msg}", + quickAcion.HotKey, ex.Message); } } @@ -123,15 +143,56 @@ private static void InitializeIndividualQuickActionsHotKeys() /// /// Process a changed quickactions hotkey /// - /// + /// /// - internal void QuickActionsHotKeyChanged(Hotkey previousKey, bool register = true) + internal void QuickActionsHotKeyChanged(string previousHotkey, bool register = true) { Variables.MainForm?.BeginInvoke(new MethodInvoker(delegate { - Variables.HotKeyListener?.Remove(previousKey); - if (register && Variables.QuickActionsHotKey != null && Variables.QuickActionsHotKey.KeyCode != Keys.None) Variables.HotKeyListener?.Add(Variables.QuickActionsHotKey); + Variables.HotKeyListener?.Remove(previousHotkey); + if (!register || Variables.QuickActionsHotKey == null + || !string.IsNullOrWhiteSpace(Variables.QuickActionsHotKey)) + { + return; + } + + var parsedHotkey = HotkeyFromString(previousHotkey); + if (parsedHotkey.Item1 != Keys.None) + { + Variables.HotKeyListener?.AddOrReplace(Variables.QuickActionsHotKey, + parsedHotkey.Item1 | parsedHotkey.Item2, OnHotkeyActivated); + } })); } + + private void OnHotkeyActivated(object sender, HotkeyEventArgs e) + { + HotkeyActivated?.Invoke(sender, e); + } + + private static (Keys, Keys) HotkeyFromString(string stringHotkey) + { + if (string.IsNullOrWhiteSpace(stringHotkey)) + { + return (Keys.None, Keys.None); + } + + var parts = stringHotkey.Split("+", 2, StringSplitOptions.TrimEntries); + var modifiersString = parts.Length == 2 ? parts[0] : string.Empty; + var keyString = parts.Length == 2 ? parts[1] : parts[0]; + + var modifiers = Keys.None; + foreach (var modKey in modifiersString.Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + if (!Enum.TryParse(modKey, out var parsedModifiers)) + { + continue; + } + + modifiers |= parsedModifiers; + } + + return Enum.TryParse(keyString, out var parsedKey) ? (parsedKey, modifiers) : (Keys.None, Keys.None); + } } -} +} \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Managers/StorageManager.cs b/src/HASS.Agent/HASS.Agent/Managers/StorageManager.cs index a4caf063..7d546a68 100644 --- a/src/HASS.Agent/HASS.Agent/Managers/StorageManager.cs +++ b/src/HASS.Agent/HASS.Agent/Managers/StorageManager.cs @@ -5,6 +5,7 @@ using System.Security.Policy; using System.Text.RegularExpressions; using System.Threading; +using HASS.Agent.Functions; using Serilog; using Task = System.Threading.Tasks.Task; @@ -12,11 +13,9 @@ namespace HASS.Agent.Managers { internal static class StorageManager { - private static bool IsAbsoluteUrl(string url) => Regex.IsMatch(url, "^https?://"); - - private static string GetElementUrl(string url) + public static string GetElementUrl(string url) { - if (IsAbsoluteUrl(url)) + if (HelperFunctions.IsAbsoluteUrl(url)) { return url; } @@ -71,7 +70,7 @@ private static string GetElementUrl(string url) } var elementUrl = GetElementUrl(uri); - if (!IsAbsoluteUrl(elementUrl)) + if (!HelperFunctions.IsAbsoluteUrl(elementUrl)) { Log.Error("[STORAGE] Unable to download image: only HTTP & file:// uri's are allowed, got: {uri}", uri); diff --git a/src/HASS.Agent/HASS.Agent/Managers/UpdateManager.cs b/src/HASS.Agent/HASS.Agent/Managers/UpdateManager.cs index a3ac28d6..eba24d26 100644 --- a/src/HASS.Agent/HASS.Agent/Managers/UpdateManager.cs +++ b/src/HASS.Agent/HASS.Agent/Managers/UpdateManager.cs @@ -23,10 +23,14 @@ internal static async void Initialize() // wait a minute in case Windows is busy launching await Task.Delay(TimeSpan.FromMinutes(1)); + // initial check - var (isAvailable, version) = await CheckIsUpdateAvailableAsync(); - if (isAvailable) - ProcessAvailableUpdate(version); + if (Variables.AppSettings.CheckForUpdates) + { + var (isAvailable, version) = await CheckIsUpdateAvailableAsync(); + if (isAvailable) + ProcessAvailableUpdate(version); + } // start periodic check _ = Task.Run(PeriodicUpdateCheck); @@ -92,7 +96,7 @@ private static void ProcessAvailableUpdate(PendingUpdate pendingUpdate) return (false, pendingUpdate); // we are interested only in releases created after 30.11.2023 - if(latestRelease.CreatedAt.CompareTo(s_releaseCutoff) <= 0) + if (latestRelease.CreatedAt.CompareTo(s_releaseCutoff) <= 0) return (false, pendingUpdate); var isNewer = UpdateIsNewer(Variables.Version, latestRelease.TagName); @@ -107,7 +111,7 @@ private static void ProcessAvailableUpdate(PendingUpdate pendingUpdate) catch (Exception ex) { Log.Fatal(ex, "[UPDATER] Error checking for updates: {err}", ex.Message); - return (false, pendingUpdate); + return (false, null); } } @@ -156,7 +160,7 @@ private static void ProcessAvailableUpdate(PendingUpdate pendingUpdate) catch (Exception ex) { Log.Fatal(ex, "[UPDATER] Error checking for beta updates: {err}", ex.Message); - return (false, pendingUpdate); + return (false, null); } } @@ -170,9 +174,11 @@ internal static PendingUpdate GetLatestVersionInfo(PendingUpdate pendingUpdate) { // get the installer var installerAssetUrl = string.Empty; - var installerAsset = pendingUpdate.GitHubRelease.Assets.Select(x => x).FirstOrDefault(y => y.BrowserDownloadUrl.ToLower().EndsWith("installer.exe")); + var installerVersionSuffix = IntPtr.Size == 8 ? "installer.exe" : "installer.x86.exe"; + + var installerAsset = pendingUpdate.GitHubRelease.Assets.Select(x => x).FirstOrDefault(y => y.BrowserDownloadUrl.ToLower().EndsWith(installerVersionSuffix)); if (installerAsset == null) - Log.Error("[UPDATER] No .installer.exe asset found for release: {v}", pendingUpdate.GitHubRelease.TagName); + Log.Error("[UPDATER] No installer asset found for release: {v}", pendingUpdate.GitHubRelease.TagName); else installerAssetUrl = installerAsset.BrowserDownloadUrl; diff --git a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs index a7f705c7..c5de6e89 100644 --- a/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs +++ b/src/HASS.Agent/HASS.Agent/Models/Config/AppSettings.cs @@ -34,6 +34,7 @@ public AppSettings() public bool TrayIconShowWebView { get; set; } = false; public int TrayIconWebViewWidth { get; set; } = 700; public int TrayIconWebViewHeight { get; set; } = 560; + public int TrayIconWebViewScreen { get; set; } = -1; public string TrayIconWebViewUrl { get; set; } = string.Empty; public bool TrayIconWebViewBackgroundLoading { get; set; } = false; public bool TrayIconWebViewShowMenuOnLeftClick { get; set; } = false; @@ -70,6 +71,7 @@ public AppSettings() public bool MqttEnabled { get; set; } = true; public string MqttAddress { get; set; } = "homeassistant.local"; public int MqttPort { get; set; } = 1883; + public bool MqttUseWebSocket { get; set; } = false; public bool MqttUseTls { get; set; } public bool MqttAllowUntrustedCertificates { get; set; } = true; public string MqttUsername { get; set; } = string.Empty; diff --git a/src/HASS.Agent/HASS.Agent/Models/Internal/PrinterInfo.cs b/src/HASS.Agent/HASS.Agent/Models/Internal/PrinterInfo.cs index a99b636c..c8ab07ba 100644 --- a/src/HASS.Agent/HASS.Agent/Models/Internal/PrinterInfo.cs +++ b/src/HASS.Agent/HASS.Agent/Models/Internal/PrinterInfo.cs @@ -67,7 +67,7 @@ public PrintJobInfo() // } - public string Nmae { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; public string Submitter { get; set; } = string.Empty; public string Status { get; set; } = "None"; public int NumberOfPages { get; set; } = 0; diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs index 711c163c..0e4dcc85 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.Designer.cs @@ -454,6 +454,7 @@ internal static string CommandsManager_HibernateCommandDescription { /// /// Looks up a localized string similar to Simulates a single keypress. + ///Making this command a "switch" type will make the button pressed as long as the switch is turned on. /// ///Click on the 'keycode' textbox and press the key you want simulated. The corresponding keycode will be entered for you. ///For TAB key please use LCTRL+TAB. @@ -560,6 +561,16 @@ internal static string CommandsManager_MonitorSleepCommandDescription { } } + /// + /// Looks up a localized string similar to Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. + ///Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep.. + /// + internal static string CommandsManager_MonitorSleepPowerPlanCommandDescription { + get { + return ResourceManager.GetString("CommandsManager_MonitorSleepPowerPlanCommandDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tries to wake up all monitors by simulating a 'arrow up' keypress.. /// @@ -740,6 +751,17 @@ internal static string CommandsManager_WebViewCommandDescription { } } + /// + /// Looks up a localized string similar to Puts the machine to sleep using WinForms API. + /// + ///Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration.. + /// + internal static string CommandsManager_WinformsSleepCommandDescription { + get { + return ResourceManager.GetString("CommandsManager_WinformsSleepCommandDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Configure Command &Parameters. /// @@ -1418,6 +1440,15 @@ internal static string CommandType_MonitorSleepCommand { } } + /// + /// Looks up a localized string similar to MonitorSleepPowerPlan. + /// + internal static string CommandType_MonitorSleepPowerPlanCommand { + get { + return ResourceManager.GetString("CommandType_MonitorSleepPowerPlanCommand", resourceCulture); + } + } + /// /// Looks up a localized string similar to MonitorWake. /// @@ -1517,6 +1548,15 @@ internal static string CommandType_WebViewCommand { } } + /// + /// Looks up a localized string similar to WinformsSleep. + /// + internal static string CommandType_WinformsSleepCommand { + get { + return ResourceManager.GetString("CommandType_WinformsSleepCommand", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error, please check logs for more information.. /// @@ -2444,6 +2484,15 @@ internal static string ConfigMqtt_CbUseRetainFlag { } } + /// + /// Looks up a localized string similar to Use &WebSocket. + /// + internal static string ConfigMqtt_CbUseWebSocket { + get { + return ResourceManager.GetString("ConfigMqtt_CbUseWebSocket", resourceCulture); + } + } + /// /// Looks up a localized string similar to Broker IP Address or Hostname. /// @@ -3173,6 +3222,15 @@ internal static string ConfigTrayIcon_LblWebViewUrl { } } + /// + /// Looks up a localized string similar to Screen to use for WebView to show. + /// + internal static string ConfigTrayIcon_NumWebViewScreen { + get { + return ResourceManager.GetString("ConfigTrayIcon_NumWebViewScreen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Notify me of &beta releases. /// @@ -3824,6 +3882,15 @@ internal static string HassDomain_Cover { } } + /// + /// Looks up a localized string similar to Fan. + /// + internal static string HassDomain_Fan { + get { + return ResourceManager.GetString("HassDomain_Fan", resourceCulture); + } + } + /// /// Looks up a localized string similar to HASS.Agent Commands. /// @@ -3842,6 +3909,15 @@ internal static string HassDomain_InputBoolean { } } + /// + /// Looks up a localized string similar to InputButton. + /// + internal static string HassDomain_InputButton { + get { + return ResourceManager.GetString("HassDomain_InputButton", resourceCulture); + } + } + /// /// Looks up a localized string similar to Light. /// @@ -4203,6 +4279,15 @@ internal static string Main_CheckForUpdate_MessageBox1 { } } + /// + /// Looks up a localized string similar to Failed checking for update!. + /// + internal static string Main_CheckForUpdateFailed_MessageBox1 { + get { + return ResourceManager.GetString("Main_CheckForUpdateFailed_MessageBox1", resourceCulture); + } + } + /// /// Looks up a localized string similar to Check for Updates. /// @@ -4728,7 +4813,7 @@ internal static string OnboardingDone_LblInfo2 { } /// - /// Looks up a localized string similar to There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + /// Looks up a localized string similar to There's a lot more to tinker with, so make sure you take a look at the Configuration Window! /// /// ///Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -4741,7 +4826,8 @@ internal static string OnboardingDone_LblInfo3 { } /// - /// Looks up a localized string similar to Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated!. + /// Looks up a localized string similar to Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) + ///Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day.. /// internal static string OnboardingDone_LblInfo6 { get { @@ -5053,6 +5139,24 @@ internal static string OnboardingManager_OnboardingTitle_Updates { } } + /// + /// Looks up a localized string similar to Unable to connect.. + /// + internal static string OnboardingMqtt_BtnTest_MessageError { + get { + return ResourceManager.GetString("OnboardingMqtt_BtnTest_MessageError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connection OK!. + /// + internal static string OnboardingMqtt_BtnTest_MessageOk { + get { + return ResourceManager.GetString("OnboardingMqtt_BtnTest_MessageOk", resourceCulture); + } + } + /// /// Looks up a localized string similar to Enable MQTT. /// @@ -5904,6 +6008,16 @@ internal static string SensorsConfig_Title { } } + /// + /// Looks up a localized string similar to Provides #RRGGBB color values for system accent colors. + ///Main accent color is the sensor value, additional accent colors are available as attributes.. + /// + internal static string SensorsManager_AccentColorSensorDescription { + get { + return ResourceManager.GetString("SensorsManager_AccentColorSensorDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Provides the ID of the currently active virtual desktop.. /// @@ -6040,7 +6154,10 @@ internal static string SensorsManager_GpuLoadSensorDescription { } /// - /// Looks up a localized string similar to Provides the current temperature of the first GPU.. + /// Looks up a localized string similar to NOTE: This is a non-functioning sensor. + /// + ///Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. + ///Please see documentation for alternative options.. /// internal static string SensorsManager_GpuTemperatureSensorDescription { get { @@ -6155,6 +6272,15 @@ internal static string SensorsManager_MonitorPowerStateSensorDescription { } } + /// + /// Looks up a localized string similar to Provides an ON/OFF value based on whether the focused window name contains configured string.. + /// + internal static string SensorsManager_NamedActiveWindowSensorDescription { + get { + return ResourceManager.GetString("SensorsManager_NamedActiveWindowSensorDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Provides an ON/OFF value based on whether the window is currently open (doesn't have to be active).. /// @@ -7177,6 +7303,15 @@ internal static string SensorType_MonitorPowerStateSensor { } } + /// + /// Looks up a localized string similar to NamedActiveWindow. + /// + internal static string SensorType_NamedActiveWindowSensor { + get { + return ResourceManager.GetString("SensorType_NamedActiveWindowSensor", resourceCulture); + } + } + /// /// Looks up a localized string similar to NamedWindow. /// @@ -7991,6 +8126,15 @@ internal static string ServiceMqtt_CbUseRetainFlag { } } + /// + /// Looks up a localized string similar to Use . + /// + internal static string ServiceMqtt_CbUseWebSocket { + get { + return ResourceManager.GetString("ServiceMqtt_CbUseWebSocket", resourceCulture); + } + } + /// /// Looks up a localized string similar to Broker IP Address or Hostname. /// diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx index 32cf4f28..c7aeedd9 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.de.resx @@ -1352,11 +1352,12 @@ Dein Befehl wird als Argument so wie er ist bereitgestellt, sodass du selbst Anf Simuliert einen einzelnen Tastendruck. +Wenn Sie diesen Befehl als „Schalter“ definieren, wird die Taste gedrückt, solange der Schalter eingeschaltet ist. -Klicken Sie auf das Textfeld „Tastencode“ und drücken Sie die Taste, die Sie simulieren möchten. Der entsprechende Schlüsselcode wird für Sie eingegeben. -Für die TAB-Taste verwenden Sie bitte LCTRL+TAB. +Klicken Sie auf das Textfeld „Tastencode“ und drücken Sie die zu simulierende Taste. Der entsprechende Tastencode wird automatisch eingegeben. +Für die Tabulatortaste verwenden Sie bitte LCTRL+TAB. -Wenn Sie weitere Tasten und/oder Modifikatoren wie STRG benötigen, verwenden Sie den Befehl MultipleKeys. +Wenn Sie weitere Tasten und/oder Modifikatoren wie STRG benötigen, verwenden Sie den Befehl „Mehrere Tasten“. Fuzzy @@ -2176,7 +2177,10 @@ Nimmt derzeit die Lautstärke deines Standardgeräts. Liefert die aktuelle Auslastung der ersten GPU in Prozent. - Liefert die aktuelle Temperatur der ersten GPU. + HINWEIS: Dieser Sensor funktioniert nicht. + +Aufgrund von Sicherheitsbedenken bezüglich Libre Hardware Monitor (Bibliothek, die HASS.Agent Zugriff auf GPU-Temperaturdaten ermöglicht) wurde dieser Sensor aus Gründen der Abwärtskompatibilität beibehalten und gibt immer 0 zurück. +Weitere Optionen finden Sie in der Dokumentation. Stellt einen Datums-/Uhrzeitwert bereit, der den letzten Zeitpunkt enthält, zu dem der Benutzer eine Eingabe vorgenommen hat. @@ -3217,7 +3221,8 @@ Sind Sie sicher, dass Sie diesen Schlüssel trotzdem verwenden möchten? Bist Du sicher, dass Du es so verwenden möchtest? - Die Entwicklung und Wartung dieses Tools (und alles, was es umgibt) nimmt viel Zeit in Anspruch. Wie die meisten Entwickler verwende ich Koffein – wenn Du es also entbehren kannst, ist eine Tasse Kaffee immer sehr willkommen! + Derzeit nehmen wir (das HASS.Agent-Team, das die Abspaltung betreut) keinerlei Spenden an. :) +Sie können aber gerne dem ursprünglichen Autor von HASS.Agent – ​​Sam – eine Spende zukommen lassen! Wo auch immer er sich gerade befindet, eine Tasse Kaffee könnte ihm den Tag verschönern. Tipp: Andere Spendenmethoden sind im Über-Fenster verfügbar. @@ -3531,4 +3536,50 @@ Muss unter „Konfiguration -> Tray-Icon“ konfiguriert werden. Zurückweisen + + Zu verwendender Bildschirm für die Anzeige von WebView + + + Suche nach Update fehlgeschlagen! + + + BenanntesAktivesFenster + + + Stellt einen EIN/AUS-Wert bereit, basierend darauf, ob der fokussierte Fenstername eine konfigurierte Zeichenfolge enthält. + + + Stellt #RRGGBB-Farbwerte für System-Akzentfarben bereit. +Die Hauptakzentfarbe ist der Sensorwert. Zusätzliche Akzentfarben sind als Attribute verfügbar. + + + Verwenden Sie &WebSocket + + + Verwenden Sie &WebSocket + + + Lüfter + + + Versetzt den Rechner mithilfe der WinForms-API in den Ruhezustand. + +Hinweis: Aufgrund des „Modern Sleep“-Modus können sich die Ruhezustandsbefehle je nach Geräte-OEM und Betriebssystemkonfiguration unterschiedlich verhalten. + + + WinformsSleep + + + Verbindung nicht möglich. + + + Verbindung OK! + + + Versetzt alle Monitore in den Energiesparmodus (niedriger Stromverbrauch) mithilfe einer alternativen Methode zur Änderung des Energiesparplans. +Sollte bei neuen Systemen mit „modernem S0ix-Schlafmodus“ eine bessere Benutzererfahrung bieten und das System nicht in den Ruhezustand versetzen. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx index 938f6d80..1b3ef33c 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.en.resx @@ -526,7 +526,7 @@ The certificate of the downloaded file will get checked before running,you will HASS.Agent GitHub page - There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + There's a lot more to tinker with, so make sure you take a look at the Configuration Window! Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -1248,6 +1248,7 @@ Your command is provided as an argument 'as is', so you have to supply your own Simulates a single keypress. +Making this command a "switch" type will make the button pressed as long as the switch is turned on. Click on the 'keycode' textbox and press the key you want simulated. The corresponding keycode will be entered for you. For TAB key please use LCTRL+TAB. @@ -2058,7 +2059,10 @@ Currently takes the volume of your default device. Provides the current load of the first GPU as a percentage. - Provides the current temperature of the first GPU. + NOTE: This is a non-functioning sensor. + +Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. +Please see documentation for alternative options. Provides a datetime value containing the last moment the user provided input. @@ -3095,7 +3099,8 @@ Are you sure you want to use this key anyway? Are you sure you want to use this URI anyway? - Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated! + Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) +Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day. Tip: Other donation methods are available on the About Window. @@ -3436,4 +3441,50 @@ Requires it to be configured in "Configuration -> Tray Icon" Dismiss + + Failed checking for update! + + + NamedActiveWindow + + + Provides an ON/OFF value based on whether the focused window name contains configured string. + + + Provides #RRGGBB color values for system accent colors. +Main accent color is the sensor value, additional accent colors are available as attributes. + + + Use &WebSocket + + + Use &WebSocket + + + InputButton + + + Fan + + + Puts the machine to sleep using WinForms API. + +Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration. + + + WinformsSleep + + + Connection OK! + + + Unable to connect. + + + MonitorSleepPowerPlan + + + Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. +Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx index 89aa0d2e..d535ac12 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.es.resx @@ -1248,11 +1248,12 @@ Su comando se proporciona como un argumento 'tal cual', por lo que debe proporci Simula una sola pulsación de tecla. +Al configurar este comando como "interruptor", el botón se mantendrá pulsado mientras el interruptor esté activado. -Haga clic en el cuadro de texto 'keycode' y presione la tecla que desea simular. El código clave correspondiente se ingresará por usted. -Para la tecla TAB, utilice LCTRL+TAB. +Haga clic en el cuadro de texto "código de tecla" y pulse la tecla que desee simular. Se introducirá automáticamente el código de tecla correspondiente. +Para la tecla TAB, utilice Ctrl L+TAB. -Si necesita más teclas y/o modificadores como CTRL, use el comando MultipleKeys. +Si necesita más teclas o modificadores como Ctrl, utilice el comando "Múltiples teclas". Lanza la URL proporcionada, por defecto en su navegador predeterminado. @@ -2062,7 +2063,10 @@ Actualmente toma el volumen de su dispositivo predeterminado. Proporciona la carga actual de la primera GPU como porcentaje. - Proporciona la temperatura actual de la primera GPU. + NOTA: Este sensor no funciona. + +Debido a problemas de seguridad relacionados con Libre Hardware Monitor (biblioteca que permite a HASS.Agent acceder a los datos de temperatura de la GPU), este sensor se mantiene por compatibilidad con versiones anteriores y siempre devolverá 0. +Consulte la documentación para ver opciones alternativas. Proporciona un valor de fecha y hora que contiene la última vez que el usuario realizó una entrada. @@ -3094,7 +3098,8 @@ Debería contener tres secciones (separadas por dos puntos). ¿Está seguro de que quiere usarlo así? - Desarrollar y mantener esta herramienta (y todo lo que la rodea) requiere mucho tiempo. Al igual que la mayoría de los desarrolladores, funciono a base de cafeína, así que, si puede dedicarle un poco de tiempo, ¡una taza de café es siempre muy apreciada! + Actualmente, nosotros (el equipo de HASS.Agent que mantiene esta versión) no aceptamos donaciones de ningún tipo :) +Sin embargo, no duden en donar al autor original de HASS.Agent, ¡Sam! Esté donde esté, una taza de café podría alegrarle el día. Sugerencia: hay otros métodos de donación disponibles en la ventana Acerca de. @@ -3407,4 +3412,50 @@ Requiere que se configure en "Configuración -> Icono de la bandeja" Despedir + + Pantalla a utilizar para mostrar WebView + + + Error al comprobar la actualización. + + + Ventana activa con nombre + + + Proporciona un valor de ENCENDIDO/APAGADO según si el nombre de la ventana enfocada contiene una cadena configurada. + + + Proporciona valores de color #RRGGBB para los colores de acento del sistema. +El color de acento principal es el valor del sensor; hay colores de acento adicionales disponibles como atributos. + + + Utilice &WebSocket + + + Utilice &WebSocket + + + Admirador + + + Pone la máquina en suspensión mediante la API de WinForms. + +Nota: Debido al modo de suspensión moderno, los comandos de suspensión pueden comportarse de forma diferente según el fabricante del dispositivo y la configuración del sistema operativo. + + + WinformsSleep + + + No se puede conectar. + + + ¡Conexión correcta! + + + Pone todos los monitores en modo de suspensión (bajo consumo de energía) utilizando un método alternativo de modificación del plan de energía. +Esto debería proporcionar una mejor experiencia con los nuevos sistemas con "suspensión moderna S0ix" y evitar que el sistema entre en modo de suspensión completa. + + + Plan de energía para el sueño del monitor + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx index 5fa1229c..cc194c4c 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.fr.resx @@ -1269,14 +1269,6 @@ Votre commande est passée en tant qu'argument 'tel quel', vous devez donc fourn Mettre Windows en veille prolongée - - Simule une seule pression de touche. - -Cliquez sur la zone de texte "keycode" et appuyez sur la touche que vous souhaitez simuler. Le code clé correspondant sera saisi pour vous. -Pour la touche TAB, veuillez utiliser LCTRL+TAB. - -Si vous avez besoin de plus de touches et/ou de modificateurs comme CTRL, utilisez la commande MultipleKeys. - Ouvre l'URL fournie, par défaut dans votre navigateur par défaut. @@ -2092,7 +2084,10 @@ Indique le volume de votre appareil par défaut. Fournit la charge actuelle du premier GPU sous forme de pourcentage. - Fournit la température actuelle du premier GPU. + REMARQUE : Ce capteur est non fonctionnel. + +Pour des raisons de sécurité concernant Libre Hardware Monitor (bibliothèque permettant à HASS.Agent d'accéder aux données de température du GPU), ce capteur est conservé pour des raisons de rétrocompatibilité et renvoie toujours 0. +Veuillez consulter la documentation pour connaître les autres options. Fournit une valeur datetime qui contient la dernière fois que l'utilisateur a effectué une entrée. @@ -3127,7 +3122,8 @@ Etes-vous sûr de vouloir l'utiliser comme ça ? Etes-vous sûr de vouloir l'utiliser ainsi ? - Développer et maintenir cet outil (et tout ce qui l'entoure) prend beaucoup de temps. Comme la plupart des développeurs, je fonctionne à la caféine - donc si vous pouvez vous le permettre, une tasse de café est toujours très appréciée ! + Actuellement, nous (l'équipe HASS.Agent qui maintient cette version dérivée) n'acceptons aucune donation, sous quelque forme que ce soit. +N'hésitez cependant pas à faire un don à l'auteur original de HASS.Agent, Sam ! Où qu'il soit actuellement, une tasse de café pourrait lui faire plaisir. Astuce : d'autres méthodes de dons sont disponibles dans la fenêtre À propos. @@ -3440,4 +3436,58 @@ Nécessite sa configuration dans « Configuration -> Icône de la barre d'ét Rejeter + + Écran à utiliser pour l'affichage du WebView + + + Échec de la vérification de la mise à jour ! + + + Fenêtre active nommée + + + Fournit une valeur ON/OFF selon que le nom de la fenêtre focalisée contient ou non une chaîne configurée. + + + Fournit les valeurs de couleur #RRGGBB pour les couleurs d'accentuation du système. La couleur d'accentuation principale est la valeur du capteur ; des couleurs d'accentuation supplémentaires sont disponibles sous forme d'attributs. + + + Utiliser &WebSocket + + + Utiliser &WebSocket + + + Simule une simple pression de touche. +Si cette commande est de type « interrupteur », le bouton restera enfoncé tant que l'interrupteur sera activé. + +Cliquez sur la zone de texte « Code touche » et appuyez sur la touche à simuler. Le code touche correspondant sera saisi automatiquement. +Pour la touche TAB, utilisez Ctrl+Clic+Tab. + +Si vous avez besoin de touches supplémentaires et/ou de modificateurs comme Ctrl, utilisez la commande TouchesMultiples. + + + Ventilateur + + + Met la machine en veille à l'aide de l'API WinForms. + +Remarque : en raison de la fonction « Modern Sleep », les commandes de mise en veille peuvent se comporter différemment selon le fabricant de l'appareil et la configuration du système d'exploitation. + + + WinformsSleep + + + Connexion impossible. + + + Connexion OK ! + + + Met tous les moniteurs en mode veille (basse consommation) en utilisant une approche alternative de modification du plan d'alimentation. +Cela devrait offrir une meilleure expérience avec les nouveaux systèmes dotés du mode veille moderne « S0ix » et éviter de mettre le système en veille complète. + + + Surveillance du mode veille/alimentation + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx index 77b704c4..71bc2a11 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.nl.resx @@ -1249,11 +1249,12 @@ Je commando wordt onveranderd toegevoegd als argument, dus je moet je eigen haak Simuleert een enkele toetsaanslag. +Door van dit commando een "switch"-type te maken, blijft de knop ingedrukt zolang de switch aan staat. -Klik op het tekstvak 'sleutelcode' en druk op de sleutel die u wilt simuleren. De bijbehorende sleutelcode wordt voor u ingevoerd. -Gebruik voor de TAB-sleutel LCTRL+TAB. +Klik op het tekstvak 'keycode' en druk op de toets die u wilt simuleren. De bijbehorende toetscode wordt automatisch ingevoerd. +Gebruik LCTRL+TAB voor de TAB-toets. -Als u meer sleutels en/of modifiers zoals CTRL nodig heeft, gebruikt u de opdracht MultipleKeys. +Als u meer toetsen en/of modifiers zoals CTRL nodig hebt, gebruik dan het commando 'MultipleKeys'. Fuzzy @@ -2073,7 +2074,10 @@ Pakt momenteel het volume van je standaardapparaat. Geeft de huidige belasting van de eerste GPU als een percentage. - Geeft de huidige temperatuur van de eerste GPU. + OPMERKING: Dit is een niet-functionerende sensor. + +Vanwege beveiligingsproblemen met Libre Hardware Monitor (bibliotheek die HASS.Agent toegang geeft tot GPU-temperatuurgegevens) wordt deze sensor om redenen van achterwaartse compatibiliteit niet gebruikt en retourneert altijd 0. +Raadpleeg de documentatie voor alternatieve opties. Biedt een datum/tijd-waarde die de laatste keer bevat dat de gebruiker iets heeft ingevoerd. @@ -3116,7 +3120,8 @@ Weet je zeker dat je 'm zo wilt gebruiken? Weet je zeker dat je 'm zo wilt gebruiken? - Het ontwikkelen en onderhouden van deze tool (en alles wat erbij komt kijken, zoals support, deze vertaling en de documentatie) neemt een hoop tijd in beslag. Zoals de meeste ontwikkelaars draai ik op caffeïne - dus als je 't kunt missen, wordt een kop koffie altijd erg gewaardeerd! + Momenteel accepteren wij (het HASS.Agent-team dat de fork beheert) geen donaties in welke vorm dan ook :) +Voel je echter vrij om te doneren aan de oorspronkelijke auteur van HASS.Agent - Sam! Waar ze zich ook bevinden, een kopje koffie kan hun dag misschien opvrolijken. Tip: andere donatie methodes zijn beschikbaar in het Over scherm. @@ -3428,4 +3433,50 @@ Vereist dat het geconfigureerd is in "Configuratie -> Tray Icon" Afwijzen + + Scherm dat moet worden gebruikt om WebView weer te geven + + + Controle op update mislukt! + + + SchermActiefNaam + + + Geeft een AAN/UIT-waarde op basis van het al dan niet bevatten van de focusvensternaam van een geconfigureerde tekenreeks. + + + Biedt #RRGGBB-kleurwaarden voor systeemaccentkleuren. +De belangrijkste accentkleur is de sensorwaarde. Aanvullende accentkleuren zijn beschikbaar als attributen. + + + Gebruik &WebSocket + + + Gebruik &WebSocket + + + Fan + + + Zet de machine in slaapstand met behulp van de WinForms API. + +Opmerking: vanwege "Modern Sleep" kunnen de slaapopdrachten zich anders gedragen, afhankelijk van de OEM van het apparaat en de configuratie van het besturingssysteem. + + + WinformsSleep + + + Kan geen verbinding maken. + + + Verbinding OK! + + + Zet alle monitoren in de slaapstand (laag energieverbruik) met behulp van een alternatieve aanpak voor het aanpassen van het energiebeheerschema. +Zou een betere ervaring moeten bieden met nieuwe systemen met een "moderne S0ix-slaapstand" en het systeem niet in de slaapstand zetten. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx index d80f7d7a..4c89d396 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pl.resx @@ -1335,9 +1335,10 @@ Twoje polecenie jest dostarczane jako argument „tak jak jest”, więc w razie Symuluje pojedyncze naciśnięcie klawisza. +Ustawienie tego polecenia jako „przełącznik” spowoduje, że przycisk będzie wciśnięty tak długo, jak przełącznik będzie włączony. -Kliknij pole tekstowe „Kod klucza” i naciśnij klawisz, który chcesz zasymulować. Odpowiedni kod zostanie wprowadzony. -W przypadku klawisza TAB użyj LCTRL+TAB. +Kliknij pole tekstowe „kod klawisza” i naciśnij klawisz, który chcesz symulować. Odpowiedni kod klawisza zostanie wprowadzony automatycznie. +W przypadku klawisza TAB użyj kombinacji klawiszy LCTRL+TAB. Jeśli potrzebujesz więcej klawiszy i/lub modyfikatorów, takich jak CTRL, użyj polecenia MultipleKeys. Fuzzy @@ -2166,7 +2167,10 @@ Obecnie zwraca głośność domyślnego urządzenia. Zwraca obciążenie pierwszego GPU w procentach. - Zwraca temperaturę pierwszego GPU. + UWAGA: Ten czujnik nie działa. + +Ze względu na obawy dotyczące bezpieczeństwa Libre Hardware Monitor (biblioteki umożliwiającej HASS.Agent dostęp do danych o temperaturze GPU), ten czujnik został pozostawiony ze względu na kompatybilność wsteczną i zawsze będzie zwracał wartość 0. +Informacje o alternatywnych opcjach znajdują się w dokumentacji. Zwraca datę/godzinę, która zawiera czas ostatniej aktywności użytkownika. (klawiatura/myszka) @@ -3204,7 +3208,8 @@ Czy jesteś pewien, że chcesz użyć tego klucza? Czy jesteś pewien, że chcesz użyć tego linku URL? - Tworzenie i utrzymanie aplikacji (i wszystko dookoła, jak wsparcie i dokumentacja) zajmuje sporo czasu. Jak większość deweloperów moim paliwem jest kofeina, więc jeżeli możesz postawić mi kubek kawy będę bardzo wdzięczny! + Obecnie my (zespół HASS.Agent, odpowiedzialny za utrzymywanie tej wersji) nie przyjmujemy żadnych darowizn :) +Prosimy jednak o wsparcie finansowe oryginalnego autora HASS.Agent – ​​Sama! Gdziekolwiek się teraz znajduje, filiżanka kawy z pewnością umili mu dzień. Wskazówka: Inne sposoby wsparcia dostępne są w zakładce O. @@ -3517,4 +3522,50 @@ Wymaga konfiguracji w „Configuration -> Tray Icon” Odrzuć + + Ekran używany do wyświetlania WebView + + + Sprawdzanie aktualizacji nie powiodło się! + + + NazwaAktywnegoOkna + + + Zapewnia wartość WŁ./WYŁ. w zależności od tego, czy nazwa wybranego okna zawiera skonfigurowany ciąg znaków. + + + Zapewnia wartości kolorów #RRGGBB dla kolorów akcentowych systemu. +Główny kolor akcentowy to wartość czujnika, dodatkowe kolory akcentowe są dostępne jako atrybuty. + + + Użyj &WebSocket + + + Użyj &WebSocket + + + Wiatrak + + + Uśpij maszynę za pomocą interfejsu API WinForms. + +Uwaga: ze względu na „Modern Sleep” polecenia uśpienia mogą zachowywać się inaczej w zależności od producenta OEM urządzenia i konfiguracji systemu operacyjnego. + + + WinformsSleep + + + Nie można nawiązać połączenia. + + + Połączenie OK! + + + Przełącza wszystkie monitory w tryb uśpienia (niskiego poboru mocy) za pomocą alternatywnej metody modyfikacji planu zasilania. +Powinno to zapewnić lepsze działanie w przypadku nowych systemów z „nowoczesnym trybem uśpienia S0ix” i zapobiec przejściu systemu w stan uśpienia. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx index 79a64949..7f6814b9 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.pt-br.resx @@ -237,12 +237,13 @@ Seu comando é fornecido como um argumento 'as is', então você deve fornecer s Coloca a máquina em hibernação. - Simula um único pressionamento de tecla. + Simula um único toque de tecla. +Tornar este comando do tipo "interruptor" fará com que o botão seja premido enquanto o interruptor estiver ligado. -Clique na caixa de texto 'keycode' e pressione a tecla que deseja simular. O código-chave correspondente será inserido para você. -Para a tecla TAB, use LCTRL+TAB. +Clique na caixa de texto "código da tecla" e pressione a tecla que pretende simular. O código da tecla correspondente será introduzido. +Para a tecla TAB, utilize LCTRL+TAB. -Se precisar de mais teclas e/ou modificadores como CTRL, use o comando MultipleKeys. +Se necessitar de mais teclas e/ou modificadores como CTRL, utilize o comando MultipleKeys. Fuzzy @@ -1732,7 +1733,8 @@ Se você encontrar algum problema, crie um ticket na página do GitHub. Fuzzy - Desenvolver e manter essa ferramenta (e tudo o que a envolve, como suporte e documentação) leva muito tempo. Como a maioria dos desenvolvedores, eu amo café - então se você puder me doar um cafezinho serei muito agradecido! + Atualmente, nós (a equipa do HASS.Agent que mantém o fork) não aceitamos donativos de qualquer forma :) +No entanto, sinta-se à vontade para doar ao autor original do HASS.Agent - Sam! Onde quer que ele esteja, uma chávena de café pode alegrar o seu dia. Dica: outros tipos de doação estão disponíveis na janela Sobre. @@ -2217,7 +2219,10 @@ Dependendo da sua versão do Windows, isso pode ser encontrado no novo painel de Fornece a carga atual da GPU como uma porcentagem. - Fornece a temperatura atual da GPU. + NOTA: Este sensor não está a funcionar. + +Devido a questões de segurança relacionadas com o Libre Hardware Monitor (biblioteca que permite ao HASS.Agent aceder aos dados de temperatura da GPU), este sensor foi mantido por motivos de compatibilidade com versões anteriores e irá sempre retornar 0. +Consulte a documentação para obter opções alternativas. Fornece um valor de data e hora que contém a última vez que o usuário fez uma entrada. @@ -3453,4 +3458,50 @@ Requer que seja configurado em "Configuration -> Tray Icon" Dispensar + + Tela a ser usada para exibição do WebView + + + Falha na verificação da atualização! + + + JanelaAtiva Nomeada + + + Fornece um valor ON/OFF com base na presença ou não de uma string configurada no nome da janela em foco. + + + Fornece valores de cor #RRGGBB para as cores de destaque do sistema. +A cor de destaque principal é o valor do sensor; cores de destaque adicionais estão disponíveis como atributos. + + + Utilizar &WebSocket + + + Utilizar &WebSocket + + + + + + Coloca a máquina em modo de espera utilizando a API do WinForms. + +Nota: devido ao "Modern Sleep", os comandos de espera podem comportar-se de forma diferente, dependendo do fabricante do dispositivo e da configuração do sistema operativo. + + + WinformsSleep + + + Não foi possível ligar. + + + igação OK! + + + Coloca todos os monitores em modo de suspensão (baixo consumo de energia) utilizando uma abordagem alternativa de modificação do plano de energia. +Deve proporcionar uma melhor experiência com novos sistemas com "suspensão S0ix moderna" e não colocar o sistema em modo de suspensão. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx index 3722c123..11153161 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.resx @@ -361,6 +361,7 @@ Your command is provided as an argument 'as is', so you have to supply your own Simulates a single keypress. +Making this command a "switch" type will make the button pressed as long as the switch is turned on. Click on the 'keycode' textbox and press the key you want simulated. The corresponding keycode will be entered for you. For TAB key please use LCTRL+TAB. @@ -1168,7 +1169,10 @@ Currently takes the volume of your default device. Provides the current load of the first GPU as a percentage. - Provides the current temperature of the first GPU. + NOTE: This is a non-functioning sensor. + +Due to the security concerns regarding Libre Hardware Monitor (library allowing HASS.Agent to access GPU temperature data) this sensor is left for backward compatibility reasons and will always return 0. +Please see documentation for alternative options. Provides a datetime value containing the last moment the user provided input. @@ -3083,10 +3087,11 @@ Tip: if you're using the HA addon, you can probably use the preset address - jus Enable &Notifications - Developing and maintaining this tool (and everything that surrounds it) takes up a lot of time. Like most developers, I run on caffeïne - so if you can spare it, a cup of coffee is always very much appreciated! + Currently, we (HASS.Agent Team maintaining the fork) do not accept donations in any form :) +Please however feel free to donate to the original author of HASS.Agent - Sam! Wherever they currently are, cup of coffee might brighten their day. - There's a lot more to tinker with, so make sure you take a look at the Configuration Wwindow! + There's a lot more to tinker with, so make sure you take a look at the Configuration Window! Thank you for using HASS.Agent, hopefully it'll be useful for you :-) @@ -3426,7 +3431,56 @@ Requires it to be configured in "Configuration -> Tray Icon" Button + + MonitorSleepPowerPlan + + + Puts all monitors in sleep (low power) mode using alternative approach of power plan modification. +Should provide better experience with new systems with "modern S0ix sleep" and not put the system to sleep. + Dismiss + + Screen to use for WebView to show + + + Failed checking for update! + + + NamedActiveWindow + + + Provides an ON/OFF value based on whether the focused window name contains configured string. + + + Provides #RRGGBB color values for system accent colors. +Main accent color is the sensor value, additional accent colors are available as attributes. + + + Use &WebSocket + + + Use + + + InputButton + + + Fan + + + Puts the machine to sleep using WinForms API. + +Note: due to "Modern Sleep" the sleep commands might behave differently depending on the device OEM and OS configuration. + + + WinformsSleep + + + Connection OK! + + + Unable to connect. + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx index cc00f13e..8d089b20 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.ru.resx @@ -1291,12 +1291,14 @@ HASS.Agent для прослушивания на указанном порту. Переводит машину в режим гибернации. - Имитирует одно нажатие клавиши. + Имитирует однократное нажатие клавиши. +Если задать для этой команды тип «переключатель», кнопка будет нажата, пока переключатель включён. + +Щёлкните по текстовому полю «код клавиши» и нажмите клавишу, которую хотите имитировать. Соответствующий код клавиши будет введён автоматически. -Нажмите на текстовое поле «keycode» и нажмите клавишу, которую вы хотите имитировать. Соответствующий ключевой код будет введен для вас. Для клавиши TAB используйте LCTRL+TAB. -Если вам нужно больше клавиш и/или модификаторов, таких как CTRL, используйте команду MultipleKeys. +Если вам нужны дополнительные клавиши и/или модификаторы, например CTRL, используйте команду MultipleKeys. Fuzzy @@ -2116,7 +2118,10 @@ Home Assistant version: {0} Показывает текущую загрузку первого графического процессора в процентах. - Показывает текущую температуру первого графического процессора. + ПРИМЕЧАНИЕ: Это неработающий датчик. + +Из-за проблем безопасности, связанных с Libre Hardware Monitor (библиотекой, позволяющей HASS.Agent получать доступ к данным о температуре графического процессора), этот датчик оставлен в целях обратной совместимости и всегда будет возвращать 0. +Альтернативные варианты см. в документации. Предоставляет значение даты и времени, содержащее время последнего ввода данных пользователем. @@ -3162,7 +3167,8 @@ Home Assistant. Вы уверены, что все равно хотите использовать этот URI? - Разработка и обслуживание этого инструмента (и всего, что его окружает) отнимает много времени. Как и большинство разработчиков, я работаю на кофеине - так что, если вы можете поделиться им, чашка кофе всегда очень ценится! + В настоящее время мы (команда HASS.Agent, поддерживающая форк) не принимаем пожертвования в какой-либо форме :) +Однако, пожалуйста, не стесняйтесь сделать пожертвование оригинальному автору HASS.Agent — Сэму! Где бы он сейчас ни находился, чашка кофе может скрасить его день. Совет: Другие способы пожертвования доступны в окне 'О программе'. @@ -3476,4 +3482,50 @@ Home Assistant. Увольнять + + Экран, который будет использоваться для отображения WebView + + + Не удалось проверить обновление! + + + ИменованноеАктивноеОкно + + + Предоставляет значение ВКЛ/ВЫКЛ в зависимости от того, содержит ли имя окна в фокусе настроенную строку. + + + Предоставляет значения цвета #RRGGBB для акцентных цветов системы. +Основной акцентный цвет — это значение датчика, дополнительные акцентные цвета доступны в качестве атрибутов. + + + Использовать &WebSocket + + + Использовать &WebSocket + + + Вентилятор + + + Переводит устройство в спящий режим с помощью API WinForms. + +Примечание: из-за «Modern Sleep» команды перехода в спящий режим могут работать по-разному в зависимости от производителя устройства и конфигурации ОС. + + + WinformsSleep + + + Не удалось подключиться. + + + Соединение в порядке! + + + Переводит все мониторы в спящий режим (режим пониженного энергопотребления), используя альтернативный подход к изменению схемы управления питанием. +Это должно обеспечить лучшую работу на новых системах с «современным спящим режимом S0ix» и предотвратить переход системы в спящий режим. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx index 7d39b632..deda2f87 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.sl.resx @@ -245,11 +245,12 @@ Vaš ukaz je podan kot argument 'tako kot je', zato morate po potrebi navesti sv Simulira en sam pritisk tipke. +Če ta ukaz nastavite na »stikalo«, bo gumb pritisnjen, dokler je stikalo vklopljeno. -Kliknite besedilno polje 'keycode' in pritisnite tipko, ki jo želite simulirati. Vnesena bo ustrezna koda tipke. +Kliknite na besedilno polje »koda tipke« in pritisnite tipko, ki jo želite simulirati. Ustrezna koda tipke bo vnesena samodejno. Za tipko TAB uporabite LCTRL+TAB. -Če potrebujete več tipk in/ali modifikatorjev, kot je CTRL, uporabite ukaz MultipleKeys. +Če potrebujete več tipk in/ali modifikatorjev, kot je CTRL, uporabite ukaz Več tipk. Fuzzy @@ -1791,7 +1792,8 @@ Hvala, ker uporabljate HASS.Agent. Upam, da vam bo koristil :-) Fuzzy - Razvoj in vzdrževanje tega dodatka (in vsega, kar spada zraven, kot je podpora, navodila) vzame veliko časa. Kot večina razvijalcev tudi jaz delam na kofein - zato bi bil zelo hvaležen kake skodelice kave, če jo lahko pogrešate! + Trenutno (ekipa HASS.Agent, ki vzdržuje forum) ne sprejema donacij v nobeni obliki :) +Vendar pa lahko brez zadržkov donirate izvirnemu avtorju HASS.Agent - Samu! Kjerkoli že je, mu lahko skodelica kave polepša dan. Namig: ostale možnosti donacij so na voljo v zavihku "Vizitka". @@ -2295,7 +2297,10 @@ Glede na verzijo Windows-ov se to lahko nahaja v Nadzorna plošča --> Sistem Zagotavlja trenutno obremenitev prvega GPU-ja v odstotkih. - Zagotavlja trenutno temperaturo prvega GPU-ja. + OPOMBA: To je nedelujoč senzor. + +Zaradi varnostnih pomislekov glede Libre Hardware Monitor (knjižnica, ki omogoča HASS.Agent dostop do podatkov o temperaturi GPU) je ta senzor ostal zaradi združljivosti s prejšnjimi različicami in bo vedno vrnil vrednost 0. +Za alternativne možnosti glejte dokumentacijo. Zagotavlja vrednost datuma in časa, ki vsebuje čas zadnjega vnosa uporabnika. @@ -3556,4 +3561,50 @@ Zahteva, da je konfiguriran v "Konfiguracija -> Ikona pladnja" Odpusti + + Zaslon, ki se uporabi za prikaz WebView + + + Neuspešno preverjanje posodobitve! + + + PoimenovanoAktivnoOkno + + + Zagotavlja vrednost VKLOP/IZKLOP glede na to, ali ime okna v fokusu vsebuje konfiguriran niz. + + + Zagotavlja barvne vrednosti #RRGGBB za sistemske poudarjene barve. +Glavna poudarjena barva je vrednost senzorja, dodatne poudarjene barve so na voljo kot atributi. + + + Uporabi &WebSocket + + + Uporabi &WebSocket + + + Ventilator + + + Preklopi računalnik v stanje mirovanja z uporabo WinForms API-ja. + +Opomba: Zaradi »modernega mirovanja« se lahko ukazi za mirovanje obnašajo drugače, odvisno od proizvajalca originalne opreme naprave in konfiguracije operacijskega sistema. + + + WinformsSleep + + + Povezave ni mogoče vzpostaviti. + + + Povezava v redu! + + + Preklopi vse monitorje v način mirovanja (nizka poraba energije) z uporabo alternativnega pristopa k spreminjanju načrta porabe energije. +Omogoča boljšo izkušnjo z novimi sistemi s »sodobnim načinom mirovanja S0ix« in ne preklopi sistema v stanje mirovanja. + + + MonitorSleepPowerPlan + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx index 3f40e569..7edeaab9 100644 --- a/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx +++ b/src/HASS.Agent/HASS.Agent/Resources/Localization/Languages.tr.resx @@ -1182,12 +1182,13 @@ Ancak bu aracı beğendiyseniz orijinal (LAB02 Araştırması) geliştiricilere Makineyi hazırda bekletme moduna geçirir. - Tek bir tuşa basmayı simüle eder. + Tek bir tuş basımını simüle eder. +Bu komutu "anahtar" türü yapmak, anahtar açık olduğu sürece düğmenin basılı kalmasını sağlar. -'Keycode' metin kutusuna tıklayın ve simüle edilmesini istediğiniz tuşa basın. İlgili anahtar kodu sizin için girilecektir. -TAB tuşu için lütfen LCTRL+TAB kullanın. +'Tuş kodu' metin kutusuna tıklayın ve simüle edilmesini istediğiniz tuşa basın. İlgili tuş kodu sizin için girilecektir. +TAB tuşu için lütfen LCTRL+TAB tuşlarını kullanın. -Daha fazla tuşa ve/veya CTRL gibi değiştiricilere ihtiyacınız varsa, MultipleKeys komutunu kullanın. +CTRL gibi daha fazla tuşa ve/veya değiştiriciye ihtiyacınız varsa, MultipleKeys komutunu kullanın. Varsayılan tarayıcınızda varsayılan olarak sağlanan URL'yi başlatır. 'Gizli' kullanmak için Yapılandırma -> Harici Araçlar'da belirli bir tarayıcı sağlayın. Yalnızca belirli bir URL'ye sahip bir pencere istiyorsanız (tam bir tarayıcı değil), bir 'WebView' komutu kullanın. @@ -1854,7 +1855,10 @@ Daha fazla tuşa ve/veya CTRL gibi değiştiricilere ihtiyacınız varsa, Multip Yüzde olarak ilk GPU'nun mevcut yükünü sağlar. - İlk GPU'nun mevcut sıcaklığını sağlar. + NOT: Bu, çalışmayan bir sensördür. + +Libre Donanım İzleyicisi (HASS.Agent'ın GPU sıcaklık verilerine erişmesine izin veren kütüphane) ile ilgili güvenlik endişeleri nedeniyle, bu sensör geriye dönük uyumluluk nedeniyle bırakılmıştır ve her zaman 0 döndürecektir. +Alternatif seçenekler için lütfen belgelere bakın. Kullanıcının en son ne zaman giriş yaptığını içeren bir tarih saat değeri sağlar. @@ -2738,7 +2742,8 @@ Daha fazla tuşa ve/veya CTRL gibi değiştiricilere ihtiyacınız varsa, Multip Sağladığınız URI geçerli görünmüyor, geçerli bir URI aşağıdakilerden biri gibi görünebilir: - http://homeassistant.local:8123 - http://192.168.0.1:8123 Kullanmak istediğinizden emin misiniz? yine de bu URI? - Bu aracı (ve onu çevreleyen her şeyi) geliştirmek ve sürdürmek çok zaman alır. Çoğu geliştirici gibi, ben de kafein kullanıyorum - bu yüzden eğer onu ayırabilirseniz, bir fincan kahve her zaman çok değerlidir! + Şu anda (çatalın bakımını üstlenen HASS.Agent Ekibi) hiçbir şekilde bağış kabul etmiyoruz :) +Ancak lütfen HASS.Agent'ın orijinal yazarı Sam'e bağış yapmaktan çekinmeyin! Şu anda nerede olurlarsa olsunlar, bir fincan kahve günlerini güzelleştirebilir. İpucu: Hakkında Penceresinde başka bağış yöntemleri de mevcuttur. @@ -3023,4 +3028,50 @@ Not: Uydu hizmetinde kullanılırsa kullanıcı alanı uygulamalarını algılam Azletmek + + WebView'in göstermesi için kullanılacak ekran + + + Güncelleme kontrolü başarısız oldu! + + + AdlandırılmışAktifPencere + + + Odaklanan pencere adının yapılandırılmış dizeyi içerip içermediğine bağlı olarak bir AÇIK/KAPALI değeri sağlar. + + + Sistem vurgu renkleri için #RRGGBB renk değerleri sağlar. +Ana vurgu rengi sensör değeridir, ek vurgu renkleri öznitelikler olarak mevcuttur. + + + &WebSocket'i kullanın + + + &WebSocket'i kullanın + + + Fan + + + WinForms API'sini kullanarak makineyi uyku moduna geçirir. + +Not: "Modern Uyku" nedeniyle uyku komutları, cihazın OEM ve işletim sistemi yapılandırmasına bağlı olarak farklı davranabilir. + + + WinformsSleep + + + Bağlantı kurulamıyor. + + + Bağlantı Tamam! + + + Güç planı değişikliği gibi alternatif bir yaklaşım kullanarak tüm monitörleri uyku moduna (düşük güç) alır. +"Modern S0ix uyku" özelliğine sahip yeni sistemlerde daha iyi bir deneyim sağlamalı ve sistemi uyku moduna almamalıdır. + + + MonitörUykuGüçPlanı + \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs index dd5546d9..1a62e301 100644 --- a/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs +++ b/src/HASS.Agent/HASS.Agent/Sensors/SensorsManager.cs @@ -66,11 +66,46 @@ internal static async void Initialize() /// Unpublishes all single- and multivalue sensors /// /// - internal static async Task UnpublishAllSensors() + internal static async Task UnpublishAllSensors(bool migration = false) { - // unpublish the autodisco's - if (SingleValueSensorsPresent()) foreach (var sensor in Variables.SingleValueSensors) await sensor.UnPublishAutoDiscoveryConfigAsync(); - if (MultiValueSensorsPresent()) foreach (var sensor in Variables.MultiValueSensors) await sensor.UnPublishAutoDiscoveryConfigAsync(); + if (SingleValueSensorsPresent()) + { + foreach (var sensor in Variables.SingleValueSensors) + { + await sensor.UnPublishAutoDiscoveryConfigAsync(migration); + } + } + if (MultiValueSensorsPresent()) + { + foreach (var sensor in Variables.MultiValueSensors) + { + await sensor.UnPublishAutoDiscoveryConfigAsync(migration); + } + } + + _discoveryPublished = false; + } + + /// + /// Publishes all single- and multivalue sensors + /// + /// + internal static async Task ForcePublishAllSensors() + { + if (SingleValueSensorsPresent()) + { + foreach (var sensor in Variables.SingleValueSensors) + { + await sensor.PublishAutoDiscoveryConfigAsync(); + } + } + if (MultiValueSensorsPresent()) + { + foreach (var sensor in Variables.MultiValueSensors) + { + await sensor.PublishAutoDiscoveryConfigAsync(); + } + } } /// @@ -390,6 +425,14 @@ internal static void LoadSensorInfo() // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.AccentColorSensor, + Languages.SensorsManager_AccentColorSensorDescription, + 120, false, true, false); + + SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); + + // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.AudioSensors, Languages.SensorsManager_AudioSensorsDescription, 20, true, true, true); @@ -486,6 +529,14 @@ internal static void LoadSensorInfo() // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.InternalDeviceSensor, + Languages.SensorsManager_InternalDeviceSensorDescription, + 10, false, true, false); + + SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); + + // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.LastActiveSensor, Languages.SensorsManager_LastActiveSensorDescription, 10, false, true, false); @@ -566,6 +617,14 @@ internal static void LoadSensorInfo() // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.NamedActiveWindowSensor, + Languages.SensorsManager_NamedActiveWindowSensorDescription, + 30, false, true, false); + + SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); + + // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.NetworkSensors, Languages.SensorsManager_NetworkSensorsDescription, 30, true, true, true); @@ -606,6 +665,14 @@ internal static void LoadSensorInfo() // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.ScreenshotSensor, + Languages.SensorsManager_ScreenshotSensorDescription, + 10, false, true, false); + + SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); + + // ================================= + sensorInfoCard = new SensorInfoCard(SensorType.ServiceStateSensor, Languages.SensorsManager_ServiceStateSensorDescription, 10, false, true, true); @@ -677,22 +744,6 @@ internal static void LoadSensorInfo() SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); // ================================= - - sensorInfoCard = new SensorInfoCard(SensorType.InternalDeviceSensor, - Languages.SensorsManager_InternalDeviceSensorDescription, - 10, false, true, false); - - SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); - - // ================================= - - sensorInfoCard = new SensorInfoCard(SensorType.ScreenshotSensor, - Languages.SensorsManager_ScreenshotSensorDescription, - 10, false, true, false); - - SensorInfoCards.Add(sensorInfoCard.SensorType, sensorInfoCard); - - // ================================= } } } diff --git a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto index a21c9157..f261aa30 100644 --- a/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto +++ b/src/HASS.Agent/HASS.Agent/Service/Protos/hassagentsatellite.proto @@ -120,6 +120,7 @@ message RpcServiceMqttSettings { string mqttRootCertificate = 9; string mqttClientCertificate = 10; string mqttClientId = 11; + bool mqttUseWebSocket = 12; } message RpcConfiguredServerSensor { diff --git a/src/HASS.Agent/HASS.Agent/Settings/SettingsManager.cs b/src/HASS.Agent/HASS.Agent/Settings/SettingsManager.cs index 01c525ac..546f2c31 100644 --- a/src/HASS.Agent/HASS.Agent/Settings/SettingsManager.cs +++ b/src/HASS.Agent/HASS.Agent/Settings/SettingsManager.cs @@ -14,7 +14,6 @@ using Newtonsoft.Json; using Serilog; using Syncfusion.Windows.Forms; -using WK.Libraries.HotkeyListenerNS; namespace HASS.Agent.Settings { @@ -139,7 +138,7 @@ private static bool LoadAppSettings() AgentSharedBase.SetCustomExecutorBinary(Variables.AppSettings.CustomExecutorBinary); // load the hotkey - Variables.QuickActionsHotKey = string.IsNullOrEmpty(Variables.AppSettings.QuickActionsHotKey) ? null : HotkeyListener.Convert(Variables.AppSettings.QuickActionsHotKey); + Variables.QuickActionsHotKey = string.IsNullOrWhiteSpace(Variables.AppSettings.QuickActionsHotKey) ? string.Empty : Variables.AppSettings.QuickActionsHotKey; // done Log.Information("[SETTINGS] Configuration loaded"); diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs index 15305e16..1bb2cc7e 100644 --- a/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs +++ b/src/HASS.Agent/HASS.Agent/Settings/StoredCommands.cs @@ -106,6 +106,9 @@ internal static AbstractCommand ConvertConfiguredToAbstract(ConfiguredCommand co case CommandType.SleepCommand: abstractCommand = new SleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; + case CommandType.WinformsSleepCommand: + abstractCommand = new WinformsSleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); + break; case CommandType.LogOffCommand: abstractCommand = new LogOffCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; @@ -166,6 +169,9 @@ internal static AbstractCommand ConvertConfiguredToAbstract(ConfiguredCommand co case CommandType.MonitorSleepCommand: abstractCommand = new MonitorSleepCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; + case CommandType.MonitorSleepPowerPlanCommand: + abstractCommand = new MonitorSleepPowerPlanCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); + break; case CommandType.MonitorWakeCommand: abstractCommand = new MonitorWakeCommand(command.EntityName, command.Name, command.EntityType, command.Id.ToString()); break; diff --git a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs index ca701c83..a8db9bef 100644 --- a/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs +++ b/src/HASS.Agent/HASS.Agent/Settings/StoredSensors.cs @@ -14,7 +14,6 @@ using HASS.Agent.Shared.HomeAssistant.Sensors.WmiSensors.SingleValue; using HASS.Agent.Shared.Models.Config; using HASS.Agent.Shared.Models.HomeAssistant; -using LibreHardwareMonitor.Hardware; using Newtonsoft.Json; using Serilog; using SensorType = HASS.Agent.Shared.Enums.SensorType; @@ -124,9 +123,15 @@ internal static AbstractSingleValueSensor ConvertConfiguredToAbstractSingleValue case SensorType.ActiveDesktopSensor: abstractSensor = new ActiveDesktopSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings); break; + case SensorType.AccentColorSensor: + abstractSensor = new AccentColorSensor(sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings); + break; case SensorType.NamedWindowSensor: abstractSensor = new NamedWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings); break; + case SensorType.NamedActiveWindowSensor: + abstractSensor = new NamedActiveWindowSensor(sensor.WindowName, sensor.EntityName, sensor.Name, sensor.UpdateInterval, sensor.Id.ToString(), sensor.AdvancedSettings); + break; case SensorType.LastActiveSensor: abstractSensor = new LastActiveSensor(sensor.ApplyRounding, sensor.Round, sensor.UpdateInterval, sensor.EntityName, sensor.Name, sensor.Id.ToString(), sensor.AdvancedSettings); break; @@ -299,6 +304,22 @@ internal static ConfiguredSensor ConvertAbstractSingleValueToConfigured(Abstract }; } + case NamedActiveWindowSensor namedActiveWindowSensor: + { + _ = Enum.TryParse(namedActiveWindowSensor.GetType().Name, out var type); + return new ConfiguredSensor + { + Id = Guid.Parse(namedActiveWindowSensor.Id), + EntityName = namedActiveWindowSensor.EntityName, + Name = namedActiveWindowSensor.Name, + Type = type, + UpdateInterval = namedActiveWindowSensor.UpdateIntervalSeconds, + IgnoreAvailability = namedActiveWindowSensor.IgnoreAvailability, + WindowName = namedActiveWindowSensor.WindowName, + AdvancedSettings = namedActiveWindowSensor.AdvancedSettings + }; + } + case PerformanceCounterSensor performanceCounterSensor: { _ = Enum.TryParse(performanceCounterSensor.GetType().Name, out var type); diff --git a/src/HASS.Agent/HASS.Agent/Variables.cs b/src/HASS.Agent/HASS.Agent/Variables.cs index c8f67547..e38a904d 100644 --- a/src/HASS.Agent/HASS.Agent/Variables.cs +++ b/src/HASS.Agent/HASS.Agent/Variables.cs @@ -1,5 +1,4 @@ -extern alias WV2; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Net.Http; @@ -7,23 +6,19 @@ using Windows.Media.Playback; using Grapevine; using HASS.Agent.Forms; -using HASS.Agent.Functions; using HASS.Agent.Managers; using HASS.Agent.Models.Config; using HASS.Agent.Models.Internal; using HASS.Agent.MQTT; using HASS.Agent.Service; using HASS.Agent.Settings; -using HASS.Agent.Shared.HomeAssistant; -using HASS.Agent.Shared.HomeAssistant.Commands; -using HASS.Agent.Shared.HomeAssistant.Sensors; using HASS.Agent.Shared.Models.HomeAssistant; using HASS.Agent.Shared.Mqtt; -using WV2::Microsoft.Web.WebView2.Core; using Microsoft.Win32; using MQTTnet; -using WK.Libraries.HotkeyListenerNS; using Serilog.Core; +using Microsoft.Web.WebView2.Core; +using NHotkey.WindowsForms; namespace HASS.Agent { @@ -56,9 +51,9 @@ public static class Variables /// internal static Main MainForm { get; set; } internal static HttpClient HttpClient { get; set; } = new(); - internal static Hotkey QuickActionsHotKey { get; set; } = new(Keys.Control | Keys.Alt, Keys.Q); - internal static HotKeyManager HotKeyManager { get; } = new(); - internal static HotkeyListener HotKeyListener { get; set; } + internal static string QuickActionsHotKey { get; set; } = "Control, Alt + Q"; + internal static InternalHotKeyManager InternalHotKeyManager { get; } = new(); + internal static HotkeyManager HotKeyListener { get; set; } internal static Random Rnd { get; } = new(); internal static Font DefaultFont { get; } = new("Segoe UI", 10F, FontStyle.Regular, GraphicsUnit.Point); internal static WebView TrayIconWebView { get; set; } = null;