add line_charts
This commit is contained in:
parent
b9dae18042
commit
8ad2fb923d
|
@ -77,33 +77,33 @@ Simple test:
|
||||||
|
|
||||||
```gdscript
|
```gdscript
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
var _time = Time.get_ticks_msec() / 1000.0
|
var _time = Time.get_ticks_msec() / 1000.0
|
||||||
var box_pos = Vector3(0, sin(_time * 4), 0)
|
var box_pos = Vector3(0, sin(_time * 4), 0)
|
||||||
var line_begin = Vector3(-1, sin(_time * 4), 0)
|
var line_begin = Vector3(-1, sin(_time * 4), 0)
|
||||||
var line_end = Vector3(1, cos(_time * 4), 0)
|
var line_end = Vector3(1, cos(_time * 4), 0)
|
||||||
|
|
||||||
DebugDraw3D.draw_box(box_pos, Vector3(1, 2, 1), Color(0, 1, 0))
|
DebugDraw3D.draw_box(box_pos, Vector3(1, 2, 1), Color(0, 1, 0))
|
||||||
DebugDraw3D.draw_line(line_begin, line_end, Color(1, 1, 0))
|
DebugDraw3D.draw_line(line_begin, line_end, Color(1, 1, 0))
|
||||||
DebugDraw2D.set_text("Time", _time)
|
DebugDraw2D.set_text("Time", _time)
|
||||||
DebugDraw2D.set_text("Frames drawn", Engine.get_frames_drawn())
|
DebugDraw2D.set_text("Frames drawn", Engine.get_frames_drawn())
|
||||||
DebugDraw2D.set_text("FPS", Engine.get_frames_per_second())
|
DebugDraw2D.set_text("FPS", Engine.get_frames_per_second())
|
||||||
DebugDraw2D.set_text("delta", delta)
|
DebugDraw2D.set_text("delta", delta)
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public override void _Process(float delta)
|
public override void _Process(float delta)
|
||||||
{
|
{
|
||||||
var _time = Time.GetTicksMsec() / 1000.0f;
|
var _time = Time.GetTicksMsec() / 1000.0f;
|
||||||
var box_pos = new Vector3(0, Mathf.Sin(_time * 4f), 0);
|
var box_pos = new Vector3(0, Mathf.Sin(_time * 4f), 0);
|
||||||
var line_begin = new Vector3(-1, Mathf.Sin(_time * 4f), 0);
|
var line_begin = new Vector3(-1, Mathf.Sin(_time * 4f), 0);
|
||||||
var line_end = new Vector3(1, Mathf.Cos(_time * 4f), 0);
|
var line_end = new Vector3(1, Mathf.Cos(_time * 4f), 0);
|
||||||
|
|
||||||
DebugDraw3D.DrawBox(box_pos, new Vector3(1, 2, 1), new Color(0, 1, 0));
|
DebugDraw3D.DrawBox(box_pos, new Vector3(1, 2, 1), new Color(0, 1, 0));
|
||||||
DebugDraw3D.DrawLine(line_begin, line_end, new Color(1, 1, 0));
|
DebugDraw3D.DrawLine(line_begin, line_end, new Color(1, 1, 0));
|
||||||
DebugDraw2D.SetText("Time", _time);
|
DebugDraw2D.SetText("Time", _time);
|
||||||
DebugDraw2D.SetText("Frames drawn", Engine.GetFramesDrawn());
|
DebugDraw2D.SetText("Frames drawn", Engine.GetFramesDrawn());
|
||||||
DebugDraw2D.SetText("FPS", Engine.GetFramesPerSecond());
|
DebugDraw2D.SetText("FPS", Engine.GetFramesPerSecond());
|
||||||
DebugDraw2D.SetText("delta", delta);
|
DebugDraw2D.SetText("delta", delta);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -117,11 +117,11 @@ A list of all functions is available in the documentation inside the editor.
|
||||||
Besides `DebugDraw2D/3D`, you can also use `Dbg2/3`.
|
Besides `DebugDraw2D/3D`, you can also use `Dbg2/3`.
|
||||||
|
|
||||||
```gdscript
|
```gdscript
|
||||||
DebugDraw3D.draw_box_xf(Transform3D(), Color.GREEN)
|
DebugDraw3D.draw_box_xf(Transform3D(), Color.GREEN)
|
||||||
Dbg3.draw_box_xf(Transform3D(), Color.GREEN)
|
Dbg3.draw_box_xf(Transform3D(), Color.GREEN)
|
||||||
|
|
||||||
DebugDraw2D.set_text("delta", delta)
|
DebugDraw2D.set_text("delta", delta)
|
||||||
Dbg2.set_text("delta", delta)
|
Dbg2.set_text("delta", delta)
|
||||||
```
|
```
|
||||||
|
|
||||||
But unfortunately at the moment `GDExtension` does not support adding documentation.
|
But unfortunately at the moment `GDExtension` does not support adding documentation.
|
||||||
|
|
40
app/content/entities/line_chart/line_chart.gd
Normal file
40
app/content/entities/line_chart/line_chart.gd
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload ("../entity.gd")
|
||||||
|
|
||||||
|
@onready var line_chart = $LineChart
|
||||||
|
@onready var timer = $Timer
|
||||||
|
@onready var label = $Label3D
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
|
label.text = entity_id
|
||||||
|
|
||||||
|
if HomeApi.has_connected() == false:
|
||||||
|
await HomeApi.on_connect
|
||||||
|
request_history()
|
||||||
|
|
||||||
|
timer.timeout.connect(request_history)
|
||||||
|
|
||||||
|
func request_history():
|
||||||
|
# Request history from the server
|
||||||
|
|
||||||
|
var now = Time.get_unix_time_from_datetime_dict(Time.get_datetime_dict_from_system())
|
||||||
|
|
||||||
|
# 2 days ago
|
||||||
|
var two_days_ago = now - 2 * 24 * 60 * 60
|
||||||
|
|
||||||
|
var start = Time.get_datetime_string_from_unix_time(two_days_ago) + ".000Z"
|
||||||
|
|
||||||
|
var result = await HomeApi.get_history(entity_id, start)
|
||||||
|
|
||||||
|
var points = result.data.map(func(point):
|
||||||
|
# Divide by 1000 to convert milliseconds to seconds
|
||||||
|
return Vector2((point.start + point.end) / (2 * 1000), point.mean)
|
||||||
|
)
|
||||||
|
|
||||||
|
line_chart.points.value = points
|
||||||
|
|
||||||
|
func get_interface():
|
||||||
|
return "line_chart"
|
33
app/content/entities/line_chart/line_chart.tscn
Normal file
33
app/content/entities/line_chart/line_chart.tscn
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://cltcs0gokeald"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://content/entities/line_chart/line_chart.gd" id="1_5dxim"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cwvykoymlrrel" path="res://content/ui/components/line_chart/line_chart.tscn" id="2_k4shm"]
|
||||||
|
[ext_resource type="Script" path="res://content/functions/movable.gd" id="3_lidml"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_rmm5v"]
|
||||||
|
size = Vector3(0.5, 0.3, 0.001)
|
||||||
|
|
||||||
|
[node name="LineChart" type="StaticBody3D" groups=["entity"]]
|
||||||
|
collision_layer = 5
|
||||||
|
collision_mask = 0
|
||||||
|
script = ExtResource("1_5dxim")
|
||||||
|
|
||||||
|
[node name="LineChart" parent="." instance=ExtResource("2_k4shm")]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, 0.15, 0)
|
||||||
|
shape = SubResource("BoxShape3D_rmm5v")
|
||||||
|
|
||||||
|
[node name="Movable" type="Node" parent="."]
|
||||||
|
script = ExtResource("3_lidml")
|
||||||
|
|
||||||
|
[node name="Timer" type="Timer" parent="."]
|
||||||
|
wait_time = 60.0
|
||||||
|
autostart = true
|
||||||
|
|
||||||
|
[node name="Label3D" type="Label3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, -0.03, 0)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "sensor.tada"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 4
|
|
@ -8,7 +8,7 @@
|
||||||
[sub_resource type="BoxShape3D" id="BoxShape3D_7mk8w"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_7mk8w"]
|
||||||
size = Vector3(0.0390625, 0.114258, 0.0142822)
|
size = Vector3(0.0390625, 0.114258, 0.0142822)
|
||||||
|
|
||||||
[node name="Number" type="StaticBody3D"]
|
[node name="Number" type="StaticBody3D" groups=["entity"]]
|
||||||
script = ExtResource("1_26xwp")
|
script = ExtResource("1_26xwp")
|
||||||
|
|
||||||
[node name="Slider" parent="." instance=ExtResource("2_sninv")]
|
[node name="Slider" parent="." instance=ExtResource("2_sninv")]
|
||||||
|
|
|
@ -4,9 +4,11 @@ const Entity = preload ("../entity.gd")
|
||||||
|
|
||||||
@onready var label: Label3D = $Label
|
@onready var label: Label3D = $Label
|
||||||
@onready var collision_shape = $CollisionShape3D
|
@onready var collision_shape = $CollisionShape3D
|
||||||
|
@onready var chart_button = $Button
|
||||||
|
|
||||||
var sensor_data = {}
|
var sensor_data = {}
|
||||||
var unit = null
|
var unit = null
|
||||||
|
var is_text = true
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
@ -19,12 +21,30 @@ func _ready():
|
||||||
set_text(new_state)
|
set_text(new_state)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
remove_child(chart_button)
|
||||||
|
|
||||||
|
chart_button.on_button_down.connect(func():
|
||||||
|
House.body.create_entity(entity_id, global_position, "line_chart")
|
||||||
|
remove_child(chart_button)
|
||||||
|
)
|
||||||
|
|
||||||
|
func _on_click(_event):
|
||||||
|
if is_text:
|
||||||
|
return
|
||||||
|
|
||||||
|
if chart_button.is_inside_tree() == false:
|
||||||
|
add_child(chart_button)
|
||||||
|
else:
|
||||||
|
remove_child(chart_button)
|
||||||
|
|
||||||
func set_text(stateInfo):
|
func set_text(stateInfo):
|
||||||
if stateInfo == null:
|
if stateInfo == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
var text = stateInfo["state"]
|
var text = stateInfo["state"]
|
||||||
|
|
||||||
|
is_text = text.is_valid_float() == false&&text.is_valid_int() == false
|
||||||
|
|
||||||
if stateInfo["attributes"]["friendly_name"] != null:
|
if stateInfo["attributes"]["friendly_name"] != null:
|
||||||
text = stateInfo["attributes"]["friendly_name"] + "\n" + text
|
text = stateInfo["attributes"]["friendly_name"] + "\n" + text
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
[gd_scene load_steps=6 format=3 uid="uid://xsiy71rsqulj"]
|
[gd_scene load_steps=7 format=3 uid="uid://xsiy71rsqulj"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/entities/sensor/sensor.gd" id="1_57ac8"]
|
[ext_resource type="Script" path="res://content/entities/sensor/sensor.gd" id="1_57ac8"]
|
||||||
[ext_resource type="FontVariation" uid="uid://d2ofyimg5s65q" path="res://assets/fonts/ui_font_500.tres" id="2_4np3x"]
|
[ext_resource type="FontVariation" uid="uid://d2ofyimg5s65q" path="res://assets/fonts/ui_font_500.tres" id="2_4np3x"]
|
||||||
[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_fpq5q"]
|
[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_fpq5q"]
|
||||||
[ext_resource type="Script" path="res://content/functions/occludable.gd" id="3_l3sp5"]
|
[ext_resource type="Script" path="res://content/functions/occludable.gd" id="3_l3sp5"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="5_bmtkc"]
|
||||||
|
|
||||||
[sub_resource type="BoxShape3D" id="BoxShape3D_phuot"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_phuot"]
|
||||||
resource_local_to_scene = true
|
resource_local_to_scene = true
|
||||||
|
@ -29,3 +30,10 @@ script = ExtResource("2_fpq5q")
|
||||||
|
|
||||||
[node name="Occludable" type="Node" parent="."]
|
[node name="Occludable" type="Node" parent="."]
|
||||||
script = ExtResource("3_l3sp5")
|
script = ExtResource("3_l3sp5")
|
||||||
|
|
||||||
|
[node name="Button" parent="." instance=ExtResource("5_bmtkc")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, -0.1, 0)
|
||||||
|
label = "add_chart"
|
||||||
|
icon = true
|
||||||
|
|
||||||
|
[node name="HoverTimer" type="Timer" parent="."]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=18 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"]
|
||||||
|
@ -12,7 +12,6 @@
|
||||||
[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"]
|
[ext_resource type="PackedScene" uid="uid://bhyddd1f0ry1x" path="res://content/ui/onboarding/onboarding.tscn" id="12_uq2nj"]
|
||||||
[ext_resource type="PackedScene" uid="uid://cwvykoymlrrel" path="res://content/ui/components/line_chart/line_chart.tscn" id="13_iw50x"]
|
|
||||||
|
|
||||||
[sub_resource type="Sky" id="Sky_vhymk"]
|
[sub_resource type="Sky" id="Sky_vhymk"]
|
||||||
sky_material = ExtResource("5_wgwf8")
|
sky_material = ExtResource("5_wgwf8")
|
||||||
|
@ -88,7 +87,4 @@ transform = Transform3D(0.499999, -0.000139169, -6.50204e-05, 5.24307e-05, 0.353
|
||||||
[node name="Onboarding" parent="." instance=ExtResource("12_uq2nj")]
|
[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)
|
transform = Transform3D(0.999999, -1.39632e-11, 0, 9.48097e-12, 0.999999, 0, 0, 0, 0.999999, -0.529594, 0.820154, -0.600147)
|
||||||
|
|
||||||
[node name="LineChart" parent="." instance=ExtResource("13_iw50x")]
|
|
||||||
transform = Transform3D(1, -7.21733e-11, 5.82077e-11, 4.42421e-11, 1, 0, 0, 9.09495e-13, 1, 0.000296143, 1, -4.5899e-06)
|
|
||||||
|
|
||||||
[editable path="XROrigin3D/XRControllerLeft"]
|
[editable path="XROrigin3D/XRControllerLeft"]
|
||||||
|
|
|
@ -38,7 +38,7 @@ func update_house():
|
||||||
for entity_index in range(Store.house.state.entities.size()):
|
for entity_index in range(Store.house.state.entities.size()):
|
||||||
var entity = Store.house.state.entities[entity_index]
|
var entity = Store.house.state.entities[entity_index]
|
||||||
|
|
||||||
var entity_instance = create_entity_in(entity.id, entity.room)
|
var entity_instance = create_entity_in(entity.id, entity.room, entity.get("interface", null))
|
||||||
|
|
||||||
if entity_instance == null:
|
if entity_instance == null:
|
||||||
continue
|
continue
|
||||||
|
@ -163,13 +163,13 @@ func get_level_aabb(level: int):
|
||||||
func get_rooms(level: int):
|
func get_rooms(level: int):
|
||||||
return get_level(level).get_children()
|
return get_level(level).get_children()
|
||||||
|
|
||||||
func create_entity(entity_id: String, entity_position: Vector3):
|
func create_entity(entity_id: String, entity_position: Vector3, type=null):
|
||||||
var room = find_room_at(entity_position)
|
var room = find_room_at(entity_position)
|
||||||
|
|
||||||
if room == null:
|
if room == null:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
var entity = EntityFactory.create_entity(entity_id)
|
var entity = EntityFactory.create_entity(entity_id, type)
|
||||||
|
|
||||||
if entity == null:
|
if entity == null:
|
||||||
return null
|
return null
|
||||||
|
@ -181,13 +181,13 @@ func create_entity(entity_id: String, entity_position: Vector3):
|
||||||
|
|
||||||
return entity
|
return entity
|
||||||
|
|
||||||
func create_entity_in(entity_id: String, room_name: String):
|
func create_entity_in(entity_id: String, room_name: String, type=null):
|
||||||
var room = find_room(room_name)
|
var room = find_room(room_name)
|
||||||
|
|
||||||
if room == null:
|
if room == null:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
var entity = EntityFactory.create_entity(entity_id)
|
var entity = EntityFactory.create_entity(entity_id, type)
|
||||||
|
|
||||||
if entity == null:
|
if entity == null:
|
||||||
return null
|
return null
|
||||||
|
@ -239,9 +239,12 @@ func save_all_entities():
|
||||||
"id": entity.entity_id,
|
"id": entity.entity_id,
|
||||||
"position": entity.global_position,
|
"position": entity.global_position,
|
||||||
"rotation": entity.global_rotation,
|
"rotation": entity.global_rotation,
|
||||||
"room": String(room.name)
|
"room": String(room.name),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entity.has_method("get_interface"):
|
||||||
|
entity_data["interface"] = entity.get_interface()
|
||||||
|
|
||||||
Store.house.state.entities.append(entity_data)
|
Store.house.state.entities.append(entity_data)
|
||||||
|
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
|
|
|
@ -72,7 +72,7 @@ func _ready():
|
||||||
if initial_active:
|
if initial_active:
|
||||||
active = true
|
active = true
|
||||||
|
|
||||||
Update.props(self, ["active", "external_value", "icon", "label", "font_size"])
|
Update.props(self, ["active", "external_value", "icon", "label", "font_size", "disabled"])
|
||||||
|
|
||||||
if echo:
|
if echo:
|
||||||
echo_timer = Timer.new()
|
echo_timer = Timer.new()
|
||||||
|
|
6
app/content/ui/components/line_chart/axis_label.tres
Normal file
6
app/content/ui/components/line_chart/axis_label.tres
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_resource type="LabelSettings" format=3 uid="uid://bc8arfh1k2gd2"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
font_size = 80
|
||||||
|
outline_size = 10
|
||||||
|
outline_color = Color(0, 0, 0, 1)
|
|
@ -4,14 +4,17 @@ extends Node3D
|
||||||
|
|
||||||
@onready var mesh: MeshInstance3D = $Line
|
@onready var mesh: MeshInstance3D = $Line
|
||||||
@onready var plane: MeshInstance3D = $Plane
|
@onready var plane: MeshInstance3D = $Plane
|
||||||
@onready var x_axis: Sprite3D = $XAxis
|
@onready var x_axis: Control = $XAxis/SubViewport/XAxis
|
||||||
|
@onready var y_axis: Control = $YAxis/SubViewport/YAxis
|
||||||
|
|
||||||
const RADIUS = 0.005
|
const WIDTH = 0.001
|
||||||
|
const DEPTH = 0.0005
|
||||||
const STEPS = 5
|
const STEPS = 5
|
||||||
|
|
||||||
@export var size: Vector2 = Vector2(0.5, 0.3)
|
@export var size: Vector2 = Vector2(0.5, 0.3)
|
||||||
|
|
||||||
var points = R.state([])
|
var points = R.state([])
|
||||||
|
var show_dates = R.state(false)
|
||||||
|
|
||||||
var minmax = R.computed(func(_arg):
|
var minmax = R.computed(func(_arg):
|
||||||
if points.value.size() == 0:
|
if points.value.size() == 0:
|
||||||
|
@ -39,13 +42,20 @@ var relative_points = R.computed(func(_arg):
|
||||||
)
|
)
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
for i in range(100):
|
for i in range(50):
|
||||||
points.value.append(Vector2(i, sin(i / 10.0)))
|
points.value.append(Vector2(i, sin(i / 8.0)))
|
||||||
|
|
||||||
R.effect(func(_arg):
|
R.effect(func(_arg):
|
||||||
mesh.mesh=generate_mesh(relative_points.value)
|
mesh.mesh=generate_mesh(relative_points.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
x_axis.minmax=Vector2(minmax.value[0].x, minmax.value[1].x)
|
||||||
|
y_axis.minmax=Vector2(minmax.value[0].y, minmax.value[1].y)
|
||||||
|
x_axis.queue_redraw()
|
||||||
|
y_axis.queue_redraw()
|
||||||
|
)
|
||||||
|
|
||||||
plane.position = Vector3(size.x / 2, size.y / 2, -0.001)
|
plane.position = Vector3(size.x / 2, size.y / 2, -0.001)
|
||||||
plane.mesh.size = size
|
plane.mesh.size = size
|
||||||
|
|
||||||
|
@ -57,33 +67,28 @@ func generate_mesh(points: Array):
|
||||||
|
|
||||||
sf.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
sf.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
||||||
|
|
||||||
for i in range(points.size() - 1):
|
for i in range(points.size()):
|
||||||
|
var prev_point = points[i - 1] if i > 0 else (points[i] + Vector2( - 1, 0))
|
||||||
var point = points[i]
|
var point = points[i]
|
||||||
var next_point = points[i + 1]
|
var next_point = points[i + 1] if i < points.size() - 1 else (points[i] + Vector2(1, 0))
|
||||||
|
|
||||||
var angle = (next_point - point).angle_to(Vector2(1, 0))
|
var prev_angle = (point - prev_point).angle_to(Vector2(1, 0))
|
||||||
|
var next_angle = (next_point - point).angle_to(Vector2(1, 0))
|
||||||
|
|
||||||
|
var angle = (prev_angle + next_angle) / 2.0
|
||||||
|
|
||||||
var up = Vector2(0, 1).rotated( - angle)
|
var up = Vector2(0, 1).rotated( - angle)
|
||||||
|
|
||||||
var p0 = point + up * RADIUS * 2
|
var p0 = (point + up * WIDTH * 2) * size
|
||||||
var p1 = point + up * - RADIUS * 2
|
var p1 = (point + up * - WIDTH * 2) * size
|
||||||
|
|
||||||
sf.add_vertex(Vector3(p0.x * size.x, p0.y * size.y, 0))
|
sf.add_vertex(Vector3(p1.x, p1.y, DEPTH))
|
||||||
sf.add_vertex(Vector3(p1.x * size.x, p1.y * size.y, 0))
|
sf.add_vertex(Vector3(p0.x, p0.y, DEPTH))
|
||||||
|
|
||||||
sf.index()
|
sf.index()
|
||||||
sf.generate_normals()
|
sf.generate_normals()
|
||||||
|
|
||||||
var _mesh = sf.commit()
|
var _mesh = sf.commit()
|
||||||
|
|
||||||
sf.clear()
|
return _mesh
|
||||||
sf.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
||||||
|
|
||||||
for i in range(points.size() - 1):
|
|
||||||
var point = points[i]
|
|
||||||
|
|
||||||
sf.add_vertex(Vector3(point.x * size.x, point.y * size.y, RADIUS))
|
|
||||||
sf.add_vertex(Vector3(point.x * size.x, point.y * size.y, -RADIUS))
|
|
||||||
|
|
||||||
return sf.commit(_mesh)
|
|
||||||
|
|
32
app/content/ui/components/line_chart/line_chart.gdshader
Normal file
32
app/content/ui/components/line_chart/line_chart.gdshader
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
shader_type spatial;
|
||||||
|
|
||||||
|
render_mode cull_disabled, unshaded;
|
||||||
|
|
||||||
|
uniform vec2 steps = vec2(10.0,10.0);
|
||||||
|
uniform vec2 size= vec2(0.002, 0.002);
|
||||||
|
uniform vec2 offset = vec2(0.001, 0.001);
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
// Called for every vertex the material is visible on.
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
ALBEDO = vec3(0.5, 0.5, 0.5);
|
||||||
|
vec2 parts = 1.0 / steps;
|
||||||
|
|
||||||
|
float y_mod = mod(UV.y + offset.y, parts.y);
|
||||||
|
float x_mod = mod(UV.x + offset.x, parts.x);
|
||||||
|
|
||||||
|
if (x_mod < size.x / 2.0 || x_mod > parts.x - size.x / 2.0 || y_mod < size.y || y_mod > parts.y - size.y / 2.0) {
|
||||||
|
|
||||||
|
ALPHA = 0.7;
|
||||||
|
} else {
|
||||||
|
ALPHA = 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//void light() {
|
||||||
|
// Called for every pixel for every light affecting the material.
|
||||||
|
// Uncomment to replace the default light processing function with this one.
|
||||||
|
//}
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,47 @@
|
||||||
@tool
|
@tool
|
||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
func _draw():
|
const axil_label_theme = preload ("./axis_label.tres")
|
||||||
draw_line(Vector2(380, 0), Vector2(380, 3000), Color(1, 1, 1), 10)
|
|
||||||
|
|
||||||
for i in range(100, 3000, 100):
|
const WIDTH = 5000
|
||||||
draw_line(Vector2(350, i), Vector2(380, i), Color(1, 1, 1), 5)
|
const HEIGHT = 400
|
||||||
draw_string_outline(get_theme_default_font(), Vector2(240, i + 15), str(i), HORIZONTAL_ALIGNMENT_RIGHT, 100, 40, 10, Color(0, 0, 0))
|
|
||||||
draw_string(get_theme_default_font(), Vector2(240, i + 15), str(i), HORIZONTAL_ALIGNMENT_RIGHT, 100, 40, Color(1, 1, 1))
|
@onready var axis_labels = $AxisLabels
|
||||||
|
|
||||||
|
var minmax = Vector2(0, 100)
|
||||||
|
var axis_divisions = R.state(10.0)
|
||||||
|
var font_size = 80
|
||||||
|
var border_size = 10
|
||||||
|
var display_dates = true
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
var base_axis = 20
|
||||||
|
var dash_width = 30
|
||||||
|
var text_pos = base_axis + dash_width
|
||||||
|
|
||||||
|
for child in axis_labels.get_children():
|
||||||
|
child.free()
|
||||||
|
|
||||||
|
draw_line(Vector2(0, base_axis), Vector2(WIDTH, base_axis), Color(1, 1, 1), 10)
|
||||||
|
|
||||||
|
var y_steps = WIDTH / axis_divisions.value
|
||||||
|
|
||||||
|
for i in range(y_steps, WIDTH, y_steps):
|
||||||
|
draw_line(Vector2(i, base_axis + dash_width), Vector2(i, base_axis), Color(1, 1, 1), 5)
|
||||||
|
|
||||||
|
var font = get_theme_default_font()
|
||||||
|
|
||||||
|
var relative_i = inverse_lerp(0, WIDTH, i)
|
||||||
|
|
||||||
|
var text = ""
|
||||||
|
if display_dates:
|
||||||
|
text = Time.get_datetime_string_from_unix_time(lerp(minmax.x, minmax.y, relative_i)).substr(11, 5)
|
||||||
|
else:
|
||||||
|
text = str(round(lerp(minmax.x, minmax.y, relative_i) * 100) / 100)
|
||||||
|
|
||||||
|
var label = Label.new()
|
||||||
|
label.text = text
|
||||||
|
label.label_settings = axil_label_theme
|
||||||
|
label.position = Vector2(i, text_pos)
|
||||||
|
|
||||||
|
axis_labels.add_child(label)
|
|
@ -1,8 +1,9 @@
|
||||||
[gd_scene load_steps=2 format=3 uid="uid://bs5wjs1sf67il"]
|
[gd_scene load_steps=3 format=3 uid="uid://bb3shmvedk1oh"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/ui/components/line_chart/x_axis.gd" id="1_5fiu5"]
|
[ext_resource type="Script" path="res://content/ui/components/line_chart/x_axis.gd" id="1_5fiu5"]
|
||||||
|
[ext_resource type="LabelSettings" uid="uid://bc8arfh1k2gd2" path="res://content/ui/components/line_chart/axis_label.tres" id="2_tapys"]
|
||||||
|
|
||||||
[node name="Control" type="Control"]
|
[node name="XAxis" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
@ -13,7 +14,15 @@ script = ExtResource("1_5fiu5")
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="Label" type="Label" parent="."]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_right = 40.0
|
offset_left = 4.0
|
||||||
offset_bottom = 20.0
|
offset_top = 43.0
|
||||||
theme_override_font_sizes/font_size = 50
|
offset_right = 123.0
|
||||||
|
offset_bottom = 93.0
|
||||||
text = "X Axis"
|
text = "X Axis"
|
||||||
|
label_settings = ExtResource("2_tapys")
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="AxisLabels" type="Control" parent="."]
|
||||||
|
anchors_preset = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
|
32
app/content/ui/components/line_chart/y_axis.gd
Normal file
32
app/content/ui/components/line_chart/y_axis.gd
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
const WIDTH = 400
|
||||||
|
const HEIGHT = 3000
|
||||||
|
|
||||||
|
var minmax = Vector2(0, 100)
|
||||||
|
var axis_divisions = R.state(10.0)
|
||||||
|
var font_size = 80
|
||||||
|
var border_size = 10
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
var base_axis = WIDTH - 20
|
||||||
|
var dash_width = -30
|
||||||
|
var text_pos = base_axis + dash_width
|
||||||
|
|
||||||
|
draw_line(Vector2(base_axis, 0), Vector2(base_axis, HEIGHT), Color(1, 1, 1), 10)
|
||||||
|
|
||||||
|
var x_steps = HEIGHT / axis_divisions.value
|
||||||
|
|
||||||
|
for i in range(x_steps, HEIGHT, x_steps):
|
||||||
|
draw_line(Vector2(base_axis + dash_width, i), Vector2(base_axis, i), Color(1, 1, 1), 5)
|
||||||
|
|
||||||
|
var font = get_theme_default_font()
|
||||||
|
|
||||||
|
var relative_i = inverse_lerp(HEIGHT, 0, i)
|
||||||
|
var text = str(round(lerp(minmax.x, minmax.y, relative_i) * 100) / 100)
|
||||||
|
|
||||||
|
var text_size = font.get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size + border_size)
|
||||||
|
|
||||||
|
draw_string_outline(get_theme_default_font(), Vector2(text_pos - text_size.x, i + text_size.y / 4), text, HORIZONTAL_ALIGNMENT_LEFT, text_size.x, font_size, border_size, Color(0, 0, 0))
|
||||||
|
draw_string(get_theme_default_font(), Vector2(text_pos - text_size.x, i + text_size.y / 4), text, HORIZONTAL_ALIGNMENT_LEFT, text_size.x, font_size, Color(1, 1, 1))
|
19
app/content/ui/components/line_chart/y_axis.tscn
Normal file
19
app/content/ui/components/line_chart/y_axis.tscn
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bs5wjs1sf67il"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://content/ui/components/line_chart/y_axis.gd" id="1_e83gd"]
|
||||||
|
|
||||||
|
[node name="YAxis" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_e83gd")
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 20.0
|
||||||
|
theme_override_font_sizes/font_size = 50
|
||||||
|
text = "Y Axis"
|
|
@ -90,8 +90,10 @@ func render():
|
||||||
|
|
||||||
previous_page_button.visible = has_prev_page
|
previous_page_button.visible = has_prev_page
|
||||||
previous_page_button.disabled = !has_prev_page
|
previous_page_button.disabled = !has_prev_page
|
||||||
|
previous_page_button.body.get_node("CollisionShape3D").disabled = !has_prev_page
|
||||||
next_page_button.visible = has_next_page
|
next_page_button.visible = has_next_page
|
||||||
next_page_button.disabled = !has_next_page
|
next_page_button.disabled = !has_next_page
|
||||||
|
next_page_button.body.get_node("CollisionShape3D").disabled = !has_next_page
|
||||||
|
|
||||||
clear_menu()
|
clear_menu()
|
||||||
if selected_device == null:
|
if selected_device == null:
|
||||||
|
|
|
@ -2,15 +2,19 @@ extends Node
|
||||||
|
|
||||||
const console_scene = preload ("res://content/ui/console.tscn")
|
const console_scene = preload ("res://content/ui/console.tscn")
|
||||||
var _console: Node3D = null
|
var _console: Node3D = null
|
||||||
|
@onready var main = get_node_or_null("/root/Main")
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
get_node("/root/Main").tree_entered.connect(func():
|
if main == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
main.tree_entered.connect(func():
|
||||||
init_console()
|
init_console()
|
||||||
)
|
)
|
||||||
|
|
||||||
func init_console():
|
func init_console():
|
||||||
_console = console_scene.instantiate()
|
_console = console_scene.instantiate()
|
||||||
get_node("/root/Main").add_child(_console)
|
main.add_child(_console)
|
||||||
var camera = get_node("/root/Main/XROrigin3D/XRCamera3D")
|
var camera = get_node("/root/Main/XROrigin3D/XRCamera3D")
|
||||||
|
|
||||||
_console.global_position = camera.global_position + camera.global_transform.basis.z * - 1
|
_console.global_position = camera.global_position + camera.global_transform.basis.z * - 1
|
||||||
|
|
|
@ -121,3 +121,11 @@ func get_voice_assistant() -> VoiceAssistant:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return api.get_voice_assistant()
|
return api.get_voice_assistant()
|
||||||
|
|
||||||
|
func get_history(entity_id, start, end=null):
|
||||||
|
assert(has_connected(), "Not connected")
|
||||||
|
|
||||||
|
if api.has_method("get_history") == false:
|
||||||
|
return null
|
||||||
|
|
||||||
|
return await api.get_history(entity_id, start, end)
|
35
app/lib/home_apis/hass_ws/handlers/history.gd
Normal file
35
app/lib/home_apis/hass_ws/handlers/history.gd
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const HASS_API = preload ("../hass.gd")
|
||||||
|
|
||||||
|
var api: HASS_API
|
||||||
|
var integration_exists: bool = false
|
||||||
|
|
||||||
|
func _init(hass: HASS_API):
|
||||||
|
self.api = hass
|
||||||
|
|
||||||
|
func get_history(entity_id: String, start: String, end=null):
|
||||||
|
var meta_response = await api.send_request_packet({
|
||||||
|
"type": "recorder/get_statistics_metadata",
|
||||||
|
"statistic_ids": [
|
||||||
|
entity_id
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
var data_response = await api.send_request_packet({
|
||||||
|
"type": "recorder/statistics_during_period",
|
||||||
|
"start_time": start,
|
||||||
|
"statistic_ids": [
|
||||||
|
entity_id
|
||||||
|
],
|
||||||
|
"period": "5minute",
|
||||||
|
"types": [
|
||||||
|
"state",
|
||||||
|
"mean"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"unit": meta_response.payload.result[0]["display_unit_of_measurement"],
|
||||||
|
"has_mean": meta_response.payload.result[0]["has_mean"],
|
||||||
|
"unit_class": meta_response.payload.result[0]["unit_class"],
|
||||||
|
"data": data_response.payload.result[entity_id]
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ extends Node
|
||||||
const AuthHandler = preload ("./handlers/auth.gd")
|
const AuthHandler = preload ("./handlers/auth.gd")
|
||||||
const IntegrationHandler = preload ("./handlers/integration.gd")
|
const IntegrationHandler = preload ("./handlers/integration.gd")
|
||||||
const AssistHandler = preload ("./handlers/assist.gd")
|
const AssistHandler = preload ("./handlers/assist.gd")
|
||||||
|
const HistoryHandler = preload ("./handlers/history.gd")
|
||||||
|
|
||||||
signal on_connect()
|
signal on_connect()
|
||||||
signal on_disconnect()
|
signal on_disconnect()
|
||||||
|
@ -27,6 +28,7 @@ var packet_callbacks := CallbackMap.new()
|
||||||
var auth_handler: AuthHandler
|
var auth_handler: AuthHandler
|
||||||
var integration_handler: IntegrationHandler
|
var integration_handler: IntegrationHandler
|
||||||
var assist_handler: AssistHandler
|
var assist_handler: AssistHandler
|
||||||
|
var history_handler: HistoryHandler
|
||||||
|
|
||||||
func _init(url:=self.url, token:=self.token):
|
func _init(url:=self.url, token:=self.token):
|
||||||
self.url = url
|
self.url = url
|
||||||
|
@ -35,6 +37,7 @@ func _init(url:=self.url, token:=self.token):
|
||||||
auth_handler = AuthHandler.new(self, url, token)
|
auth_handler = AuthHandler.new(self, url, token)
|
||||||
integration_handler = IntegrationHandler.new(self)
|
integration_handler = IntegrationHandler.new(self)
|
||||||
assist_handler = AssistHandler.new(self)
|
assist_handler = AssistHandler.new(self)
|
||||||
|
history_handler = HistoryHandler.new(self)
|
||||||
|
|
||||||
devices_template = devices_template.replace("\n", " ").replace("\t", "").replace("\r", " ")
|
devices_template = devices_template.replace("\n", " ").replace("\t", "").replace("\r", " ")
|
||||||
connect_ws()
|
connect_ws()
|
||||||
|
@ -285,3 +288,6 @@ func update_room(room: String):
|
||||||
|
|
||||||
func get_voice_assistant():
|
func get_voice_assistant():
|
||||||
return assist_handler
|
return assist_handler
|
||||||
|
|
||||||
|
func get_history(entity_id, start, end=null):
|
||||||
|
return await history_handler.get_history(entity_id, start, end)
|
|
@ -8,15 +8,16 @@ func _init():
|
||||||
|
|
||||||
self.state = R.store({
|
self.state = R.store({
|
||||||
## Type Room
|
## Type Room
|
||||||
## name: String
|
## name: String
|
||||||
## corners: Vec2[]
|
## corners: Vec2[]
|
||||||
## height: float
|
## height: float
|
||||||
"rooms": [],
|
"rooms": [],
|
||||||
## Type Entity
|
## Type Entity
|
||||||
## id: String
|
## id: String
|
||||||
## position: Vec3
|
## position: Vec3
|
||||||
## rotation: Vec3
|
## rotation: Vec3
|
||||||
## room: String
|
## room: String
|
||||||
|
## interface: String
|
||||||
"entities": [],
|
"entities": [],
|
||||||
"align_position1": Vector3(),
|
"align_position1": Vector3(),
|
||||||
"align_position2": Vector3()
|
"align_position2": Vector3()
|
||||||
|
|
|
@ -9,10 +9,13 @@ const MediaPlayer = preload ("res://content/entities/media_player/media_player.t
|
||||||
const Camera = preload ("res://content/entities/camera/camera.tscn")
|
const Camera = preload ("res://content/entities/camera/camera.tscn")
|
||||||
const ButtonEntity = preload ("res://content/entities/button/button.tscn")
|
const ButtonEntity = preload ("res://content/entities/button/button.tscn")
|
||||||
const NumberEntity = preload ("res://content/entities/number/number.tscn")
|
const NumberEntity = preload ("res://content/entities/number/number.tscn")
|
||||||
|
const LineGraphEntity = preload ("res://content/entities/line_chart/line_chart.tscn")
|
||||||
|
|
||||||
static func create_entity(id: String):
|
static func create_entity(id: String, type=null):
|
||||||
var entity = null
|
var entity = null
|
||||||
var type = id.split(".")[0]
|
|
||||||
|
if type == null:
|
||||||
|
type = id.split(".")[0]
|
||||||
|
|
||||||
match type:
|
match type:
|
||||||
"switch":
|
"switch":
|
||||||
|
@ -29,6 +32,8 @@ static func create_entity(id: String):
|
||||||
entity = ButtonEntity.instantiate()
|
entity = ButtonEntity.instantiate()
|
||||||
"number":
|
"number":
|
||||||
entity = NumberEntity.instantiate()
|
entity = NumberEntity.instantiate()
|
||||||
|
"line_chart":
|
||||||
|
entity = LineGraphEntity.instantiate()
|
||||||
_:
|
_:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user