clean up light and switch

This commit is contained in:
Nitwel 2024-04-18 12:35:57 +02:00
parent 90cfe87ae8
commit 8d1a380977
4 changed files with 183 additions and 199 deletions

View File

@ -4,9 +4,9 @@ class_name RdotGraph
static var instance: RdotGraph = null static var instance: RdotGraph = null
static func getInstance() -> RdotGraph: static func getInstance() -> RdotGraph:
if instance == null: if instance == null:
instance = RdotGraph.new() instance = RdotGraph.new()
return instance return instance
var activeConsumer: RdotNode = null var activeConsumer: RdotNode = null
var inNotificationPhase := false var inNotificationPhase := false
@ -17,209 +17,209 @@ var postSignalSetFn := Callable()
var watcherPending := false var watcherPending := false
var watcher = R.Watcher.new(func(_arg): var watcher = R.Watcher.new(func(_arg):
if watcherPending: if watcherPending:
return return
watcherPending=true watcherPending=true
var endOfFrame=func(): var endOfFrame=func():
watcherPending=false watcherPending=false
for s in watcher.getPending(): for s in watcher.getPending():
s.do_get() s.do_get()
watcher.watch() watcher.watch()
endOfFrame.call_deferred() endOfFrame.call_deferred()
) )
func setActiveConsumer(consumer: RdotNode) -> RdotNode: func setActiveConsumer(consumer: RdotNode) -> RdotNode:
var prev = activeConsumer var prev = activeConsumer
activeConsumer = consumer activeConsumer = consumer
return prev return prev
func getActiveConsumer() -> RdotNode: func getActiveConsumer() -> RdotNode:
return activeConsumer return activeConsumer
func isInNotificationPhase() -> bool: func isInNotificationPhase() -> bool:
return inNotificationPhase return inNotificationPhase
func producerAccessed(node: RdotNode): func producerAccessed(node: RdotNode):
assert(inNotificationPhase == false, "Signal read during notification phase") assert(inNotificationPhase == false, "Signal read during notification phase")
if activeConsumer == null: if activeConsumer == null:
return return
if activeConsumer.consumerOnSignalRead.is_null() == false: if activeConsumer.consumerOnSignalRead.is_null() == false:
activeConsumer.consumerOnSignalRead.call(node) activeConsumer.consumerOnSignalRead.call(node)
var idx = activeConsumer.nextProducerIndex; var idx = activeConsumer.nextProducerIndex;
activeConsumer.nextProducerIndex += 1 activeConsumer.nextProducerIndex += 1
assertConsumerNode(activeConsumer) assertConsumerNode(activeConsumer)
if idx < activeConsumer.producerNode.size()&&activeConsumer.producerNode[idx] != node: if idx < activeConsumer.producerNode.size()&&activeConsumer.producerNode[idx] != node:
if consumerIsLive(activeConsumer): if consumerIsLive(activeConsumer):
var staleProducer = activeConsumer.producerNode[idx] var staleProducer = activeConsumer.producerNode[idx]
producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]) producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx])
if RdotArray.do_get(activeConsumer.producerNode, idx) != node: if RdotArray.do_get(activeConsumer.producerNode, idx) != node:
RdotArray.do_set(activeConsumer.producerNode, idx, node) RdotArray.do_set(activeConsumer.producerNode, idx, node)
RdotArray.do_set(activeConsumer.producerIndexOfThis, idx, producerAddLiveConsumer(node, activeConsumer, idx) if consumerIsLive(activeConsumer) else 0) RdotArray.do_set(activeConsumer.producerIndexOfThis, idx, producerAddLiveConsumer(node, activeConsumer, idx) if consumerIsLive(activeConsumer) else 0)
RdotArray.do_set(activeConsumer.producerLastReadVersion, idx, node.version) RdotArray.do_set(activeConsumer.producerLastReadVersion, idx, node.version)
func producerIncrementEpoch(): func producerIncrementEpoch():
epoch += 1 epoch += 1
func producerUpdateValueVersion(node: RdotNode): func producerUpdateValueVersion(node: RdotNode):
if consumerIsLive(node)&&!node.dirty: if consumerIsLive(node)&&!node.dirty:
return return
if !node.dirty&&node.lastCleanEpoch == epoch: if !node.dirty&&node.lastCleanEpoch == epoch:
return return
if !node.producerMustRecompute(node)&&!consumerPollProducersForChange(node): if !node.producerMustRecompute(node)&&!consumerPollProducersForChange(node):
node.dirty = false; node.dirty = false;
node.lastCleanEpoch = epoch node.lastCleanEpoch = epoch
return return
if node.producerRecomputeValue.is_null() == false: if node.producerRecomputeValue.is_null() == false:
node.producerRecomputeValue.call(node) node.producerRecomputeValue.call(node)
node.dirty = false node.dirty = false
node.lastCleanEpoch = epoch node.lastCleanEpoch = epoch
func producerNotifyConsumers(node: RdotNode): func producerNotifyConsumers(node: RdotNode):
if node.liveConsumerNode == null: if node.liveConsumerNode == null:
return return
var prev = inNotificationPhase var prev = inNotificationPhase
inNotificationPhase = true inNotificationPhase = true
for consumer in node.liveConsumerNode: for consumer in node.liveConsumerNode:
if !consumer.dirty: if !consumer.dirty:
consumerMarkDirty(consumer) consumerMarkDirty(consumer)
inNotificationPhase = prev inNotificationPhase = prev
func producerUpdatesAllowed() -> bool: func producerUpdatesAllowed() -> bool:
return activeConsumer == null||activeConsumer.consumerAllowSignalWrites != false return activeConsumer == null||activeConsumer.consumerAllowSignalWrites != false
func consumerMarkDirty(node: RdotNode): func consumerMarkDirty(node: RdotNode):
node.dirty = true node.dirty = true
producerNotifyConsumers(node) producerNotifyConsumers(node)
if node.consumerMarkedDirty.is_null() == false: if node.consumerMarkedDirty.is_null() == false:
node.consumerMarkedDirty.call(node) node.consumerMarkedDirty.call(node)
func consumerBeforeComputation(node: RdotNode) -> RdotNode: func consumerBeforeComputation(node: RdotNode) -> RdotNode:
if node: if node:
node.nextProducerIndex = 0 node.nextProducerIndex = 0
return setActiveConsumer(node) return setActiveConsumer(node)
func consumerAfterComputation(node: RdotNode, prevConsumer: RdotNode): func consumerAfterComputation(node: RdotNode, prevConsumer: RdotNode):
setActiveConsumer(prevConsumer) setActiveConsumer(prevConsumer)
if node == null||node.producerNode == null||node.producerIndexOfThis == null||node.producerLastReadVersion == null: if node == null||node.producerNode == null||node.producerIndexOfThis == null||node.producerLastReadVersion == null:
return return
if consumerIsLive(node): if consumerIsLive(node):
for i in range(node.nextProducerIndex, node.producerNode.size()): for i in range(node.nextProducerIndex, node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]) producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
while node.producerNode.size() > node.nextProducerIndex: while node.producerNode.size() > node.nextProducerIndex:
node.producerNode.pop_back() node.producerNode.pop_back()
node.producerLastReadVersion.pop_back() node.producerLastReadVersion.pop_back()
node.producerIndexOfThis.pop_back() node.producerIndexOfThis.pop_back()
func consumerPollProducersForChange(node: RdotNode) -> bool: func consumerPollProducersForChange(node: RdotNode) -> bool:
assertConsumerNode(node) assertConsumerNode(node)
for i in range(node.producerNode.size()): for i in range(node.producerNode.size()):
var producer = node.producerNode[i] var producer = node.producerNode[i]
var seenVersion = node.producerLastReadVersion[i] var seenVersion = node.producerLastReadVersion[i]
if seenVersion != producer.version: if seenVersion != producer.version:
return true return true
producerUpdateValueVersion(producer) producerUpdateValueVersion(producer)
if seenVersion != producer.version: if seenVersion != producer.version:
return true return true
return false return false
func consumerDestroy(node: RdotNode): func consumerDestroy(node: RdotNode):
assertConsumerNode(node) assertConsumerNode(node)
if consumerIsLive(node): if consumerIsLive(node):
for i in range(node.producerNode.size()): for i in range(node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]) producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
node.producerNode.clear() node.producerNode.clear()
node.producerLastReadVersion.clear() node.producerLastReadVersion.clear()
node.producerIndexOfThis.clear() node.producerIndexOfThis.clear()
if node.liveConsumerNode: if node.liveConsumerNode:
node.liveConsumerNode.clear() node.liveConsumerNode.clear()
node.liveConsumerIndexOfThis.clear() node.liveConsumerIndexOfThis.clear()
static func producerAddLiveConsumer(node: RdotNode, consumer: RdotNode, indexOfThis: int) -> int: static func producerAddLiveConsumer(node: RdotNode, consumer: RdotNode, indexOfThis: int) -> int:
assertProducerNode(node) assertProducerNode(node)
assertConsumerNode(node) assertConsumerNode(node)
if node.liveConsumerNode.size() == 0: if node.liveConsumerNode.size() == 0:
if node.watched.is_null() == false: if node.watched.is_null() == false:
node.watched.call(node.wrapper) node.watched.call(node.wrapper)
for i in range(node.producerNode.size()): for i in range(node.producerNode.size()):
node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i) node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i)
node.liveConsumerIndexOfThis.push_back(indexOfThis) node.liveConsumerIndexOfThis.push_back(indexOfThis)
node.liveConsumerNode.push_back(consumer) node.liveConsumerNode.push_back(consumer)
return node.liveConsumerNode.size() - 1 return node.liveConsumerNode.size() - 1
static func producerRemoveLiveConsumerAtIndex(node: RdotNode, idx: int): static func producerRemoveLiveConsumerAtIndex(node: RdotNode, idx: int):
assertProducerNode(node) assertProducerNode(node)
assertConsumerNode(node) assertConsumerNode(node)
assert(idx < node.liveConsumerNode.size(), "active consumer index %s is out of bounds of %s consumers)" % [idx, node.liveConsumerNode.size()]) assert(idx < node.liveConsumerNode.size(), "active consumer index %s is out of bounds of %s consumers)" % [idx, node.liveConsumerNode.size()])
if node.liveConsumerNode.size() == 1: if node.liveConsumerNode.size() == 1:
if node.unwatched.is_null() == false: if node.unwatched.is_null() == false:
node.unwatched.call(node.wrapper) node.unwatched.call(node.wrapper)
for i in range(node.producerNode.size()): for i in range(node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]) producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
var lastIdx = node.liveConsumerNode.size() - 1 var lastIdx = node.liveConsumerNode.size() - 1
node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx] node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx]
node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx] node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx]
node.liveConsumerNode.pop_back() node.liveConsumerNode.pop_back()
node.liveConsumerIndexOfThis.pop_back() node.liveConsumerIndexOfThis.pop_back()
if idx < node.liveConsumerNode.size(): if idx < node.liveConsumerNode.size():
var idxProducer = node.liveConsumerIndexOfThis[idx] var idxProducer = node.liveConsumerIndexOfThis[idx]
var consumer = node.liveConsumerNode[idx] var consumer = node.liveConsumerNode[idx]
assertConsumerNode(consumer) assertConsumerNode(consumer)
consumer.producerIndexOfThis[idxProducer] = idx consumer.producerIndexOfThis[idxProducer] = idx
static func consumerIsLive(node: RdotNode) -> bool: static func consumerIsLive(node: RdotNode) -> bool:
return node.consumerIsAlwaysLive||(node.liveConsumerNode != null&&node.liveConsumerNode.size() > 0) return node.consumerIsAlwaysLive||(node.liveConsumerNode != null&&node.liveConsumerNode.size() > 0)
static func assertConsumerNode(node: RdotNode): static func assertConsumerNode(node: RdotNode):
if node.producerNode == null: if node.producerNode == null:
node.producerNode = [] node.producerNode = []
if node.producerIndexOfThis == null: if node.producerIndexOfThis == null:
node.producerIndexOfThis = [] node.producerIndexOfThis = []
if node.producerLastReadVersion == null: if node.producerLastReadVersion == null:
node.producerLastReadVersion = [] node.producerLastReadVersion = []
static func assertProducerNode(node: RdotNode): static func assertProducerNode(node: RdotNode):
if node.liveConsumerNode == null: if node.liveConsumerNode == null:
node.liveConsumerNode = [] node.liveConsumerNode = []
if node.liveConsumerIndexOfThis == null: if node.liveConsumerIndexOfThis == null:
node.liveConsumerIndexOfThis = [] node.liveConsumerIndexOfThis = []

