From 772050673a869563cf7c33ca91b50e75c2301693 Mon Sep 17 00:00:00 2001 From: Raphiiko Date: Sat, 28 Sep 2024 23:33:53 +0200 Subject: [PATCH] Add (hidden) "When my headset connects to SteamVR" brightness automation event --- package-lock.json | 24 +++++++++++++++ package.json | 2 ++ src-ui/app/models/automations.ts | 13 ++++++++ src-ui/app/models/one-time-flags.ts | 1 + src-ui/app/services/app-settings.service.ts | 4 +++ .../brightness-cct-automation.service.ts | 16 ++++++++-- .../brightness-control-models.ts | 3 +- .../brightness-automations-tab.component.ts | 30 +++++++++++++++++-- src-ui/assets/i18n/en.json | 5 +++- 9 files changed, 91 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1957995b..44a209d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "eslint-plugin-json": "^3.1.0", "flag-icons": "^6.6.6", "fuse.js": "^6.6.2", + "konami-code-js": "^0.8.1", "lodash": "^4.17.21", "marked": "^4.2.2", "material-icons": "^1.12.1", @@ -77,6 +78,7 @@ "@protobuf-ts/plugin": "^2.9.0", "@tauri-apps/cli": "^1.5.11", "@types/cookie": "^0.6.0", + "@types/konami-code-js": "^0.8.3", "@types/lodash": "^4.14.188", "@types/marked": "^4.0.7", "@types/set-cookie-parser": "^2.4.2", @@ -3386,6 +3388,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/konami-code-js": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@types/konami-code-js/-/konami-code-js-0.8.3.tgz", + "integrity": "sha512-TYwIDZ16MuzqHdgv/zhbbE3p/iXY/FotJkMYx1oOzZKJoINezl91stc/U/i5AknH5lrEpXoims+tsCnAwIJGew==", + "dev": true + }, "node_modules/@types/lodash": { "version": "4.14.194", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", @@ -7331,6 +7339,11 @@ "node": ">=0.10.0" } }, + "node_modules/konami-code-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/konami-code-js/-/konami-code-js-0.8.1.tgz", + "integrity": "sha512-bJ0tuWYLYiUueIVTpA0MV4h4Gz1X16uuJggh5TpIWXOQoLv0238SU7Im23z2wYKCCBsOfk5j4HKWB/pqdCgu5Q==" + }, "node_modules/less": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", @@ -14112,6 +14125,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "@types/konami-code-js": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@types/konami-code-js/-/konami-code-js-0.8.3.tgz", + "integrity": "sha512-TYwIDZ16MuzqHdgv/zhbbE3p/iXY/FotJkMYx1oOzZKJoINezl91stc/U/i5AknH5lrEpXoims+tsCnAwIJGew==", + "dev": true + }, "@types/lodash": { "version": "4.14.194", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", @@ -16912,6 +16931,11 @@ "integrity": "sha512-3KF80UaaSSxo8jVnRYtMKNGFOoVPBdkkVPsw+Ad0y4oxKXPduS6G6iHkrf69yJVff/VAaYXkV42rtZ7daJxU3w==", "dev": true }, + "konami-code-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/konami-code-js/-/konami-code-js-0.8.1.tgz", + "integrity": "sha512-bJ0tuWYLYiUueIVTpA0MV4h4Gz1X16uuJggh5TpIWXOQoLv0238SU7Im23z2wYKCCBsOfk5j4HKWB/pqdCgu5Q==" + }, "less": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", diff --git a/package.json b/package.json index db8d8ea0..59265cb7 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "eslint-plugin-json": "^3.1.0", "flag-icons": "^6.6.6", "fuse.js": "^6.6.2", + "konami-code-js": "^0.8.1", "lodash": "^4.17.21", "marked": "^4.2.2", "material-icons": "^1.12.1", @@ -109,6 +110,7 @@ "@protobuf-ts/plugin": "^2.9.0", "@tauri-apps/cli": "^1.5.11", "@types/cookie": "^0.6.0", + "@types/konami-code-js": "^0.8.3", "@types/lodash": "^4.14.188", "@types/marked": "^4.0.7", "@types/set-cookie-parser": "^2.4.2", diff --git a/src-ui/app/models/automations.ts b/src-ui/app/models/automations.ts index b3b5948a..8ead9c4a 100644 --- a/src-ui/app/models/automations.ts +++ b/src-ui/app/models/automations.ts @@ -123,6 +123,7 @@ export const BrightnessEvents = [ 'SLEEP_PREPARATION', 'AT_SUNSET', 'AT_SUNRISE', + 'HMD_CONNECT', ] as const; export type BrightnessEvent = (typeof BrightnessEvents)[number]; @@ -133,6 +134,7 @@ export type BrightnessAutomationsConfig = AutomationConfig & { SLEEP_PREPARATION: GenericBrightnessEventAutomationConfig; SLEEP_MODE_ENABLE: GenericBrightnessEventAutomationConfig; SLEEP_MODE_DISABLE: GenericBrightnessEventAutomationConfig; + HMD_CONNECT: GenericBrightnessEventAutomationConfig; }; export type BrightnessEventAutomationConfig = @@ -548,6 +550,17 @@ export const AUTOMATION_CONFIGS_DEFAULT: AutomationConfigs = { onlyWhenSleepDisabled: true, activationTime: null, }, + HMD_CONNECT: { + enabled: false, + changeBrightness: true, + changeColorTemperature: true, + brightness: 100, + softwareBrightness: 100, + hardwareBrightness: 100, + transition: true, + transitionTime: 10000, + colorTemperature: 6600, + }, }, // RESOLUTION AUTOMATIONS RENDER_RESOLUTION_ON_SLEEP_MODE_ENABLE: { diff --git a/src-ui/app/models/one-time-flags.ts b/src-ui/app/models/one-time-flags.ts index 2244a64a..28c3f12f 100644 --- a/src-ui/app/models/one-time-flags.ts +++ b/src-ui/app/models/one-time-flags.ts @@ -1,6 +1,7 @@ export const OneTimeFlags = [ 'CCT_CONTROL_WARNING_DIALOG', 'BASESTATION_COUNT_WARNING_DIALOG', + 'BRIGHTNESS_AUTOMATION_ON_HMD_CONNECT_EVENT_FEATURE', ] as const; export type OneTimeFlag = (typeof OneTimeFlags)[number]; diff --git a/src-ui/app/services/app-settings.service.ts b/src-ui/app/services/app-settings.service.ts index 85e1a7e1..19618fed 100644 --- a/src-ui/app/services/app-settings.service.ts +++ b/src-ui/app/services/app-settings.service.ts @@ -4,6 +4,7 @@ import { asyncScheduler, BehaviorSubject, firstValueFrom, + map, Observable, skip, switchMap, @@ -82,6 +83,9 @@ export class AppSettingsService { public oneTimeFlagSet(flag: OneTimeFlag): boolean { return this._settings.value.oneTimeFlags.includes(flag); } + public oneTimeFlagSetAsync(flag: OneTimeFlag): Observable { + return this._settings.pipe(map((settings) => settings.oneTimeFlags.includes(flag))); + } public setOneTimeFlag(flag: OneTimeFlag, set = true): void { if (set === this.oneTimeFlagSet(flag)) return; diff --git a/src-ui/app/services/brightness-cct-automation.service.ts b/src-ui/app/services/brightness-cct-automation.service.ts index 80b78e8b..6f72302f 100644 --- a/src-ui/app/services/brightness-cct-automation.service.ts +++ b/src-ui/app/services/brightness-cct-automation.service.ts @@ -230,14 +230,21 @@ export class BrightnessCctAutomationService { | 'AT_SUNRISE' | 'SLEEP_MODE_ENABLE' | 'SLEEP_MODE_DISABLE' + | 'HMD_CONNECT' | undefined; let cctAutomation: | 'AT_SUNSET' | 'AT_SUNRISE' | 'SLEEP_MODE_ENABLE' | 'SLEEP_MODE_DISABLE' + | 'HMD_CONNECT' | undefined; - // If sunrise/sunset times are both enabled, use their settings for brightness and CCT, if configured. + // If HMD connect is enabled, use its settings for brightness and CCT, if configured. + if (config.HMD_CONNECT.enabled) { + if (config.HMD_CONNECT.changeBrightness) brightnessAutomation = 'HMD_CONNECT'; + if (config.HMD_CONNECT.changeColorTemperature) cctAutomation = 'HMD_CONNECT'; + } + // Otherwise If sunrise/sunset times are both enabled, use their settings for brightness and CCT, if configured. if ( config.AT_SUNSET.enabled && config.AT_SUNRISE.enabled && @@ -261,8 +268,10 @@ export class BrightnessCctAutomationService { } else { runAutomation = timesInverted ? 'AT_SUNSET' : 'AT_SUNRISE'; } - if (config[runAutomation].changeBrightness) brightnessAutomation = runAutomation; - if (config[runAutomation].changeColorTemperature) cctAutomation = runAutomation; + if (!brightnessAutomation && config[runAutomation].changeBrightness) + brightnessAutomation = runAutomation; + if (!cctAutomation && config[runAutomation].changeColorTemperature) + cctAutomation = runAutomation; } // Otherwise, use the values configured for the sleep mode automations (if they're enabled) const sleepMode = await firstValueFrom(this.sleepService.mode); @@ -326,6 +335,7 @@ export class BrightnessCctAutomationService { SLEEP_PREPARATION: 'SLEEP_PREPARATION', AT_SUNRISE: 'AT_SUNRISE', AT_SUNSET: 'AT_SUNSET', + HMD_CONNECT: 'HMD_CONNECT', }; const logReason: SetBrightnessOrCCTReason = eventLogReasonMap[automationType]; // Handle CCT diff --git a/src-ui/app/services/brightness-control/brightness-control-models.ts b/src-ui/app/services/brightness-control/brightness-control-models.ts index a5abf124..57dd7d42 100644 --- a/src-ui/app/services/brightness-control/brightness-control-models.ts +++ b/src-ui/app/services/brightness-control/brightness-control-models.ts @@ -3,7 +3,8 @@ export type SetBrightnessOrCCTReason = | 'SLEEP_MODE_DISABLE' | 'SLEEP_PREPARATION' | 'AT_SUNSET' - | 'AT_SUNRISE'; + | 'AT_SUNRISE' + | 'HMD_CONNECT'; export interface SetBrightnessOrCCTOptions { cancelActiveTransition: boolean; diff --git a/src-ui/app/views/dashboard-view/views/brightness-automations-view/tabs/new-brightness-automations-tab/brightness-automations-tab.component.ts b/src-ui/app/views/dashboard-view/views/brightness-automations-view/tabs/new-brightness-automations-tab/brightness-automations-tab.component.ts index f94a9077..09f0380e 100644 --- a/src-ui/app/views/dashboard-view/views/brightness-automations-view/tabs/new-brightness-automations-tab/brightness-automations-tab.component.ts +++ b/src-ui/app/views/dashboard-view/views/brightness-automations-view/tabs/new-brightness-automations-tab/brightness-automations-tab.component.ts @@ -1,6 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, DestroyRef, OnDestroy, OnInit } from '@angular/core'; import { BrightnessEvent } from '../../../../../../models/automations'; import { triggerChildren } from '../../../../../../utils/animations'; +import { AppSettingsService } from '../../../../../../services/app-settings.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { filter } from 'rxjs'; +import KonamiCode from 'konami-code-js'; export interface BrightnessEventViewModel { name: BrightnessEvent; @@ -16,7 +20,7 @@ export interface BrightnessEventViewModel { styleUrls: ['./brightness-automations-tab.component.scss'], animations: [triggerChildren()], }) -export class BrightnessAutomationsTabComponent { +export class BrightnessAutomationsTabComponent implements OnInit, OnDestroy { protected editEvent?: BrightnessEventViewModel; protected events: Array = [ @@ -32,4 +36,26 @@ export class BrightnessAutomationsTabComponent { sunMode: 'SUNRISE', }, ]; + + private konami: KonamiCode | undefined; + + constructor(private appSettings: AppSettingsService, private destroyRef: DestroyRef) {} + + ngOnInit() { + this.appSettings + .oneTimeFlagSetAsync('BRIGHTNESS_AUTOMATION_ON_HMD_CONNECT_EVENT_FEATURE') + .pipe(takeUntilDestroyed(this.destroyRef), filter(Boolean)) + .subscribe(() => { + this.events.push({ name: 'HMD_CONNECT', inProgress: false, icon: 'head_mounted_device' }); + }); + + this.konami = new KonamiCode(() => { + this.konami?.disable(); + this.appSettings.setOneTimeFlag('BRIGHTNESS_AUTOMATION_ON_HMD_CONNECT_EVENT_FEATURE'); + }); + } + + ngOnDestroy() { + this.konami?.disable(); + } } diff --git a/src-ui/assets/i18n/en.json b/src-ui/assets/i18n/en.json index f2684e14..d73fd808 100644 --- a/src-ui/assets/i18n/en.json +++ b/src-ui/assets/i18n/en.json @@ -163,6 +163,9 @@ "SLEEP_PREPARATION": { "title": "When I prepare to go to sleep" }, + "HMD_CONNECT": { + "title": "When my headset connects to SteamVR" + }, "activation": { "category": "Activation" }, @@ -2626,4 +2629,4 @@ }, "title": "VRChat Microphone Mute Automations" } -} \ No newline at end of file +}