diff --git a/assets/design.afdesign b/assets/design.afdesign index 507d4a3..e80316b 100644 --- a/assets/design.afdesign +++ b/assets/design.afdesign @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3782adb5391dac4872890ccd7c7820c6c4eb20d56031598c1f3c4d978438a914 -size 12684397 +oid sha256:5a691fa84af96f6cb9243d817266e0a4064cee5384eb97bf2bf45ce799b859ae +size 16818156 diff --git a/assets/materials/pri-500.material b/assets/materials/pri-500.material index d726374..9b00b4d 100644 --- a/assets/materials/pri-500.material +++ b/assets/materials/pri-500.material @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92f36b94bc49caee6ea06bae49983840ce37d27c0e38309038c20163c8b1c7b4 -size 1035 +oid sha256:130236cec1c40b3b471db1c7099d86383a83b28d79316dd2ac45bc445e4a9bc5 +size 1038 diff --git a/content/system/keyboard/keyboard.tscn b/content/system/keyboard/keyboard.tscn index 86db653..de85526 100644 --- a/content/system/keyboard/keyboard.tscn +++ b/content/system/keyboard/keyboard.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://content/system/keyboard/keyboard.gd" id="1_maojw"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_xdpwr"] -[ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_mx544"] +[ext_resource type="Script" path="res://content/ui/components/grid_container/grid_container.gd" id="3_mx544"] [ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"] [ext_resource type="Material" uid="uid://bnwimm214q67g" path="res://assets/materials/sec-500.material" id="5_8c8rc"] [ext_resource type="Script" path="res://content/functions/occludable.gd" id="6_y4sdl"] diff --git a/content/ui/menu/container3d.gd b/content/ui/components/container/container3d.gd similarity index 100% rename from content/ui/menu/container3d.gd rename to content/ui/components/container/container3d.gd diff --git a/content/ui/menu/grid.gd b/content/ui/components/grid_container/grid_container.gd similarity index 100% rename from content/ui/menu/grid.gd rename to content/ui/components/grid_container/grid_container.gd diff --git a/content/ui/components/notification/notification.gd b/content/ui/components/notification/notification.gd new file mode 100644 index 0000000..5b8e6b6 --- /dev/null +++ b/content/ui/components/notification/notification.gd @@ -0,0 +1,54 @@ +@tool +extends Node3D + +@onready var label: Label3D = $AnimationNode/Text +@onready var icon_label: Label3D = $AnimationNode/Icon +@onready var mesh: MeshInstance3D = $AnimationNode/MeshInstance3D +@onready var collision: CollisionShape3D = $AnimationNode/CollisionShape3D +@onready var animation_player: AnimationPlayer = $AnimationNode/AnimationPlayer +@onready var button = $AnimationNode/Button +@onready var timer = $AnimationNode/Timer + +@export var type: EventNotify.Type = EventNotify.Type.INFO: + set(value): + type = value + if !is_node_ready(): await ready + print(value, " ", _type_to_string(value)) + icon_label.text = _type_to_string(value) +@export var text: String = "": + set(value): + text = value + if !is_node_ready(): await ready + label.text = value + +func _ready(): + button.on_button_down.connect(fade_out) + fade_in() + + timer.timeout.connect(func(): + fade_out() + ) + +func fade_in(): + if !is_node_ready(): await ready + + animation_player.play("fade_in") + +func fade_out(): + animation_player.play_backwards("fade_in") + + await animation_player.animation_finished + queue_free() + +func _type_to_string(type: EventNotify.Type) -> String: + match type: + EventNotify.Type.INFO: + return "info" + EventNotify.Type.SUCCESS: + return "check_circle" + EventNotify.Type.WARNING: + return "error" + EventNotify.Type.DANGER: + return "warning" + _: + return "info" diff --git a/content/ui/components/notification/notification.tscn b/content/ui/components/notification/notification.tscn new file mode 100644 index 0000000..2d4b1a7 --- /dev/null +++ b/content/ui/components/notification/notification.tscn @@ -0,0 +1,202 @@ +[gd_scene load_steps=10 format=3 uid="uid://bqj7qwj5mgd30"] + +[ext_resource type="Script" path="res://content/ui/components/notification/notification.gd" id="1_yw3yb"] +[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/pri-500.material" id="2_5b8oo"] +[ext_resource type="FontVariation" uid="uid://sshfnckriqxn" path="res://assets/icons/icons.tres" id="3_1ljpc"] +[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="4_ocg5j"] + +[sub_resource type="BoxMesh" id="BoxMesh_s37oj"] +size = Vector3(0.25, 0.01, 0.05) + +[sub_resource type="BoxShape3D" id="BoxShape3D_m4d21"] +size = Vector3(0.25, 0.01, 0.05) + +[sub_resource type="Animation" id="Animation_bkual"] +length = 0.001 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:scale:y") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/2/type = "bezier" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:scale:z") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/3/type = "bezier" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath(".:position:x") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/4/type = "bezier" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath(".:position:y") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/5/type = "bezier" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath(".:position:z") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} + +[sub_resource type="Animation" id="Animation_r1tka"] +resource_name = "fade_in" +length = 0.3 +step = 0.01 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(0.01, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:scale:y") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/2/type = "bezier" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:scale:z") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/3/type = "bezier" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath(".:position:x") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-0.13, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/4/type = "bezier" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath(".:position:y") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/5/type = "bezier" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath(".:position:z") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_kbhuj"] +_data = { +"RESET": SubResource("Animation_bkual"), +"fade_in": SubResource("Animation_r1tka") +} + +[node name="Notification" type="Node3D"] +script = ExtResource("1_yw3yb") + +[node name="AnimationNode" type="StaticBody3D" parent="."] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="AnimationNode"] +material_override = ExtResource("2_5b8oo") +mesh = SubResource("BoxMesh_s37oj") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AnimationNode"] +shape = SubResource("BoxShape3D_m4d21") + +[node name="Text" type="Label3D" parent="AnimationNode"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.08, 0.006, 0) +pixel_size = 0.001 +text = "Example Text" +font_size = 10 +outline_size = 0 +horizontal_alignment = 0 +autowrap_mode = 3 +width = 190.0 + +[node name="Icon" type="Label3D" parent="AnimationNode"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.1, 0.006, 0) +pixel_size = 0.001 +text = "check_circle" +font = ExtResource("3_1ljpc") +font_size = 24 +outline_size = 0 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="AnimationNode"] +libraries = { +"": SubResource("AnimationLibrary_kbhuj") +} + +[node name="Button" parent="AnimationNode" instance=ExtResource("4_ocg5j")] +transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0.12, -0.003, -0.02) +label = "close" +icon = true + +[node name="Timer" type="Timer" parent="AnimationNode"] +wait_time = 3.0 +autostart = true diff --git a/content/ui/menu/edit/edit_menu.tscn b/content/ui/menu/edit/edit_menu.tscn index 0c1c898..7be08fb 100644 --- a/content/ui/menu/edit/edit_menu.tscn +++ b/content/ui/menu/edit/edit_menu.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=3 uid="uid://crrb0l3ekuotj"] [ext_resource type="Script" path="res://content/ui/menu/edit/edit_menu.gd" id="1_34cbn"] -[ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_0xvyw"] +[ext_resource type="Script" path="res://content/ui/components/grid_container/grid_container.gd" id="3_0xvyw"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="4_tvimg"] [node name="EditMenu" type="Node3D"] diff --git a/content/ui/menu/menu.gd b/content/ui/menu/menu.gd index 061bb0d..e1b4f5b 100644 --- a/content/ui/menu/menu.gd +++ b/content/ui/menu/menu.gd @@ -1,6 +1,7 @@ extends Node3D const Proxy = preload("res://lib/utils/proxy.gd") +const Notification = preload("res://content/ui/components/notification/notification.tscn") @onready var _controller := XRHelpers.get_xr_controller(self) @@ -17,6 +18,7 @@ const Proxy = preload("res://lib/utils/proxy.gd") @onready var content = $AnimationContainer/Content @onready var nav = $AnimationContainer/Navigation @onready var animation_player = $AnimationPlayer +@onready var notify_place = $AnimationContainer/NotifyPlace var selected_nav = null @@ -34,6 +36,21 @@ func _ready(): _controller.button_pressed.connect(func(button): if button == "by_button": show_menu = !show_menu + ) + + EventSystem.on_notify.connect(func(event: EventNotify): + var notification_node = Notification.instantiate() + notification_node.text = event.message + notification_node.type = event.type + + for child in notify_place.get_children(): + child.position += Vector3(0, 0, -0.06) + + notify_place.add_child(notification_node) + + + + ) var nav_buttons = [ diff --git a/content/ui/menu/menu.tscn b/content/ui/menu/menu.tscn index 2305381..c6e45a3 100644 --- a/content/ui/menu/menu.tscn +++ b/content/ui/menu/menu.tscn @@ -213,6 +213,9 @@ visible = false [node name="SettingsMenu" parent="AnimationContainer/Content" instance=ExtResource("11_7wm6b")] visible = false +[node name="NotifyPlace" type="Marker3D" parent="AnimationContainer"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.2, 0, -0.05) + [node name="ImmersiveHomePanels" type="MeshInstance3D" parent="."] transform = Transform3D(-4.37114e-10, 0, 0.01, 0, 0.01, 0, -0.01, 0, -4.37114e-10, 0.32, 0, -0.0500001) visible = false diff --git a/lib/events/event_notify.gd b/lib/events/event_notify.gd new file mode 100644 index 0000000..991a847 --- /dev/null +++ b/lib/events/event_notify.gd @@ -0,0 +1,12 @@ +extends Event +class_name EventNotify + +enum Type { + INFO, + SUCCESS, + WARNING, + DANGER +} + +var message: String +var type: Type \ No newline at end of file diff --git a/lib/globals/event_system.gd b/lib/globals/event_system.gd index 3d3da8b..af4fc18 100644 --- a/lib/globals/event_system.gd +++ b/lib/globals/event_system.gd @@ -24,6 +24,8 @@ signal on_touch_enter(event: EventTouch) signal on_touch_move(event: EventTouch) signal on_touch_leave(event: EventTouch) +signal on_notify(event: EventNotify) + var _active_node: Node = null func emit(type: String, event: Event): @@ -32,6 +34,12 @@ func emit(type: String, event: Event): else: _root_call(type, event) +func notify(message: String, type := EventNotify.Type.INFO): + var event = EventNotify.new() + event.message = message + event.type = type + emit("notify", event) + func is_focused(node: Node): return _active_node == node diff --git a/lib/home_apis/hass_ws/hass.gd b/lib/home_apis/hass_ws/hass.gd index b66f9ce..863d935 100644 --- a/lib/home_apis/hass_ws/hass.gd +++ b/lib/home_apis/hass_ws/hass.gd @@ -20,8 +20,6 @@ var authenticated := false var id := 1 var entities: Dictionary = {} -var retries := 5 - var entitiy_callbacks := CallbackMap.new() var packet_callbacks := CallbackMap.new() @@ -36,18 +34,13 @@ func connect_ws(): if url == "" || token == "": return - retries -= 1 - if retries < 0: - print("Failed to connect to %s" % self.url) - return - print("Connecting to %s" % self.url) socket.connect_to_url(self.url) set_process(true) # https://github.com/godotengine/godot/issues/84423 # Otherwise the WebSocketPeer will crash when receiving large packets - socket.set_inbound_buffer_size(65535 * 2) + socket.set_inbound_buffer_size(65535 * 4) func _process(delta): socket.poll() @@ -66,7 +59,13 @@ func _process(delta): 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]) + + if reason == "": + reason = "Invalid URL" + + var message = "WS connection closed with code: %s, reason: %s" % [code, reason] + EventSystem.notify(message, EventNotify.Type.DANGER) + print(message) handle_disconnect() func handle_packet(packet: Dictionary): @@ -83,6 +82,8 @@ func handle_packet(packet: Dictionary): 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: packet_callbacks.call_key(int(packet.id), [packet]) @@ -136,9 +137,6 @@ func handle_disconnect(): set_process(false) on_disconnect.emit() - # Reconnect - connect_ws() - func send_subscribe_packet(packet: Dictionary, callback: Callable): packet.id = id id += 1