View File

@ -15,9 +15,9 @@ const color_wheel_img := preload ("res://assets/canvas.png")
@onready var mode_before = $Modes/Previous @onready var mode_before = $Modes/Previous
@onready var mode_label = $Modes/Label @onready var mode_label = $Modes/Label
var state = true var active = R.state(false)
var brightness = 0 # 0-255 var brightness = R.state(0) # 0-255
var color = color_on var color = R.state(color_on)
var color_supported = false var color_supported = false
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
@ -26,7 +26,7 @@ func _ready():
icon.value = "lightbulb" icon.value = "lightbulb"
var stateInfo = await HomeApi.get_state(entity_id) var stateInfo = await HomeApi.get_state(entity_id)
set_state(stateInfo["state"] == "on", stateInfo["attributes"]) set_state(stateInfo)
if stateInfo.has("attributes")&&stateInfo["attributes"].has("effect_list")&&stateInfo["attributes"]["effect_list"].size() > 0: if stateInfo.has("attributes")&&stateInfo["attributes"].has("effect_list")&&stateInfo["attributes"]["effect_list"].size() > 0:
if stateInfo["attributes"].has("effect")&&stateInfo["attributes"]["effect"] != null: if stateInfo["attributes"].has("effect")&&stateInfo["attributes"]["effect"] != null:
@ -65,79 +65,70 @@ func _ready():
var delta=Vector2(target_point.x, target_point.z) * (1.0 / 0.08) var delta=Vector2(target_point.x, target_point.z) * (1.0 / 0.08)
if delta.length() > 1: if delta.length() > 1:
delta=delta.normalized() delta=delta.normalized()
print("delta", delta)
var color=color_wheel_img.get_image().get_pixel((delta.x * 0.5 + 0.5) * 1000, (delta.y * 0.5 + 0.5) * 1000) var picked_color=color_wheel_img.get_image().get_pixel((delta.x * 0.5 + 0.5) * 1000, (delta.y * 0.5 + 0.5) * 1000)
print("color", color) color_puck.material_override.albedo_color=picked_color
color_puck.material_override.albedo_color=color
color_puck.position=Vector3(target_point.x, color_puck.position.y, target_point.z) color_puck.position=Vector3(target_point.x, color_puck.position.y, target_point.z)
var attributes={ var attributes={
"rgb_color": [int(color.r * 255), int(color.g * 255), int(color.b * 255)], "rgb_color": [int(picked_color.r * 255), int(picked_color.g * 255), int(picked_color.b * 255)],
} }
HomeApi.set_state(entity_id, "on", attributes) HomeApi.set_state(entity_id, "on", attributes)
set_state(state, attributes)
) )
color_supported = true color_supported = true
else: else:
remove_child(color_wheel) remove_child(color_wheel)
await HomeApi.watch_state(entity_id, func(new_state): await HomeApi.watch_state(entity_id, func(new_state):
if (new_state["state"] == "on") == state: set_state(new_state)
return
set_state(new_state["state"] == "on", new_state["attributes"])
) )
slider.on_value_changed.connect(func(new_value): slider.on_value_changed.connect(func(new_value):
var value=new_value / 100 * 255 var value=new_value / 100 * 255
HomeApi.set_state(entity_id, "on" if state else "off", {"brightness": int(value)}) HomeApi.set_state(entity_id, "on" if active.value else "off", {"brightness": int(value)})
set_state(state, {"brightness": value})
) )
func set_state(new_state: bool, attributes={}): func set_state(stateInfo):
if state == false&&new_state == false: if active.value == false&&stateInfo["state"] == "off":
return return
state = new_state var attributes = stateInfo["attributes"]
if attributes.has("brightness"): active.value = stateInfo["state"] == "on"
brightness = attributes["brightness"]
if attributes.has("brightness")&&attributes["brightness"] != null:
brightness.value = attributes["brightness"]
slider.value = attributes["brightness"] / 255.0 * 100
if attributes.has("rgb_color")&&attributes["rgb_color"] != null: if attributes.has("rgb_color")&&attributes["rgb_color"] != null:
color = Color(attributes["rgb_color"][0] / 255.0, attributes["rgb_color"][1] / 255.0, attributes["rgb_color"][2] / 255.0, 1) color.value = Color(attributes["rgb_color"][0] / 255.0, attributes["rgb_color"][1] / 255.0, attributes["rgb_color"][2] / 255.0, 1)
var tween = create_tween() var tween = create_tween()
var target_color = color_off var target_color = color_off
if state: if active.value:
if brightness == null: if brightness.value == null:
target_color = color if color_supported else color_on target_color = color.value if color_supported else color_on
else: else:
target_color = color_off.lerp(color if color_supported else color_on, brightness / 255.0) target_color = color_off.lerp(color.value if color_supported else color_on, brightness.value / 255.0)
icon_color.value = target_color icon_color.value = target_color
tween.tween_property(lightbulb, "material_override:albedo_color", target_color, 0.3) tween.tween_property(lightbulb, "material_override:albedo_color", target_color, 0.3)
func _on_click(event): func _on_click(event):
if event.target == self: if event.target == self:
var attributes = {} _toggle()
if !state&&brightness != null:
attributes["brightness"] = int(brightness)
HomeApi.set_state(entity_id, "on" if !state else "off", attributes)
set_state(!state, attributes)
func quick_action(): func quick_action():
_toggle()
func _toggle():
var attributes = {} var attributes = {}
if !state&&brightness != null: if !active.value&&brightness.value != null:
attributes["brightness"] = int(brightness) attributes["brightness"] = int(brightness.value)
HomeApi.set_state(entity_id, "on" if !state else "off", attributes) HomeApi.set_state(entity_id, "off" if active.value else "on", attributes)
set_state(!state, attributes)

