diff --git a/app/content/entities/timer/timer.gd b/app/content/entities/timer/timer.gd new file mode 100644 index 0000000..416d194 --- /dev/null +++ b/app/content/entities/timer/timer.gd @@ -0,0 +1,96 @@ +extends Entity + +const Entity = preload ("../entity.gd") + +@onready var time_label: Label3D = $TimerLabel +@onready var name_label: Label3D = $NameLabel +@onready var icon_label: Label3D = $IconLabel + +@onready var play_button: Button3D = $PlayButton +@onready var stop_button: Button3D = $StopButton + +var duration = "00:00:00" +var finishes_at = null +var running = false + +# Called when the node enters the scene tree for the first time. +func _ready(): + super() + var stateInfo = await HomeApi.get_state(entity_id) + + set_state(stateInfo) + + await HomeApi.watch_state(entity_id, func(new_state): + set_state(new_state) + ) + + play_button.on_button_up.connect(func(): + if running == false: + HomeApi.set_state(entity_id, "start") + else: + HomeApi.set_state(entity_id, "pause") + ) + + stop_button.on_button_up.connect(func(): + HomeApi.set_state(entity_id, "cancel") + ) + + EventSystem.on_slow_tick.connect(_slow_tick) + +func _slow_tick(_delta: float): + if running == false: + return + + if finishes_at != null: + var time_left = finishes_at - round(Time.get_unix_time_from_system()) + if time_left < 0: + time_left = 0 + + var time_dict = Time.get_time_dict_from_unix_time(time_left) + time_label.text = "%01d:%02d:%02d" % [time_dict["hour"],time_dict["minute"],time_dict["second"]] + +func set_state(stateInfo): + if stateInfo == null: + return + + print("stateInfo: ", stateInfo) + + var state = stateInfo["state"] + var attributes = stateInfo["attributes"] + + # if attributes.has("icon")&&attributes["icon"] != null&&attributes["icon"].begins_with("mdi:"): + # icon_label.text = attributes["icon"].split(":")[1] + # else: + # icon_label.text = "timer" + + if attributes.has("friendly_name")&&attributes["friendly_name"] != null: + name_label.text = attributes["friendly_name"] + else: + name_label.text = "Timer" + + if attributes.has("duration")&&attributes["duration"] != null: + duration = attributes["duration"] + else: + duration = "00:00:00" + + stop_button.visible = state == "active"||state == "paused" + stop_button.disabled = state == "idle" + + match state: + "idle": + running = false + finishes_at = null + time_label.text = duration + play_button.position.x = 0 + play_button.label = "play_arrow" + "paused": + running = false + play_button.position.x = -0.03 + play_button.label = "resume" + "active": + if attributes.has("finishes_at")&&attributes["finishes_at"] != null: + finishes_at = Time.get_unix_time_from_datetime_string(attributes["finishes_at"]) + running = true + + play_button.position.x = -0.03 + play_button.label = "pause" \ No newline at end of file diff --git a/app/content/entities/timer/timer.tscn b/app/content/entities/timer/timer.tscn new file mode 100644 index 0000000..2ba65d8 --- /dev/null +++ b/app/content/entities/timer/timer.tscn @@ -0,0 +1,83 @@ +[gd_scene load_steps=10 format=3 uid="uid://chxdrb8qenypb"] + +[ext_resource type="Script" path="res://content/entities/timer/timer.gd" id="1_bomjl"] +[ext_resource type="FontVariation" uid="uid://sshfnckriqxn" path="res://assets/icons/icons.tres" id="2_ug1vg"] +[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_x0bn6"] +[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="3_v7356"] +[ext_resource type="PackedScene" uid="uid://dnam3fe36gg62" path="res://content/ui/components/panel/panel.tscn" id="5_j3gsb"] +[ext_resource type="Shader" path="res://content/ui/components/panel/glass.gdshader" id="6_40cd1"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_3qyo4"] +size = Vector3(0.32, 0.16, 0.02) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_71h4x"] +resource_local_to_scene = true +render_priority = 10 +shader = ExtResource("6_40cd1") +shader_parameter/color = Color(1, 1, 1, 0.3) +shader_parameter/border_color = Color(1, 1, 1, 1) +shader_parameter/edge_color = Color(0, 0, 0, 1) +shader_parameter/size = Vector2(8, 4) +shader_parameter/border_size = 0.01 +shader_parameter/border_fade_in = 0.05 +shader_parameter/border_fade_out = 0.0 +shader_parameter/corner_radius = 0.2 +shader_parameter/roughness = 0.3 +shader_parameter/grain_amount = 0.02 + +[sub_resource type="QuadMesh" id="QuadMesh_3qjpe"] +size = Vector2(0.32, 0.16) + +[node name="Timer" type="StaticBody3D" groups=["entity"]] +script = ExtResource("1_bomjl") + +[node name="TimerLabel" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.01, 0) +pixel_size = 0.001 +render_priority = 15 +outline_render_priority = 14 +text = "10:00:00" +font_size = 70 +outline_size = 0 + +[node name="NameLabel" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.12, 0.06, 0) +pixel_size = 0.001 +render_priority = 15 +outline_render_priority = 14 +text = "Timer" +font_size = 28 +outline_size = 0 +horizontal_alignment = 0 + +[node name="IconLabel" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.14, 0.06, 0) +pixel_size = 0.001 +render_priority = 15 +outline_render_priority = 14 +text = "timer" +font = ExtResource("2_ug1vg") +font_size = 28 +outline_size = 0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.01) +shape = SubResource("BoxShape3D_3qyo4") + +[node name="Movable" type="Node" parent="."] +script = ExtResource("2_x0bn6") + +[node name="PlayButton" parent="." instance=ExtResource("3_v7356")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0) +label = "play_arrow" +icon = true + +[node name="StopButton" parent="." instance=ExtResource("3_v7356")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.03, -0.05, 0) +label = "stop" +icon = true + +[node name="Panel" parent="." instance=ExtResource("5_j3gsb")] +material_override = SubResource("ShaderMaterial_71h4x") +mesh = SubResource("QuadMesh_3qjpe") +size = Vector2(0.32, 0.16) diff --git a/app/lib/home_apis/hass_ws/hass.gd b/app/lib/home_apis/hass_ws/hass.gd index b3c23d2..cb2e26e 100644 --- a/app/lib/home_apis/hass_ws/hass.gd +++ b/app/lib/home_apis/hass_ws/hass.gd @@ -259,6 +259,13 @@ func set_state(entity: String, state: Variant, attributes: Dictionary={}): elif domain == 'number': service = 'set_value' attributes["value"] = state + elif domain == 'timer': + if state == 'start': + service = 'start' + elif state == 'cancel': + service = 'cancel' + elif state == 'pause': + service = 'pause' if service == null: return null diff --git a/app/lib/utils/entity_factory.gd b/app/lib/utils/entity_factory.gd index e1aa5dc..b8f4339 100644 --- a/app/lib/utils/entity_factory.gd +++ b/app/lib/utils/entity_factory.gd @@ -10,6 +10,7 @@ const Camera = preload ("res://content/entities/camera/camera.tscn") const ButtonEntity = preload ("res://content/entities/button/button.tscn") const NumberEntity = preload ("res://content/entities/number/number.tscn") const LineGraphEntity = preload ("res://content/entities/line_chart/line_chart.tscn") +const TimerEntity = preload ("res://content/entities/timer/timer.tscn") static func create_entity(id: String, type=null): var entity = null @@ -34,6 +35,8 @@ static func create_entity(id: String, type=null): entity = NumberEntity.instantiate() "line_chart": entity = LineGraphEntity.instantiate() + "timer": + entity = TimerEntity.instantiate() _: return null @@ -58,5 +61,7 @@ static func get_entity_icon(type: String) -> String: return "sliders" "line_chart": return "finance" + "timer": + return "timer" _: return "question_mark" \ No newline at end of file