Skip to content

Commit

Permalink
Expose HMD Activity over MQTT (experimental)
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphiiko committed Jun 22, 2024
1 parent b53e4ab commit 62c35e5
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Exposed HMD activity level over MQTT as "HMD On Head" (Experimental).

## [1.13.1]

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions src-core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions src-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,15 @@ rev = "2949a3f"

[dependencies.tauri]
version = "1.6.1"
features = [ "window-unminimize", "window-start-dragging", "window-show", "window-hide", "window-close", "window-unmaximize", "window-minimize", "window-maximize",
features = [
"window-unminimize",
"window-start-dragging",
"window-show",
"window-hide",
"window-close",
"window-unmaximize",
"window-minimize",
"window-maximize",
"global-shortcut-all",
"clipboard-write-text",
"cli",
Expand All @@ -125,7 +133,7 @@ features = [ "window-unminimize", "window-start-dragging", "window-show", "windo

[dependencies.ovr_overlay]
git = "https://github.com/Raphiiko/ovr_overlay_oyasumi"
rev = "b7e51d0"
rev = "f400e12a21355edc897c7a82c6ddf1889d740b05"
# path = "../../ovr_overlay_oyasumi"
features = ["ovr_system", "ovr_settings"]

Expand Down
52 changes: 44 additions & 8 deletions src-core/src/openvr/devices.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::collections::HashMap;

use super::models::{
DeviceUpdateEvent, OVRDevice, OVRDevicePose, OpenVRInputEvent, TrackedDeviceClass, OVRHandleType,
DeviceUpdateEvent, OVRDevice, OVRDevicePose, OVRHandleType, OpenVRInputEvent,
TrackedDeviceClass,
};
use super::{GestureDetector, SleepDetector, OVR_CONTEXT};
use crate::utils::send_event;
Expand Down Expand Up @@ -85,7 +86,6 @@ async fn update_handle_types() {
}

async fn update_handle_type(handle_type: OVRHandleType) {

let context = OVR_CONTEXT.lock().await;
let mut device_handle_cache = DEVICE_HANDLE_TYPE_CACHE.lock().await;
let mut input = match context.as_ref() {
Expand All @@ -96,7 +96,10 @@ async fn update_handle_type(handle_type: OVRHandleType) {
let action_handle = match input.get_input_source_handle(&handle_type.as_action_handle()) {
Ok(handle) => handle,
Err(err) => {
error!("[Core] Unable to get action handle by name {}: {err}", handle_type.as_action_handle()); // shouldn't happen but log just in case
error!(
"[Core] Unable to get action handle by name {}: {err}",
handle_type.as_action_handle()
); // shouldn't happen but log just in case
return;
}
};
Expand All @@ -105,10 +108,15 @@ async fn update_handle_type(handle_type: OVRHandleType) {
Ok(info) => info,
Err(err) => {
// expected errors
if err == EVRInputError::VRInputError_NoData.into() || err == EVRInputError::VRInputError_InvalidHandle.into() {
if err == EVRInputError::VRInputError_NoData.into()
|| err == EVRInputError::VRInputError_InvalidHandle.into()
{
return;
}
error!("[Core] Unable to get device info for handle {}: {err}", handle_type.as_action_handle()); // unexpected error
error!(
"[Core] Unable to get device info for handle {}: {err}",
handle_type.as_action_handle()
); // unexpected error
return;
}
};
Expand Down Expand Up @@ -141,8 +149,10 @@ async fn update_device<'a>(device_index: ovr::TrackedDeviceIndex, emit: bool) {
device_class_cache.insert(device_index.0, class.clone());
}
drop(device_class_cache);

let handle_type: Option<OVRHandleType> = device_handle_cache.get(&device_index.0).map(|it| it.clone());

let handle_type: Option<OVRHandleType> = device_handle_cache
.get(&device_index.0)
.map(|it| it.clone());
drop(device_handle_cache);
// Get device properties
let battery: Option<f32> = system
Expand Down Expand Up @@ -199,6 +209,30 @@ async fn update_device<'a>(device_index: ovr::TrackedDeviceIndex, emit: bool) {
ovr::sys::ETrackedDeviceProperty::Prop_ModelNumber_String,
)
.ok();
let mut hmd_on_head = None;
let mut debug_hmd_activity = None;
if class == TrackedDeviceClass::HMD {
let activity_level = system.get_tracked_device_activity_level(device_index);
hmd_on_head = Some(activity_level == ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_UserInteraction || activity_level == ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_UserInteraction_Timeout);
// Serialize activity level
debug_hmd_activity = Some(
match activity_level {
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_Unknown => "Unknown",
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_Idle => "Idle",
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_UserInteraction => {
"UserInteraction"
}
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_UserInteraction_Timeout => {
"UserInteractionTimeout"
}
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_Standby => "Standby",
ovr::sys::EDeviceActivityLevel::k_EDeviceActivityLevel_Idle_Timeout => {
"IdleTimeout"
}
}
.to_string(),
);
}

let device = OVRDevice {
index: device_index.0,
Expand All @@ -215,7 +249,9 @@ async fn update_device<'a>(device_index: ovr::TrackedDeviceIndex, emit: bool) {
hardware_revision,
manufacturer_name,
model_number,
handle_type
handle_type,
hmd_on_head,
debug_hmd_activity,
};

// Add or update device in list
Expand Down
12 changes: 7 additions & 5 deletions src-core/src/openvr/models.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ovr_overlay::input::{ActionHandle, ActionSetHandle};
use serde::{Deserialize, Serialize};
use strum_macros::{IntoStaticStr, EnumIter};
use strum_macros::{EnumIter, IntoStaticStr};

pub struct OpenVRAction {
pub name: String,
Expand Down Expand Up @@ -128,7 +128,9 @@ pub struct OVRDevice {
pub hardware_revision: Option<String>,
pub manufacturer_name: Option<String>,
pub model_number: Option<String>,
pub handle_type: Option<OVRHandleType>
pub handle_type: Option<OVRHandleType>,
pub hmd_on_head: Option<bool>,
pub debug_hmd_activity: Option<String>,
}

#[derive(Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -162,7 +164,7 @@ pub enum OVRHandleType {
Waist,
Chest,
Camera,
Keyboard
Keyboard,
}

impl OVRHandleType {
Expand All @@ -189,8 +191,8 @@ impl OVRHandleType {
OVRHandleType::Waist => "/user/waist",
OVRHandleType::Chest => "/user/chest",
OVRHandleType::Camera => "/user/camera",
OVRHandleType::Keyboard => "/user/keyboard"
}
OVRHandleType::Keyboard => "/user/keyboard",
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src-ui/app/models/ovr-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export interface OVRDevice {
providesBatteryStatus: boolean;
serialNumber: string;
pose: any;
hmdOnHead: boolean;
debugHmdActivity: string;

// Status properties
isTurningOff: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { map } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class HMDModelMqttIntegrationService {
export class HMDDataMqttIntegrationService {
constructor(private mqtt: MqttDiscoveryService, private openvr: OpenVRService) {}

async init() {
// Init property
// Init properties
await this.mqtt.initProperty({
type: 'SENSOR',
id: 'hmdModel',
Expand All @@ -19,15 +19,36 @@ export class HMDModelMqttIntegrationService {
value: 'null',
available: false,
});
await this.mqtt.initProperty({
type: 'SENSOR',
id: 'hmdOnHead',
topicPath: 'hmdOnHead',
displayName: 'VR HMD On Head',
value: 'off',
available: false,
});
await this.mqtt.initProperty({
type: 'SENSOR',
id: 'debugHmdActivity',
topicPath: 'debugHmdActivity',
displayName: 'VR HMD Activity (DEBUG)',
value: 'null',
available: false,
});
this.openvr.status.subscribe((status) => {
this.mqtt.setPropertyAvailability('hmdModel', status === 'INITIALIZED');
this.mqtt.setPropertyAvailability('hmdOnHead', status === 'INITIALIZED');
this.mqtt.setPropertyAvailability('debugHmdActivity', status === 'INITIALIZED');
});
this.openvr.devices
.pipe(map((devices) => devices.find((d) => d.class === 'HMD')))
.subscribe((device) => {
const name =
[device?.manufacturerName, device?.modelNumber].filter(Boolean).join(' ') ?? 'null';
this.mqtt.setSensorPropertyValue('hmdModel', name);
console.log(device);
this.mqtt.setSensorPropertyValue('hmdOnHead', device?.hmdOnHead ? 'on' : 'off');
this.mqtt.setSensorPropertyValue('debugHmdActivity', device?.debugHmdActivity ?? 'null');
});
}
}
6 changes: 3 additions & 3 deletions src-ui/app/services/mqtt/mqtt-integration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SleepModeMqttIntegrationService } from './integrations/sleep-mode.mqtt-
import { SleepPreparationMqttIntegrationService } from './integrations/sleep-preparation.mqtt-integration.service';
import { BaseStationMqttIntegrationService } from './integrations/base-station.mqtt-integration.service';
import { TrackerControllerMqttIntegrationService } from './integrations/tracker-controller.mqtt-integration';
import { HMDModelMqttIntegrationService } from './integrations/hmd-model.mqtt-integration';
import { HMDDataMqttIntegrationService } from './integrations/hmd-data.mqtt-integration';
import { SleepingPositionMqttIntegrationService } from './integrations/sleeping-position.mqtt-integration.service';
import { ProcessActiveMqttIntegrationService } from './integrations/process-active.mqtt-integration.service';
import { VRChatMqttIntegrationService } from './integrations/vrchat.mqtt-integration.service';
Expand All @@ -21,7 +21,7 @@ export class MqttIntegrationService {
private sleepPreparationMqttIntegrationService: SleepPreparationMqttIntegrationService,
private baseStationMqttIntegrationService: BaseStationMqttIntegrationService,
private trackerControllerMqttIntegrationService: TrackerControllerMqttIntegrationService,
private hmdModelMqttIntegrationService: HMDModelMqttIntegrationService,
private hmdDataMqttIntegrationService: HMDDataMqttIntegrationService,
private sleepingPositionMqttIntegrationService: SleepingPositionMqttIntegrationService,
private processActiveMqttIntegrationService: ProcessActiveMqttIntegrationService,
private vrchatMqttIntegrationService: VRChatMqttIntegrationService,
Expand All @@ -37,7 +37,7 @@ export class MqttIntegrationService {
this.sleepPreparationMqttIntegrationService.init(),
this.baseStationMqttIntegrationService.init(),
this.trackerControllerMqttIntegrationService.init(),
this.hmdModelMqttIntegrationService.init(),
this.hmdDataMqttIntegrationService.init(),
this.sleepingPositionMqttIntegrationService.init(),
this.processActiveMqttIntegrationService.init(),
this.vrchatMqttIntegrationService.init(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ export class SettingsStatusInfoViewComponent {
key: 'Devices',
value: openvr.devices.pipe(map((devices) => devices.length + '')),
},
{
key: 'HMD On Head',
value: openvr.devices.pipe(
map((devices) =>
devices.find((d) => d.class === 'HMD')?.hmdOnHead ? 'True' : 'False'
)
),
},
{
key: 'HMD Activity (Debug)',
value: openvr.devices.pipe(
map(
(devices) => devices.find((d) => d.class === 'HMD')?.debugHmdActivity ?? 'No Value'
)
),
},
],
},
{
Expand Down

0 comments on commit 62c35e5

Please sign in to comment.