Skip to content

Commit

Permalink
Automatically run automations when connecting a HMD: Brightness/CCT, …
Browse files Browse the repository at this point in the history
…Render resolution, BSB Fan, BSB LED & Chaperone distance
  • Loading branch information
Raphiiko committed Jul 13, 2024
1 parent 44984cf commit 5e4c233
Show file tree
Hide file tree
Showing 21 changed files with 215 additions and 110 deletions.
4 changes: 3 additions & 1 deletion src-ui/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ import { BrightnessAutomationDetailsComponent } from './views/dashboard-view/vie
import { DurationInputSettingComponent } from './components/duration-input-setting/duration-input-setting.component';
import { CCTControlService } from './services/cct-control/cct-control.service';
import { CCTControlModalComponent } from './components/cct-control-modal/cct-control-modal.component';
import { SettingsBrightnessCctViewComponent } from './views/dashboard-view/views/settings-brightness-cct-view/settings-brightness-cct-view.component';
import {
SettingsBrightnessCctViewComponent,
} from './views/dashboard-view/views/settings-brightness-cct-view/settings-brightness-cct-view.component';
import { CCTInputSettingComponent } from './components/cct-input-setting/cct-input-setting.component';

[
Expand Down
70 changes: 62 additions & 8 deletions src-ui/app/services/brightness-cct-automation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { SleepService } from './sleep.service';
import {
BehaviorSubject,
combineLatest,
debounceTime,
delay,
distinctUntilChanged,
filter,
firstValueFrom,
interval,
map,
merge,
Observable,
of,
pairwise,
skip,
startWith,
switchMap,
Expand Down Expand Up @@ -40,6 +43,7 @@ import { SetBrightnessOrCCTReason } from './brightness-control/brightness-contro
import { invoke } from '@tauri-apps/api';
import { error } from 'tauri-plugin-log-api';
import { listen } from '@tauri-apps/api/event';
import { OpenVRService } from './openvr.service';

@Injectable({
providedIn: 'root',
Expand Down Expand Up @@ -95,10 +99,21 @@ export class BrightnessCctAutomationService {
private softwareBrightnessControl: SoftwareBrightnessControlService,
private cctControl: CCTControlService,
private eventLog: EventLogService,
private sleepPreparation: SleepPreparationService
private sleepPreparation: SleepPreparationService,
private openvr: OpenVRService
) {}

async init() {
// Run automations when the HMD gets connected
this.openvr.devices
.pipe(
map((devices) => devices.find((d) => d.class === 'HMD')?.serialNumber ?? null),
distinctUntilChanged(),
pairwise(),
filter(([prev, current]) => prev === null && current !== null),
debounceTime(3000)
)
.subscribe(() => this.onHmdConnect());
// Run automations when the sleep mode changes
this.sleepService.mode
.pipe(
Expand Down Expand Up @@ -206,10 +221,49 @@ export class BrightnessCctAutomationService {
);
}

private async onHmdConnect() {
const config = await firstValueFrom(this.automationConfigService.configs).then(
(c) => c.BRIGHTNESS_AUTOMATIONS
);
// If sunrise/sunset times are both enabled, use the relevant values configured for those
if (
config.AT_SUNSET.enabled &&
config.AT_SUNRISE.enabled &&
config.AT_SUNSET.activationTime &&
config.AT_SUNRISE.activationTime
) {
const d = new Date();
const currentHour = d.getHours();
const currentMinute = d.getMinutes();
const [sunriseHour, sunriseMinute] = config.AT_SUNRISE.activationTime.split(':');
const [sunsetHour, sunsetMinute] = config.AT_SUNSET.activationTime.split(':');
const currentTime = currentHour * 3600 + currentMinute * 60;
const sunriseTime = parseInt(sunriseHour) * 3600 + parseInt(sunriseMinute) * 60;
const sunsetTime = parseInt(sunsetHour) * 3600 + parseInt(sunsetMinute) * 60;
const timesInverted = sunriseTime >= sunsetTime;
const firstTime = timesInverted ? sunsetTime : sunriseTime;
const secondTime = timesInverted ? sunriseTime : sunsetTime;
let runAutomation: BrightnessEvent;
if (currentTime < firstTime || currentTime >= secondTime) {
runAutomation = timesInverted ? 'AT_SUNRISE' : 'AT_SUNSET';
} else {
runAutomation = timesInverted ? 'AT_SUNSET' : 'AT_SUNRISE';
}
await this.onAutomationTrigger(runAutomation, config[runAutomation], true, false);
}
// Otherwise, use the values configured for the sleep mode automations (if they're enabled)
else if (await firstValueFrom(this.sleepService.mode)) {
await this.onAutomationTrigger('SLEEP_MODE_ENABLE', config.SLEEP_MODE_ENABLE, true, false);
} else {
await this.onAutomationTrigger('SLEEP_MODE_DISABLE', config.SLEEP_MODE_DISABLE, true, false);
}
}

private async onAutomationTrigger(
automationType: BrightnessEvent,
config: BrightnessEventAutomationConfig,
forceInstant = false
forceInstant = false,
logging = true
) {
// Stop if the automation is disabled
if (!config.enabled || (!config.changeBrightness && !config.changeColorTemperature)) return;
Expand Down Expand Up @@ -238,7 +292,7 @@ export class BrightnessCctAutomationService {
await this.cctControl.setCCT(config.colorTemperature, { logReason });
}
const eventLogReason = eventLogReasonMap[automationType];
if (eventLogReason) {
if (logging && eventLogReason) {
this.eventLog.logEvent({
type: 'cctChanged',
reason: eventLogReason,
Expand Down Expand Up @@ -314,7 +368,7 @@ export class BrightnessCctAutomationService {
}
}
const eventLogReason = eventLogReasonMap[automationType];
if (eventLogReason) {
if (logging && eventLogReason) {
if (advancedMode) {
this.eventLog.logEvent({
type: 'softwareBrightnessChanged',
Expand Down Expand Up @@ -358,15 +412,15 @@ export class BrightnessCctAutomationService {
const sunsetTime = config.AT_SUNSET.activationTime ?? this.autoSunsetTime;
if (
config.AT_SUNSET.enabled &&
((config.AT_SUNSET.onlyWhenSleepDisabled && !this.sleepMode) || this.sleepMode) &&
sunsetTime === currentTime
sunsetTime === currentTime &&
(!config.AT_SUNSET.onlyWhenSleepDisabled || !this.sleepMode)
) {
await this.onAutomationTrigger('AT_SUNSET', config.AT_SUNSET);
}
if (
config.AT_SUNRISE.enabled &&
((config.AT_SUNRISE.onlyWhenSleepDisabled && !this.sleepMode) || this.sleepMode) &&
sunriseTime === currentTime
sunriseTime === currentTime &&
(!config.AT_SUNRISE.onlyWhenSleepDisabled || !this.sleepMode)
) {
await this.onAutomationTrigger('AT_SUNRISE', config.AT_SUNRISE);
}
Expand Down
37 changes: 30 additions & 7 deletions src-ui/app/services/fade-distance-automation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { Injectable } from '@angular/core';
import { AutomationConfigService } from './automation-config.service';
import { SleepService } from './sleep.service';
import { EventLogService } from './event-log.service';
import { distinctUntilChanged, firstValueFrom, skip } from 'rxjs';
import {
debounceTime,
distinctUntilChanged,
filter,
firstValueFrom,
map,
pairwise,
skip,
} from 'rxjs';
import { OpenVRService } from './openvr.service';
import { EventLogChaperoneFadeDistanceChanged } from '../models/event-log-entry';

Expand All @@ -21,9 +29,22 @@ export class ChaperoneFadeDistanceAutomationService {
this.sleepService.mode
.pipe(skip(1), distinctUntilChanged())
.subscribe((sleepMode) => this.onSleepModeChange(sleepMode));
// Run automations when the HMD gets connected
this.openvr.devices
.pipe(
map((devices) => devices.find((d) => d.class === 'HMD')?.serialNumber ?? null),
distinctUntilChanged(),
pairwise(),
filter(([prev, current]) => prev === null && current !== null),
debounceTime(3000)
)
.subscribe(() => this.onHmdConnect());
}

private async onSleepModeChange(sleepMode: boolean) {
private async onHmdConnect() {
this.onSleepModeChange(await firstValueFrom(this.sleepService.mode), false);
}
private async onSleepModeChange(sleepMode: boolean, logging = true) {
const config = await firstValueFrom(this.automationConfigService.configs).then((c) =>
sleepMode
? c.CHAPERONE_FADE_DISTANCE_ON_SLEEP_MODE_ENABLE
Expand All @@ -37,10 +58,12 @@ export class ChaperoneFadeDistanceAutomationService {
return;
}
await this.openvr.setFadeDistance(config.fadeDistance);
this.eventLog.logEvent({
type: 'chaperoneFadeDistanceChanged',
reason: sleepMode ? 'SLEEP_MODE_ENABLED' : 'SLEEP_MODE_DISABLED',
fadeDistance: config.fadeDistance,
} as EventLogChaperoneFadeDistanceChanged);
if (logging) {
this.eventLog.logEvent({
type: 'chaperoneFadeDistanceChanged',
reason: sleepMode ? 'SLEEP_MODE_ENABLED' : 'SLEEP_MODE_DISABLED',
fadeDistance: config.fadeDistance,
} as EventLogChaperoneFadeDistanceChanged);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,29 @@ export class BigscreenBeyondFanAutomationService {
})
)
.subscribe();
// Attempt to set the fan speed to the value saved by the beyond driver utility, whenever the headset is detected
// Handle fan speed when the HMD is being connected
this._connected
.pipe(distinctUntilChanged(), debounceTime(500), filter(Boolean))
.subscribe(async () => {
const savedFanSpeed = await this.getBeyondDriverSavedFanSpeed();
if (savedFanSpeed !== null) await this.setFanSpeed(savedFanSpeed, true);
});
.subscribe(() => this.onHmdConnect());
// Setup the automations
this.handleFanSafety();
this.sleepService.mode
.pipe(distinctUntilChanged(), skip(1))
.subscribe((sleepMode) => this.onSleepModeChange(sleepMode));
this.sleepPreparation.onSleepPreparation.subscribe(() => this.onSleepPreparation());
this._connected.pipe(filter(Boolean), distinctUntilChanged()).subscribe(async () => {
await this.onSleepModeChange(await firstValueFrom(this.sleepService.mode));
});
}

private async onHmdConnect() {
const sleepMode = await firstValueFrom(this.sleepService.mode);
let setFanValue;
if (this.config.onSleepEnable && sleepMode) {
setFanValue = this.config.onSleepEnableFanSpeed;
} else if (this.config.onSleepDisable && !sleepMode) {
setFanValue = this.config.onSleepDisableFanSpeed;
} else {
setFanValue = await this.getBeyondDriverSavedFanSpeed();
}
if (setFanValue !== null) await this.setFanSpeed(setFanValue, true);
}

private async onSleepModeChange(sleepMode: boolean) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import {
BehaviorSubject,
debounceTime,
distinctUntilChanged,
filter,
firstValueFrom,
Expand Down Expand Up @@ -64,14 +65,24 @@ export class BigscreenBeyondLedAutomationService {
)
)
.subscribe();
// Handle LED color when the HMD is being connected
this.connected
.pipe(distinctUntilChanged(), debounceTime(500), filter(Boolean))
.subscribe(() => this.onHmdConnect());
// Setup the automations
this.sleepService.mode
.pipe(distinctUntilChanged(), skip(1))
.subscribe((sleepMode) => this.onSleepModeChange(sleepMode));
this.sleepPreparation.onSleepPreparation.subscribe(() => this.onSleepPreparation());
this.connected.pipe(filter(Boolean), distinctUntilChanged()).subscribe(async () => {
await this.onSleepModeChange(await firstValueFrom(this.sleepService.mode));
});
}

private async onHmdConnect() {
const sleepMode = await firstValueFrom(this.sleepService.mode);
if (sleepMode && this.config.onSleepEnable) {
await this.setLedColor(this.config.onSleepEnableRgb, false);
} else if (!sleepMode && this.config.onSleepDisable) {
await this.setLedColor(this.config.onSleepDisableRgb, false);
}
}

private async onSleepModeChange(sleepMode: boolean) {
Expand Down
38 changes: 31 additions & 7 deletions src-ui/app/services/render-resolution-automation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { Injectable } from '@angular/core';
import { AutomationConfigService } from './automation-config.service';
import { SleepService } from './sleep.service';
import { EventLogService } from './event-log.service';
import { distinctUntilChanged, firstValueFrom, skip } from 'rxjs';
import {
debounceTime,
distinctUntilChanged,
filter,
firstValueFrom,
map,
pairwise,
skip,
} from 'rxjs';
import { EventLogRenderResolutionChanged } from '../models/event-log-entry';
import { OpenVRService } from './openvr.service';

Expand All @@ -21,9 +29,19 @@ export class RenderResolutionAutomationService {
this.sleepService.mode
.pipe(skip(1), distinctUntilChanged())
.subscribe((sleepMode) => this.onSleepModeChange(sleepMode));
// Run automations when the HMD gets connected
this.openvr.devices
.pipe(
map((devices) => devices.find((d) => d.class === 'HMD')?.serialNumber ?? null),
distinctUntilChanged(),
pairwise(),
filter(([prev, current]) => prev === null && current !== null),
debounceTime(3000)
)
.subscribe(() => this.onHmdConnect());
}

private async onSleepModeChange(sleepMode: boolean) {
private async onSleepModeChange(sleepMode: boolean, logging = true) {
const config = await firstValueFrom(this.automationConfigService.configs).then((c) =>
sleepMode
? c.RENDER_RESOLUTION_ON_SLEEP_MODE_ENABLE
Expand All @@ -33,10 +51,16 @@ export class RenderResolutionAutomationService {
const openvrStatus = await firstValueFrom(this.openvr.status);
if (openvrStatus !== 'INITIALIZED') return;
await this.openvr.setSupersampleScale(config.resolution ? config.resolution / 100 : null);
this.eventLog.logEvent({
type: 'renderResolutionChanged',
reason: sleepMode ? 'SLEEP_MODE_ENABLED' : 'SLEEP_MODE_DISABLED',
resolution: config.resolution,
} as EventLogRenderResolutionChanged);
if (logging) {
this.eventLog.logEvent({
type: 'renderResolutionChanged',
reason: sleepMode ? 'SLEEP_MODE_ENABLED' : 'SLEEP_MODE_DISABLED',
resolution: config.resolution,
} as EventLogRenderResolutionChanged);
}
}

private async onHmdConnect() {
await this.onSleepModeChange(await firstValueFrom(this.sleepService.mode), false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ <h2 translate>brightness-automations.events</h2>
<div class="setting-row" *ngFor="let event of events">
<div class="setting-row-icon">
<i class="material-symbols-outlined" [class.icon-filled]="event.iconFilled">{{
event.icon
}}</i>
event.icon
}}</i>
</div>
<div class="setting-row-label" translate>
<span>{{ 'brightness-automations.' + event.name + '.title' | translate }}</span>
Expand All @@ -32,3 +32,14 @@ <h2 translate>brightness-automations.events</h2>
</div>
</div>
</div>
<div
class="alert"
>
<div class="alert-bg"></div>
<div class="alert-icon">
<i class="material-symbols-outlined">info</i>
</div>
<div class="alert-content">
<span translate>brightness-automations.onHmdConnectInfo</span>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
padding: 2em;
height: 100%;
}

.alert {
margin-top: 1em;
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,18 @@ <h2 translate>hmd-automations.bigscreenBeyond.fanControl.title</h2>
<div class="setting-row-label" translate>
<span class="row-title">
<span translate
>brightness-automations.hmdSettings.bigscreenBeyond.forceFanSafety.title</span
>settings.brightnessCct.bigscreenBeyond.forceFanSafety.title</span
>
<span
class="experimental-flag"
[tooltip]="'brightness-automations.hmdSettings.bigscreenBeyond.forceFanSafety.warning'"
[tooltip]="'settings.brightnessCct.bigscreenBeyond.forceFanSafety.warning'"
[tooltipMode]="'right'"
>
<i class="material-icons">warning</i>
</span>
</span>
<span translate
>brightness-automations.hmdSettings.bigscreenBeyond.forceFanSafety.description</span
>settings.brightnessCct.bigscreenBeyond.forceFanSafety.description</span
>
</div>
<div class="setting-row-action">
Expand Down
Loading

0 comments on commit 5e4c233

Please sign in to comment.