View File

@ -4,42 +4,35 @@ const Entity = preload ("../entity.gd")
@onready var sprite: AnimatedSprite3D = $Icon @onready var sprite: AnimatedSprite3D = $Icon
var active = R.state(false)
# 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():
super() super()
var stateInfo = await HomeApi.get_state(entity_id) var stateInfo = await HomeApi.get_state(entity_id)
set_state(stateInfo)
await HomeApi.watch_state(entity_id, func(new_state):
set_state(new_state)
)
R.effect(func(_arg):
sprite.set_frame(1 if active.value else 0)
)
func set_state(stateInfo):
if stateInfo == null: if stateInfo == null:
return return
if stateInfo["state"] == "on": active.value = stateInfo["state"] == "on"
sprite.set_frame(0)
else:
sprite.set_frame(1)
icon.value = "toggle_" + stateInfo["state"] icon.value = "toggle_" + stateInfo["state"]
await HomeApi.watch_state(entity_id, func(new_state):
if new_state["state"] == "on":
sprite.set_frame(0)
else:
sprite.set_frame(1)
icon.value="toggle_" + new_state["state"]
)
func _on_click(_event): func _on_click(_event):
HomeApi.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") _toggle()
if sprite.get_frame() == 0:
sprite.set_frame(1)
else:
sprite.set_frame(0)
func _on_request_completed():
pass
func quick_action(): func quick_action():
HomeApi.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") _toggle()
if sprite.get_frame() == 0:
sprite.set_frame(1) func _toggle():
else: HomeApi.set_state(entity_id, "off" if active.value else "on")
sprite.set_frame(0)

View File

@ -13,17 +13,17 @@ radius = 0.0482081
animations = [{ animations = [{
"frames": [{ "frames": [{
"duration": 1.0, "duration": 1.0,
"texture": ExtResource("1_w68gw") "texture": ExtResource("2_86ba1")
}, { }, {
"duration": 1.0, "duration": 1.0,
"texture": ExtResource("2_86ba1") "texture": ExtResource("1_w68gw")
}], }],
"loop": true, "loop": true,
"name": &"default", "name": &"default",
"speed": 5.0 "speed": 5.0
}] }]
[node name="Switch" type="StaticBody3D" ] [node name="Switch" type="StaticBody3D"]
collision_mask = 0 collision_mask = 0
script = ExtResource("1_8ffhi") script = ExtResource("1_8ffhi")