continue working on websocket implementation
This commit is contained in:
parent
40adc7b8ef
commit
3d411188a6
|
@ -5,7 +5,7 @@ extends StaticBody3D
|
||||||
|
|
||||||
# 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():
|
||||||
var stateInfo = await HomeAdapters.adapter.get_state(entity_id)
|
var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id)
|
||||||
if stateInfo["state"] == "on":
|
if stateInfo["state"] == "on":
|
||||||
sprite.set_frame(0)
|
sprite.set_frame(0)
|
||||||
else:
|
else:
|
||||||
|
@ -13,7 +13,7 @@ func _ready():
|
||||||
|
|
||||||
|
|
||||||
func _on_toggle():
|
func _on_toggle():
|
||||||
HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on")
|
HomeAdapters.adapter_ws.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on")
|
||||||
if sprite.get_frame() == 0:
|
if sprite.get_frame() == 0:
|
||||||
sprite.set_frame(1)
|
sprite.set_frame(1)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -5,7 +5,7 @@ extends StaticBody3D
|
||||||
|
|
||||||
# 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():
|
||||||
var stateInfo = await HomeAdapters.adapter.get_state(entity_id)
|
var stateInfo = await HomeAdapters.adapter_ws.get_state(entity_id)
|
||||||
if stateInfo["state"] == "on":
|
if stateInfo["state"] == "on":
|
||||||
sprite.set_frame(0)
|
sprite.set_frame(0)
|
||||||
else:
|
else:
|
||||||
|
@ -13,7 +13,7 @@ func _ready():
|
||||||
|
|
||||||
|
|
||||||
func _on_toggle():
|
func _on_toggle():
|
||||||
HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on")
|
HomeAdapters.adapter_ws.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on")
|
||||||
if sprite.get_frame() == 0:
|
if sprite.get_frame() == 0:
|
||||||
sprite.set_frame(1)
|
sprite.set_frame(1)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -6,21 +6,6 @@ var adapter = Adapter.new(Adapter.ADAPTER_TYPES.HASS)
|
||||||
var adapter_ws = Adapter.new(Adapter.ADAPTER_TYPES.HASS_WS)
|
var adapter_ws = Adapter.new(Adapter.ADAPTER_TYPES.HASS_WS)
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
add_child(adapter)
|
||||||
add_child(adapter_ws)
|
add_child(adapter_ws)
|
||||||
|
|
||||||
var timer = Timer.new()
|
|
||||||
timer.set_wait_time(1)
|
|
||||||
timer.set_one_shot(true)
|
|
||||||
|
|
||||||
print("timer started")
|
|
||||||
timer.timeout.connect(func():
|
|
||||||
print("timer done")
|
|
||||||
|
|
||||||
var result = await adapter_ws.get_state("light.living_room")
|
|
||||||
print(result)
|
|
||||||
)
|
|
||||||
|
|
||||||
add_child(timer)
|
|
||||||
timer.start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
40
src/home_adapters/hass_ws/callback_map.gd
Normal file
40
src/home_adapters/hass_ws/callback_map.gd
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
class_name CallbackMap
|
||||||
|
|
||||||
|
var callbacks := {}
|
||||||
|
|
||||||
|
func add(key: Variant, callback: Callable) -> void:
|
||||||
|
_validate_key(key)
|
||||||
|
|
||||||
|
if callbacks.has(key):
|
||||||
|
callbacks[key].append(callback)
|
||||||
|
else:
|
||||||
|
callbacks[key] = [callback]
|
||||||
|
|
||||||
|
func add_once(key: Variant, callback: Callable) -> void:
|
||||||
|
_validate_key(key)
|
||||||
|
|
||||||
|
var fn: Callable
|
||||||
|
|
||||||
|
fn = func(args: Array):
|
||||||
|
remove(key, fn)
|
||||||
|
callback.callv(args)
|
||||||
|
|
||||||
|
add(key, fn)
|
||||||
|
|
||||||
|
func remove(key: Variant, callback: Callable) -> void:
|
||||||
|
_validate_key(key)
|
||||||
|
|
||||||
|
if callbacks.has(key):
|
||||||
|
callbacks[key].erase(callback)
|
||||||
|
|
||||||
|
func call_key(key: Variant, args: Array) -> void:
|
||||||
|
_validate_key(key)
|
||||||
|
|
||||||
|
if callbacks.has(key):
|
||||||
|
for callback in callbacks[key]:
|
||||||
|
callback.callv(args)
|
||||||
|
|
||||||
|
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")
|
|
@ -1,18 +1,23 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var devices_template = FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2")
|
var devices_template := FileAccess.get_file_as_string("res://src/home_adapters/hass/templates/devices.j2")
|
||||||
var socket = WebSocketPeer.new()
|
var socket := WebSocketPeer.new()
|
||||||
# in seconds
|
# in seconds
|
||||||
var request_timeout: float = 10
|
var request_timeout := 10.0
|
||||||
|
|
||||||
var url: String = "ws://192.168.33.33:8123/api/websocket"
|
var url := "ws://192.168.33.33:8123/api/websocket"
|
||||||
var token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc"
|
var token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc"
|
||||||
|
|
||||||
|
|
||||||
var authenticated: bool = false
|
var authenticated := false
|
||||||
var id = 1
|
var id := 1
|
||||||
|
var entities: Dictionary = {}
|
||||||
|
|
||||||
signal packet_received(packet: Dictionary)
|
var entitiy_callbacks := CallbackMap.new()
|
||||||
|
var packet_callbacks := CallbackMap.new()
|
||||||
|
|
||||||
|
signal on_connect()
|
||||||
|
signal on_disconnect()
|
||||||
|
|
||||||
func _init(url := self.url, token := self.token):
|
func _init(url := self.url, token := self.token):
|
||||||
self.url = url
|
self.url = url
|
||||||
|
@ -29,6 +34,7 @@ func _process(delta):
|
||||||
socket.poll()
|
socket.poll()
|
||||||
|
|
||||||
var state = socket.get_ready_state()
|
var state = socket.get_ready_state()
|
||||||
|
print(state, "POLLING")
|
||||||
if state == WebSocketPeer.STATE_OPEN:
|
if state == WebSocketPeer.STATE_OPEN:
|
||||||
while socket.get_available_packet_count():
|
while socket.get_available_packet_count():
|
||||||
handle_packet(socket.get_packet())
|
handle_packet(socket.get_packet())
|
||||||
|
@ -38,8 +44,7 @@ func _process(delta):
|
||||||
var code = socket.get_close_code()
|
var code = socket.get_close_code()
|
||||||
var reason = socket.get_close_reason()
|
var reason = socket.get_close_reason()
|
||||||
print("WS connection closed with code: %s, reason: %s" % [code, reason])
|
print("WS connection closed with code: %s, reason: %s" % [code, reason])
|
||||||
authenticated = false
|
handle_disconnect()
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
func handle_packet(raw_packet: PackedByteArray):
|
func handle_packet(raw_packet: PackedByteArray):
|
||||||
var packet = decode_packet(raw_packet)
|
var packet = decode_packet(raw_packet)
|
||||||
|
@ -54,12 +59,62 @@ func handle_packet(raw_packet: PackedByteArray):
|
||||||
|
|
||||||
elif packet.type == "auth_ok":
|
elif packet.type == "auth_ok":
|
||||||
authenticated = true
|
authenticated = true
|
||||||
|
start_subscriptions()
|
||||||
|
|
||||||
elif packet.type == "auth_invalid":
|
elif packet.type == "auth_invalid":
|
||||||
authenticated = false
|
handle_disconnect()
|
||||||
print("Authentication failed")
|
|
||||||
set_process(false)
|
|
||||||
else:
|
else:
|
||||||
packet_received.emit(packet)
|
packet_callbacks.call_key(packet.id, [packet])
|
||||||
|
|
||||||
|
func start_subscriptions():
|
||||||
|
assert(authenticated, "Not authenticated")
|
||||||
|
|
||||||
|
await send_request_packet({
|
||||||
|
"type": "supported_features",
|
||||||
|
"features": {
|
||||||
|
"coalesce_messages": 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
send_subscribe_packet({
|
||||||
|
"type": "subscribe_entities"
|
||||||
|
}, func(packet: Dictionary):
|
||||||
|
if packet.type != "event":
|
||||||
|
return
|
||||||
|
|
||||||
|
if packet.event.has("a"):
|
||||||
|
for entity in packet.event.a.keys():
|
||||||
|
entities[entity] = packet.event.a[entity]
|
||||||
|
entitiy_callbacks.call_key(entity, [entities[entity]])
|
||||||
|
on_connect.emit()
|
||||||
|
|
||||||
|
if packet.event.has("c"):
|
||||||
|
for entity in packet.event.c.keys():
|
||||||
|
if packet.event.c[entity].has("+"):
|
||||||
|
entities[entity].merge(packet.event.c[entity]["+"])
|
||||||
|
entitiy_callbacks.call_key(entity, [entities[entity]])
|
||||||
|
)
|
||||||
|
|
||||||
|
func handle_disconnect():
|
||||||
|
authenticated = false
|
||||||
|
set_process(false)
|
||||||
|
on_disconnect.emit()
|
||||||
|
|
||||||
|
func send_subscribe_packet(packet: Dictionary, callback: Callable):
|
||||||
|
packet.id = id
|
||||||
|
id += 1
|
||||||
|
|
||||||
|
send_packet(packet)
|
||||||
|
packet_callbacks.add(packet.id, callback)
|
||||||
|
|
||||||
|
return func():
|
||||||
|
packet_callbacks.remove(packet.id, callback)
|
||||||
|
send_packet({
|
||||||
|
id: id,
|
||||||
|
"type": packet.type.replace("subscribe", "unsubscribe"),
|
||||||
|
"subscription": packet.id
|
||||||
|
})
|
||||||
|
id += 1
|
||||||
|
|
||||||
|
|
||||||
func send_request_packet(packet: Dictionary):
|
func send_request_packet(packet: Dictionary):
|
||||||
|
@ -69,21 +124,14 @@ func send_request_packet(packet: Dictionary):
|
||||||
send_packet(packet)
|
send_packet(packet)
|
||||||
|
|
||||||
var promise = Promise.new(func(resolve: Callable, reject: Callable):
|
var promise = Promise.new(func(resolve: Callable, reject: Callable):
|
||||||
var handle_packet = func(recieved_packet: Dictionary):
|
packet_callbacks.add_once(packet.id, resolve)
|
||||||
print("Received packet in handler: %s" % recieved_packet)
|
|
||||||
if packet.id == recieved_packet.id:
|
|
||||||
print("same id")
|
|
||||||
resolve.call(recieved_packet)
|
|
||||||
packet_received.disconnect(handle_packet)
|
|
||||||
|
|
||||||
packet_received.connect(handle_packet)
|
|
||||||
|
|
||||||
var timeout = Timer.new()
|
var timeout = Timer.new()
|
||||||
timeout.set_wait_time(request_timeout)
|
timeout.set_wait_time(request_timeout)
|
||||||
timeout.set_one_shot(true)
|
timeout.set_one_shot(true)
|
||||||
timeout.timeout.connect(func():
|
timeout.timeout.connect(func():
|
||||||
reject.call(Promise.Rejection.new("Request timed out"))
|
reject.call(Promise.Rejection.new("Request timed out"))
|
||||||
packet_received.disconnect(handle_packet)
|
packet_callbacks.remove(packet.id, resolve)
|
||||||
)
|
)
|
||||||
add_child(timeout)
|
add_child(timeout)
|
||||||
timeout.start()
|
timeout.start()
|
||||||
|
@ -106,22 +154,36 @@ func load_devices():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
func get_state(entity: String):
|
func get_state(entity: String):
|
||||||
assert(authenticated, "Not authenticated")
|
if !authenticated:
|
||||||
|
await on_connect
|
||||||
|
|
||||||
var result = await send_request_packet({
|
if entities.has(entity):
|
||||||
"type": "get_states"
|
return entities[entity]
|
||||||
})
|
else:
|
||||||
|
print(entities, entity)
|
||||||
if result.status == Promise.Status.RESOLVED:
|
|
||||||
return result.payload
|
|
||||||
return null
|
|
||||||
|
|
||||||
|
|
||||||
func watch_state(entity: String, callback: Callable):
|
func watch_state(entity: String, callback: Callable):
|
||||||
assert(authenticated, "Not authenticated")
|
if !authenticated:
|
||||||
|
await on_connect
|
||||||
|
|
||||||
|
entitiy_callbacks.add(entity, callback)
|
||||||
|
|
||||||
|
|
||||||
func set_state(entity: String, state: String, attributes: Dictionary = {}):
|
func set_state(entity: String, state: String, attributes: Dictionary = {}):
|
||||||
assert(authenticated, "Not authenticated")
|
assert(authenticated, "Not authenticated")
|
||||||
|
|
||||||
|
var domain = entity.split(".")[0]
|
||||||
|
var service = entity.split(".")[1]
|
||||||
|
|
||||||
|
return await send_request_packet({
|
||||||
|
"type": "call_service",
|
||||||
|
"domain": domain,
|
||||||
|
"service": service,
|
||||||
|
"service_data": attributes,
|
||||||
|
"target": {
|
||||||
|
"entity_id": entity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user