From ffdb1b607f87745ceb45c108228886ea4bf98497 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sun, 17 Mar 2024 17:05:45 +0100 Subject: [PATCH 1/2] add onboarding flow --- app/content/main.tscn | 6 +- app/content/system/assist/assist.gd | 26 +++++--- app/content/system/camera/camera.gd | 6 +- app/content/ui/console.gd | 4 +- app/content/ui/onboarding/onboarding.gd | 43 +++++++++++++ app/content/ui/onboarding/onboarding.tscn | 68 ++++++++++++++++++++ app/lib/globals/home_api.gd | 28 ++++++-- app/lib/globals/main_store.gd | 6 +- app/lib/home_apis/hass_ws/handlers/assist.gd | 12 +--- app/lib/home_apis/hass_ws/hass.gd | 6 +- app/lib/home_apis/voice_handler.gd | 11 ++++ app/lib/stores/settings.gd | 6 +- app/lib/stores/store.gd | 5 +- 13 files changed, 193 insertions(+), 34 deletions(-) create mode 100644 app/content/ui/onboarding/onboarding.gd create mode 100644 app/content/ui/onboarding/onboarding.tscn create mode 100644 app/lib/home_apis/voice_handler.gd diff --git a/app/content/main.tscn b/app/content/main.tscn index 421b116..8ca3ce1 100644 --- a/app/content/main.tscn +++ b/app/content/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=3 uid="uid://eecv28y6jxk4"] +[gd_scene load_steps=17 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://content/main.gd" id="1_uvrd4"] @@ -11,6 +11,7 @@ [ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://content/ui/menu/menu.tscn" id="8_du83w"] [ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/system/keyboard/keyboard.tscn" id="9_e5n3p"] [ext_resource type="PackedScene" uid="uid://cbemihbxkd4ll" path="res://content/system/house/house.tscn" id="9_np6mw"] +[ext_resource type="PackedScene" uid="uid://bhyddd1f0ry1x" path="res://content/ui/onboarding/onboarding.tscn" id="12_uq2nj"] [sub_resource type="Sky" id="Sky_vhymk"] sky_material = ExtResource("5_wgwf8") @@ -83,4 +84,7 @@ transform = Transform3D(0.499999, -0.000139169, -6.50204e-05, 5.24307e-05, 0.353 [node name="House" parent="." instance=ExtResource("9_np6mw")] +[node name="Onboarding" parent="." instance=ExtResource("12_uq2nj")] +transform = Transform3D(0.999999, -1.39632e-11, 0, 9.48097e-12, 0.999999, 0, 0, 0, 0.999999, -0.529594, 0.820154, -0.600147) + [editable path="XROrigin3D/XRControllerLeft"] diff --git a/app/content/system/assist/assist.gd b/app/content/system/assist/assist.gd index ce3143a..f2aa43f 100644 --- a/app/content/system/assist/assist.gd +++ b/app/content/system/assist/assist.gd @@ -1,5 +1,6 @@ extends Node3D +const VoiceAssistant = preload ("res://lib/home_apis/voice_handler.gd") const sample_hold = preload ("res://lib/utils/sample_hold.gd") const Chat = preload ("./chat.gd") @@ -19,18 +20,27 @@ var effect: AudioEffectCapture @onready var camera = $"/root/Main/XROrigin3D/XRCamera3D" var running := false +var voice_assistant: VoiceAssistant func _ready(): var index = AudioServer.get_bus_index("Record") effect = AudioServer.get_bus_effect(index, 0) + if !HomeApi.has_connected(): + await HomeApi.on_connect + + voice_assistant = HomeApi.get_voice_assistant() + + if voice_assistant == null: + return + finish() audio_timer.timeout.connect(func(): - HomeApi.api.assist_handler.send_data(PackedByteArray()) + voice_assistant.send_data(PackedByteArray()) ) - HomeApi.api.assist_handler.on_wake_word.connect(func(text): + voice_assistant.on_wake_word.connect(func(_text): loader.visible=true chat_user.visible=false chat_assistant.visible=false @@ -40,24 +50,24 @@ func _ready(): running=true ) - HomeApi.api.assist_handler.on_stt_message.connect(func(text): + voice_assistant.on_stt_message.connect(func(text): loader.visible=false chat_user.visible=true chat_user.text=text ) - HomeApi.api.assist_handler.on_tts_message.connect(func(text): + voice_assistant.on_tts_message.connect(func(text): chat_assistant.visible=true chat_assistant.text=text ) - HomeApi.api.assist_handler.on_tts_sound.connect(func(audio): + voice_assistant.on_tts_sound.connect(func(audio): audio_player_3d.stream=audio audio_player_3d.play() visual_timer.start() running=false ) - HomeApi.api.assist_handler.on_error.connect(func(): + voice_assistant.on_error.connect(func(): running=false finish() ) @@ -101,9 +111,9 @@ func _process(_delta): if max_amplitude > input_threshold: if audio_timer.is_stopped(): - HomeApi.api.assist_handler.start_wakeword() + voice_assistant.start_wakeword() audio_timer.start() if audio_timer.is_stopped() == false: - HomeApi.api.assist_handler.send_data(data) + voice_assistant.send_data(data) diff --git a/app/content/system/camera/camera.gd b/app/content/system/camera/camera.gd index a6b7e01..486d514 100644 --- a/app/content/system/camera/camera.gd +++ b/app/content/system/camera/camera.gd @@ -3,7 +3,7 @@ extends XRCamera3D var last_room = null func _physics_process(_delta): - if HomeApi.api.has_integration(): + if HomeApi.has_integration(): update_room() func update_room(): @@ -11,8 +11,8 @@ func update_room(): if room != last_room: if room: - HomeApi.api.update_room(room.name) + HomeApi.update_room(room.name) last_room = room else: - HomeApi.api.update_room("outside") + HomeApi.update_room("outside") last_room = null \ No newline at end of file diff --git a/app/content/ui/console.gd b/app/content/ui/console.gd index ab59980..9f9c6c7 100644 --- a/app/content/ui/console.gd +++ b/app/content/ui/console.gd @@ -5,8 +5,8 @@ extends StaticBody3D var max_message = 30 var messages: Array = ["aaa", "bbb"] -func log(text: String) -> void: - messages.push_front(text) +func log(text: Variant) -> void: + messages.push_front(str(text)) if messages.size() > max_message: messages.pop_back() diff --git a/app/content/ui/onboarding/onboarding.gd b/app/content/ui/onboarding/onboarding.gd new file mode 100644 index 0000000..b30bf4d --- /dev/null +++ b/app/content/ui/onboarding/onboarding.gd @@ -0,0 +1,43 @@ +extends Node3D + +@onready var getting_started_button = $GettingStartedButton +@onready var close_button = $CloseButton +@onready var camera = $"/root/Main/XROrigin3D/XRCamera3D" +var next_new_position = global_position + +func _ready(): + if Store.settings.is_loaded() == false: + await Store.settings.on_loaded + + if (Store.settings.url != ""&&Store.settings.url != null)||Store.settings.onboarding_complete: + close() + return + + getting_started_button.on_button_down.connect(func(): + OS.shell_open("https://docs.immersive-home.org/") + ) + + close_button.on_button_down.connect(func(): + close() + ) + + EventSystem.on_slow_tick.connect(_slow_tick) + +func close(): + Store.settings.onboarding_complete = true + Store.settings.save_local() + queue_free() + +func _slow_tick(delta): + var new_position = camera.global_position + camera.global_transform.basis.z * - 0.5 + + if next_new_position.distance_to(new_position) > 0.2: + next_new_position = new_position + var new_direction = Basis.looking_at((camera.global_position - new_position) * - 1) + + var tween = create_tween() + tween.set_parallel(true) + tween.set_trans(Tween.TransitionType.TRANS_QUAD) + + tween.tween_property(self, "global_position", new_position, 0.6) + tween.tween_property(self, "global_transform:basis", new_direction, 0.6) diff --git a/app/content/ui/onboarding/onboarding.tscn b/app/content/ui/onboarding/onboarding.tscn new file mode 100644 index 0000000..5dcbd29 --- /dev/null +++ b/app/content/ui/onboarding/onboarding.tscn @@ -0,0 +1,68 @@ +[gd_scene load_steps=6 format=3 uid="uid://bhyddd1f0ry1x"] + +[ext_resource type="Script" path="res://content/ui/onboarding/onboarding.gd" id="1_k4yvw"] +[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/pri-500.material" id="2_aleti"] +[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="3_hlpow"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_nfwtf"] +size = Vector3(0.5, 0.3, 0.01) + +[sub_resource type="BoxMesh" id="BoxMesh_yknqs"] +size = Vector3(0.5, 0.3, 0.01) + +[node name="Onboarding" type="StaticBody3D"] +script = ExtResource("1_k4yvw") + +[node name="Label3D2" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.23, -0.1, 0.006) +pixel_size = 0.001 +text = "Getting Started" +font_size = 18 +outline_size = 0 +horizontal_alignment = 0 +vertical_alignment = 0 +autowrap_mode = 3 +width = 470.0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_nfwtf") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +material_override = ExtResource("2_aleti") +mesh = SubResource("BoxMesh_yknqs") + +[node name="GettingStartedLabel" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.23, 0.14, 0.006) +pixel_size = 0.001 +text = "Hey! + +This app is still early in development and still has a lot of rough edges. Things might break or are sometimes difficult to use, this will improve the more the app ages. + +If this is the first time you try the app, please first read through the \"Getting Started\" Guide below." +font_size = 18 +outline_size = 0 +horizontal_alignment = 0 +vertical_alignment = 0 +autowrap_mode = 3 +width = 470.0 + +[node name="GettingStartedButton" parent="." instance=ExtResource("3_hlpow")] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, -0.05, -0.11, 0) +label = "open_in_new" +icon = true + +[node name="CloseButton" parent="." instance=ExtResource("3_hlpow")] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0.21, -0.11, 0) +label = "done" +icon = true + +[node name="CloseLabel" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, -0.1, 0.006) +pixel_size = 0.001 +text = "Understood" +font_size = 18 +outline_size = 0 +horizontal_alignment = 0 +vertical_alignment = 0 +autowrap_mode = 3 +width = 470.0 diff --git a/app/lib/globals/home_api.gd b/app/lib/globals/home_api.gd index 97b2b0a..1b37a7a 100644 --- a/app/lib/globals/home_api.gd +++ b/app/lib/globals/home_api.gd @@ -3,6 +3,7 @@ extends Node const Hass = preload ("res://lib/home_apis/hass/hass.gd") const HassWebSocket = preload ("res://lib/home_apis/hass_ws/hass.gd") +const VoiceAssistant = preload ("res://lib/home_apis/voice_handler.gd") const apis = { "hass": Hass, @@ -96,7 +97,26 @@ func watch_state(entity: String, callback: Callable): assert(has_connected(), "Not connected") return api.watch_state(entity, callback) -func _notification(what): - if what == NOTIFICATION_WM_CLOSE_REQUEST||what == NOTIFICATION_WM_GO_BACK_REQUEST: - # Store.house.save_local() - pass +## Returns true if the adapter has an integration in the home automation system +## allowing to send the room position of the headset. +func has_integration() -> bool: + if has_connected() == false||api.has_method("has_integration") == false: + return false + + return api.has_integration() + +## Updates the room position of the headset in the home automation system +func update_room(room: String) -> void: + if has_connected() == false||api.has_method("update_room") == false: + return + + api.update_room(room) + +## Returns the VoiceHandler if the adapter has a voice assistant +func get_voice_assistant() -> VoiceAssistant: + assert(has_connected(), "Not connected") + + if api.has_method("get_voice_assistant") == false: + return null + + return api.get_voice_assistant() \ No newline at end of file diff --git a/app/lib/globals/main_store.gd b/app/lib/globals/main_store.gd index eebfc97..daedd9e 100644 --- a/app/lib/globals/main_store.gd +++ b/app/lib/globals/main_store.gd @@ -5,6 +5,6 @@ const SettingsStore = preload ("res://lib/stores/settings.gd") const HouseStore = preload ("res://lib/stores/house.gd") const DevicesStore = preload ("res://lib/stores/devices.gd") -var settings = SettingsStore.new() -var house = HouseStore.new() -var devices = DevicesStore.new() +var settings := SettingsStore.new() +var house := HouseStore.new() +var devices := DevicesStore.new() diff --git a/app/lib/home_apis/hass_ws/handlers/assist.gd b/app/lib/home_apis/hass_ws/handlers/assist.gd index dde4eba..0efbfcb 100644 --- a/app/lib/home_apis/hass_ws/handlers/assist.gd +++ b/app/lib/home_apis/hass_ws/handlers/assist.gd @@ -1,10 +1,7 @@ -const HASS_API = preload ("../hass.gd") +extends VoiceHandler -signal on_wake_word(wake_word: String) -signal on_stt_message(message: String) -signal on_tts_message(message: String) -signal on_tts_sound(sound: AudioStreamMP3) -signal on_error() +const HASS_API = preload ("../hass.gd") +const VoiceHandler = preload ("res://lib/home_apis/voice_handler.gd") var api: HASS_API var pipe_running := false @@ -36,9 +33,6 @@ var tts_sound = null: func _init(hass: HASS_API): self.api = hass -func on_connect(): - pass - func start_wakeword(): if pipe_running: return diff --git a/app/lib/home_apis/hass_ws/hass.gd b/app/lib/home_apis/hass_ws/hass.gd index 91907e9..675010f 100644 --- a/app/lib/home_apis/hass_ws/hass.gd +++ b/app/lib/home_apis/hass_ws/hass.gd @@ -121,7 +121,6 @@ func start_subscriptions(): func handle_connect(): integration_handler.on_connect() - assist_handler.on_connect() connected = true on_connect.emit() @@ -282,4 +281,7 @@ func update_room(room: String): }) if response.status == Promise.Status.RESOLVED: - pass \ No newline at end of file + pass + +func get_voice_assistant(): + return assist_handler diff --git a/app/lib/home_apis/voice_handler.gd b/app/lib/home_apis/voice_handler.gd new file mode 100644 index 0000000..ec999ac --- /dev/null +++ b/app/lib/home_apis/voice_handler.gd @@ -0,0 +1,11 @@ +signal on_wake_word(wake_word: String) +signal on_stt_message(message: String) +signal on_tts_message(message: String) +signal on_tts_sound(sound: AudioStreamMP3) +signal on_error() + +func start_wakeword() -> bool: + return false + +func send_data(data: PackedByteArray) -> void: + pass \ No newline at end of file diff --git a/app/lib/stores/settings.gd b/app/lib/stores/settings.gd index 3c39ec0..84d50fd 100644 --- a/app/lib/stores/settings.gd +++ b/app/lib/stores/settings.gd @@ -11,6 +11,9 @@ var token: String = "" ## If the voice assistant should be enabled var voice_assistant: bool = false +## If the onboarding process has been completed +var onboarding_complete: bool = false + func _init(): _save_path = "user://settings.json" @@ -18,4 +21,5 @@ func clear(): type = "HASS_WS" url = "" token = "" - voice_assistant = false \ No newline at end of file + voice_assistant = false + onboarding_complete = false \ No newline at end of file diff --git a/app/lib/stores/store.gd b/app/lib/stores/store.gd index 0392f15..fb058b3 100644 --- a/app/lib/stores/store.gd +++ b/app/lib/stores/store.gd @@ -77,8 +77,11 @@ func load_local(path=_save_path): var save_file = FileAccess.open(path, FileAccess.READ) + # In case that there is no store file yet if save_file == null: - return false + _loaded = true + on_loaded.emit() + return true var json_text = save_file.get_as_text() var save_data = VariantSerializer.parse_value(JSON.parse_string(json_text)) From d11e63fec870c8f9aacbe8e0344448debd5229ff Mon Sep 17 00:00:00 2001 From: Nitwel Date: Sun, 17 Mar 2024 17:08:16 +0100 Subject: [PATCH 2/2] update text --- app/content/ui/onboarding/onboarding.tscn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/content/ui/onboarding/onboarding.tscn b/app/content/ui/onboarding/onboarding.tscn index 5dcbd29..06dd7a5 100644 --- a/app/content/ui/onboarding/onboarding.tscn +++ b/app/content/ui/onboarding/onboarding.tscn @@ -36,7 +36,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.23, 0.14, 0.006) pixel_size = 0.001 text = "Hey! -This app is still early in development and still has a lot of rough edges. Things might break or are sometimes difficult to use, this will improve the more the app ages. +This app is still early in development and still has a lot of rough edges. Things might break or are sometimes difficult to use, this will improve the more polished the app becomes. If this is the first time you try the app, please first read through the \"Getting Started\" Guide below." font_size = 18