From ad48075af91fcc60746225869810c4d5351e4434 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sun, 22 Oct 2023 23:01:06 +0200 Subject: [PATCH 01/13] do some testing --- .../godot-xr-tools/materials/highlight.tres | 2 + .../misc/hold_button_visualshader.tres | 2 +- .../loading_screen/loading_screen_shader.tres | 2 +- .../user_settings/user_settings.gd | 6 +- main.tscn | 44 +- openxr_action_map.tres | 742 +++++++++--------- project.godot | 9 + 7 files changed, 419 insertions(+), 388 deletions(-) diff --git a/addons/godot-xr-tools/materials/highlight.tres b/addons/godot-xr-tools/materials/highlight.tres index d736712..0480f11 100644 --- a/addons/godot-xr-tools/materials/highlight.tres +++ b/addons/godot-xr-tools/materials/highlight.tres @@ -17,6 +17,8 @@ operator = 2 [sub_resource type="VisualShader" id="VisualShader_wb0u4"] code = "shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx; + uniform vec4 Color : source_color; diff --git a/addons/godot-xr-tools/misc/hold_button_visualshader.tres b/addons/godot-xr-tools/misc/hold_button_visualshader.tres index bddcbec..084664c 100644 --- a/addons/godot-xr-tools/misc/hold_button_visualshader.tres +++ b/addons/godot-xr-tools/misc/hold_button_visualshader.tres @@ -102,7 +102,7 @@ function = 12 [resource] code = "shader_type spatial; -render_mode unshaded; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx, unshaded; uniform vec4 albedo : source_color; uniform float value; diff --git a/addons/godot-xr-tools/staging/loading_screen/loading_screen_shader.tres b/addons/godot-xr-tools/staging/loading_screen/loading_screen_shader.tres index a31535e..2d83f5f 100644 --- a/addons/godot-xr-tools/staging/loading_screen/loading_screen_shader.tres +++ b/addons/godot-xr-tools/staging/loading_screen/loading_screen_shader.tres @@ -63,7 +63,7 @@ condition = 1 [resource] code = "shader_type spatial; -render_mode unshaded; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx, unshaded; uniform vec4 bar_color : source_color; uniform sampler2D bar_texture : source_color; diff --git a/addons/godot-xr-tools/user_settings/user_settings.gd b/addons/godot-xr-tools/user_settings/user_settings.gd index d00ebaf..36bc920 100644 --- a/addons/godot-xr-tools/user_settings/user_settings.gd +++ b/addons/godot-xr-tools/user_settings/user_settings.gd @@ -43,9 +43,9 @@ var webxr_auto_primary := 0 # Called when the node enters the scene tree for the first time. func _ready(): - var webxr_interface = XRServer.find_interface("WebXR") - if webxr_interface: - XRServer.tracker_added.connect(self._on_webxr_tracker_added) + # var webxr_interface = XRServer.find_interface("WebXR") + # if webxr_interface: + # XRServer.tracker_added.connect(self._on_webxr_tracker_added) _load() diff --git a/main.tscn b/main.tscn index ab9ae86..96fbf37 100644 --- a/main.tscn +++ b/main.tscn @@ -1,29 +1,49 @@ -[gd_scene load_steps=4 format=3 uid="uid://18sldbn0hij8"] +[gd_scene load_steps=6 format=3 uid="uid://eecv28y6jxk4"] -[ext_resource type="PackedScene" uid="uid://b4kad2kuba1yn" path="res://addons/godot-xr-tools/hands/scenes/lowpoly/left_hand_low.tscn" id="1_66jmx"] -[ext_resource type="PackedScene" uid="uid://l2n30mpbkdyw" path="res://addons/godot-xr-tools/hands/scenes/lowpoly/right_hand_low.tscn" id="2_3f5tl"] -[ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="3_iaq1p"] +[ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] + +[sub_resource type="BoxMesh" id="BoxMesh_ir3co"] +size = Vector3(0.1, 0.1, 0.1) + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_p6hri"] +sky_top_color = Color(0.384314, 0.454902, 0.54902, 1) +use_debanding = false + +[sub_resource type="Sky" id="Sky_vhymk"] +sky_material = SubResource("ProceduralSkyMaterial_p6hri") + +[sub_resource type="Environment" id="Environment_7ghp0"] +background_mode = 1 +background_color = Color(1, 1, 1, 0) +sky = SubResource("Sky_vhymk") [node name="Main" type="Node3D"] [node name="XROrigin3D" type="XROrigin3D" parent="."] [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6555, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.798091, 0.311748) -[node name="LeftHand" type="XRController3D" parent="XROrigin3D"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.341606, 0.506389, 0) +[node name="XRControllerLeft" type="XRController3D" parent="XROrigin3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.469893, 0.597213, -0.251112) tracker = &"left_hand" pose = &"aim" -[node name="LeftHand" parent="XROrigin3D/LeftHand" instance=ExtResource("1_66jmx")] +[node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerLeft"] +mesh = SubResource("BoxMesh_ir3co") -[node name="RightHand" type="XRController3D" parent="XROrigin3D"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.408271, 0.538159, 0) +[node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) tracker = &"right_hand" pose = &"aim" -[node name="RightHand" parent="XROrigin3D/RightHand" instance=ExtResource("2_3f5tl")] +[node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerRight"] +mesh = SubResource("BoxMesh_ir3co") -[node name="StartXR" parent="." instance=ExtResource("3_iaq1p")] +[node name="StartXR" parent="." instance=ExtResource("1_i4c04")] enable_passthrough = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_7ghp0") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] diff --git a/openxr_action_map.tres b/openxr_action_map.tres index b731baf..cc2e589 100644 --- a/openxr_action_map.tres +++ b/openxr_action_map.tres @@ -1,835 +1,835 @@ -[gd_resource type="OpenXRActionMap" load_steps=197 format=3 uid="uid://gs7cp42p8eto"] +[gd_resource type="OpenXRActionMap" load_steps=197 format=3 uid="uid://dr4u0neixrr5o"] -[sub_resource type="OpenXRAction" id="OpenXRAction_q1i8v"] +[sub_resource type="OpenXRAction" id="OpenXRAction_7a7cg"] resource_name = "trigger" localized_name = "Trigger" toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_2la4t"] +[sub_resource type="OpenXRAction" id="OpenXRAction_hfach"] resource_name = "trigger_click" localized_name = "Trigger click" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_ahush"] +[sub_resource type="OpenXRAction" id="OpenXRAction_v806b"] resource_name = "trigger_touch" localized_name = "Trigger touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_w50st"] +[sub_resource type="OpenXRAction" id="OpenXRAction_qc0ui"] resource_name = "grip" localized_name = "Grip" toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_hjfn1"] +[sub_resource type="OpenXRAction" id="OpenXRAction_tq28e"] resource_name = "grip_click" localized_name = "Grip click" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_uqlso"] +[sub_resource type="OpenXRAction" id="OpenXRAction_7kes1"] resource_name = "grip_touch" localized_name = "Grip touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_d81o5"] +[sub_resource type="OpenXRAction" id="OpenXRAction_8lf75"] resource_name = "grip_force" localized_name = "Grip force" toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_1qkem"] +[sub_resource type="OpenXRAction" id="OpenXRAction_pnwp5"] resource_name = "primary" localized_name = "Primary joystick/thumbstick/trackpad" action_type = 2 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_mw62d"] +[sub_resource type="OpenXRAction" id="OpenXRAction_6r5q7"] resource_name = "primary_click" localized_name = "Primary joystick/thumbstick/trackpad click" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_vyfej"] +[sub_resource type="OpenXRAction" id="OpenXRAction_mqjaa"] resource_name = "primary_touch" localized_name = "Primary joystick/thumbstick/trackpad touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_skph0"] +[sub_resource type="OpenXRAction" id="OpenXRAction_k0xt7"] resource_name = "secondary" localized_name = "Secondary joystick/thumbstick/trackpad" action_type = 2 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_ex4re"] +[sub_resource type="OpenXRAction" id="OpenXRAction_5a2y3"] resource_name = "secondary_click" localized_name = "Secondary joystick/thumbstick/trackpad click" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_g81y4"] +[sub_resource type="OpenXRAction" id="OpenXRAction_184m6"] resource_name = "secondary_touch" localized_name = "Secondary joystick/thumbstick/trackpad touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_lc4t5"] +[sub_resource type="OpenXRAction" id="OpenXRAction_yvxai"] resource_name = "menu_button" localized_name = "Menu button" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_diti2"] +[sub_resource type="OpenXRAction" id="OpenXRAction_wmfn2"] resource_name = "select_button" localized_name = "Select button" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_8nx4f"] +[sub_resource type="OpenXRAction" id="OpenXRAction_ug7ra"] resource_name = "ax_button" localized_name = "A/X button" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_2q3wy"] +[sub_resource type="OpenXRAction" id="OpenXRAction_rgv6o"] resource_name = "ax_touch" localized_name = "A/X touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_mv7tf"] +[sub_resource type="OpenXRAction" id="OpenXRAction_01don"] resource_name = "by_button" localized_name = "B/Y button" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_1whyb"] +[sub_resource type="OpenXRAction" id="OpenXRAction_0dqbt"] resource_name = "by_touch" localized_name = "B/Y touching" action_type = 0 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_040i8"] +[sub_resource type="OpenXRAction" id="OpenXRAction_0lo1w"] resource_name = "default_pose" localized_name = "Default pose" action_type = 3 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/keyboard") -[sub_resource type="OpenXRAction" id="OpenXRAction_iq3mb"] +[sub_resource type="OpenXRAction" id="OpenXRAction_7xwcn"] resource_name = "aim_pose" localized_name = "Aim pose" action_type = 3 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_mesn2"] +[sub_resource type="OpenXRAction" id="OpenXRAction_1l7gi"] resource_name = "grip_pose" localized_name = "Grip pose" action_type = 3 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_esp51"] +[sub_resource type="OpenXRAction" id="OpenXRAction_6hayr"] resource_name = "palm_pose" localized_name = "Palm pose" action_type = 3 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") -[sub_resource type="OpenXRAction" id="OpenXRAction_c2ak4"] +[sub_resource type="OpenXRAction" id="OpenXRAction_s2ilf"] resource_name = "haptic" localized_name = "Haptic" action_type = 4 toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/keyboard") -[sub_resource type="OpenXRActionSet" id="OpenXRActionSet_6doge"] +[sub_resource type="OpenXRActionSet" id="OpenXRActionSet_r4msx"] resource_name = "godot" localized_name = "Godot action set" -actions = [SubResource("OpenXRAction_q1i8v"), SubResource("OpenXRAction_2la4t"), SubResource("OpenXRAction_ahush"), SubResource("OpenXRAction_w50st"), SubResource("OpenXRAction_hjfn1"), SubResource("OpenXRAction_uqlso"), SubResource("OpenXRAction_d81o5"), SubResource("OpenXRAction_1qkem"), SubResource("OpenXRAction_mw62d"), SubResource("OpenXRAction_vyfej"), SubResource("OpenXRAction_skph0"), SubResource("OpenXRAction_ex4re"), SubResource("OpenXRAction_g81y4"), SubResource("OpenXRAction_lc4t5"), SubResource("OpenXRAction_diti2"), SubResource("OpenXRAction_8nx4f"), SubResource("OpenXRAction_2q3wy"), SubResource("OpenXRAction_mv7tf"), SubResource("OpenXRAction_1whyb"), SubResource("OpenXRAction_040i8"), SubResource("OpenXRAction_iq3mb"), SubResource("OpenXRAction_mesn2"), SubResource("OpenXRAction_esp51"), SubResource("OpenXRAction_c2ak4")] +actions = [SubResource("OpenXRAction_7a7cg"), SubResource("OpenXRAction_hfach"), SubResource("OpenXRAction_v806b"), SubResource("OpenXRAction_qc0ui"), SubResource("OpenXRAction_tq28e"), SubResource("OpenXRAction_7kes1"), SubResource("OpenXRAction_8lf75"), SubResource("OpenXRAction_pnwp5"), SubResource("OpenXRAction_6r5q7"), SubResource("OpenXRAction_mqjaa"), SubResource("OpenXRAction_k0xt7"), SubResource("OpenXRAction_5a2y3"), SubResource("OpenXRAction_184m6"), SubResource("OpenXRAction_yvxai"), SubResource("OpenXRAction_wmfn2"), SubResource("OpenXRAction_ug7ra"), SubResource("OpenXRAction_rgv6o"), SubResource("OpenXRAction_01don"), SubResource("OpenXRAction_0dqbt"), SubResource("OpenXRAction_0lo1w"), SubResource("OpenXRAction_7xwcn"), SubResource("OpenXRAction_1l7gi"), SubResource("OpenXRAction_6hayr"), SubResource("OpenXRAction_s2ilf")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_21jpr"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_58o4c"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lxp56"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8c4bl"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_etinh"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_wp0x3"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_otf88"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3o58l"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lq08k"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g5dlg"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_reu7a"] -action = SubResource("OpenXRAction_diti2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8skej"] +action = SubResource("OpenXRAction_wmfn2") paths = PackedStringArray("/user/hand/left/input/select/click", "/user/hand/right/input/select/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vlix5"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mpvnx"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_rtx12"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_gy0l8"] interaction_profile_path = "/interaction_profiles/khr/simple_controller" -bindings = [SubResource("OpenXRIPBinding_21jpr"), SubResource("OpenXRIPBinding_lxp56"), SubResource("OpenXRIPBinding_etinh"), SubResource("OpenXRIPBinding_otf88"), SubResource("OpenXRIPBinding_lq08k"), SubResource("OpenXRIPBinding_reu7a"), SubResource("OpenXRIPBinding_vlix5")] +bindings = [SubResource("OpenXRIPBinding_58o4c"), SubResource("OpenXRIPBinding_8c4bl"), SubResource("OpenXRIPBinding_wp0x3"), SubResource("OpenXRIPBinding_3o58l"), SubResource("OpenXRIPBinding_g5dlg"), SubResource("OpenXRIPBinding_8skej"), SubResource("OpenXRIPBinding_mpvnx")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_w2bt2"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2nl40"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_to525"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_noaa6"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_btktf"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_rq5co"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4qcdk"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gg5xg"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jyeev"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_rxyew"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_whd2k"] -action = SubResource("OpenXRAction_diti2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_li0ky"] +action = SubResource("OpenXRAction_wmfn2") paths = PackedStringArray("/user/hand/left/input/system/click", "/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_l3a4b"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vtwy0"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_q4105"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_eu8xb"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/click", "/user/hand/right/input/trigger/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2u4i6"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_maxvx"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jbtss"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mu07h"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hcoo3"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o1y5o"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/trackpad", "/user/hand/right/input/trackpad") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mdwfo"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dauyl"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/trackpad/click", "/user/hand/right/input/trackpad/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mxb6s"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8fui1"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/trackpad/touch", "/user/hand/right/input/trackpad/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4oitw"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ocf5c"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_uxbat"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_asoou"] interaction_profile_path = "/interaction_profiles/htc/vive_controller" -bindings = [SubResource("OpenXRIPBinding_w2bt2"), SubResource("OpenXRIPBinding_to525"), SubResource("OpenXRIPBinding_btktf"), SubResource("OpenXRIPBinding_4qcdk"), SubResource("OpenXRIPBinding_jyeev"), SubResource("OpenXRIPBinding_whd2k"), SubResource("OpenXRIPBinding_l3a4b"), SubResource("OpenXRIPBinding_q4105"), SubResource("OpenXRIPBinding_2u4i6"), SubResource("OpenXRIPBinding_jbtss"), SubResource("OpenXRIPBinding_hcoo3"), SubResource("OpenXRIPBinding_mdwfo"), SubResource("OpenXRIPBinding_mxb6s"), SubResource("OpenXRIPBinding_4oitw")] +bindings = [SubResource("OpenXRIPBinding_2nl40"), SubResource("OpenXRIPBinding_noaa6"), SubResource("OpenXRIPBinding_rq5co"), SubResource("OpenXRIPBinding_gg5xg"), SubResource("OpenXRIPBinding_rxyew"), SubResource("OpenXRIPBinding_li0ky"), SubResource("OpenXRIPBinding_vtwy0"), SubResource("OpenXRIPBinding_eu8xb"), SubResource("OpenXRIPBinding_maxvx"), SubResource("OpenXRIPBinding_mu07h"), SubResource("OpenXRIPBinding_o1y5o"), SubResource("OpenXRIPBinding_dauyl"), SubResource("OpenXRIPBinding_8fui1"), SubResource("OpenXRIPBinding_ocf5c")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ptjub"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_070x1"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_f8dib"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d413j"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_su4mj"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4qy5y"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vssiw"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o226e"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5yqo7"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0q01l"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7ylaf"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hnisl"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hm0w6"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xv4on"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_wya8w"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_r3mf1"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ixh3t"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o2xh3"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_entpe"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5wl22"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_krypv"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hrj6o"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_p3w8u"] -action = SubResource("OpenXRAction_skph0") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0qlji"] +action = SubResource("OpenXRAction_k0xt7") paths = PackedStringArray("/user/hand/left/input/trackpad", "/user/hand/right/input/trackpad") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_sttdx"] -action = SubResource("OpenXRAction_ex4re") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jbk6m"] +action = SubResource("OpenXRAction_5a2y3") paths = PackedStringArray("/user/hand/left/input/trackpad/click", "/user/hand/right/input/trackpad/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_1p2a4"] -action = SubResource("OpenXRAction_g81y4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g0t3a"] +action = SubResource("OpenXRAction_184m6") paths = PackedStringArray("/user/hand/left/input/trackpad/touch", "/user/hand/right/input/trackpad/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jc6rm"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ckpr0"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_1xmth"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_nuit2"] interaction_profile_path = "/interaction_profiles/microsoft/motion_controller" -bindings = [SubResource("OpenXRIPBinding_ptjub"), SubResource("OpenXRIPBinding_f8dib"), SubResource("OpenXRIPBinding_su4mj"), SubResource("OpenXRIPBinding_vssiw"), SubResource("OpenXRIPBinding_5yqo7"), SubResource("OpenXRIPBinding_7ylaf"), SubResource("OpenXRIPBinding_hm0w6"), SubResource("OpenXRIPBinding_wya8w"), SubResource("OpenXRIPBinding_ixh3t"), SubResource("OpenXRIPBinding_entpe"), SubResource("OpenXRIPBinding_krypv"), SubResource("OpenXRIPBinding_p3w8u"), SubResource("OpenXRIPBinding_sttdx"), SubResource("OpenXRIPBinding_1p2a4"), SubResource("OpenXRIPBinding_jc6rm")] +bindings = [SubResource("OpenXRIPBinding_070x1"), SubResource("OpenXRIPBinding_d413j"), SubResource("OpenXRIPBinding_4qy5y"), SubResource("OpenXRIPBinding_o226e"), SubResource("OpenXRIPBinding_0q01l"), SubResource("OpenXRIPBinding_hnisl"), SubResource("OpenXRIPBinding_xv4on"), SubResource("OpenXRIPBinding_r3mf1"), SubResource("OpenXRIPBinding_o2xh3"), SubResource("OpenXRIPBinding_5wl22"), SubResource("OpenXRIPBinding_hrj6o"), SubResource("OpenXRIPBinding_0qlji"), SubResource("OpenXRIPBinding_jbk6m"), SubResource("OpenXRIPBinding_g0t3a"), SubResource("OpenXRIPBinding_ckpr0")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2jq5g"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6bfv6"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_l362f"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_w42m1"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_i3ili"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xuqbb"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4l544"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6hqx2"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g1dmg"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_n1wd2"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ynk27"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5osis"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/x/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d37e8"] -action = SubResource("OpenXRAction_2q3wy") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_w1kb5"] +action = SubResource("OpenXRAction_rgv6o") paths = PackedStringArray("/user/hand/left/input/x/touch", "/user/hand/right/input/a/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_kmrl4"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pmlun"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/y/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_a2i4g"] -action = SubResource("OpenXRAction_1whyb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o8jrr"] +action = SubResource("OpenXRAction_0dqbt") paths = PackedStringArray("/user/hand/left/input/y/touch", "/user/hand/right/input/b/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ex53t"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m5ari"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_x0sxv"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_cj5qr"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gtpts"] -action = SubResource("OpenXRAction_ahush") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0lhad"] +action = SubResource("OpenXRAction_v806b") paths = PackedStringArray("/user/hand/left/input/trigger/touch", "/user/hand/right/input/trigger/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8xcvw"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_i0fsl"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m8y8f"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_u5su8"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2cx4b"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_yccgl"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_u168g"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_llpxi"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_12o0y"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_uh4o2"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/thumbstick/touch", "/user/hand/right/input/thumbstick/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_03jek"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_227eo"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_0qfe6"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_12802"] interaction_profile_path = "/interaction_profiles/oculus/touch_controller" -bindings = [SubResource("OpenXRIPBinding_2jq5g"), SubResource("OpenXRIPBinding_l362f"), SubResource("OpenXRIPBinding_i3ili"), SubResource("OpenXRIPBinding_4l544"), SubResource("OpenXRIPBinding_g1dmg"), SubResource("OpenXRIPBinding_ynk27"), SubResource("OpenXRIPBinding_d37e8"), SubResource("OpenXRIPBinding_kmrl4"), SubResource("OpenXRIPBinding_a2i4g"), SubResource("OpenXRIPBinding_ex53t"), SubResource("OpenXRIPBinding_x0sxv"), SubResource("OpenXRIPBinding_gtpts"), SubResource("OpenXRIPBinding_8xcvw"), SubResource("OpenXRIPBinding_m8y8f"), SubResource("OpenXRIPBinding_2cx4b"), SubResource("OpenXRIPBinding_u168g"), SubResource("OpenXRIPBinding_12o0y"), SubResource("OpenXRIPBinding_03jek")] +bindings = [SubResource("OpenXRIPBinding_6bfv6"), SubResource("OpenXRIPBinding_w42m1"), SubResource("OpenXRIPBinding_xuqbb"), SubResource("OpenXRIPBinding_6hqx2"), SubResource("OpenXRIPBinding_n1wd2"), SubResource("OpenXRIPBinding_5osis"), SubResource("OpenXRIPBinding_w1kb5"), SubResource("OpenXRIPBinding_pmlun"), SubResource("OpenXRIPBinding_o8jrr"), SubResource("OpenXRIPBinding_m5ari"), SubResource("OpenXRIPBinding_cj5qr"), SubResource("OpenXRIPBinding_0lhad"), SubResource("OpenXRIPBinding_i0fsl"), SubResource("OpenXRIPBinding_u5su8"), SubResource("OpenXRIPBinding_yccgl"), SubResource("OpenXRIPBinding_llpxi"), SubResource("OpenXRIPBinding_uh4o2"), SubResource("OpenXRIPBinding_227eo")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_47571"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ilvpg"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ilo31"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0ejlp"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_48w8c"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dpqpu"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_fx6qk"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pcvxv"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_bvr3b"] -action = SubResource("OpenXRAction_diti2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mcdl3"] +action = SubResource("OpenXRAction_wmfn2") paths = PackedStringArray("/user/hand/left/input/system/click", "/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hg8ga"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mqckp"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/back/click", "/user/hand/right/input/back/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d8wry"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xa1ef"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/x/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0a0cj"] -action = SubResource("OpenXRAction_2q3wy") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0g3pm"] +action = SubResource("OpenXRAction_rgv6o") paths = PackedStringArray("/user/hand/left/input/x/touch", "/user/hand/right/input/a/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jlujy"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_73i0f"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/y/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g3qew"] -action = SubResource("OpenXRAction_1whyb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4vjt3"] +action = SubResource("OpenXRAction_0dqbt") paths = PackedStringArray("/user/hand/left/input/y/touch", "/user/hand/right/input/b/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_widx5"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2sv8m"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_068a7"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_c013x"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g0tpl"] -action = SubResource("OpenXRAction_ahush") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xdhxq"] +action = SubResource("OpenXRAction_v806b") paths = PackedStringArray("/user/hand/left/input/trigger/touch", "/user/hand/right/input/trigger/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_je64b"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ykvhh"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dnixg"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_qwaat"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_357q1"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4ybkm"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e8wqe"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_qbjs1"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mjcfo"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_osqat"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/thumbstick/touch", "/user/hand/right/input/thumbstick/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_n5up2"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_u8axp"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_noym7"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_juyn6"] interaction_profile_path = "/interaction_profiles/pico/neo3_controller" -bindings = [SubResource("OpenXRIPBinding_47571"), SubResource("OpenXRIPBinding_ilo31"), SubResource("OpenXRIPBinding_48w8c"), SubResource("OpenXRIPBinding_fx6qk"), SubResource("OpenXRIPBinding_bvr3b"), SubResource("OpenXRIPBinding_hg8ga"), SubResource("OpenXRIPBinding_d8wry"), SubResource("OpenXRIPBinding_0a0cj"), SubResource("OpenXRIPBinding_jlujy"), SubResource("OpenXRIPBinding_g3qew"), SubResource("OpenXRIPBinding_widx5"), SubResource("OpenXRIPBinding_068a7"), SubResource("OpenXRIPBinding_g0tpl"), SubResource("OpenXRIPBinding_je64b"), SubResource("OpenXRIPBinding_dnixg"), SubResource("OpenXRIPBinding_357q1"), SubResource("OpenXRIPBinding_e8wqe"), SubResource("OpenXRIPBinding_mjcfo"), SubResource("OpenXRIPBinding_n5up2")] +bindings = [SubResource("OpenXRIPBinding_ilvpg"), SubResource("OpenXRIPBinding_0ejlp"), SubResource("OpenXRIPBinding_dpqpu"), SubResource("OpenXRIPBinding_pcvxv"), SubResource("OpenXRIPBinding_mcdl3"), SubResource("OpenXRIPBinding_mqckp"), SubResource("OpenXRIPBinding_xa1ef"), SubResource("OpenXRIPBinding_0g3pm"), SubResource("OpenXRIPBinding_73i0f"), SubResource("OpenXRIPBinding_4vjt3"), SubResource("OpenXRIPBinding_2sv8m"), SubResource("OpenXRIPBinding_c013x"), SubResource("OpenXRIPBinding_xdhxq"), SubResource("OpenXRIPBinding_ykvhh"), SubResource("OpenXRIPBinding_qwaat"), SubResource("OpenXRIPBinding_4ybkm"), SubResource("OpenXRIPBinding_qbjs1"), SubResource("OpenXRIPBinding_osqat"), SubResource("OpenXRIPBinding_u8axp")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_bir01"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_14pu4"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5xxbt"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_fk58r"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6cnvv"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dfa3g"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4fqf8"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hd1jv"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_drihh"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_57s08"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/system/click", "/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xig66"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3erja"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/a/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_l4f4o"] -action = SubResource("OpenXRAction_2q3wy") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_s2pvn"] +action = SubResource("OpenXRAction_rgv6o") paths = PackedStringArray("/user/hand/left/input/a/touch", "/user/hand/right/input/a/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_cfn11"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d2ubt"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/b/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_tmb5p"] -action = SubResource("OpenXRAction_1whyb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xoeqs"] +action = SubResource("OpenXRAction_0dqbt") paths = PackedStringArray("/user/hand/left/input/b/touch", "/user/hand/right/input/b/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_bui2t"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_i8vt5"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e7bku"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_imuhg"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/click", "/user/hand/right/input/trigger/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_34ym5"] -action = SubResource("OpenXRAction_ahush") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_scid8"] +action = SubResource("OpenXRAction_v806b") paths = PackedStringArray("/user/hand/left/input/trigger/touch", "/user/hand/right/input/trigger/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_sa2ae"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3fds2"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_leb3g"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_qfs8m"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3bypt"] -action = SubResource("OpenXRAction_d81o5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_tb3jn"] +action = SubResource("OpenXRAction_8lf75") paths = PackedStringArray("/user/hand/left/input/squeeze/force", "/user/hand/right/input/squeeze/force") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hlviy"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_amjrf"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xpg41"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3o3vv"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pegk7"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hjvfk"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/thumbstick/touch", "/user/hand/right/input/thumbstick/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_syq2y"] -action = SubResource("OpenXRAction_skph0") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_1xg4t"] +action = SubResource("OpenXRAction_k0xt7") paths = PackedStringArray("/user/hand/left/input/trackpad", "/user/hand/right/input/trackpad") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vvoo3"] -action = SubResource("OpenXRAction_ex4re") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3cvw2"] +action = SubResource("OpenXRAction_5a2y3") paths = PackedStringArray("/user/hand/left/input/trackpad/force", "/user/hand/right/input/trackpad/force") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o361t"] -action = SubResource("OpenXRAction_g81y4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_phx35"] +action = SubResource("OpenXRAction_184m6") paths = PackedStringArray("/user/hand/left/input/trackpad/touch", "/user/hand/right/input/trackpad/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pi7e4"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_tdhas"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_sqhjc"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_crui4"] interaction_profile_path = "/interaction_profiles/valve/index_controller" -bindings = [SubResource("OpenXRIPBinding_bir01"), SubResource("OpenXRIPBinding_5xxbt"), SubResource("OpenXRIPBinding_6cnvv"), SubResource("OpenXRIPBinding_4fqf8"), SubResource("OpenXRIPBinding_drihh"), SubResource("OpenXRIPBinding_xig66"), SubResource("OpenXRIPBinding_l4f4o"), SubResource("OpenXRIPBinding_cfn11"), SubResource("OpenXRIPBinding_tmb5p"), SubResource("OpenXRIPBinding_bui2t"), SubResource("OpenXRIPBinding_e7bku"), SubResource("OpenXRIPBinding_34ym5"), SubResource("OpenXRIPBinding_sa2ae"), SubResource("OpenXRIPBinding_leb3g"), SubResource("OpenXRIPBinding_3bypt"), SubResource("OpenXRIPBinding_hlviy"), SubResource("OpenXRIPBinding_xpg41"), SubResource("OpenXRIPBinding_pegk7"), SubResource("OpenXRIPBinding_syq2y"), SubResource("OpenXRIPBinding_vvoo3"), SubResource("OpenXRIPBinding_o361t"), SubResource("OpenXRIPBinding_pi7e4")] +bindings = [SubResource("OpenXRIPBinding_14pu4"), SubResource("OpenXRIPBinding_fk58r"), SubResource("OpenXRIPBinding_dfa3g"), SubResource("OpenXRIPBinding_hd1jv"), SubResource("OpenXRIPBinding_57s08"), SubResource("OpenXRIPBinding_3erja"), SubResource("OpenXRIPBinding_s2pvn"), SubResource("OpenXRIPBinding_d2ubt"), SubResource("OpenXRIPBinding_xoeqs"), SubResource("OpenXRIPBinding_i8vt5"), SubResource("OpenXRIPBinding_imuhg"), SubResource("OpenXRIPBinding_scid8"), SubResource("OpenXRIPBinding_3fds2"), SubResource("OpenXRIPBinding_qfs8m"), SubResource("OpenXRIPBinding_tb3jn"), SubResource("OpenXRIPBinding_amjrf"), SubResource("OpenXRIPBinding_3o3vv"), SubResource("OpenXRIPBinding_hjvfk"), SubResource("OpenXRIPBinding_1xg4t"), SubResource("OpenXRIPBinding_3cvw2"), SubResource("OpenXRIPBinding_phx35"), SubResource("OpenXRIPBinding_tdhas")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_rvivf"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4omj1"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_clrk1"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_h7w5c"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hr42u"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_374sn"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7vxh4"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_etb3c"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_qyg7l"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4o4ii"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lxsux"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_767dd"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/x/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mruk0"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3umyv"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/y/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e6saq"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_a50tr"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_joekh"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xvgpp"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_tosn0"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_s0i7a"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_kgirs"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_klle5"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/value", "/user/hand/right/input/squeeze/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dducc"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_47651"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_o5dv8"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m0jt3"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ctkhc"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4yalq"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_3kktn"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_n8d2x"] interaction_profile_path = "/interaction_profiles/hp/mixed_reality_controller" -bindings = [SubResource("OpenXRIPBinding_rvivf"), SubResource("OpenXRIPBinding_clrk1"), SubResource("OpenXRIPBinding_hr42u"), SubResource("OpenXRIPBinding_7vxh4"), SubResource("OpenXRIPBinding_qyg7l"), SubResource("OpenXRIPBinding_lxsux"), SubResource("OpenXRIPBinding_mruk0"), SubResource("OpenXRIPBinding_e6saq"), SubResource("OpenXRIPBinding_joekh"), SubResource("OpenXRIPBinding_tosn0"), SubResource("OpenXRIPBinding_kgirs"), SubResource("OpenXRIPBinding_dducc"), SubResource("OpenXRIPBinding_o5dv8"), SubResource("OpenXRIPBinding_ctkhc")] +bindings = [SubResource("OpenXRIPBinding_4omj1"), SubResource("OpenXRIPBinding_h7w5c"), SubResource("OpenXRIPBinding_374sn"), SubResource("OpenXRIPBinding_etb3c"), SubResource("OpenXRIPBinding_4o4ii"), SubResource("OpenXRIPBinding_767dd"), SubResource("OpenXRIPBinding_3umyv"), SubResource("OpenXRIPBinding_a50tr"), SubResource("OpenXRIPBinding_xvgpp"), SubResource("OpenXRIPBinding_s0i7a"), SubResource("OpenXRIPBinding_klle5"), SubResource("OpenXRIPBinding_47651"), SubResource("OpenXRIPBinding_m0jt3"), SubResource("OpenXRIPBinding_4yalq")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_k1gr8"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vof3g"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ny1od"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vb033"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hdbl3"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7cwnm"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_y6i05"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pobar"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_468ph"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_1oayv"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click", "/user/hand/right/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5kra7"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m4awp"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8j6ge"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gt3w0"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_yrus0"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_152ib"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_neu3a"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e5tqk"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2wqk6"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4qajh"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_yw2on"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ynvlk"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_exlak"] -action = SubResource("OpenXRAction_skph0") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6hkk8"] +action = SubResource("OpenXRAction_k0xt7") paths = PackedStringArray("/user/hand/left/input/trackpad", "/user/hand/right/input/trackpad") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_qe211"] -action = SubResource("OpenXRAction_ex4re") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8qcns"] +action = SubResource("OpenXRAction_5a2y3") paths = PackedStringArray("/user/hand/left/input/trackpad/click", "/user/hand/right/input/trackpad/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_r1mnx"] -action = SubResource("OpenXRAction_g81y4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gnt5i"] +action = SubResource("OpenXRAction_184m6") paths = PackedStringArray("/user/hand/left/input/trackpad/touch", "/user/hand/right/input/trackpad/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6o035"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_cfnrx"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_yfuta"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_vix80"] interaction_profile_path = "/interaction_profiles/samsung/odyssey_controller" -bindings = [SubResource("OpenXRIPBinding_k1gr8"), SubResource("OpenXRIPBinding_ny1od"), SubResource("OpenXRIPBinding_hdbl3"), SubResource("OpenXRIPBinding_y6i05"), SubResource("OpenXRIPBinding_468ph"), SubResource("OpenXRIPBinding_5kra7"), SubResource("OpenXRIPBinding_8j6ge"), SubResource("OpenXRIPBinding_yrus0"), SubResource("OpenXRIPBinding_neu3a"), SubResource("OpenXRIPBinding_2wqk6"), SubResource("OpenXRIPBinding_yw2on"), SubResource("OpenXRIPBinding_exlak"), SubResource("OpenXRIPBinding_qe211"), SubResource("OpenXRIPBinding_r1mnx"), SubResource("OpenXRIPBinding_6o035")] +bindings = [SubResource("OpenXRIPBinding_vof3g"), SubResource("OpenXRIPBinding_vb033"), SubResource("OpenXRIPBinding_7cwnm"), SubResource("OpenXRIPBinding_pobar"), SubResource("OpenXRIPBinding_1oayv"), SubResource("OpenXRIPBinding_m4awp"), SubResource("OpenXRIPBinding_gt3w0"), SubResource("OpenXRIPBinding_152ib"), SubResource("OpenXRIPBinding_e5tqk"), SubResource("OpenXRIPBinding_4qajh"), SubResource("OpenXRIPBinding_ynvlk"), SubResource("OpenXRIPBinding_6hkk8"), SubResource("OpenXRIPBinding_8qcns"), SubResource("OpenXRIPBinding_gnt5i"), SubResource("OpenXRIPBinding_cfnrx")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_fvntp"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_klrg8"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vok7j"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_cu7xd"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8k82x"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7wdbv"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3l83b"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_icdkq"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_28c24"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8dj82"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_axft7"] -action = SubResource("OpenXRAction_diti2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_fguo8"] +action = SubResource("OpenXRAction_wmfn2") paths = PackedStringArray("/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_yu0jj"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_sxpdh"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/x/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_buy1w"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_xw8v6"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/y/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_usmfb"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d4mvt"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e3hx6"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_g2egg"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/click", "/user/hand/right/input/trigger/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d5732"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_83iei"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_18avs"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_bfv0f"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ci246"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7eh3s"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ewfxa"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lns88"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_3nmmf"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ljmj8"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/thumbstick/touch", "/user/hand/right/input/thumbstick/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4j872"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gjy2k"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_j1wg5"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_1wuu4"] interaction_profile_path = "/interaction_profiles/htc/vive_cosmos_controller" -bindings = [SubResource("OpenXRIPBinding_fvntp"), SubResource("OpenXRIPBinding_vok7j"), SubResource("OpenXRIPBinding_8k82x"), SubResource("OpenXRIPBinding_3l83b"), SubResource("OpenXRIPBinding_28c24"), SubResource("OpenXRIPBinding_axft7"), SubResource("OpenXRIPBinding_yu0jj"), SubResource("OpenXRIPBinding_buy1w"), SubResource("OpenXRIPBinding_usmfb"), SubResource("OpenXRIPBinding_e3hx6"), SubResource("OpenXRIPBinding_d5732"), SubResource("OpenXRIPBinding_18avs"), SubResource("OpenXRIPBinding_ci246"), SubResource("OpenXRIPBinding_ewfxa"), SubResource("OpenXRIPBinding_3nmmf"), SubResource("OpenXRIPBinding_4j872")] +bindings = [SubResource("OpenXRIPBinding_klrg8"), SubResource("OpenXRIPBinding_cu7xd"), SubResource("OpenXRIPBinding_7wdbv"), SubResource("OpenXRIPBinding_icdkq"), SubResource("OpenXRIPBinding_8dj82"), SubResource("OpenXRIPBinding_fguo8"), SubResource("OpenXRIPBinding_sxpdh"), SubResource("OpenXRIPBinding_xw8v6"), SubResource("OpenXRIPBinding_d4mvt"), SubResource("OpenXRIPBinding_g2egg"), SubResource("OpenXRIPBinding_83iei"), SubResource("OpenXRIPBinding_bfv0f"), SubResource("OpenXRIPBinding_7eh3s"), SubResource("OpenXRIPBinding_lns88"), SubResource("OpenXRIPBinding_ljmj8"), SubResource("OpenXRIPBinding_gjy2k")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_u0ute"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_fmn5x"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_hi5xa"] -action = SubResource("OpenXRAction_iq3mb") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_k6ydw"] +action = SubResource("OpenXRAction_7xwcn") paths = PackedStringArray("/user/hand/left/input/aim/pose", "/user/hand/right/input/aim/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_6j7ml"] -action = SubResource("OpenXRAction_mesn2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4dqnl"] +action = SubResource("OpenXRAction_1l7gi") paths = PackedStringArray("/user/hand/left/input/grip/pose", "/user/hand/right/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m0tt0"] -action = SubResource("OpenXRAction_esp51") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0ee87"] +action = SubResource("OpenXRAction_6hayr") paths = PackedStringArray("/user/hand/left/input/palm_ext/pose", "/user/hand/right/input/palm_ext/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_e3by6"] -action = SubResource("OpenXRAction_lc4t5") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vclos"] +action = SubResource("OpenXRAction_yvxai") paths = PackedStringArray("/user/hand/left/input/menu/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7bw5r"] -action = SubResource("OpenXRAction_diti2") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_osekd"] +action = SubResource("OpenXRAction_wmfn2") paths = PackedStringArray("/user/hand/right/input/system/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_74eml"] -action = SubResource("OpenXRAction_8nx4f") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_inrw5"] +action = SubResource("OpenXRAction_ug7ra") paths = PackedStringArray("/user/hand/left/input/x/click", "/user/hand/right/input/a/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_c61kg"] -action = SubResource("OpenXRAction_mv7tf") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gfd40"] +action = SubResource("OpenXRAction_01don") paths = PackedStringArray("/user/hand/left/input/y/click", "/user/hand/right/input/b/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_mklgv"] -action = SubResource("OpenXRAction_q1i8v") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8yj8p"] +action = SubResource("OpenXRAction_7a7cg") paths = PackedStringArray("/user/hand/left/input/trigger/value", "/user/hand/right/input/trigger/value") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_0ja3q"] -action = SubResource("OpenXRAction_2la4t") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_m6yoq"] +action = SubResource("OpenXRAction_hfach") paths = PackedStringArray("/user/hand/left/input/trigger/click", "/user/hand/right/input/trigger/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_t37db"] -action = SubResource("OpenXRAction_ahush") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_44aae"] +action = SubResource("OpenXRAction_v806b") paths = PackedStringArray("/user/hand/left/input/trigger/touch", "/user/hand/right/input/trigger/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_v6asv"] -action = SubResource("OpenXRAction_w50st") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_u5ni0"] +action = SubResource("OpenXRAction_qc0ui") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_h2mmq"] -action = SubResource("OpenXRAction_hjfn1") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5c20q"] +action = SubResource("OpenXRAction_tq28e") paths = PackedStringArray("/user/hand/left/input/squeeze/click", "/user/hand/right/input/squeeze/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8y8t1"] -action = SubResource("OpenXRAction_1qkem") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_dhtar"] +action = SubResource("OpenXRAction_pnwp5") paths = PackedStringArray("/user/hand/left/input/thumbstick", "/user/hand/right/input/thumbstick") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_f7fqb"] -action = SubResource("OpenXRAction_mw62d") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_tpo35"] +action = SubResource("OpenXRAction_6r5q7") paths = PackedStringArray("/user/hand/left/input/thumbstick/click", "/user/hand/right/input/thumbstick/click") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_4d1hu"] -action = SubResource("OpenXRAction_vyfej") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pxdud"] +action = SubResource("OpenXRAction_mqjaa") paths = PackedStringArray("/user/hand/left/input/thumbstick/touch", "/user/hand/right/input/thumbstick/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_f77m5"] -action = SubResource("OpenXRAction_g81y4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_no2kx"] +action = SubResource("OpenXRAction_184m6") paths = PackedStringArray("/user/hand/left/input/thumbrest/touch", "/user/hand/right/input/thumbrest/touch") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_n7yoi"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8bp6p"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_tvngl"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_60lgi"] interaction_profile_path = "/interaction_profiles/htc/vive_focus3_controller" -bindings = [SubResource("OpenXRIPBinding_u0ute"), SubResource("OpenXRIPBinding_hi5xa"), SubResource("OpenXRIPBinding_6j7ml"), SubResource("OpenXRIPBinding_m0tt0"), SubResource("OpenXRIPBinding_e3by6"), SubResource("OpenXRIPBinding_7bw5r"), SubResource("OpenXRIPBinding_74eml"), SubResource("OpenXRIPBinding_c61kg"), SubResource("OpenXRIPBinding_mklgv"), SubResource("OpenXRIPBinding_0ja3q"), SubResource("OpenXRIPBinding_t37db"), SubResource("OpenXRIPBinding_v6asv"), SubResource("OpenXRIPBinding_h2mmq"), SubResource("OpenXRIPBinding_8y8t1"), SubResource("OpenXRIPBinding_f7fqb"), SubResource("OpenXRIPBinding_4d1hu"), SubResource("OpenXRIPBinding_f77m5"), SubResource("OpenXRIPBinding_n7yoi")] +bindings = [SubResource("OpenXRIPBinding_fmn5x"), SubResource("OpenXRIPBinding_k6ydw"), SubResource("OpenXRIPBinding_4dqnl"), SubResource("OpenXRIPBinding_0ee87"), SubResource("OpenXRIPBinding_vclos"), SubResource("OpenXRIPBinding_osekd"), SubResource("OpenXRIPBinding_inrw5"), SubResource("OpenXRIPBinding_gfd40"), SubResource("OpenXRIPBinding_8yj8p"), SubResource("OpenXRIPBinding_m6yoq"), SubResource("OpenXRIPBinding_44aae"), SubResource("OpenXRIPBinding_u5ni0"), SubResource("OpenXRIPBinding_5c20q"), SubResource("OpenXRIPBinding_dhtar"), SubResource("OpenXRIPBinding_tpo35"), SubResource("OpenXRIPBinding_pxdud"), SubResource("OpenXRIPBinding_no2kx"), SubResource("OpenXRIPBinding_8bp6p")] -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_28xpp"] -action = SubResource("OpenXRAction_040i8") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_wtsp8"] +action = SubResource("OpenXRAction_0lo1w") paths = PackedStringArray("/user/vive_tracker_htcx/role/left_foot/input/grip/pose", "/user/vive_tracker_htcx/role/right_foot/input/grip/pose", "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose", "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose", "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose", "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose", "/user/vive_tracker_htcx/role/left_knee/input/grip/pose", "/user/vive_tracker_htcx/role/right_knee/input/grip/pose", "/user/vive_tracker_htcx/role/waist/input/grip/pose", "/user/vive_tracker_htcx/role/chest/input/grip/pose", "/user/vive_tracker_htcx/role/camera/input/grip/pose", "/user/vive_tracker_htcx/role/keyboard/input/grip/pose") -[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_5t1m4"] -action = SubResource("OpenXRAction_c2ak4") +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_baisj"] +action = SubResource("OpenXRAction_s2ilf") paths = PackedStringArray("/user/vive_tracker_htcx/role/left_foot/output/haptic", "/user/vive_tracker_htcx/role/right_foot/output/haptic", "/user/vive_tracker_htcx/role/left_shoulder/output/haptic", "/user/vive_tracker_htcx/role/right_shoulder/output/haptic", "/user/vive_tracker_htcx/role/left_elbow/output/haptic", "/user/vive_tracker_htcx/role/right_elbow/output/haptic", "/user/vive_tracker_htcx/role/left_knee/output/haptic", "/user/vive_tracker_htcx/role/right_knee/output/haptic", "/user/vive_tracker_htcx/role/waist/output/haptic", "/user/vive_tracker_htcx/role/chest/output/haptic", "/user/vive_tracker_htcx/role/camera/output/haptic", "/user/vive_tracker_htcx/role/keyboard/output/haptic") -[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_r81v3"] +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_2udeq"] interaction_profile_path = "/interaction_profiles/htc/vive_tracker_htcx" -bindings = [SubResource("OpenXRIPBinding_28xpp"), SubResource("OpenXRIPBinding_5t1m4")] +bindings = [SubResource("OpenXRIPBinding_wtsp8"), SubResource("OpenXRIPBinding_baisj")] [resource] -action_sets = [SubResource("OpenXRActionSet_6doge")] -interaction_profiles = [SubResource("OpenXRInteractionProfile_rtx12"), SubResource("OpenXRInteractionProfile_uxbat"), SubResource("OpenXRInteractionProfile_1xmth"), SubResource("OpenXRInteractionProfile_0qfe6"), SubResource("OpenXRInteractionProfile_noym7"), SubResource("OpenXRInteractionProfile_sqhjc"), SubResource("OpenXRInteractionProfile_3kktn"), SubResource("OpenXRInteractionProfile_yfuta"), SubResource("OpenXRInteractionProfile_j1wg5"), SubResource("OpenXRInteractionProfile_tvngl"), SubResource("OpenXRInteractionProfile_r81v3")] +action_sets = [SubResource("OpenXRActionSet_r4msx")] +interaction_profiles = [SubResource("OpenXRInteractionProfile_gy0l8"), SubResource("OpenXRInteractionProfile_asoou"), SubResource("OpenXRInteractionProfile_nuit2"), SubResource("OpenXRInteractionProfile_12802"), SubResource("OpenXRInteractionProfile_juyn6"), SubResource("OpenXRInteractionProfile_crui4"), SubResource("OpenXRInteractionProfile_n8d2x"), SubResource("OpenXRInteractionProfile_vix80"), SubResource("OpenXRInteractionProfile_1wuu4"), SubResource("OpenXRInteractionProfile_60lgi"), SubResource("OpenXRInteractionProfile_2udeq")] diff --git a/project.godot b/project.godot index b97f2e2..2fbc9db 100644 --- a/project.godot +++ b/project.godot @@ -11,9 +11,18 @@ config_version=5 [application] config/name="ImmersiveHome" +run/main_scene="res://main.tscn" config/features=PackedStringArray("4.1", "Mobile") config/icon="res://icon.svg" +[autoload] + +XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg") + [rendering] renderer/rendering_method="mobile" From 1271986faf38383aa679c951df925539adc9272d Mon Sep 17 00:00:00 2001 From: Nitwel Date: Mon, 23 Oct 2023 01:58:18 +0200 Subject: [PATCH 02/13] basic light implementation --- light.gd | 27 +++++++++++++++++++++++++ main.tscn | 55 +++++++++++++++++++++++++++++++++++++++++++++----- project.godot | 2 ++ src/model.gd | 23 +++++++++++++++++++++ src/raycast.gd | 27 +++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 light.gd create mode 100644 src/model.gd create mode 100644 src/raycast.gd diff --git a/light.gd b/light.gd new file mode 100644 index 0000000..cc8d680 --- /dev/null +++ b/light.gd @@ -0,0 +1,27 @@ +extends StaticBody3D + +@onready var http_request = HTTPRequest.new() +@export var id = "switch.plug_printer_2" +@export var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" + +# Called when the node enters the scene tree for the first time. +func _ready(): + add_child(http_request) + http_request.request_completed.connect(self._on_request_completed) + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass + +func _on_toggle(): + print("Toggling " + id) + var headers = PackedStringArray(["Authorization: Bearer " + token, "Content-Type: application/json"]) + + var error = http_request.request("http://192.168.33.33:8123/api/services/switch/toggle", headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"" + id + "\"}") + + if error != OK: + push_error("An error occurred in the HTTP request.") + +func _on_request_completed(): + pass diff --git a/main.tscn b/main.tscn index 96fbf37..e79d8fc 100644 --- a/main.tscn +++ b/main.tscn @@ -1,24 +1,55 @@ -[gd_scene load_steps=6 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=12 format=3 uid="uid://eecv28y6jxk4"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] +[ext_resource type="Script" path="res://src/raycast.gd" id="1_tsqxc"] +[ext_resource type="Script" path="res://src/model.gd" id="2_7f1x4"] +[ext_resource type="Script" path="res://light.gd" id="4_bm5yj"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_2igmd"] +radius = 0.1 + +[sub_resource type="SphereMesh" id="SphereMesh_eme7e"] +radius = 0.1 +height = 0.2 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] +ao_enabled = true [sub_resource type="BoxMesh" id="BoxMesh_ir3co"] -size = Vector3(0.1, 0.1, 0.1) +material = SubResource("StandardMaterial3D_m58yb") +size = Vector3(0.01, 0.01, 0.01) [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_p6hri"] -sky_top_color = Color(0.384314, 0.454902, 0.54902, 1) +sky_top_color = Color(0, 0, 0, 1) +sky_horizon_color = Color(0, 0, 0, 1) +sky_curve = 9.13367e-05 +sky_cover_modulate = Color(0, 0, 0, 1) +ground_bottom_color = Color(0, 0, 0, 1) +ground_horizon_color = Color(0, 0, 0, 1) use_debanding = false [sub_resource type="Sky" id="Sky_vhymk"] sky_material = SubResource("ProceduralSkyMaterial_p6hri") [sub_resource type="Environment" id="Environment_7ghp0"] -background_mode = 1 -background_color = Color(1, 1, 1, 0) +background_mode = 2 +background_color = Color(0.466667, 0.47451, 0.462745, 0) sky = SubResource("Sky_vhymk") +ambient_light_color = Color(1, 1, 1, 1) +ambient_light_sky_contribution = 0.72 [node name="Main" type="Node3D"] +[node name="Light" type="StaticBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.45064, 0, 0.20152) +script = ExtResource("4_bm5yj") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Light"] +shape = SubResource("SphereShape3D_2igmd") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Light"] +mesh = SubResource("SphereMesh_eme7e") + [node name="XROrigin3D" type="XROrigin3D" parent="."] [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] @@ -32,6 +63,10 @@ pose = &"aim" [node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerLeft"] mesh = SubResource("BoxMesh_ir3co") +[node name="Model" type="Node3D" parent="XROrigin3D/XRControllerLeft" node_paths=PackedStringArray("light")] +script = ExtResource("2_7f1x4") +light = NodePath("../../../Light") + [node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) tracker = &"right_hand" @@ -40,6 +75,14 @@ pose = &"aim" [node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerRight"] mesh = SubResource("BoxMesh_ir3co") +[node name="Raycast" type="Node3D" parent="XROrigin3D/XRControllerRight" node_paths=PackedStringArray("ray")] +script = ExtResource("1_tsqxc") +ray = NodePath("RayCast3D") + +[node name="RayCast3D" type="RayCast3D" parent="XROrigin3D/XRControllerRight/Raycast"] +transform = Transform3D(1.91069e-15, 4.37114e-08, 1, 1, -4.37114e-08, 0, 4.37114e-08, 1, -4.37114e-08, 0, 0, 0) +target_position = Vector3(0, -5, 0) + [node name="StartXR" parent="." instance=ExtResource("1_i4c04")] enable_passthrough = true @@ -47,3 +90,5 @@ enable_passthrough = true environment = SubResource("Environment_7ghp0") [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.834925, -0.386727, -0.39159, 0.550364, 0.586681, 0.594058, 0, -0.711511, 0.702675, 0, 7.21041, 2.06458) +shadow_enabled = true diff --git a/project.godot b/project.godot index 2fbc9db..482aeb0 100644 --- a/project.godot +++ b/project.godot @@ -27,6 +27,8 @@ enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg") renderer/rendering_method="mobile" textures/vram_compression/import_etc2_astc=true +lights_and_shadows/directional_shadow/soft_shadow_filter_quality=4 +lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile=4 [xr] diff --git a/src/model.gd b/src/model.gd new file mode 100644 index 0000000..4eeb541 --- /dev/null +++ b/src/model.gd @@ -0,0 +1,23 @@ +extends Node3D + +@onready var _controller := XRHelpers.get_xr_controller(self) +@export var light: StaticBody3D + +# Called when the node enters the scene tree for the first time. +func _ready(): + _controller.button_pressed.connect(self._on_button_pressed) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass + +func _on_button_pressed(button): + print("right: ", button) + if button != "trigger_click": + return + + if light == null: + return + + # set light position to controller position + light.transform.origin = _controller.transform.origin diff --git a/src/raycast.gd b/src/raycast.gd new file mode 100644 index 0000000..badbf27 --- /dev/null +++ b/src/raycast.gd @@ -0,0 +1,27 @@ +extends Node3D + +@onready var _controller := XRHelpers.get_xr_controller(self) +@export var ray: RayCast3D + +# Called when the node enters the scene tree for the first time. +func _ready(): + _controller.button_pressed.connect(self._on_button_pressed) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass + +func _on_button_pressed(button): + print(button) + if button != "trigger_click": + return + + var collider = ray.get_collider() + + if collider == null: + return + + print(collider) + + if collider.has_method("_on_toggle"): + collider._on_toggle() From 97aa0f723a38e288bd0b2be963c8f744053df186 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sun, 29 Oct 2023 23:23:40 +0100 Subject: [PATCH 03/13] update gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 2585aa0..3388761 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ # Normalize EOL for all files that Git considers text files. * text=auto eol=lf -Assets/** filter=lfs diff=lfs merge=lfs -text +assets/** filter=lfs diff=lfs merge=lfs -text From d24f3a7c7e89e8e5eddb19508edb27d3b3110e5d Mon Sep 17 00:00:00 2001 From: Nitwel Date: Mon, 30 Oct 2023 01:21:50 +0100 Subject: [PATCH 04/13] load devices, show menu, view entities --- .editorconfig | 12 +++ assets/materials/interface.tres | 3 + icon.svg | 1 - icon.svg.import | 37 --------- main.tscn | 10 ++- project.godot | 3 +- scenes/device.tscn | 23 ++++++ scenes/entity.tscn | 23 ++++++ scenes/menu.tscn | 17 +++++ light.gd => src/devices/light.gd | 0 src/globals/home_adapters.gd | 6 ++ src/globals/request.gd | 1 + src/home_adapters/adapter.gd | 26 +++++++ src/home_adapters/hass/hass.gd | 23 ++++++ src/home_adapters/hass/templates/devices.j2 | 12 +++ src/menu.gd | 83 +++++++++++++++++++++ src/ui/device.gd | 12 +++ src/ui/entity.gd | 12 +++ 18 files changed, 262 insertions(+), 42 deletions(-) create mode 100644 .editorconfig create mode 100644 assets/materials/interface.tres delete mode 100644 icon.svg delete mode 100644 icon.svg.import create mode 100644 scenes/device.tscn create mode 100644 scenes/entity.tscn create mode 100644 scenes/menu.tscn rename light.gd => src/devices/light.gd (100%) create mode 100644 src/globals/home_adapters.gd create mode 100644 src/globals/request.gd create mode 100644 src/home_adapters/adapter.gd create mode 100644 src/home_adapters/hass/hass.gd create mode 100644 src/home_adapters/hass/templates/devices.j2 create mode 100644 src/menu.gd create mode 100644 src/ui/device.gd create mode 100644 src/ui/entity.gd diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..381ee74 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/assets/materials/interface.tres b/assets/materials/interface.tres new file mode 100644 index 0000000..3af2e71 --- /dev/null +++ b/assets/materials/interface.tres @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55d5f30db336a8f8f7633743c23e41077d5c1a1b900c475a6d3811746eba3d1b +size 141 diff --git a/icon.svg b/icon.svg deleted file mode 100644 index b370ceb..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 226566a..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bnc4gf261swvs" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/main.tscn b/main.tscn index e79d8fc..f8bc998 100644 --- a/main.tscn +++ b/main.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=12 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=13 format=3 uid="uid://eecv28y6jxk4"] +[ext_resource type="Script" path="res://src/devices/light.gd" id="1_5mqma"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] [ext_resource type="Script" path="res://src/raycast.gd" id="1_tsqxc"] [ext_resource type="Script" path="res://src/model.gd" id="2_7f1x4"] -[ext_resource type="Script" path="res://light.gd" id="4_bm5yj"] +[ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://scenes/menu.tscn" id="3_1tbp3"] [sub_resource type="SphereShape3D" id="SphereShape3D_2igmd"] radius = 0.1 @@ -42,7 +43,7 @@ ambient_light_sky_contribution = 0.72 [node name="Light" type="StaticBody3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.45064, 0, 0.20152) -script = ExtResource("4_bm5yj") +script = ExtResource("1_5mqma") [node name="CollisionShape3D" type="CollisionShape3D" parent="Light"] shape = SubResource("SphereShape3D_2igmd") @@ -67,6 +68,9 @@ mesh = SubResource("BoxMesh_ir3co") script = ExtResource("2_7f1x4") light = NodePath("../../../Light") +[node name="Menu" parent="XROrigin3D/XRControllerLeft" instance=ExtResource("3_1tbp3")] +transform = Transform3D(-4.37114e-08, 0, -1, -0.707107, 0.707107, 3.09086e-08, 0.707107, 0.707107, -3.09086e-08, 0.194945, 0, -0.0534939) + [node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) tracker = &"right_hand" diff --git a/project.godot b/project.godot index 482aeb0..f605205 100644 --- a/project.godot +++ b/project.godot @@ -13,11 +13,12 @@ config_version=5 config/name="ImmersiveHome" run/main_scene="res://main.tscn" config/features=PackedStringArray("4.1", "Mobile") -config/icon="res://icon.svg" [autoload] XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd" +Request="*res://src/globals/request.gd" +HomeAdapters="*res://src/globals/home_adapters.gd" [editor_plugins] diff --git a/scenes/device.tscn b/scenes/device.tscn new file mode 100644 index 0000000..c3e4958 --- /dev/null +++ b/scenes/device.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=4 format=3 uid="uid://dbe8slnyhro2n"] + +[ext_resource type="Script" path="res://src/ui/device.gd" id="1_rbo86"] + +[sub_resource type="BoxMesh" id="BoxMesh_aa3i4"] +size = Vector3(0.05, 0.01, 0.05) + +[sub_resource type="BoxShape3D" id="BoxShape3D_28fjq"] +size = Vector3(0.05, 0.01, 0.05) + +[node name="Device" type="StaticBody3D"] +script = ExtResource("1_rbo86") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("BoxMesh_aa3i4") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_28fjq") + +[node name="Label" type="Label3D" parent="."] +transform = Transform3D(-2.18557e-09, -0.05, -2.18557e-09, 0, -2.18557e-09, 0.05, -0.05, 2.18557e-09, 9.55343e-17, 0, 0.00918245, 0) +text = "Text" +autowrap_mode = 3 diff --git a/scenes/entity.tscn b/scenes/entity.tscn new file mode 100644 index 0000000..e22a66b --- /dev/null +++ b/scenes/entity.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=4 format=3 uid="uid://xo0o5nrfjl23"] + +[ext_resource type="Script" path="res://src/ui/entity.gd" id="1_825oj"] + +[sub_resource type="BoxMesh" id="BoxMesh_aa3i4"] +size = Vector3(0.05, 0.01, 0.05) + +[sub_resource type="BoxShape3D" id="BoxShape3D_28fjq"] +size = Vector3(0.05, 0.01, 0.05) + +[node name="Entity" type="StaticBody3D"] +script = ExtResource("1_825oj") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("BoxMesh_aa3i4") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_28fjq") + +[node name="Label" type="Label3D" parent="."] +transform = Transform3D(-2.18557e-09, -0.05, -2.18557e-09, 0, -2.18557e-09, 0.05, -0.05, 2.18557e-09, 9.55343e-17, 0, 0.00918245, 0) +text = "Text" +autowrap_mode = 3 diff --git a/scenes/menu.tscn b/scenes/menu.tscn new file mode 100644 index 0000000..374f8fe --- /dev/null +++ b/scenes/menu.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=4 format=3 uid="uid://c3kdssrmv84kv"] + +[ext_resource type="Script" path="res://src/menu.gd" id="1_ng4u3"] +[ext_resource type="Material" uid="uid://bertj8bp8b5l1" path="res://assets/materials/interface.tres" id="2_nsukb"] + +[sub_resource type="PlaneMesh" id="PlaneMesh_6t3dn"] +material = ExtResource("2_nsukb") +size = Vector2(0.3, 0.3) + +[node name="Menu" type="Node3D"] +script = ExtResource("1_ng4u3") + +[node name="Background" type="MeshInstance3D" parent="."] +mesh = SubResource("PlaneMesh_6t3dn") + +[node name="Devices" type="Node3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.149223, 0, 0.150667) diff --git a/light.gd b/src/devices/light.gd similarity index 100% rename from light.gd rename to src/devices/light.gd diff --git a/src/globals/home_adapters.gd b/src/globals/home_adapters.gd new file mode 100644 index 0000000..3717b94 --- /dev/null +++ b/src/globals/home_adapters.gd @@ -0,0 +1,6 @@ +extends Node + +var Adapter = preload("res://src/home_adapters/adapter.gd") + +var adapter = Adapter.new(Adapter.ADAPTER_TYPES.HASS) + \ No newline at end of file diff --git a/src/globals/request.gd b/src/globals/request.gd new file mode 100644 index 0000000..6685be2 --- /dev/null +++ b/src/globals/request.gd @@ -0,0 +1 @@ +extends HTTPRequest \ No newline at end of file diff --git a/src/home_adapters/adapter.gd b/src/home_adapters/adapter.gd new file mode 100644 index 0000000..c18ae44 --- /dev/null +++ b/src/home_adapters/adapter.gd @@ -0,0 +1,26 @@ +extends Node + +const hass = preload("res://src/home_adapters/hass/hass.gd") + +enum ADAPTER_TYPES { + HASS +} + +const adapters = { + ADAPTER_TYPES.HASS: hass +} + +const methods = [ + "load_devices" +] + +var adapter: Node + +func _init(type: ADAPTER_TYPES): + adapter = adapters[type].new() + + for method in methods: + assert(adapter.has_method(method), "Adapter does not implement method: " + method) + +func load_devices(): + return await adapter.load_devices() diff --git a/src/home_adapters/hass/hass.gd b/src/home_adapters/hass/hass.gd new file mode 100644 index 0000000..9c0bc35 --- /dev/null +++ b/src/home_adapters/hass/hass.gd @@ -0,0 +1,23 @@ +extends Node + +var url: String = "http://192.168.33.33:8123" +var token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" +var headers: PackedStringArray = PackedStringArray([]) + +var devices_template = FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2") + +func _init(url := self.url, token := self.token): + self.url = url + self.token = token + + headers = PackedStringArray(["Authorization: Bearer %s" % token, "Content-Type: application/json"]) + devices_template = devices_template.replace("\n", " ").replace("\t", "").replace("\r", " ").replace("\"", "\\\"") + +func load_devices(): + Request.request("%s/api/template" % [url], headers, HTTPClient.METHOD_POST, "{\"template\": \"%s\"}" % [devices_template]) + var response = await Request.request_completed + var data_string = response[3].get_string_from_utf8().replace("'", "\"") + var json = JSON.parse_string(data_string).data + + print(json) + return json diff --git a/src/home_adapters/hass/templates/devices.j2 b/src/home_adapters/hass/templates/devices.j2 new file mode 100644 index 0000000..d1750ba --- /dev/null +++ b/src/home_adapters/hass/templates/devices.j2 @@ -0,0 +1,12 @@ +{% set devices = states | map(attribute='entity_id') | map('device_id') | unique | reject('eq',None) | list %} + +{%- set ns = namespace(devices = []) %} +{%- for device in devices %} + {%- set entities = device_entities(device) | list %} + {%- if entities %} + {%- set ns.devices = ns.devices + [ {device: {"name": device_attr(device, "name"), "entities": entities }} ] %} + {%- endif %} +{%- endfor %} +{ + "data": {{ ns.devices }} +} \ No newline at end of file diff --git a/src/menu.gd b/src/menu.gd new file mode 100644 index 0000000..da4d042 --- /dev/null +++ b/src/menu.gd @@ -0,0 +1,83 @@ +extends Node3D + +const Device = preload("res://scenes/device.tscn") +const Entity = preload("res://scenes/entity.tscn") + +@onready var devices_node = $Devices +var devices + +var selected_device = null +# Called when the node enters the scene tree for the first time. +func _ready(): + devices = await HomeAdapters.adapter.load_devices() + render_devices() + +func render_devices(): + var x = 0 + var y = 0 + + for device in devices: + var info = device.values()[0] + + var device_instance = Device.instantiate() + device_instance.set_position(Vector3(y * 0.08, 0, -x * 0.08)) + device_instance.click.connect(_on_device_click) + + devices_node.add_child(device_instance) + + device_instance.set_device_name(info["name"]) + + x += 1 + if x % 5 == 0: + x = 0 + y += 1 + +func render_entities(): + var x = 0 + var y = 0 + + var info + + for device in devices: + if device.values()[0].name == selected_device: + info = device.values()[0] + break + + if info == null: + return + + var entities = info["entities"] + + for entity in entities: + var entity_instance = Entity.instantiate() + entity_instance.set_position(Vector3(y * 0.08, 0, -x * 0.08)) + entity_instance.click.connect(_on_entity_click) + + devices_node.add_child(entity_instance) + + entity_instance.set_entity_name(entity) + + x += 1 + if x % 5 == 0: + x = 0 + y += 1 + +func _on_device_click(device_name): + selected_device = device_name + print(selected_device) + clear_menu() + render_entities() + +func _on_entity_click(entity_name): + print(entity_name) + selected_device = null + clear_menu() + render_devices() + +func clear_menu(): + for child in devices_node.get_children(): + devices_node.remove_child(child) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass diff --git a/src/ui/device.gd b/src/ui/device.gd new file mode 100644 index 0000000..d112556 --- /dev/null +++ b/src/ui/device.gd @@ -0,0 +1,12 @@ +extends StaticBody3D + +@onready var label: Label3D = $Label + +signal click(name: String) + +func _on_toggle(): + click.emit(label.text) + +func set_device_name(text): + assert(label != null, "Device has to be added to the scene tree") + label.text = text \ No newline at end of file diff --git a/src/ui/entity.gd b/src/ui/entity.gd new file mode 100644 index 0000000..0d5a281 --- /dev/null +++ b/src/ui/entity.gd @@ -0,0 +1,12 @@ +extends StaticBody3D + +@onready var label: Label3D = $Label + +signal click(name: String) + +func _on_toggle(): + click.emit(label.text) + +func set_entity_name(text): + assert(label != null, "Entity has to be added to the scene tree") + label.text = text From 555d501e184e4ead6646e3485ad89998d524b28e Mon Sep 17 00:00:00 2001 From: Nitwel Date: Tue, 31 Oct 2023 22:15:33 +0100 Subject: [PATCH 05/13] add option to add entities, switch + simple light support --- assets/design.afdesign | 3 ++ assets/logo.png | 3 ++ assets/logo.png.import | 3 ++ assets/materials/swich_on.png | 3 ++ assets/materials/swich_on.png.import | 3 ++ assets/materials/switch_off.png | 3 ++ assets/materials/switch_off.png.import | 3 ++ main.tscn | 23 ++------------- scenes/entities/light.tscn | 33 +++++++++++++++++++++ scenes/entities/switch.tscn | 33 +++++++++++++++++++++ src/devices/light.gd | 27 ----------------- src/entities/light.gd | 23 +++++++++++++++ src/entities/switch.gd | 23 +++++++++++++++ src/home_adapters/adapter.gd | 10 ++++++- src/home_adapters/hass/hass.gd | 40 ++++++++++++++++++++++++++ src/menu.gd | 26 +++++++++++++++-- src/ui/device.gd | 5 ++-- src/ui/entity.gd | 6 ++-- 18 files changed, 214 insertions(+), 56 deletions(-) create mode 100644 assets/design.afdesign create mode 100644 assets/logo.png create mode 100644 assets/logo.png.import create mode 100644 assets/materials/swich_on.png create mode 100644 assets/materials/swich_on.png.import create mode 100644 assets/materials/switch_off.png create mode 100644 assets/materials/switch_off.png.import create mode 100644 scenes/entities/light.tscn create mode 100644 scenes/entities/switch.tscn delete mode 100644 src/devices/light.gd create mode 100644 src/entities/light.gd create mode 100644 src/entities/switch.gd diff --git a/assets/design.afdesign b/assets/design.afdesign new file mode 100644 index 0000000..35321b1 --- /dev/null +++ b/assets/design.afdesign @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a3204f122024fe25657532ccbe1c0d07eba475f37ebb686046ba71d358ad58 +size 2087560 diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..140c2c9 --- /dev/null +++ b/assets/logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5d79d4a0c78bcce53fb01fa0353ec971eca5f6f8f3fb1fa7322f18f87a4781b +size 1122652 diff --git a/assets/logo.png.import b/assets/logo.png.import new file mode 100644 index 0000000..1605cdd --- /dev/null +++ b/assets/logo.png.import @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1daf27643292c3a222f2a9e60dfe38db5e62963cbfa2dd313588aa98f4e053b4 +size 753 diff --git a/assets/materials/swich_on.png b/assets/materials/swich_on.png new file mode 100644 index 0000000..0319380 --- /dev/null +++ b/assets/materials/swich_on.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06c7745de9d5236d8096979e16708530a3c61112b604a7603033ea8bc9097791 +size 258636 diff --git a/assets/materials/swich_on.png.import b/assets/materials/swich_on.png.import new file mode 100644 index 0000000..f74b6ff --- /dev/null +++ b/assets/materials/swich_on.png.import @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b936cb0718d6b36ba9070d10fe2a16b2dbff0a4f9649e80205e3218fdb4b9df9 +size 1007 diff --git a/assets/materials/switch_off.png b/assets/materials/switch_off.png new file mode 100644 index 0000000..b05a9be --- /dev/null +++ b/assets/materials/switch_off.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fa30c33f89997925bc7164245d51209879a912597a72b227b861f9a9841acec +size 249715 diff --git a/assets/materials/switch_off.png.import b/assets/materials/switch_off.png.import new file mode 100644 index 0000000..4e74575 --- /dev/null +++ b/assets/materials/switch_off.png.import @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71e68850377c2368a7433084c550cc7c5fa5110184cf59fc6d7a8f77f5f4af89 +size 1017 diff --git a/main.tscn b/main.tscn index f8bc998..79ccc7c 100644 --- a/main.tscn +++ b/main.tscn @@ -1,18 +1,10 @@ -[gd_scene load_steps=13 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=10 format=3 uid="uid://eecv28y6jxk4"] -[ext_resource type="Script" path="res://src/devices/light.gd" id="1_5mqma"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] [ext_resource type="Script" path="res://src/raycast.gd" id="1_tsqxc"] [ext_resource type="Script" path="res://src/model.gd" id="2_7f1x4"] [ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://scenes/menu.tscn" id="3_1tbp3"] -[sub_resource type="SphereShape3D" id="SphereShape3D_2igmd"] -radius = 0.1 - -[sub_resource type="SphereMesh" id="SphereMesh_eme7e"] -radius = 0.1 -height = 0.2 - [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true @@ -41,16 +33,6 @@ ambient_light_sky_contribution = 0.72 [node name="Main" type="Node3D"] -[node name="Light" type="StaticBody3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.45064, 0, 0.20152) -script = ExtResource("1_5mqma") - -[node name="CollisionShape3D" type="CollisionShape3D" parent="Light"] -shape = SubResource("SphereShape3D_2igmd") - -[node name="MeshInstance3D" type="MeshInstance3D" parent="Light"] -mesh = SubResource("SphereMesh_eme7e") - [node name="XROrigin3D" type="XROrigin3D" parent="."] [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] @@ -64,9 +46,8 @@ pose = &"aim" [node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerLeft"] mesh = SubResource("BoxMesh_ir3co") -[node name="Model" type="Node3D" parent="XROrigin3D/XRControllerLeft" node_paths=PackedStringArray("light")] +[node name="Model" type="Node3D" parent="XROrigin3D/XRControllerLeft"] script = ExtResource("2_7f1x4") -light = NodePath("../../../Light") [node name="Menu" parent="XROrigin3D/XRControllerLeft" instance=ExtResource("3_1tbp3")] transform = Transform3D(-4.37114e-08, 0, -1, -0.707107, 0.707107, 3.09086e-08, 0.707107, 0.707107, -3.09086e-08, 0.194945, 0, -0.0534939) diff --git a/scenes/entities/light.tscn b/scenes/entities/light.tscn new file mode 100644 index 0000000..37de134 --- /dev/null +++ b/scenes/entities/light.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=6 format=3 uid="uid://cw86rc42dv2d8"] + +[ext_resource type="Script" path="res://src/entities/light.gd" id="1_ykxy3"] +[ext_resource type="Texture2D" uid="uid://b72vsbcvqqxg7" path="res://assets/materials/swich_on.png" id="2_6gn2e"] +[ext_resource type="Texture2D" uid="uid://cvc0o6dsktnvl" path="res://assets/materials/switch_off.png" id="3_qlm62"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_ukj14"] +radius = 0.1 + +[sub_resource type="SpriteFrames" id="SpriteFrames_ldpuo"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": ExtResource("2_6gn2e") +}, { +"duration": 1.0, +"texture": ExtResource("3_qlm62") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[node name="Light" type="StaticBody3D"] +script = ExtResource("1_ykxy3") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_ukj14") + +[node name="Icon" type="AnimatedSprite3D" parent="."] +pixel_size = 0.0005 +billboard = 1 +sprite_frames = SubResource("SpriteFrames_ldpuo") diff --git a/scenes/entities/switch.tscn b/scenes/entities/switch.tscn new file mode 100644 index 0000000..d99fce8 --- /dev/null +++ b/scenes/entities/switch.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=6 format=3 uid="uid://cscl5k7lhopj5"] + +[ext_resource type="Script" path="res://src/entities/switch.gd" id="1_8ffhi"] +[ext_resource type="Texture2D" uid="uid://b72vsbcvqqxg7" path="res://assets/materials/swich_on.png" id="1_w68gw"] +[ext_resource type="Texture2D" uid="uid://cvc0o6dsktnvl" path="res://assets/materials/switch_off.png" id="2_86ba1"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_ukj14"] +radius = 0.1 + +[sub_resource type="SpriteFrames" id="SpriteFrames_ldpuo"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": ExtResource("1_w68gw") +}, { +"duration": 1.0, +"texture": ExtResource("2_86ba1") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[node name="Switch" type="StaticBody3D"] +script = ExtResource("1_8ffhi") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_ukj14") + +[node name="Icon" type="AnimatedSprite3D" parent="."] +pixel_size = 0.0005 +billboard = 1 +sprite_frames = SubResource("SpriteFrames_ldpuo") diff --git a/src/devices/light.gd b/src/devices/light.gd deleted file mode 100644 index cc8d680..0000000 --- a/src/devices/light.gd +++ /dev/null @@ -1,27 +0,0 @@ -extends StaticBody3D - -@onready var http_request = HTTPRequest.new() -@export var id = "switch.plug_printer_2" -@export var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" - -# Called when the node enters the scene tree for the first time. -func _ready(): - add_child(http_request) - http_request.request_completed.connect(self._on_request_completed) - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass - -func _on_toggle(): - print("Toggling " + id) - var headers = PackedStringArray(["Authorization: Bearer " + token, "Content-Type: application/json"]) - - var error = http_request.request("http://192.168.33.33:8123/api/services/switch/toggle", headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"" + id + "\"}") - - if error != OK: - push_error("An error occurred in the HTTP request.") - -func _on_request_completed(): - pass diff --git a/src/entities/light.gd b/src/entities/light.gd new file mode 100644 index 0000000..5f77c50 --- /dev/null +++ b/src/entities/light.gd @@ -0,0 +1,23 @@ +extends StaticBody3D + +@export var entity_id = "switch.plug_printer_2" +@onready var sprite: AnimatedSprite3D = $Icon + +# Called when the node enters the scene tree for the first time. +func _ready(): + var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + if stateInfo["state"] == "on": + sprite.set_frame(0) + else: + sprite.set_frame(1) + + +func _on_toggle(): + HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") + if sprite.get_frame() == 0: + sprite.set_frame(1) + else: + sprite.set_frame(0) + +func _on_request_completed(): + pass diff --git a/src/entities/switch.gd b/src/entities/switch.gd new file mode 100644 index 0000000..5f77c50 --- /dev/null +++ b/src/entities/switch.gd @@ -0,0 +1,23 @@ +extends StaticBody3D + +@export var entity_id = "switch.plug_printer_2" +@onready var sprite: AnimatedSprite3D = $Icon + +# Called when the node enters the scene tree for the first time. +func _ready(): + var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + if stateInfo["state"] == "on": + sprite.set_frame(0) + else: + sprite.set_frame(1) + + +func _on_toggle(): + HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") + if sprite.get_frame() == 0: + sprite.set_frame(1) + else: + sprite.set_frame(0) + +func _on_request_completed(): + pass diff --git a/src/home_adapters/adapter.gd b/src/home_adapters/adapter.gd index c18ae44..21c862d 100644 --- a/src/home_adapters/adapter.gd +++ b/src/home_adapters/adapter.gd @@ -11,7 +11,9 @@ const adapters = { } const methods = [ - "load_devices" + "load_devices", + "get_state", + "set_state" ] var adapter: Node @@ -24,3 +26,9 @@ func _init(type: ADAPTER_TYPES): func load_devices(): return await adapter.load_devices() + +func get_state(entity: String): + return await adapter.get_state(entity) + +func set_state(entity: String, state: String, attributes: Dictionary = {}): + return await adapter.set_state(entity, state, attributes) \ No newline at end of file diff --git a/src/home_adapters/hass/hass.gd b/src/home_adapters/hass/hass.gd index 9c0bc35..93d2aa1 100644 --- a/src/home_adapters/hass/hass.gd +++ b/src/home_adapters/hass/hass.gd @@ -21,3 +21,43 @@ func load_devices(): print(json) return json + +func get_state(entity: String): + var type = entity.split('.')[0] + + Request.request("%s/api/states/%s" % [url, entity], headers, HTTPClient.METHOD_GET) + var response = await Request.request_completed + + var data_string = response[3].get_string_from_utf8().replace("'", "\"") + var json = JSON.parse_string(data_string) + + print(json) + return json + + + +func set_state(entity: String, state: String, attributes: Dictionary = {}): + var type = entity.split('.')[0] + var response + + if type == 'switch': + if state == 'on': + Request.request("%s/api/services/switch/turn_on" % [url], headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"%s\"}" % [entity]) + response = await Request.request_completed + elif state == 'off': + Request.request("%s/api/services/switch/turn_off" % [url], headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"%s\"}" % [entity]) + response = await Request.request_completed + elif type == 'light': + if state == 'on': + Request.request("%s/api/services/light/turn_on" % [url], headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"%s\"}" % [entity]) + response = await Request.request_completed + elif state == 'off': + Request.request("%s/api/services/light/turn_off" % [url], headers, HTTPClient.METHOD_POST, "{\"entity_id\": \"%s\"}" % [entity]) + response = await Request.request_completed + + var data_string = response[3].get_string_from_utf8().replace("'", "\"") + var json = JSON.parse_string(data_string) + + print(json) + return json + diff --git a/src/menu.gd b/src/menu.gd index da4d042..b50aff6 100644 --- a/src/menu.gd +++ b/src/menu.gd @@ -2,6 +2,8 @@ extends Node3D const Device = preload("res://scenes/device.tscn") const Entity = preload("res://scenes/entity.tscn") +const Switch = preload("res://scenes/entities/switch.tscn") +const Light = preload("res://scenes/entities/light.tscn") @onready var devices_node = $Devices var devices @@ -22,6 +24,7 @@ func render_devices(): var device_instance = Device.instantiate() device_instance.set_position(Vector3(y * 0.08, 0, -x * 0.08)) device_instance.click.connect(_on_device_click) + device_instance.id = device.keys()[0] devices_node.add_child(device_instance) @@ -39,7 +42,7 @@ func render_entities(): var info for device in devices: - if device.values()[0].name == selected_device: + if device.keys()[0] == selected_device: info = device.values()[0] break @@ -62,8 +65,8 @@ func render_entities(): x = 0 y += 1 -func _on_device_click(device_name): - selected_device = device_name +func _on_device_click(device_id): + selected_device = device_id print(selected_device) clear_menu() render_entities() @@ -73,6 +76,23 @@ func _on_entity_click(entity_name): selected_device = null clear_menu() render_devices() + + var type = entity_name.split(".")[0] + print(type) + + if type == "switch": + var switch = Switch.instantiate() + switch.entity_id = entity_name + + switch.set_position(global_position) + get_node("/root").add_child(switch) + + if type == "light": + var light = Light.instantiate() + light.entity_id = entity_name + + light.set_position(global_position) + get_node("/root").add_child(light) func clear_menu(): for child in devices_node.get_children(): diff --git a/src/ui/device.gd b/src/ui/device.gd index d112556..e11ab14 100644 --- a/src/ui/device.gd +++ b/src/ui/device.gd @@ -1,11 +1,12 @@ extends StaticBody3D @onready var label: Label3D = $Label +@export var id: String = "0" -signal click(name: String) +signal click(id: String) func _on_toggle(): - click.emit(label.text) + click.emit(id) func set_device_name(text): assert(label != null, "Device has to be added to the scene tree") diff --git a/src/ui/entity.gd b/src/ui/entity.gd index 0d5a281..e324331 100644 --- a/src/ui/entity.gd +++ b/src/ui/entity.gd @@ -1,12 +1,14 @@ extends StaticBody3D @onready var label: Label3D = $Label +@export var text = "Default" signal click(name: String) func _on_toggle(): - click.emit(label.text) + click.emit(text) func set_entity_name(text): assert(label != null, "Entity has to be added to the scene tree") - label.text = text + label.text = text.replace(".", "\n") + self.text = text From de976f34bc7b80c74dd7c11f49816781db886dfc Mon Sep 17 00:00:00 2001 From: Nitwel Date: Thu, 2 Nov 2023 01:22:48 +0100 Subject: [PATCH 06/13] support working without vr headset --- addons/xr-simulator/XRSimulator.gd | 242 ++++++++++++++++++++++ addons/xr-simulator/XRSimulator.tscn | 7 + assets/materials/sky.material | 3 + assets/materials/sky_passthrough.material | 3 + main.gd | 11 + main.tscn | 24 +-- 6 files changed, 278 insertions(+), 12 deletions(-) create mode 100644 addons/xr-simulator/XRSimulator.gd create mode 100644 addons/xr-simulator/XRSimulator.tscn create mode 100644 assets/materials/sky.material create mode 100644 assets/materials/sky_passthrough.material create mode 100644 main.gd diff --git a/addons/xr-simulator/XRSimulator.gd b/addons/xr-simulator/XRSimulator.gd new file mode 100644 index 0000000..cedeec3 --- /dev/null +++ b/addons/xr-simulator/XRSimulator.gd @@ -0,0 +1,242 @@ +extends Node + +enum ControllerSelectionMode {Hold, Toggle} + +@export var enabled: bool +@export var disable_xr_in_editor: bool = true +@export var controller_selection_mode: ControllerSelectionMode = ControllerSelectionMode.Hold +@export var device_x_sensitivity: float = 1 +@export var device_y_sensitivity: float = 1 +@export var scroll_sensitivity: float = 1 +@export var is_camera_height_limited: bool = true +@export var min_camera_height: float = 0.5 +@export var max_camera_height: float = 2.0 +@export var xr_origin: NodePath + +var origin: XROrigin3D +var camera: XRCamera3D +var left_controller: XRController3D +var right_controller: XRController3D +var left_tracker: XRPositionalTracker +var right_tracker: XRPositionalTracker + +var toggle_left_controller = false +var toggle_right_controller = false +var toggle_shift = false + +var key_map = { + KEY_1: "by_button", + KEY_2: "ax_button", + KEY_3: "by_touch", + KEY_4: "ax_touch", + KEY_5: "trigger_touch", + KEY_6: "grip_touch", + KEY_7: "secondary_click", + KEY_8: "secondary_touch", + KEY_9: "", + KEY_0: "", + KEY_MINUS: "primary_click", + KEY_EQUAL: "primary_touch", + KEY_BACKSPACE: "", + KEY_ENTER: "menu_button" +} + +@onready var viewport: Viewport = get_viewport() + +func _ready(): + if not enabled or not OS.has_feature("editor"): + enabled = false + return + + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + + origin = get_node(xr_origin) + + camera = origin.get_node("XRCamera3D") + + var left_hand = XRServer.get_tracker("left_hand") + if left_hand == null: + left_tracker = XRPositionalTracker.new() + left_tracker.type = XRServer.TRACKER_CONTROLLER + left_tracker.hand = XRPositionalTracker.TRACKER_HAND_LEFT + left_tracker.name = "left_hand" + else: + left_tracker = left_hand + + var right_hand = XRServer.get_tracker("right_hand") + if right_hand == null: + right_tracker = XRPositionalTracker.new() + right_tracker.type = XRServer.TRACKER_CONTROLLER + right_tracker.hand = XRPositionalTracker.TRACKER_HAND_RIGHT + right_tracker.name = "right_hand" + else: + right_tracker = right_hand + + for child in origin.get_children(): + if child.get("tracker"): + var pose = child.pose + if child.tracker == "left_hand": + left_controller = child + left_tracker.set_pose(pose, child.transform, Vector3.ZERO, Vector3.ZERO, XRPose.XR_TRACKING_CONFIDENCE_HIGH) + XRServer.add_tracker(left_tracker) + elif child.tracker == "right_hand": + right_controller = child + right_tracker.set_pose(pose, child.transform, Vector3.ZERO, Vector3.ZERO, XRPose.XR_TRACKING_CONFIDENCE_HIGH) + XRServer.add_tracker(right_tracker) + + +func _process(_delta): + if enabled and disable_xr_in_editor and OS.has_feature("editor") and viewport.use_xr: + viewport.use_xr = false + +func _input(event): + if not enabled or not origin.current or not OS.has_feature("editor"): + return + if Input.is_key_pressed(KEY_ESCAPE): + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + elif Input.mouse_mode != Input.MOUSE_MODE_CAPTURED and event is InputEventMouseButton: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + + if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: + return + + simulate_joysticks() + var is_any_controller_selected = false + if event is InputEventMouseMotion: + if Input.is_physical_key_pressed(KEY_Q) or toggle_left_controller: + is_any_controller_selected = true + if Input.is_key_pressed(KEY_SHIFT): + rotate_device(event, left_controller) + else: + move_controller(event, left_controller) + if Input.is_physical_key_pressed(KEY_E) or toggle_right_controller: + is_any_controller_selected = true + if Input.is_key_pressed(KEY_SHIFT): + rotate_device(event, right_controller) + else: + move_controller(event, right_controller) + if not is_any_controller_selected: + rotate_device(event, camera) + elif event is InputEventMouseButton: + if Input.is_physical_key_pressed(KEY_Q) or toggle_left_controller: + is_any_controller_selected = true + attract_controller(event, left_controller) + simulate_trigger(event, left_controller) + simulate_grip(event, left_controller) + if Input.is_physical_key_pressed(KEY_E) or toggle_right_controller: + is_any_controller_selected = true + attract_controller(event, right_controller) + simulate_trigger(event, right_controller) + simulate_grip(event, right_controller) + if not is_any_controller_selected: + camera_height(event) + elif event is InputEventKey: + if controller_selection_mode == ControllerSelectionMode.Toggle and event.pressed: + if event.keycode == KEY_Q: + toggle_left_controller = !toggle_left_controller + elif event.keycode == KEY_E: + toggle_right_controller = !toggle_right_controller + + if Input.is_physical_key_pressed(KEY_Q) or toggle_left_controller: + simulate_buttons(event, left_controller) + if Input.is_physical_key_pressed(KEY_E) or toggle_right_controller: + simulate_buttons(event, right_controller) + +func camera_height(event: InputEventMouseButton): + var direction = -1 + + if not event.pressed: + return + + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + direction = 1 + elif event.button_index != MOUSE_BUTTON_WHEEL_DOWN: + return + + var pos = camera.transform.origin + var camera_y = pos.y + (scroll_sensitivity * direction)/20 + if (camera_y >= max_camera_height or camera_y <= min_camera_height) and is_camera_height_limited: + camera_y = pos.y + camera.transform.origin = Vector3(pos.x, camera_y , pos.z) + +func simulate_joysticks(): + var vec_left = vector_key_mapping(KEY_D, KEY_A, KEY_W, KEY_S) + left_tracker.set_input("primary", vec_left) + + var vec_right = vector_key_mapping(KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN) + + right_tracker.set_input("primary", vec_right) + +func simulate_trigger(event: InputEventMouseButton, controller: XRController3D): + if event.button_index == MOUSE_BUTTON_LEFT: + if controller.tracker == "left_hand": + left_tracker.set_input("trigger", float(event.pressed)) + left_tracker.set_input("trigger_click", event.pressed) + else: + right_tracker.set_input("trigger", float(event.pressed)) + right_tracker.set_input("trigger_click", event.pressed) + +func simulate_grip(event: InputEventMouseButton, controller: XRController3D): + if event.button_index == MOUSE_BUTTON_RIGHT: + if controller.tracker == "left_hand": + left_tracker.set_input("grip", float(event.pressed)) + left_tracker.set_input("grip_click", event.pressed) + else: + right_tracker.set_input("grip", float(event.pressed)) + right_tracker.set_input("grip_click", event.pressed) + +func simulate_buttons(event: InputEventKey, controller: XRController3D): + if key_map.has(event.keycode): + var button = key_map[event.keycode] + if controller.tracker == "left_hand": + left_tracker.set_input(button, event.pressed) + else: + right_tracker.set_input(button, event.pressed) + +func move_controller(event: InputEventMouseMotion, controller: XRController3D): + var movement = Vector3() + movement += camera.global_transform.basis.x * event.relative.x * device_x_sensitivity/1000 + movement += camera.global_transform.basis.y * event.relative.y * -device_y_sensitivity/1000 + controller.global_translate(movement) + +func attract_controller(event: InputEventMouseButton, controller: XRController3D): + var direction = -1 + + if not event.pressed: + return + + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + direction = 1 + elif event.button_index != MOUSE_BUTTON_WHEEL_DOWN: + return + + var distance_vector = controller.global_transform.origin - camera.global_transform.origin + var forward = distance_vector.normalized() * direction + var movement = distance_vector + forward * (scroll_sensitivity/20) + if distance_vector.length() > 0.1 and movement.length() > 0.1: + controller.global_translate(forward * (scroll_sensitivity/20)) + +func rotate_device(event: InputEventMouseMotion, device: Node3D): + var motion = event.relative + device.rotate_y(motion.x * -device_x_sensitivity/1000) + device.rotate(device.transform.basis.x, motion.y * -device_y_sensitivity/1000) + +func vector_key_mapping(key_positive_x: int, key_negative_x: int, key_positive_y: int, key_negative_y: int): + var x = 0 + var y = 0 + if Input.is_physical_key_pressed (key_positive_y): + y = 1 + elif Input.is_physical_key_pressed (key_negative_y): + y = -1 + + if Input.is_physical_key_pressed (key_positive_x): + x = 1 + elif Input.is_physical_key_pressed (key_negative_x): + x = -1 + + var vec = Vector2(x, y) + + if vec: + vec = vec.normalized() + + return vec diff --git a/addons/xr-simulator/XRSimulator.tscn b/addons/xr-simulator/XRSimulator.tscn new file mode 100644 index 0000000..00befb0 --- /dev/null +++ b/addons/xr-simulator/XRSimulator.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=2 format=3 uid="uid://ctltchlf2j2r4"] + +[ext_resource type="Script" path="res://addons/xr-simulator/XRSimulator.gd" id="1_uljju"] + +[node name="XRSimulator" type="Node"] +script = ExtResource("1_uljju") +enabled = true diff --git a/assets/materials/sky.material b/assets/materials/sky.material new file mode 100644 index 0000000..3765603 --- /dev/null +++ b/assets/materials/sky.material @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25ef12c9534c8bbb08a816492562253c5c6fe0257f87427ce77b223231b07de5 +size 299 diff --git a/assets/materials/sky_passthrough.material b/assets/materials/sky_passthrough.material new file mode 100644 index 0000000..ccc78b5 --- /dev/null +++ b/assets/materials/sky_passthrough.material @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eead3733b7242e93c1739fcebd649efd1ce56ad1a3934e69d850c6ef3df66b9c +size 351 diff --git a/main.gd b/main.gd new file mode 100644 index 0000000..0ca4af0 --- /dev/null +++ b/main.gd @@ -0,0 +1,11 @@ +extends Node3D + +var sky = preload("res://assets/materials/sky.material") +var sky_passthrough = preload("res://assets/materials/sky_passthrough.material") + +@export var passthrough: bool = true +@onready var environment: WorldEnvironment = $WorldEnvironment + +func _ready(): + if passthrough: + environment.environment.sky.set_material(sky_passthrough) \ No newline at end of file diff --git a/main.tscn b/main.tscn index 79ccc7c..fd5f1b5 100644 --- a/main.tscn +++ b/main.tscn @@ -1,9 +1,12 @@ -[gd_scene load_steps=10 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=12 format=3 uid="uid://eecv28y6jxk4"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] [ext_resource type="Script" path="res://src/raycast.gd" id="1_tsqxc"] +[ext_resource type="Script" path="res://main.gd" id="1_uvrd4"] [ext_resource type="Script" path="res://src/model.gd" id="2_7f1x4"] [ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://scenes/menu.tscn" id="3_1tbp3"] +[ext_resource type="PackedScene" uid="uid://ctltchlf2j2r4" path="res://addons/xr-simulator/XRSimulator.tscn" id="5_3qc8g"] +[ext_resource type="Material" uid="uid://bf5ina366dwm6" path="res://assets/materials/sky.material" id="5_wgwf8"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true @@ -12,17 +15,8 @@ ao_enabled = true material = SubResource("StandardMaterial3D_m58yb") size = Vector3(0.01, 0.01, 0.01) -[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_p6hri"] -sky_top_color = Color(0, 0, 0, 1) -sky_horizon_color = Color(0, 0, 0, 1) -sky_curve = 9.13367e-05 -sky_cover_modulate = Color(0, 0, 0, 1) -ground_bottom_color = Color(0, 0, 0, 1) -ground_horizon_color = Color(0, 0, 0, 1) -use_debanding = false - [sub_resource type="Sky" id="Sky_vhymk"] -sky_material = SubResource("ProceduralSkyMaterial_p6hri") +sky_material = ExtResource("5_wgwf8") [sub_resource type="Environment" id="Environment_7ghp0"] background_mode = 2 @@ -32,6 +26,9 @@ ambient_light_color = Color(1, 1, 1, 1) ambient_light_sky_contribution = 0.72 [node name="Main" type="Node3D"] +transform = Transform3D(1, -0.000296142, 0.000270963, 0.000296143, 1, -4.61078e-06, -0.000270962, 4.67014e-06, 1, 0, 0, 0) +script = ExtResource("1_uvrd4") +passthrough = false [node name="XROrigin3D" type="XROrigin3D" parent="."] @@ -50,7 +47,7 @@ mesh = SubResource("BoxMesh_ir3co") script = ExtResource("2_7f1x4") [node name="Menu" parent="XROrigin3D/XRControllerLeft" instance=ExtResource("3_1tbp3")] -transform = Transform3D(-4.37114e-08, 0, -1, -0.707107, 0.707107, 3.09086e-08, 0.707107, 0.707107, -3.09086e-08, 0.194945, 0, -0.0534939) +transform = Transform3D(-4.37114e-08, 0, -1, -0.707107, 0.707107, 3.09086e-08, 0.707107, 0.707107, -3.09086e-08, 0.183517, 0, -0.0534939) [node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) @@ -77,3 +74,6 @@ environment = SubResource("Environment_7ghp0") [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(0.834925, -0.386727, -0.39159, 0.550364, 0.586681, 0.594058, 0, -0.711511, 0.702675, 0, 7.21041, 2.06458) shadow_enabled = true + +[node name="XRSimulator" parent="." instance=ExtResource("5_3qc8g")] +xr_origin = NodePath("../XROrigin3D") From 40adc7b8efe26f690696127bf4e598a95592f5f7 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Thu, 2 Nov 2023 23:13:55 +0100 Subject: [PATCH 07/13] start implementing ws support --- main.gd | 6 +- main.tscn | 1 - promise/LICENSE | 21 +++ promise/promise.gd | 165 ++++++++++++++++++ src/globals/home_adapters.gd | 22 ++- src/home_adapters/adapter.gd | 13 +- src/home_adapters/hass/hass.gd | 3 - src/home_adapters/hass_ws/hass.gd | 127 ++++++++++++++ .../hass_ws/templates/devices.j2 | 12 ++ 9 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 promise/LICENSE create mode 100644 promise/promise.gd create mode 100644 src/home_adapters/hass_ws/hass.gd create mode 100644 src/home_adapters/hass_ws/templates/devices.j2 diff --git a/main.gd b/main.gd index 0ca4af0..478168c 100644 --- a/main.gd +++ b/main.gd @@ -3,9 +3,9 @@ extends Node3D var sky = preload("res://assets/materials/sky.material") var sky_passthrough = preload("res://assets/materials/sky_passthrough.material") -@export var passthrough: bool = true @onready var environment: WorldEnvironment = $WorldEnvironment func _ready(): - if passthrough: - environment.environment.sky.set_material(sky_passthrough) \ No newline at end of file + # In case we're running on the headset, use the passthrough sky + if OS.get_name() == "Android": + environment.environment.sky.set_material(sky_passthrough) diff --git a/main.tscn b/main.tscn index fd5f1b5..c3d7c33 100644 --- a/main.tscn +++ b/main.tscn @@ -28,7 +28,6 @@ ambient_light_sky_contribution = 0.72 [node name="Main" type="Node3D"] transform = Transform3D(1, -0.000296142, 0.000270963, 0.000296143, 1, -4.61078e-06, -0.000270962, 4.67014e-06, 1, 0, 0, 0) script = ExtResource("1_uvrd4") -passthrough = false [node name="XROrigin3D" type="XROrigin3D" parent="."] diff --git a/promise/LICENSE b/promise/LICENSE new file mode 100644 index 0000000..3155814 --- /dev/null +++ b/promise/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 TheWalruzz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/promise/promise.gd b/promise/promise.gd new file mode 100644 index 0000000..c25a84f --- /dev/null +++ b/promise/promise.gd @@ -0,0 +1,165 @@ +extends RefCounted +class_name Promise + + +enum Status { + RESOLVED, + REJECTED +} + + +signal settled(status: PromiseResult) +signal resolved(value: Variant) +signal rejected(reason: Rejection) + + +## Generic rejection reason +const PROMISE_REJECTED := "Promise rejected" + + +var is_settled := false + + +func _init(callable: Callable): + resolved.connect( + func(value: Variant): + is_settled = true + settled.emit(PromiseResult.new(Status.RESOLVED, value)), + CONNECT_ONE_SHOT + ) + rejected.connect( + func(rejection: Rejection): + is_settled = true + settled.emit(PromiseResult.new(Status.REJECTED, rejection)), + CONNECT_ONE_SHOT + ) + + callable.call_deferred( + func(value: Variant): + if not is_settled: + resolved.emit(value), + func(rejection: Rejection): + if not is_settled: + rejected.emit(rejection) + ) + + +func then(resolved_callback: Callable) -> Promise: + resolved.connect( + resolved_callback, + CONNECT_ONE_SHOT + ) + return self + + +func catch(rejected_callback: Callable) -> Promise: + rejected.connect( + rejected_callback, + CONNECT_ONE_SHOT + ) + return self + + +static func from(input_signal: Signal) -> Promise: + return Promise.new( + func(resolve: Callable, _reject: Callable): + var number_of_args := input_signal.get_object().get_signal_list() \ + .filter(func(signal_info: Dictionary) -> bool: return signal_info["name"] == input_signal.get_name()) \ + .map(func(signal_info: Dictionary) -> int: return signal_info["args"].size()) \ + .front() as int + + if number_of_args == 0: + await input_signal + resolve.call(null) + else: + # only one arg in signal is allowed for now + var result = await input_signal + resolve.call(result) + ) + + +static func from_many(input_signals: Array[Signal]) -> Array[Promise]: + return input_signals.map( + func(input_signal: Signal): + return Promise.from(input_signal) + ) + + +static func all(promises: Array[Promise]) -> Promise: + return Promise.new( + func(resolve: Callable, reject: Callable): + var resolved_promises: Array[bool] = [] + var results := [] + results.resize(promises.size()) + resolved_promises.resize(promises.size()) + resolved_promises.fill(false) + + for i in promises.size(): + promises[i].then( + func(value: Variant): + results[i] = value + resolved_promises[i] = true + if resolved_promises.all(func(value: bool): return value): + resolve.call(results) + ).catch( + func(rejection: Rejection): + reject.call(rejection) + ) + ) + + +static func any(promises: Array[Promise]) -> Promise: + return Promise.new( + func(resolve: Callable, reject: Callable): + var rejected_promises: Array[bool] = [] + var rejections: Array[Rejection] = [] + rejections.resize(promises.size()) + rejected_promises.resize(promises.size()) + rejected_promises.fill(false) + + for i in promises.size(): + promises[i].then( + func(value: Variant): + resolve.call(value) + ).catch( + func(rejection: Rejection): + rejections[i] = rejection + rejected_promises[i] = true + if rejected_promises.all(func(value: bool): return value): + reject.call(PromiseAnyRejection.new(PROMISE_REJECTED, rejections)) + ) + ) + + +class PromiseResult: + var status: Status + var payload: Variant + + func _init(_status: Status, _payload: Variant): + status = _status + payload = _payload + + +class Rejection: + var reason: String + var stack: Array + + func _init(_reason: String): + reason = _reason + stack = get_stack() if OS.is_debug_build() else [] + + + func as_string() -> String: + return ("%s\n" % reason) + "\n".join( + stack.map( + func(dict: Dictionary) -> String: + return "At %s:%i:%s" % [dict["source"], dict["line"], dict["function"]] + )) + + +class PromiseAnyRejection extends Rejection: + var group: Array[Rejection] + + func _init(_reason: String, _group: Array[Rejection]): + super(_reason) + group = _group diff --git a/src/globals/home_adapters.gd b/src/globals/home_adapters.gd index 3717b94..3b54b51 100644 --- a/src/globals/home_adapters.gd +++ b/src/globals/home_adapters.gd @@ -3,4 +3,24 @@ extends Node var Adapter = preload("res://src/home_adapters/adapter.gd") var adapter = Adapter.new(Adapter.ADAPTER_TYPES.HASS) - \ No newline at end of file +var adapter_ws = Adapter.new(Adapter.ADAPTER_TYPES.HASS_WS) + +func _ready(): + add_child(adapter_ws) + + var timer = Timer.new() + timer.set_wait_time(1) + timer.set_one_shot(true) + + print("timer started") + timer.timeout.connect(func(): + print("timer done") + + var result = await adapter_ws.get_state("light.living_room") + print(result) + ) + + add_child(timer) + timer.start() + + diff --git a/src/home_adapters/adapter.gd b/src/home_adapters/adapter.gd index 21c862d..e6de48a 100644 --- a/src/home_adapters/adapter.gd +++ b/src/home_adapters/adapter.gd @@ -1,13 +1,16 @@ extends Node const hass = preload("res://src/home_adapters/hass/hass.gd") +const hass_ws = preload("res://src/home_adapters/hass_ws/hass.gd") enum ADAPTER_TYPES { - HASS + HASS, + HASS_WS } const adapters = { - ADAPTER_TYPES.HASS: hass + ADAPTER_TYPES.HASS: hass, + ADAPTER_TYPES.HASS_WS: hass_ws } const methods = [ @@ -20,6 +23,7 @@ var adapter: Node func _init(type: ADAPTER_TYPES): adapter = adapters[type].new() + add_child(adapter) for method in methods: assert(adapter.has_method(method), "Adapter does not implement method: " + method) @@ -31,4 +35,7 @@ func get_state(entity: String): return await adapter.get_state(entity) func set_state(entity: String, state: String, attributes: Dictionary = {}): - return await adapter.set_state(entity, state, attributes) \ No newline at end of file + return await adapter.set_state(entity, state, attributes) + +func watch_state(entity: String, callback: Callable): + return adapter.watch_state(entity, callback) diff --git a/src/home_adapters/hass/hass.gd b/src/home_adapters/hass/hass.gd index 93d2aa1..da4f49e 100644 --- a/src/home_adapters/hass/hass.gd +++ b/src/home_adapters/hass/hass.gd @@ -19,7 +19,6 @@ func load_devices(): var data_string = response[3].get_string_from_utf8().replace("'", "\"") var json = JSON.parse_string(data_string).data - print(json) return json func get_state(entity: String): @@ -31,7 +30,6 @@ func get_state(entity: String): var data_string = response[3].get_string_from_utf8().replace("'", "\"") var json = JSON.parse_string(data_string) - print(json) return json @@ -58,6 +56,5 @@ func set_state(entity: String, state: String, attributes: Dictionary = {}): var data_string = response[3].get_string_from_utf8().replace("'", "\"") var json = JSON.parse_string(data_string) - print(json) return json diff --git a/src/home_adapters/hass_ws/hass.gd b/src/home_adapters/hass_ws/hass.gd new file mode 100644 index 0000000..b54e920 --- /dev/null +++ b/src/home_adapters/hass_ws/hass.gd @@ -0,0 +1,127 @@ +extends Node + +var devices_template = FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2") +var socket = WebSocketPeer.new() +# in seconds +var request_timeout: float = 10 + +var url: String = "ws://192.168.33.33:8123/api/websocket" +var token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" + + +var authenticated: bool = false +var id = 1 + +signal packet_received(packet: Dictionary) + +func _init(url := self.url, token := self.token): + self.url = url + self.token = token + + devices_template = devices_template.replace("\n", " ").replace("\t", "").replace("\r", " ").replace("\"", "\\\"") + connect_ws() + +func connect_ws(): + print("Connecting to %s" % self.url) + socket.connect_to_url(self.url) + +func _process(delta): + socket.poll() + + var state = socket.get_ready_state() + if state == WebSocketPeer.STATE_OPEN: + while socket.get_available_packet_count(): + handle_packet(socket.get_packet()) + elif state == WebSocketPeer.STATE_CLOSING: + pass + elif state == WebSocketPeer.STATE_CLOSED: + var code = socket.get_close_code() + var reason = socket.get_close_reason() + print("WS connection closed with code: %s, reason: %s" % [code, reason]) + authenticated = false + set_process(false) + +func handle_packet(raw_packet: PackedByteArray): + var packet = decode_packet(raw_packet) + + print("Received packet: %s" % packet) + + if packet.type == "auth_required": + send_packet({ + "type": "auth", + "access_token": self.token + }) + + elif packet.type == "auth_ok": + authenticated = true + elif packet.type == "auth_invalid": + authenticated = false + print("Authentication failed") + set_process(false) + else: + packet_received.emit(packet) + + +func send_request_packet(packet: Dictionary): + packet.id = id + id += 1 + + send_packet(packet) + + var promise = Promise.new(func(resolve: Callable, reject: Callable): + var handle_packet = func(recieved_packet: Dictionary): + print("Received packet in handler: %s" % recieved_packet) + if packet.id == recieved_packet.id: + print("same id") + resolve.call(recieved_packet) + packet_received.disconnect(handle_packet) + + packet_received.connect(handle_packet) + + var timeout = Timer.new() + timeout.set_wait_time(request_timeout) + timeout.set_one_shot(true) + timeout.timeout.connect(func(): + reject.call(Promise.Rejection.new("Request timed out")) + packet_received.disconnect(handle_packet) + ) + add_child(timeout) + timeout.start() + ) + + return await promise.settled + + +func send_packet(packet: Dictionary): + print("Sending packet: %s" % encode_packet(packet)) + socket.send_text(encode_packet(packet)) + +func decode_packet(packet: PackedByteArray): + return JSON.parse_string(packet.get_string_from_utf8()) + +func encode_packet(packet: Dictionary): + return JSON.stringify(packet) + +func load_devices(): + pass + +func get_state(entity: String): + assert(authenticated, "Not authenticated") + + var result = await send_request_packet({ + "type": "get_states" + }) + + if result.status == Promise.Status.RESOLVED: + return result.payload + return null + + +func watch_state(entity: String, callback: Callable): + assert(authenticated, "Not authenticated") + + +func set_state(entity: String, state: String, attributes: Dictionary = {}): + assert(authenticated, "Not authenticated") + + diff --git a/src/home_adapters/hass_ws/templates/devices.j2 b/src/home_adapters/hass_ws/templates/devices.j2 new file mode 100644 index 0000000..d1750ba --- /dev/null +++ b/src/home_adapters/hass_ws/templates/devices.j2 @@ -0,0 +1,12 @@ +{% set devices = states | map(attribute='entity_id') | map('device_id') | unique | reject('eq',None) | list %} + +{%- set ns = namespace(devices = []) %} +{%- for device in devices %} + {%- set entities = device_entities(device) | list %} + {%- if entities %} + {%- set ns.devices = ns.devices + [ {device: {"name": device_attr(device, "name"), "entities": entities }} ] %} + {%- endif %} +{%- endfor %} +{ + "data": {{ ns.devices }} +} \ No newline at end of file From 3d411188a6054b3f554c46672561220e0ecc4f0d Mon Sep 17 00:00:00 2001 From: Nitwel Date: Fri, 3 Nov 2023 02:07:53 +0100 Subject: [PATCH 08/13] continue working on websocket implementation --- {promise => addons/promise}/LICENSE | 0 {promise => addons/promise}/promise.gd | 0 src/entities/light.gd | 4 +- src/entities/switch.gd | 4 +- src/globals/home_adapters.gd | 17 +-- src/home_adapters/hass_ws/callback_map.gd | 40 +++++++ src/home_adapters/hass_ws/hass.gd | 132 ++++++++++++++++------ 7 files changed, 142 insertions(+), 55 deletions(-) rename {promise => addons/promise}/LICENSE (100%) rename {promise => addons/promise}/promise.gd (100%) create mode 100644 src/home_adapters/hass_ws/callback_map.gd diff --git a/promise/LICENSE b/addons/promise/LICENSE similarity index 100% rename from promise/LICENSE rename to addons/promise/LICENSE diff --git a/promise/promise.gd b/addons/promise/promise.gd similarity index 100% rename from promise/promise.gd rename to addons/promise/promise.gd diff --git a/src/entities/light.gd b/src/entities/light.gd index 5f77c50..aced8f0 100644 --- a/src/entities/light.gd +++ b/src/entities/light.gd @@ -5,7 +5,7 @@ extends StaticBody3D # Called when the node enters the scene tree for the first time. func _ready(): - var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id) if stateInfo["state"] == "on": sprite.set_frame(0) else: @@ -13,7 +13,7 @@ func _ready(): func _on_toggle(): - HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") + HomeAdapters.adapter_ws.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") if sprite.get_frame() == 0: sprite.set_frame(1) else: diff --git a/src/entities/switch.gd b/src/entities/switch.gd index 5f77c50..aced8f0 100644 --- a/src/entities/switch.gd +++ b/src/entities/switch.gd @@ -5,7 +5,7 @@ extends StaticBody3D # Called when the node enters the scene tree for the first time. func _ready(): - var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id) if stateInfo["state"] == "on": sprite.set_frame(0) else: @@ -13,7 +13,7 @@ func _ready(): func _on_toggle(): - HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") + HomeAdapters.adapter_ws.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") if sprite.get_frame() == 0: sprite.set_frame(1) else: diff --git a/src/globals/home_adapters.gd b/src/globals/home_adapters.gd index 3b54b51..11a4922 100644 --- a/src/globals/home_adapters.gd +++ b/src/globals/home_adapters.gd @@ -6,21 +6,6 @@ var adapter = Adapter.new(Adapter.ADAPTER_TYPES.HASS) var adapter_ws = Adapter.new(Adapter.ADAPTER_TYPES.HASS_WS) func _ready(): + add_child(adapter) add_child(adapter_ws) - - var timer = Timer.new() - timer.set_wait_time(1) - timer.set_one_shot(true) - - print("timer started") - timer.timeout.connect(func(): - print("timer done") - - var result = await adapter_ws.get_state("light.living_room") - print(result) - ) - - add_child(timer) - timer.start() - diff --git a/src/home_adapters/hass_ws/callback_map.gd b/src/home_adapters/hass_ws/callback_map.gd new file mode 100644 index 0000000..ee85c12 --- /dev/null +++ b/src/home_adapters/hass_ws/callback_map.gd @@ -0,0 +1,40 @@ +extends Node + +class_name CallbackMap + +var callbacks := {} + +func add(key: Variant, callback: Callable) -> void: + _validate_key(key) + + if callbacks.has(key): + callbacks[key].append(callback) + else: + callbacks[key] = [callback] + +func add_once(key: Variant, callback: Callable) -> void: + _validate_key(key) + + var fn: Callable + + fn = func(args: Array): + remove(key, fn) + callback.callv(args) + + add(key, fn) + +func remove(key: Variant, callback: Callable) -> void: + _validate_key(key) + + if callbacks.has(key): + callbacks[key].erase(callback) + +func call_key(key: Variant, args: Array) -> void: + _validate_key(key) + + if callbacks.has(key): + for callback in callbacks[key]: + callback.callv(args) + +func _validate_key(key: Variant): + assert(typeof(key) == TYPE_STRING || typeof(key) == TYPE_INT || typeof(key) == TYPE_FLOAT, "key must be a string or number") diff --git a/src/home_adapters/hass_ws/hass.gd b/src/home_adapters/hass_ws/hass.gd index b54e920..7f25671 100644 --- a/src/home_adapters/hass_ws/hass.gd +++ b/src/home_adapters/hass_ws/hass.gd @@ -1,18 +1,23 @@ extends Node -var devices_template = FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2") -var socket = WebSocketPeer.new() +var devices_template := FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2") +var socket := WebSocketPeer.new() # in seconds -var request_timeout: float = 10 +var request_timeout := 10.0 -var url: String = "ws://192.168.33.33:8123/api/websocket" -var token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" +var url := "ws://192.168.33.33:8123/api/websocket" +var token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" -var authenticated: bool = false -var id = 1 +var authenticated := false +var id := 1 +var entities: Dictionary = {} -signal packet_received(packet: Dictionary) +var entitiy_callbacks := CallbackMap.new() +var packet_callbacks := CallbackMap.new() + +signal on_connect() +signal on_disconnect() func _init(url := self.url, token := self.token): self.url = url @@ -27,8 +32,9 @@ func connect_ws(): func _process(delta): socket.poll() - + var state = socket.get_ready_state() + print(state, "POLLING") if state == WebSocketPeer.STATE_OPEN: while socket.get_available_packet_count(): handle_packet(socket.get_packet()) @@ -38,8 +44,7 @@ func _process(delta): var code = socket.get_close_code() var reason = socket.get_close_reason() print("WS connection closed with code: %s, reason: %s" % [code, reason]) - authenticated = false - set_process(false) + handle_disconnect() func handle_packet(raw_packet: PackedByteArray): var packet = decode_packet(raw_packet) @@ -54,36 +59,79 @@ func handle_packet(raw_packet: PackedByteArray): elif packet.type == "auth_ok": authenticated = true + start_subscriptions() + elif packet.type == "auth_invalid": - authenticated = false - print("Authentication failed") - set_process(false) + handle_disconnect() else: - packet_received.emit(packet) + packet_callbacks.call_key(packet.id, [packet]) + +func start_subscriptions(): + assert(authenticated, "Not authenticated") + + await send_request_packet({ + "type": "supported_features", + "features": { + "coalesce_messages": 1 + } + }) + + send_subscribe_packet({ + "type": "subscribe_entities" + }, func(packet: Dictionary): + if packet.type != "event": + return + + if packet.event.has("a"): + for entity in packet.event.a.keys(): + entities[entity] = packet.event.a[entity] + entitiy_callbacks.call_key(entity, [entities[entity]]) + on_connect.emit() + + if packet.event.has("c"): + for entity in packet.event.c.keys(): + if packet.event.c[entity].has("+"): + entities[entity].merge(packet.event.c[entity]["+"]) + entitiy_callbacks.call_key(entity, [entities[entity]]) + ) + +func handle_disconnect(): + authenticated = false + set_process(false) + on_disconnect.emit() + +func send_subscribe_packet(packet: Dictionary, callback: Callable): + packet.id = id + id += 1 + + send_packet(packet) + packet_callbacks.add(packet.id, callback) + + return func(): + packet_callbacks.remove(packet.id, callback) + send_packet({ + id: id, + "type": packet.type.replace("subscribe", "unsubscribe"), + "subscription": packet.id + }) + id += 1 + - func send_request_packet(packet: Dictionary): packet.id = id id += 1 send_packet(packet) - var promise = Promise.new(func(resolve: Callable, reject: Callable): - var handle_packet = func(recieved_packet: Dictionary): - print("Received packet in handler: %s" % recieved_packet) - if packet.id == recieved_packet.id: - print("same id") - resolve.call(recieved_packet) - packet_received.disconnect(handle_packet) - - packet_received.connect(handle_packet) + var promise = Promise.new(func(resolve: Callable, reject: Callable): + packet_callbacks.add_once(packet.id, resolve) var timeout = Timer.new() timeout.set_wait_time(request_timeout) timeout.set_one_shot(true) timeout.timeout.connect(func(): reject.call(Promise.Rejection.new("Request timed out")) - packet_received.disconnect(handle_packet) + packet_callbacks.remove(packet.id, resolve) ) add_child(timeout) timeout.start() @@ -106,22 +154,36 @@ func load_devices(): pass func get_state(entity: String): - assert(authenticated, "Not authenticated") + if !authenticated: + await on_connect - var result = await send_request_packet({ - "type": "get_states" - }) - - if result.status == Promise.Status.RESOLVED: - return result.payload - return null + if entities.has(entity): + return entities[entity] + else: + print(entities, entity) func watch_state(entity: String, callback: Callable): - assert(authenticated, "Not authenticated") + if !authenticated: + await on_connect + + entitiy_callbacks.add(entity, callback) func set_state(entity: String, state: String, attributes: Dictionary = {}): assert(authenticated, "Not authenticated") + var domain = entity.split(".")[0] + var service = entity.split(".")[1] + + return await send_request_packet({ + "type": "call_service", + "domain": domain, + "service": service, + "service_data": attributes, + "target": { + "entity_id": entity + } + }) + From f3f0c93547035bf106a04a847d020118eff448cb Mon Sep 17 00:00:00 2001 From: Nitwel Date: Fri, 3 Nov 2023 22:00:05 +0100 Subject: [PATCH 09/13] finally fix websockets and add sensor entity --- scenes/entities/sensor.tscn | 15 ++++++ src/entities/sensor.gd | 16 +++++++ src/entities/switch.gd | 7 +++ src/home_adapters/hass_ws/hass.gd | 77 ++++++++++++++++++++++--------- src/menu.gd | 8 ++++ 5 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 scenes/entities/sensor.tscn create mode 100644 src/entities/sensor.gd diff --git a/scenes/entities/sensor.tscn b/scenes/entities/sensor.tscn new file mode 100644 index 0000000..1efb530 --- /dev/null +++ b/scenes/entities/sensor.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://xsiy71rsqulj"] + +[ext_resource type="Script" path="res://src/entities/sensor.gd" id="1_57ac8"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_r20gc"] +radius = 0.1 + +[node name="Sensor" type="StaticBody3D"] +script = ExtResource("1_57ac8") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_r20gc") + +[node name="Label" type="Label3D" parent="."] +text = "some text" diff --git a/src/entities/sensor.gd b/src/entities/sensor.gd new file mode 100644 index 0000000..5aa4d22 --- /dev/null +++ b/src/entities/sensor.gd @@ -0,0 +1,16 @@ +extends StaticBody3D + +@export var entity_id = "sensor.sun_next_dawn" +@onready var label: Label3D = $Label + +# Called when the node enters the scene tree for the first time. +func _ready(): + var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id) + label.text = stateInfo["state"] + + await HomeAdapters.adapter_ws.watch_state(entity_id, func(new_state): + label.text = new_state["state"] + ) + +func _on_toggle(): + pass \ No newline at end of file diff --git a/src/entities/switch.gd b/src/entities/switch.gd index aced8f0..b9367f7 100644 --- a/src/entities/switch.gd +++ b/src/entities/switch.gd @@ -11,6 +11,13 @@ func _ready(): else: sprite.set_frame(1) + await HomeAdapters.adapter_ws.watch_state(entity_id, func(new_state): + if new_state["state"] == "on": + sprite.set_frame(0) + else: + sprite.set_frame(1) + ) + func _on_toggle(): HomeAdapters.adapter_ws.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") diff --git a/src/home_adapters/hass_ws/hass.gd b/src/home_adapters/hass_ws/hass.gd index 7f25671..d7323ad 100644 --- a/src/home_adapters/hass_ws/hass.gd +++ b/src/home_adapters/hass_ws/hass.gd @@ -7,7 +7,7 @@ var request_timeout := 10.0 var url := "ws://192.168.33.33:8123/api/websocket" var token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" - +var LOG_MESSAGES := false var authenticated := false var id := 1 @@ -30,14 +30,22 @@ func connect_ws(): print("Connecting to %s" % self.url) socket.connect_to_url(self.url) + # https://github.com/godotengine/godot/issues/84423 + # Otherwise the WebSocketPeer will crash when receiving large packets + socket.set_inbound_buffer_size(65535 * 2) + func _process(delta): socket.poll() var state = socket.get_ready_state() - print(state, "POLLING") if state == WebSocketPeer.STATE_OPEN: while socket.get_available_packet_count(): - handle_packet(socket.get_packet()) + var packet = decode_packet(socket.get_packet()) + if typeof(packet) == TYPE_DICTIONARY: + handle_packet(packet) + elif typeof(packet) == TYPE_ARRAY: + for p in packet: + handle_packet(p) elif state == WebSocketPeer.STATE_CLOSING: pass elif state == WebSocketPeer.STATE_CLOSED: @@ -46,10 +54,8 @@ func _process(delta): print("WS connection closed with code: %s, reason: %s" % [code, reason]) handle_disconnect() -func handle_packet(raw_packet: PackedByteArray): - var packet = decode_packet(raw_packet) - - print("Received packet: %s" % packet) +func handle_packet(packet: Dictionary): + if LOG_MESSAGES: print("Received packet: %s" % packet) if packet.type == "auth_required": send_packet({ @@ -64,17 +70,22 @@ func handle_packet(raw_packet: PackedByteArray): elif packet.type == "auth_invalid": handle_disconnect() else: - packet_callbacks.call_key(packet.id, [packet]) + packet_callbacks.call_key(int(packet.id), [packet]) func start_subscriptions(): assert(authenticated, "Not authenticated") - await send_request_packet({ - "type": "supported_features", - "features": { - "coalesce_messages": 1 - } - }) + # await send_request_packet({ + # "type": "supported_features", + # "features": { + # "coalesce_messages": 1 + # } + # }) + + # await send_request_packet({ + # "type": "subscribe_events", + # "event_type": "state_changed" + # }) send_subscribe_packet({ "type": "subscribe_entities" @@ -84,14 +95,23 @@ func start_subscriptions(): if packet.event.has("a"): for entity in packet.event.a.keys(): - entities[entity] = packet.event.a[entity] + entities[entity] = { + "state": packet.event.a[entity]["s"], + "attributes": packet.event.a[entity]["a"] + } entitiy_callbacks.call_key(entity, [entities[entity]]) on_connect.emit() if packet.event.has("c"): for entity in packet.event.c.keys(): + if !entities.has(entity): + continue + if packet.event.c[entity].has("+"): - entities[entity].merge(packet.event.c[entity]["+"]) + if packet.event.c[entity]["+"].has("s"): + entities[entity]["state"] = packet.event.c[entity]["+"]["s"] + if packet.event.c[entity]["+"].has("a"): + entities[entity]["attributes"].merge(packet.event.c[entity]["+"]["a"]) entitiy_callbacks.call_key(entity, [entities[entity]]) ) @@ -104,8 +124,8 @@ func send_subscribe_packet(packet: Dictionary, callback: Callable): packet.id = id id += 1 - send_packet(packet) packet_callbacks.add(packet.id, callback) + send_packet(packet) return func(): packet_callbacks.remove(packet.id, callback) @@ -141,7 +161,7 @@ func send_request_packet(packet: Dictionary): func send_packet(packet: Dictionary): - print("Sending packet: %s" % encode_packet(packet)) + if LOG_MESSAGES || true: print("Sending packet: %s" % encode_packet(packet)) socket.send_text(encode_packet(packet)) func decode_packet(packet: PackedByteArray): @@ -151,7 +171,10 @@ func encode_packet(packet: Dictionary): return JSON.stringify(packet) func load_devices(): - pass + if !authenticated: + await on_connect + + return entities func get_state(entity: String): if !authenticated: @@ -159,8 +182,7 @@ func get_state(entity: String): if entities.has(entity): return entities[entity] - else: - print(entities, entity) + return null func watch_state(entity: String, callback: Callable): @@ -174,7 +196,18 @@ func set_state(entity: String, state: String, attributes: Dictionary = {}): assert(authenticated, "Not authenticated") var domain = entity.split(".")[0] - var service = entity.split(".")[1] + var service: String + + if domain == 'switch': + if state == 'on': + service = 'turn_on' + elif state == 'off': + service = 'turn_off' + elif domain == 'light': + if state == 'on': + service = 'turn_on' + elif state == 'off': + service = 'turn_off' return await send_request_packet({ "type": "call_service", diff --git a/src/menu.gd b/src/menu.gd index b50aff6..5944239 100644 --- a/src/menu.gd +++ b/src/menu.gd @@ -4,6 +4,7 @@ const Device = preload("res://scenes/device.tscn") const Entity = preload("res://scenes/entity.tscn") const Switch = preload("res://scenes/entities/switch.tscn") const Light = preload("res://scenes/entities/light.tscn") +const Sensor = preload("res://scenes/entities/sensor.tscn") @onready var devices_node = $Devices var devices @@ -93,6 +94,13 @@ func _on_entity_click(entity_name): light.set_position(global_position) get_node("/root").add_child(light) + + if type == "sensor": + var sensor = Sensor.instantiate() + sensor.entity_id = entity_name + + sensor.set_position(global_position) + get_node("/root").add_child(sensor) func clear_menu(): for child in devices_node.get_children(): From a8ba5520a444970e803f18dcd1abd79b18657445 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sat, 4 Nov 2023 14:23:57 +0100 Subject: [PATCH 10/13] add banner --- assets/banner.png | 3 +++ assets/banner.png.import | 3 +++ assets/design.afdesign | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 assets/banner.png create mode 100644 assets/banner.png.import diff --git a/assets/banner.png b/assets/banner.png new file mode 100644 index 0000000..c35fbb4 --- /dev/null +++ b/assets/banner.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f70162100261f12060f1c82a27d6bc547a465c4f401c7914b3b2d0ed51d78f81 +size 431818 diff --git a/assets/banner.png.import b/assets/banner.png.import new file mode 100644 index 0000000..9fca7b8 --- /dev/null +++ b/assets/banner.png.import @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d859d4114970ee061de56a0c9e108deddaac675613b45ea1c6ac09a6225d63e +size 759 diff --git a/assets/design.afdesign b/assets/design.afdesign index 35321b1..f8a9066 100644 --- a/assets/design.afdesign +++ b/assets/design.afdesign @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3a3204f122024fe25657532ccbe1c0d07eba475f37ebb686046ba71d358ad58 -size 2087560 +oid sha256:c48298302f3b19b61742398aa55b7e554074f72632cee50db3cde6696d60cdaa +size 3305459 From 25f2caf4a63e14cd3aa89cd2b9c54f8444f8951f Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sat, 4 Nov 2023 14:24:50 +0100 Subject: [PATCH 11/13] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b8b86b4..d0feebb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Immersive Home +![logo](assets/banner.png) -Using Godot 4.1.2 \ No newline at end of file +Using Godot 4.1.3 \ No newline at end of file From 3da02db70c3d59e103b2124afdbaa37eccff3052 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sat, 4 Nov 2023 15:34:46 +0100 Subject: [PATCH 12/13] Update README.md --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d0feebb..76913a4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,94 @@ ![logo](assets/banner.png) -Using Godot 4.1.3 \ No newline at end of file +# 🏠 Introduction + +Immersive Home is project to bring Smart Home and Mixed Reality technologies together for an intuitive and immersive experience. + +## Features + +- **Fast and Intuitive control over IoT devices** +- **Live overview over your smart home** +- **Simple way for creating automations** +- **Comfortable way of consuming virtual media unobstructed** +- **Advanced automations based on position in room** + +## Supported Devices + +**Smart Home Platforms** +- [Home Assistant](https://www.home-assistant.io/) + +**Mixed Reality Headsets** +- Meta Quest 2 / Pro / 3 + +# Development + +In order to contribute to this project, you need the following to be setup before you can start working: +- Godot 4.1.3 installed + +## Fundamentals + +Communication with the Smart Home Environment is done using the `HomeAdapters` global. Each environment is made up of devices and entities. +A device is a collection of different entities and entities can represent many different things in a smart home. +For example, the entity of name `lights.smart_lamp_1` would control the kitchen lamps while `state.smart_lamp_1_temp` would show the current temperature of the lamp. + +### Home Adapters + +The `HomeAdapters` global allows to communicate with different backends and offers a set of fundamental functions allowing communication with the Smart Home. + +```python +Device { + "id": String, + "name": String, + "entities": Array[Entity] +} + +Entity { + "state": String + "attributes": Dictionary +} + +# Get a list of all devices +func get_devices() -> Signal[Array[Device]] + +# Get a single device by id +func get_device(id: String) -> Signal[Device] + +# Returns the current state of an entity. +func get_state(entity: String) -> Signal[Entity] + +# Updates the state of the entity and returns the resulting state +func set_state(entity: String, state: String, attributes: Dictionary) -> Signal[Entity] + +# Watches the state and each time it changes, calls the callback with the changed state, returns a function to stop watching the state +func watch_state(entity: String, callback: Callable[entity: Entity]) -> Callable +``` + +### Interaction Events + +Each time a button is pressed on the primary controller, a raycast is done to be able to interact with devices or the UI. + +**InteractionEvent** +```js +{ + position: Vector3, + rotation: Vector3 +} +``` + +| Function called | Args | Description | +| -- | -- | -- | +| `_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | +| `_dbl_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released twice in a row | +| `_long_click` | `[event: InteractionEvent]` | The back trigger button has been pressed, then hold still for a short period, then released | +| `_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | +| `_press_move` | `[event: InteractionEvent]` | The back trigger button has been moved while pressed down | +| `_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | +| `_grab_down` | `[event: InteractionEvent]` | The side grap button been pressed down | +| `_grab_move` | `[event: InteractionEvent]` | The side grap button been pressed down | +| `_grab_up` | `[event: InteractionEvent]` | The side grap button been released | + +### Testing without a VR Headset + +In order to test without a headset, press the run project (F5) button in Godot and ignore the prompt that OpenXR failed to start. +To simulate the headset and controller movement, we're using the [XR Input Simulator](https://godotengine.org/asset-library/asset/1775) asset. +Click at the link to get a list of the supported controlls. From ef7a5354083dd526343ec27cad3152e8faff06c0 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sat, 4 Nov 2023 15:35:48 +0100 Subject: [PATCH 13/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76913a4..d508edf 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Immersive Home is project to bring Smart Home and Mixed Reality technologies tog **Mixed Reality Headsets** - Meta Quest 2 / Pro / 3 -# Development +# 🛠 Development In order to contribute to this project, you need the following to be setup before you can start working: - Godot 4.1.3 installed