Merge pull request #104 from Nitwel/integration
Add support for Home Assistant Integration
This commit is contained in:
commit
2a1207c513
|
@ -83,7 +83,6 @@ func _ready():
|
|||
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:
|
||||
|
@ -95,7 +94,7 @@ func _input(event):
|
|||
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
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
|
||||
return
|
||||
|
@ -154,10 +153,10 @@ func camera_height(event: InputEventMouseButton):
|
|||
return
|
||||
|
||||
var pos = camera.transform.origin
|
||||
var camera_y = pos.y + (scroll_sensitivity * direction)/20
|
||||
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)
|
||||
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)
|
||||
|
@ -195,8 +194,8 @@ func simulate_buttons(event: InputEventKey, controller: XRController3D):
|
|||
|
||||
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
|
||||
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):
|
||||
|
@ -212,26 +211,26 @@ func attract_controller(event: InputEventMouseButton, controller: XRController3D
|
|||
|
||||
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)
|
||||
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))
|
||||
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)
|
||||
device.rotate_y(motion.x * - device_x_sensitivity / 1000)
|
||||
device.rotate(device.transform.basis.x.normalized(), 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):
|
||||
if Input.is_physical_key_pressed(key_positive_y):
|
||||
y = 1
|
||||
elif Input.is_physical_key_pressed (key_negative_y):
|
||||
elif Input.is_physical_key_pressed(key_negative_y):
|
||||
y = -1
|
||||
|
||||
if Input.is_physical_key_pressed (key_positive_x):
|
||||
if Input.is_physical_key_pressed(key_positive_x):
|
||||
x = 1
|
||||
elif Input.is_physical_key_pressed (key_negative_x):
|
||||
elif Input.is_physical_key_pressed(key_negative_x):
|
||||
x = -1
|
||||
|
||||
var vec = Vector2(x, y)
|
||||
|
|
|
@ -12,11 +12,9 @@ func _ready():
|
|||
EventSystem.on_slow_tick.connect(_slow_tick)
|
||||
|
||||
func _slow_tick(_delta):
|
||||
if player_camera.is_inside_tree() == false:
|
||||
printerr("Player camera is not inside the tree")
|
||||
if player_camera.is_inside_tree() == false||ray.is_inside_tree() == false:
|
||||
return
|
||||
|
||||
ray.target_position = get_parent().to_local(player_camera.global_position)
|
||||
|
||||
get_parent().visible = ray.is_colliding() == false
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extends Node3D
|
||||
|
||||
var sky = preload("res://assets/materials/sky.material")
|
||||
var sky_passthrough = preload("res://assets/materials/sky_passthrough.material")
|
||||
var sky = preload ("res://assets/materials/sky.material")
|
||||
var sky_passthrough = preload ("res://assets/materials/sky_passthrough.material")
|
||||
|
||||
@onready var environment: WorldEnvironment = $WorldEnvironment
|
||||
@onready var camera: XRCamera3D = $XROrigin3D/XRCamera3D
|
||||
|
@ -43,7 +43,7 @@ func _ready():
|
|||
if action.name == "menu_button":
|
||||
toggle_menu()
|
||||
elif action.name == "by_button":
|
||||
House.body.mini_view = !House.body.mini_view
|
||||
House.body.mini_view=!House.body.mini_view
|
||||
)
|
||||
|
||||
EventSystem.on_focus_in.connect(func(event):
|
||||
|
@ -51,7 +51,7 @@ func _ready():
|
|||
return
|
||||
|
||||
add_child(keyboard)
|
||||
keyboard.global_transform = menu.get_node("AnimationContainer/KeyboardPlace").global_transform
|
||||
keyboard.global_transform=menu.get_node("AnimationContainer/KeyboardPlace").global_transform
|
||||
)
|
||||
|
||||
EventSystem.on_focus_out.connect(func(event):
|
||||
|
@ -70,7 +70,7 @@ func toggle_menu():
|
|||
if menu.show_menu == false:
|
||||
remove_child(menu)
|
||||
|
||||
func _emit_action(name: String, value, right_controller: bool = true):
|
||||
func _emit_action(name: String, value, right_controller: bool=true):
|
||||
var event = EventAction.new()
|
||||
event.name = name
|
||||
event.value = value
|
||||
|
@ -128,7 +128,7 @@ func vector_key_mapping(key_positive_x: int, key_negative_x: int, key_positive_y
|
|||
elif Input.is_physical_key_pressed(key_negative_x):
|
||||
x = -1
|
||||
|
||||
var vec = Vector3(x, 0 , y)
|
||||
var vec = Vector3(x, 0, y)
|
||||
|
||||
if vec:
|
||||
vec = vec.normalized()
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[gd_scene load_steps=15 format=3 uid="uid://eecv28y6jxk4"]
|
||||
[gd_scene load_steps=16 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"]
|
||||
[ext_resource type="PackedScene" uid="uid://b30w6tywfj4fp" path="res://content/system/controller_left/controller_left.tscn" id="2_2lraw"]
|
||||
[ext_resource type="PackedScene" uid="uid://d3f8glx1xgm5w" path="res://content/system/raycast/raycast.tscn" id="3_67lii"]
|
||||
[ext_resource type="PackedScene" uid="uid://b2kjh1fpjptdr" path="res://content/system/camera/camera.tscn" id="3_rj4ac"]
|
||||
[ext_resource type="PackedScene" uid="uid://bsx12q23v8apy" path="res://content/system/hands/hands.tscn" id="4_v8xu6"]
|
||||
[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"]
|
||||
|
@ -46,10 +47,7 @@ shadow_enabled = true
|
|||
|
||||
[node name="XROrigin3D" type="XROrigin3D" parent="."]
|
||||
|
||||
[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"]
|
||||
transform = Transform3D(1, 1.8976e-10, 4.07454e-10, 6.76872e-11, 1, 2.08734e-08, -5.82077e-11, 1.09139e-11, 1, 0.0356618, 0.71033, 0.00564247)
|
||||
cull_mask = 524287
|
||||
current = true
|
||||
[node name="XRCamera3D" parent="XROrigin3D" instance=ExtResource("3_rj4ac")]
|
||||
|
||||
[node name="XRControllerLeft" parent="XROrigin3D" instance=ExtResource("2_2lraw")]
|
||||
transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, -0.355145, 0.550439, -0.477945)
|
||||
|
|
18
content/system/camera/camera.gd
Normal file
18
content/system/camera/camera.gd
Normal file
|
@ -0,0 +1,18 @@
|
|||
extends XRCamera3D
|
||||
|
||||
var last_room = null
|
||||
|
||||
func _physics_process(_delta):
|
||||
if HomeApi.api.has_integration():
|
||||
update_room()
|
||||
|
||||
func update_room():
|
||||
var room = House.body.find_room_at(global_position)
|
||||
|
||||
if room != last_room:
|
||||
if room:
|
||||
HomeApi.api.update_room(room.name)
|
||||
last_room = room
|
||||
else:
|
||||
HomeApi.api.update_room("outside")
|
||||
last_room = null
|
6
content/system/camera/camera.tscn
Normal file
6
content/system/camera/camera.tscn
Normal file
|
@ -0,0 +1,6 @@
|
|||
[gd_scene format=3 uid="uid://b2kjh1fpjptdr"]
|
||||
|
||||
[node name="XRCamera3D" type="XRCamera3D"]
|
||||
transform = Transform3D(0.999999, 0.00106283, -0.000420545, -0.00106267, 0.999999, 0.000379457, 0.000420948, -0.000378989, 1, 0.0356617, 0.71033, 0.00564247)
|
||||
cull_mask = 524287
|
||||
current = true
|
|
@ -1,7 +1,7 @@
|
|||
extends Node3D
|
||||
|
||||
const Room = preload("./room/room.tscn")
|
||||
const RoomType = preload("./room/room.gd")
|
||||
const Room = preload ("./room/room.tscn")
|
||||
const RoomType = preload ("./room/room.gd")
|
||||
|
||||
@onready var levels = $Levels
|
||||
@onready var collision_shape = $Levels/CollisionShape3D
|
||||
|
@ -89,7 +89,6 @@ func is_valid_room(room_name):
|
|||
return
|
||||
|
||||
return room.wall_corners.get_child_count() >= 3
|
||||
|
||||
|
||||
func delete_room(room_name):
|
||||
var room = find_room(room_name)
|
||||
|
@ -111,7 +110,7 @@ func delete_room(room_name):
|
|||
Store.house.save_local()
|
||||
|
||||
func is_editiong(room_name):
|
||||
return editing_room != null && editing_room.name == room_name
|
||||
return editing_room != null&&editing_room.name == room_name
|
||||
|
||||
func find_room(room_name):
|
||||
for room in get_rooms(0):
|
||||
|
@ -125,6 +124,22 @@ func find_room_at(entity_position: Vector3):
|
|||
return room
|
||||
return null
|
||||
|
||||
func rename_room(old_room: String, new_name: String):
|
||||
var room = find_room(old_room)
|
||||
|
||||
if room == null:
|
||||
return
|
||||
|
||||
room.name = new_name
|
||||
|
||||
var store_room = Store.house.get_room(old_room)
|
||||
|
||||
if store_room != null:
|
||||
store_room.name = new_name
|
||||
|
||||
save_all_entities()
|
||||
Store.house.save_local()
|
||||
|
||||
func get_level(level: int):
|
||||
return levels.get_child(level)
|
||||
|
||||
|
@ -206,7 +221,6 @@ func update_mini_view():
|
|||
camera_position.y *= 0.5
|
||||
camera_direction.y = 0.0
|
||||
|
||||
|
||||
var target_position = camera_position + camera_direction.normalized() * 0.2
|
||||
levels.global_position = target_position - center * 0.1
|
||||
else:
|
||||
|
|
|
@ -26,6 +26,9 @@ func has_point(point: Vector3) -> bool:
|
|||
func get_aabb():
|
||||
var room_store = Store.house.get_room(name)
|
||||
|
||||
if room_store == null:
|
||||
return AABB()
|
||||
|
||||
var corners = room_store.corners
|
||||
|
||||
if corners.size() == 0:
|
||||
|
@ -119,4 +122,3 @@ static func generate_ceiling_mesh(room_store: Dictionary):
|
|||
var mesh = st.commit()
|
||||
|
||||
return mesh
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ func _ready():
|
|||
func update_animation():
|
||||
var length = animation_player.get_animation("down").length
|
||||
|
||||
if animation_player.current_animation == "":
|
||||
return
|
||||
|
||||
if active&&animation_player.current_animation_position != length:
|
||||
animation_player.play("down")
|
||||
elif !active&&animation_player.current_animation_position != 0:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
extends StaticBody3D
|
||||
class_name Input3D
|
||||
|
||||
var text_handler = preload("res://content/ui/components/input/text_handler.gd").new()
|
||||
var text_handler = preload ("res://content/ui/components/input/text_handler.gd").new()
|
||||
|
||||
@onready var caret: MeshInstance3D = $Label/Caret
|
||||
@onready var mesh_box: MeshInstance3D = $Box
|
||||
|
@ -26,13 +26,27 @@ var text_handler = preload("res://content/ui/components/input/text_handler.gd").
|
|||
get:
|
||||
return text_handler.text
|
||||
set(value):
|
||||
var focused = Engine.is_editor_hint() == false && EventSystem.is_focused(self) == false
|
||||
var focused = Engine.is_editor_hint() == false&&EventSystem.is_focused(self) == false
|
||||
|
||||
if !is_node_ready(): await ready
|
||||
|
||||
text_handler.set_text(value, focused)
|
||||
label.text = text_handler.get_display_text()
|
||||
|
||||
@export var disabled: bool = false:
|
||||
set(value):
|
||||
if !is_node_ready(): await ready
|
||||
|
||||
disabled = value
|
||||
if disabled:
|
||||
label.modulate = Color(0.7, 0.7, 0.7)
|
||||
add_to_group("ui_focus_skip")
|
||||
animation.stop()
|
||||
caret.hide()
|
||||
else:
|
||||
label.modulate = Color(1, 1, 1)
|
||||
remove_from_group("ui_focus_skip")
|
||||
|
||||
var keyboard_input: bool = false
|
||||
|
||||
var input_plane = Plane(Vector3.UP, Vector3.ZERO)
|
||||
|
@ -44,20 +58,23 @@ func _ready():
|
|||
return
|
||||
|
||||
EventSystem.on_key_down.connect(func(event):
|
||||
if EventSystem.is_focused(self) == false:
|
||||
if EventSystem.is_focused(self) == false||disabled:
|
||||
return
|
||||
|
||||
text = EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, text_handler.caret_position)) + text.substr(text_handler.caret_position, text.length())
|
||||
caret.position.x = text_handler.get_caret_position()
|
||||
label.text = text_handler.get_display_text()
|
||||
text=EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, text_handler.caret_position)) + text.substr(text_handler.caret_position, text.length())
|
||||
caret.position.x=text_handler.get_caret_position()
|
||||
label.text=text_handler.get_display_text()
|
||||
)
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventKey && EventSystem.is_focused(self) && event.pressed:
|
||||
if event is InputEventKey&&EventSystem.is_focused(self)&&event.pressed:
|
||||
if event.keycode == KEY_F1:
|
||||
keyboard_input = !keyboard_input
|
||||
return
|
||||
|
||||
if disabled:
|
||||
return
|
||||
|
||||
if keyboard_input:
|
||||
text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, text_handler.caret_position)) + text.substr(text_handler.caret_position, text.length())
|
||||
caret.position.x = text_handler.get_caret_position()
|
||||
|
@ -66,14 +83,20 @@ func _process(_delta):
|
|||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if get_tree().debug_collisions_hint && OS.get_name() != "Android":
|
||||
if get_tree().debug_collisions_hint&&OS.get_name() != "Android":
|
||||
_draw_debug_text_gaps()
|
||||
|
||||
func _on_press_down(event):
|
||||
if disabled:
|
||||
return
|
||||
|
||||
var pos_x = label.to_local(event.ray.get_collision_point()).x
|
||||
text_handler.update_caret_position(pos_x)
|
||||
|
||||
func _on_press_move(event):
|
||||
if disabled:
|
||||
return
|
||||
|
||||
var ray_pos = event.ray.global_position
|
||||
var ray_dir = -event.ray.global_transform.basis.z
|
||||
|
||||
|
@ -92,6 +115,9 @@ func _on_press_move(event):
|
|||
label.text = text_handler.get_display_text()
|
||||
|
||||
func _on_focus_in(_event):
|
||||
if disabled:
|
||||
return
|
||||
|
||||
caret.position.x = text_handler.get_caret_position()
|
||||
label.text = text_handler.get_display_text()
|
||||
caret.show()
|
||||
|
@ -113,9 +139,11 @@ func update_caret_position(event):
|
|||
text_handler.update_caret_position(pos_x)
|
||||
|
||||
caret.position.x = text_handler.get_caret_position()
|
||||
|
||||
|
||||
func _on_focus_out(_event):
|
||||
if disabled:
|
||||
return
|
||||
|
||||
animation.stop()
|
||||
caret.hide()
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@ func set_text(value: String, insert: bool=false):
|
|||
overflow_index = _calculate_overflow_index()
|
||||
focus_caret()
|
||||
|
||||
print(overflow_index, " ", char_offset, " ", caret_position)
|
||||
|
||||
func get_display_text():
|
||||
# In case all chars fit, return the whole text.
|
||||
if overflow_index == - 1:
|
||||
|
|
|
@ -154,4 +154,4 @@ func _on_entity_click(entity_name):
|
|||
func clear_menu():
|
||||
for child in devices_node.get_children():
|
||||
devices_node.remove_child(child)
|
||||
child.queue_free()
|
||||
child.queue_free()
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
extends Node3D
|
||||
|
||||
const Room = preload("res://content/system/house/room/room.tscn")
|
||||
const RoomType = preload("res://content/system/house/room/room.gd")
|
||||
|
||||
const material_selected = preload("../room_selected.tres")
|
||||
const material_unselected = preload("../room_unselected.tres")
|
||||
const Room = preload ("res://content/system/house/room/room.tscn")
|
||||
const RoomType = preload ("res://content/system/house/room/room.gd")
|
||||
|
||||
const material_selected = preload ("../room_selected.tres")
|
||||
const material_unselected = preload ("../room_unselected.tres")
|
||||
|
||||
@onready var room_button = $Button
|
||||
@onready var input = $Input
|
||||
|
@ -13,32 +12,41 @@ const material_unselected = preload("../room_unselected.tres")
|
|||
|
||||
var selected_room = null:
|
||||
set(value):
|
||||
if selected_room != null && value == null:
|
||||
if selected_room != null&&value == null:
|
||||
room_button.label = "add"
|
||||
input.text = "Room %s" % (rooms_map.get_child_count() + 1)
|
||||
|
||||
if selected_room != null:
|
||||
var old_room = get_room(selected_room)
|
||||
if old_room != null:
|
||||
old_room.get_node("MeshInstance3D").material_override = material_unselected
|
||||
var old_room = get_room(selected_room)
|
||||
|
||||
if old_room != null:
|
||||
old_room.get_node("MeshInstance3D").material_override = material_unselected
|
||||
|
||||
if value != null:
|
||||
room_button.label = "edit"
|
||||
input.text = value
|
||||
edit_room = false
|
||||
var new_room = get_room(value)
|
||||
if new_room != null:
|
||||
new_room.get_node("MeshInstance3D").material_override = material_selected
|
||||
|
||||
|
||||
selected_room = value
|
||||
|
||||
var edit_room = false:
|
||||
set(value):
|
||||
if value == edit_room:
|
||||
return
|
||||
|
||||
edit_room = value
|
||||
if value:
|
||||
room_button.label = "save"
|
||||
input.disabled = false
|
||||
else:
|
||||
room_button.label = "edit"
|
||||
input.disabled = true
|
||||
|
||||
if selected_room != null&&selected_room != input.text:
|
||||
House.body.rename_room(selected_room, input.text)
|
||||
selected_room = input.text
|
||||
|
||||
func _ready():
|
||||
if Store.house.is_loaded():
|
||||
|
@ -50,33 +58,36 @@ func _ready():
|
|||
|
||||
room_button.on_button_down.connect(func():
|
||||
if selected_room == null:
|
||||
var room_name = input.text
|
||||
var room_name=input.text
|
||||
if get_room(room_name) != null:
|
||||
EventSystem.notify("Name already taken", EventNotify.Type.WARNING)
|
||||
return
|
||||
|
||||
House.body.create_room(room_name, 0)
|
||||
House.body.edit_room(room_name)
|
||||
selected_room = room_name
|
||||
edit_room = true
|
||||
selected_room=room_name
|
||||
edit_room=true
|
||||
else:
|
||||
if edit_room:
|
||||
edit_room = false
|
||||
edit_room=false
|
||||
|
||||
if !House.body.is_valid_room(selected_room):
|
||||
House.body.delete_room(selected_room)
|
||||
selected_room = null
|
||||
selected_room=null
|
||||
else:
|
||||
House.body.edit_room(null)
|
||||
_generate_room_map()
|
||||
else:
|
||||
edit_room = true
|
||||
edit_room=true
|
||||
House.body.edit_room(selected_room)
|
||||
)
|
||||
|
||||
func get_room(room_name):
|
||||
if rooms_map.has_node("%s" % room_name):
|
||||
return rooms_map.get_node("%s" % room_name)
|
||||
if room_name == null:
|
||||
return null
|
||||
|
||||
if rooms_map.has_node("%s"% room_name):
|
||||
return rooms_map.get_node("%s"% room_name)
|
||||
return null
|
||||
|
||||
func _on_click(event: EventPointer):
|
||||
|
|
|
@ -18,3 +18,4 @@ icon = true
|
|||
[node name="Input" parent="." instance=ExtResource("2_hstw7")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.005, 0.27)
|
||||
text = "Room 1"
|
||||
disabled = true
|
||||
|
|
|
@ -43,6 +43,7 @@ horizontal_alignment = 0
|
|||
[node name="InputURL" parent="Content" instance=ExtResource("4_q3x6k")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.03)
|
||||
text = "ws://192.168.0.1:8123"
|
||||
disabled = null
|
||||
|
||||
[node name="LabelToken" type="Label3D" parent="Content"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.07)
|
||||
|
@ -55,6 +56,7 @@ horizontal_alignment = 0
|
|||
[node name="InputToken" parent="Content" instance=ExtResource("4_q3x6k")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.07)
|
||||
text = "..."
|
||||
disabled = null
|
||||
|
||||
[node name="LabelConnect" type="Label3D" parent="Content"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.14, 0, 0.12)
|
||||
|
|
|
@ -3,6 +3,7 @@ extends Node
|
|||
class_name CallbackMap
|
||||
|
||||
var callbacks := {}
|
||||
var single_callbacks: Array = []
|
||||
|
||||
func add(key: Variant, callback: Callable) -> void:
|
||||
_validate_key(key)
|
||||
|
@ -15,13 +16,9 @@ func add(key: Variant, callback: Callable) -> void:
|
|||
func add_once(key: Variant, callback: Callable) -> void:
|
||||
_validate_key(key)
|
||||
|
||||
var fn: Callable
|
||||
|
||||
fn = func(args: Array):
|
||||
remove(key, fn)
|
||||
callback.callv(args)
|
||||
single_callbacks.append(callback)
|
||||
|
||||
add(key, fn)
|
||||
add(key, callback)
|
||||
|
||||
func remove(key: Variant, callback: Callable) -> void:
|
||||
_validate_key(key)
|
||||
|
@ -29,6 +26,9 @@ func remove(key: Variant, callback: Callable) -> void:
|
|||
if callbacks.has(key):
|
||||
callbacks[key].erase(callback)
|
||||
|
||||
if single_callbacks.has(callback):
|
||||
single_callbacks.erase(callback)
|
||||
|
||||
func call_key(key: Variant, args: Array) -> void:
|
||||
_validate_key(key)
|
||||
|
||||
|
@ -36,5 +36,8 @@ func call_key(key: Variant, args: Array) -> void:
|
|||
for callback in callbacks[key]:
|
||||
callback.callv(args)
|
||||
|
||||
if single_callbacks.has(callback):
|
||||
remove(key, callback)
|
||||
|
||||
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")
|
||||
assert(typeof(key) == TYPE_STRING||typeof(key) == TYPE_INT||typeof(key) == TYPE_FLOAT, "key must be a string or number")
|
||||
|
|
28
lib/home_apis/hass_ws/handlers/auth.gd
Normal file
28
lib/home_apis/hass_ws/handlers/auth.gd
Normal file
|
@ -0,0 +1,28 @@
|
|||
const HASS_API = preload ("../hass.gd")
|
||||
|
||||
signal on_authenticated()
|
||||
|
||||
var api: HASS_API
|
||||
var url: String
|
||||
var token: String
|
||||
|
||||
var authenticated := false
|
||||
|
||||
func _init(hass: HASS_API, url: String, token: String):
|
||||
self.api = hass
|
||||
self.url = url
|
||||
self.token = token
|
||||
|
||||
func handle_message(message):
|
||||
match message["type"]:
|
||||
"auth_required":
|
||||
api.send_packet({"type": "auth", "access_token": self.token})
|
||||
"auth_ok":
|
||||
authenticated = true
|
||||
on_authenticated.emit()
|
||||
"auth_invalid":
|
||||
EventSystem.notify("Failed to authenticate with Home Assistant. Check your token and try again.", EventNotify.Type.DANGER)
|
||||
api.handle_disconnect()
|
||||
|
||||
func on_disconnect():
|
||||
authenticated = false
|
19
lib/home_apis/hass_ws/handlers/integration.gd
Normal file
19
lib/home_apis/hass_ws/handlers/integration.gd
Normal file
|
@ -0,0 +1,19 @@
|
|||
const HASS_API = preload ("../hass.gd")
|
||||
|
||||
var api: HASS_API
|
||||
var integration_exists: bool = false
|
||||
|
||||
func _init(hass: HASS_API):
|
||||
self.api = hass
|
||||
|
||||
func on_connect():
|
||||
var response = await api.send_request_packet({
|
||||
"type": "immersive_home/register",
|
||||
"device_id": OS.get_unique_id(),
|
||||
"name": OS.get_model_name(),
|
||||
"version": OS.get_version(),
|
||||
"platform": OS.get_name(),
|
||||
})
|
||||
|
||||
if response.status == Promise.Status.RESOLVED:
|
||||
integration_exists = true
|
|
@ -1,5 +1,8 @@
|
|||
extends Node
|
||||
|
||||
const AuthHandler = preload ("./handlers/auth.gd")
|
||||
const IntegrationHandler = preload ("./handlers/integration.gd")
|
||||
|
||||
signal on_connect()
|
||||
signal on_disconnect()
|
||||
var connected := false
|
||||
|
@ -13,25 +16,32 @@ var request_timeout := 10.0
|
|||
var url := ""
|
||||
var token := ""
|
||||
|
||||
|
||||
var LOG_MESSAGES := false
|
||||
|
||||
var authenticated := false
|
||||
|
||||
var id := 1
|
||||
var entities: Dictionary = {}
|
||||
var entitiy_callbacks := CallbackMap.new()
|
||||
var packet_callbacks := CallbackMap.new()
|
||||
|
||||
func _init(url := self.url, token := self.token):
|
||||
var auth_handler: AuthHandler
|
||||
var integration_handler: IntegrationHandler
|
||||
|
||||
func _init(url:=self.url, token:=self.token):
|
||||
self.url = url
|
||||
self.token = token
|
||||
|
||||
auth_handler = AuthHandler.new(self, url, token)
|
||||
integration_handler = IntegrationHandler.new(self)
|
||||
|
||||
devices_template = devices_template.replace("\n", " ").replace("\t", "").replace("\r", " ")
|
||||
connect_ws()
|
||||
|
||||
auth_handler.on_authenticated.connect(func():
|
||||
start_subscriptions()
|
||||
)
|
||||
|
||||
func connect_ws():
|
||||
if url == "" || token == "":
|
||||
if url == ""||token == "":
|
||||
return
|
||||
|
||||
print("Connecting to %s" % url + "/api/websocket")
|
||||
|
@ -71,38 +81,12 @@ func _process(delta):
|
|||
func handle_packet(packet: Dictionary):
|
||||
if LOG_MESSAGES: print("Received packet: %s" % str(packet).substr(0, 1000))
|
||||
|
||||
if packet.type == "auth_required":
|
||||
send_packet({
|
||||
"type": "auth",
|
||||
"access_token": self.token
|
||||
})
|
||||
auth_handler.handle_message(packet)
|
||||
|
||||
elif packet.type == "auth_ok":
|
||||
authenticated = true
|
||||
start_subscriptions()
|
||||
|
||||
elif packet.type == "auth_invalid":
|
||||
EventSystem.notify("Failed to authenticate, invalid auth token", EventNotify.Type.DANGER)
|
||||
print("Failed to authenticate, invalid auth token")
|
||||
handle_disconnect()
|
||||
else:
|
||||
if packet.has("id"):
|
||||
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": "subscribe_events",
|
||||
# "event_type": "state_changed"
|
||||
# })
|
||||
|
||||
send_subscribe_packet({
|
||||
"type": "subscribe_entities"
|
||||
}, func(packet: Dictionary):
|
||||
|
@ -111,13 +95,12 @@ func start_subscriptions():
|
|||
|
||||
if packet.event.has("a"):
|
||||
for entity in packet.event.a.keys():
|
||||
entities[entity] = {
|
||||
entities[entity]={
|
||||
"state": packet.event.a[entity]["s"],
|
||||
"attributes": packet.event.a[entity]["a"]
|
||||
}
|
||||
entitiy_callbacks.call_key(entity, [entities[entity]])
|
||||
connected = true
|
||||
on_connect.emit()
|
||||
handle_connect()
|
||||
|
||||
if packet.event.has("c"):
|
||||
for entity in packet.event.c.keys():
|
||||
|
@ -126,14 +109,19 @@ func start_subscriptions():
|
|||
|
||||
if packet.event.c[entity].has("+"):
|
||||
if packet.event.c[entity]["+"].has("s"):
|
||||
entities[entity]["state"] = packet.event.c[entity]["+"]["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"], true)
|
||||
entitiy_callbacks.call_key(entity, [entities[entity]])
|
||||
)
|
||||
|
||||
func handle_connect():
|
||||
integration_handler.on_connect()
|
||||
connected = true
|
||||
on_connect.emit()
|
||||
|
||||
func handle_disconnect():
|
||||
authenticated = false
|
||||
auth_handler.on_disconnect()
|
||||
set_process(false)
|
||||
on_disconnect.emit()
|
||||
|
||||
|
@ -153,18 +141,15 @@ func send_subscribe_packet(packet: Dictionary, callback: Callable):
|
|||
})
|
||||
id += 1
|
||||
|
||||
|
||||
func send_request_packet(packet: Dictionary, ignore_initial := false):
|
||||
func send_request_packet(packet: Dictionary, ignore_initial:=false):
|
||||
packet.id = id
|
||||
id += 1
|
||||
|
||||
send_packet(packet)
|
||||
|
||||
var promise = Promise.new(func(resolve: Callable, reject: Callable):
|
||||
var fn: Callable
|
||||
|
||||
if ignore_initial:
|
||||
fn = func(packet: Dictionary):
|
||||
fn=func(packet: Dictionary):
|
||||
if packet.type == "event":
|
||||
resolve.call(packet)
|
||||
packet_callbacks.remove(packet.id, fn)
|
||||
|
@ -173,7 +158,7 @@ func send_request_packet(packet: Dictionary, ignore_initial := false):
|
|||
else:
|
||||
packet_callbacks.add_once(packet.id, resolve)
|
||||
|
||||
var timeout = Timer.new()
|
||||
var timeout=Timer.new()
|
||||
timeout.set_wait_time(request_timeout)
|
||||
timeout.set_one_shot(true)
|
||||
timeout.timeout.connect(func():
|
||||
|
@ -187,8 +172,9 @@ func send_request_packet(packet: Dictionary, ignore_initial := false):
|
|||
timeout.start()
|
||||
)
|
||||
|
||||
return await promise.settled
|
||||
send_packet(packet)
|
||||
|
||||
return await promise.settled
|
||||
|
||||
func send_packet(packet: Dictionary):
|
||||
if LOG_MESSAGES: print("Sending packet: %s" % encode_packet(packet))
|
||||
|
@ -221,15 +207,13 @@ func get_state(entity: String):
|
|||
return entities[entity]
|
||||
return null
|
||||
|
||||
|
||||
func watch_state(entity: String, callback: Callable):
|
||||
entitiy_callbacks.add(entity, callback)
|
||||
|
||||
return func():
|
||||
entitiy_callbacks.remove(entity, callback)
|
||||
|
||||
|
||||
func set_state(entity: String, state: String, attributes: Dictionary = {}):
|
||||
func set_state(entity: String, state: String, attributes: Dictionary={}):
|
||||
var domain = entity.split(".")[0]
|
||||
var service: String
|
||||
|
||||
|
@ -271,4 +255,15 @@ func set_state(entity: String, state: String, attributes: Dictionary = {}):
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
func has_integration():
|
||||
return integration_handler.integration_exists
|
||||
|
||||
func update_room(room: String):
|
||||
var response = await send_request_packet({
|
||||
"type": "immersive_home/update",
|
||||
"device_id": OS.get_unique_id(),
|
||||
"room": room
|
||||
})
|
||||
|
||||
if response.status == Promise.Status.RESOLVED:
|
||||
pass
|
Loading…
Reference in New Issue
Block a user