add onboarding flow
This commit is contained in:
parent
bd5b630c04
commit
ffdb1b607f
|
@ -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="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"]
|
[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://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://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://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"]
|
[sub_resource type="Sky" id="Sky_vhymk"]
|
||||||
sky_material = ExtResource("5_wgwf8")
|
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="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"]
|
[editable path="XROrigin3D/XRControllerLeft"]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
|
const VoiceAssistant = preload ("res://lib/home_apis/voice_handler.gd")
|
||||||
const sample_hold = preload ("res://lib/utils/sample_hold.gd")
|
const sample_hold = preload ("res://lib/utils/sample_hold.gd")
|
||||||
const Chat = preload ("./chat.gd")
|
const Chat = preload ("./chat.gd")
|
||||||
|
|
||||||
|
@ -19,18 +20,27 @@ var effect: AudioEffectCapture
|
||||||
@onready var camera = $"/root/Main/XROrigin3D/XRCamera3D"
|
@onready var camera = $"/root/Main/XROrigin3D/XRCamera3D"
|
||||||
|
|
||||||
var running := false
|
var running := false
|
||||||
|
var voice_assistant: VoiceAssistant
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
var index = AudioServer.get_bus_index("Record")
|
var index = AudioServer.get_bus_index("Record")
|
||||||
effect = AudioServer.get_bus_effect(index, 0)
|
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()
|
finish()
|
||||||
|
|
||||||
audio_timer.timeout.connect(func():
|
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
|
loader.visible=true
|
||||||
chat_user.visible=false
|
chat_user.visible=false
|
||||||
chat_assistant.visible=false
|
chat_assistant.visible=false
|
||||||
|
@ -40,24 +50,24 @@ func _ready():
|
||||||
running=true
|
running=true
|
||||||
)
|
)
|
||||||
|
|
||||||
HomeApi.api.assist_handler.on_stt_message.connect(func(text):
|
voice_assistant.on_stt_message.connect(func(text):
|
||||||
loader.visible=false
|
loader.visible=false
|
||||||
chat_user.visible=true
|
chat_user.visible=true
|
||||||
chat_user.text=text
|
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.visible=true
|
||||||
chat_assistant.text=text
|
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.stream=audio
|
||||||
audio_player_3d.play()
|
audio_player_3d.play()
|
||||||
visual_timer.start()
|
visual_timer.start()
|
||||||
running=false
|
running=false
|
||||||
)
|
)
|
||||||
|
|
||||||
HomeApi.api.assist_handler.on_error.connect(func():
|
voice_assistant.on_error.connect(func():
|
||||||
running=false
|
running=false
|
||||||
finish()
|
finish()
|
||||||
)
|
)
|
||||||
|
@ -101,9 +111,9 @@ func _process(_delta):
|
||||||
|
|
||||||
if max_amplitude > input_threshold:
|
if max_amplitude > input_threshold:
|
||||||
if audio_timer.is_stopped():
|
if audio_timer.is_stopped():
|
||||||
HomeApi.api.assist_handler.start_wakeword()
|
voice_assistant.start_wakeword()
|
||||||
|
|
||||||
audio_timer.start()
|
audio_timer.start()
|
||||||
|
|
||||||
if audio_timer.is_stopped() == false:
|
if audio_timer.is_stopped() == false:
|
||||||
HomeApi.api.assist_handler.send_data(data)
|
voice_assistant.send_data(data)
|
||||||
|
|
|
@ -3,7 +3,7 @@ extends XRCamera3D
|
||||||
var last_room = null
|
var last_room = null
|
||||||
|
|
||||||
func _physics_process(_delta):
|
func _physics_process(_delta):
|
||||||
if HomeApi.api.has_integration():
|
if HomeApi.has_integration():
|
||||||
update_room()
|
update_room()
|
||||||
|
|
||||||
func update_room():
|
func update_room():
|
||||||
|
@ -11,8 +11,8 @@ func update_room():
|
||||||
|
|
||||||
if room != last_room:
|
if room != last_room:
|
||||||
if room:
|
if room:
|
||||||
HomeApi.api.update_room(room.name)
|
HomeApi.update_room(room.name)
|
||||||
last_room = room
|
last_room = room
|
||||||
else:
|
else:
|
||||||
HomeApi.api.update_room("outside")
|
HomeApi.update_room("outside")
|
||||||
last_room = null
|
last_room = null
|
|
@ -5,8 +5,8 @@ extends StaticBody3D
|
||||||
var max_message = 30
|
var max_message = 30
|
||||||
var messages: Array = ["aaa", "bbb"]
|
var messages: Array = ["aaa", "bbb"]
|
||||||
|
|
||||||
func log(text: String) -> void:
|
func log(text: Variant) -> void:
|
||||||
messages.push_front(text)
|
messages.push_front(str(text))
|
||||||
|
|
||||||
if messages.size() > max_message:
|
if messages.size() > max_message:
|
||||||
messages.pop_back()
|
messages.pop_back()
|
||||||
|
|
43
app/content/ui/onboarding/onboarding.gd
Normal file
43
app/content/ui/onboarding/onboarding.gd
Normal file
|
@ -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)
|
68
app/content/ui/onboarding/onboarding.tscn
Normal file
68
app/content/ui/onboarding/onboarding.tscn
Normal file
|
@ -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
|
|
@ -3,6 +3,7 @@ extends Node
|
||||||
|
|
||||||
const Hass = preload ("res://lib/home_apis/hass/hass.gd")
|
const Hass = preload ("res://lib/home_apis/hass/hass.gd")
|
||||||
const HassWebSocket = preload ("res://lib/home_apis/hass_ws/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 = {
|
const apis = {
|
||||||
"hass": Hass,
|
"hass": Hass,
|
||||||
|
@ -96,7 +97,26 @@ func watch_state(entity: String, callback: Callable):
|
||||||
assert(has_connected(), "Not connected")
|
assert(has_connected(), "Not connected")
|
||||||
return api.watch_state(entity, callback)
|
return api.watch_state(entity, callback)
|
||||||
|
|
||||||
func _notification(what):
|
## Returns true if the adapter has an integration in the home automation system
|
||||||
if what == NOTIFICATION_WM_CLOSE_REQUEST||what == NOTIFICATION_WM_GO_BACK_REQUEST:
|
## allowing to send the room position of the headset.
|
||||||
# Store.house.save_local()
|
func has_integration() -> bool:
|
||||||
pass
|
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()
|
|
@ -5,6 +5,6 @@ const SettingsStore = preload ("res://lib/stores/settings.gd")
|
||||||
const HouseStore = preload ("res://lib/stores/house.gd")
|
const HouseStore = preload ("res://lib/stores/house.gd")
|
||||||
const DevicesStore = preload ("res://lib/stores/devices.gd")
|
const DevicesStore = preload ("res://lib/stores/devices.gd")
|
||||||
|
|
||||||
var settings = SettingsStore.new()
|
var settings := SettingsStore.new()
|
||||||
var house = HouseStore.new()
|
var house := HouseStore.new()
|
||||||
var devices = DevicesStore.new()
|
var devices := DevicesStore.new()
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
const HASS_API = preload ("../hass.gd")
|
extends VoiceHandler
|
||||||
|
|
||||||
signal on_wake_word(wake_word: String)
|
const HASS_API = preload ("../hass.gd")
|
||||||
signal on_stt_message(message: String)
|
const VoiceHandler = preload ("res://lib/home_apis/voice_handler.gd")
|
||||||
signal on_tts_message(message: String)
|
|
||||||
signal on_tts_sound(sound: AudioStreamMP3)
|
|
||||||
signal on_error()
|
|
||||||
|
|
||||||
var api: HASS_API
|
var api: HASS_API
|
||||||
var pipe_running := false
|
var pipe_running := false
|
||||||
|
@ -36,9 +33,6 @@ var tts_sound = null:
|
||||||
func _init(hass: HASS_API):
|
func _init(hass: HASS_API):
|
||||||
self.api = hass
|
self.api = hass
|
||||||
|
|
||||||
func on_connect():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func start_wakeword():
|
func start_wakeword():
|
||||||
if pipe_running:
|
if pipe_running:
|
||||||
return
|
return
|
||||||
|
|
|
@ -121,7 +121,6 @@ func start_subscriptions():
|
||||||
|
|
||||||
func handle_connect():
|
func handle_connect():
|
||||||
integration_handler.on_connect()
|
integration_handler.on_connect()
|
||||||
assist_handler.on_connect()
|
|
||||||
connected = true
|
connected = true
|
||||||
on_connect.emit()
|
on_connect.emit()
|
||||||
|
|
||||||
|
@ -282,4 +281,7 @@ func update_room(room: String):
|
||||||
})
|
})
|
||||||
|
|
||||||
if response.status == Promise.Status.RESOLVED:
|
if response.status == Promise.Status.RESOLVED:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
func get_voice_assistant():
|
||||||
|
return assist_handler
|
||||||
|
|
11
app/lib/home_apis/voice_handler.gd
Normal file
11
app/lib/home_apis/voice_handler.gd
Normal file
|
@ -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
|
|
@ -11,6 +11,9 @@ var token: String = ""
|
||||||
## If the voice assistant should be enabled
|
## If the voice assistant should be enabled
|
||||||
var voice_assistant: bool = false
|
var voice_assistant: bool = false
|
||||||
|
|
||||||
|
## If the onboarding process has been completed
|
||||||
|
var onboarding_complete: bool = false
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
_save_path = "user://settings.json"
|
_save_path = "user://settings.json"
|
||||||
|
|
||||||
|
@ -18,4 +21,5 @@ func clear():
|
||||||
type = "HASS_WS"
|
type = "HASS_WS"
|
||||||
url = ""
|
url = ""
|
||||||
token = ""
|
token = ""
|
||||||
voice_assistant = false
|
voice_assistant = false
|
||||||
|
onboarding_complete = false
|
|
@ -77,8 +77,11 @@ func load_local(path=_save_path):
|
||||||
|
|
||||||
var save_file = FileAccess.open(path, FileAccess.READ)
|
var save_file = FileAccess.open(path, FileAccess.READ)
|
||||||
|
|
||||||
|
# In case that there is no store file yet
|
||||||
if save_file == null:
|
if save_file == null:
|
||||||
return false
|
_loaded = true
|
||||||
|
on_loaded.emit()
|
||||||
|
return true
|
||||||
|
|
||||||
var json_text = save_file.get_as_text()
|
var json_text = save_file.get_as_text()
|
||||||
var save_data = VariantSerializer.parse_value(JSON.parse_string(json_text))
|
var save_data = VariantSerializer.parse_value(JSON.parse_string(json_text))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user