-- Base.lua
-- Variáveis globais
local ha_host = nil
local ha_port = nil
local ha_token = nil
local socket_id = nil -- Variável para armazenar o ID do socket
local auth_sent = false -- Flag para verificar se a autenticação já foi enviada
local next_message_id = 1 -- ID para rastrear mensagens enviadas ao Home Assistant
-- Função para obter as configurações do Home Assistant do Configurator
local function getHAConfig()
local config = {}
config.host = ELAN_GetIPString()
config.port = tonumber(ELAN_GetIPPort())
config.token = ELAN_GetConfigurationString("ha_token")
-- Validação das configurações
if not config.host or config.host == "" then
ELAN_Trace("Erro: Home Assistant Host não configurado.")
return nil
end
if not config.port then
ELAN_Trace("Erro: Home Assistant Port não configurada.")
return nil
end
if not config.token or config.token == "" then
ELAN_Trace("Erro: Home Assistant Token não configurado.")
return nil
end
return config
end
-- Função para escapar caracteres especiais em strings JSON
local function escape_json_string(str)
-- Escapa aspas duplas e barras invertidas adicionando uma barra invertida antes delas
return string.gsub(str, '(["\\])', '\\%1')
end
-- Função para converter uma estrutura JSON em uma string
local function table_to_json(hJSON)
local json_string = "{"
local first = true
local i = 0
while true do
local hNode = ELAN_GetJSONSubNode(hJSON, hJSON, i)
if not hNode then
break
end
local key, value = ELAN_GetJSONValue(hJSON, hNode)
local nodeType, nodeKey = ELAN_GetJSONNodeType(hJSON, hNode)
if not first then
json_string = json_string .. ","
else
first = false
end
-- Escapa a chave JSON
local escapedNodeKey = escape_json_string(nodeKey)
json_string = json_string .. "\"" .. escapedNodeKey .. "\":"
if nodeType == "String" then
-- Escapa o valor JSON
local escapedValue = escape_json_string(value)
json_string = json_string .. "\"" .. escapedValue .. "\""
elseif nodeType == "Number" or nodeType == "Bool" then
json_string = json_string .. value
elseif nodeType == "Null" then
json_string = json_string .. "null"
else
-- Para outros tipos, como Object ou Array, você precisará lidar com eles recursivamente
json_string = json_string .. "null"
end
i = i + 1
end
json_string = json_string .. "}"
return json_string
end
-- Função para enviar mensagens JSON via WebSocket (recebe objeto JSON)
local function send_json(socket, hJSON)
if not hJSON then
ELAN_Trace("Erro: Objeto JSON é nil.")
return
end
local json_string = table_to_json(hJSON)
if not json_string then
ELAN_Trace("Erro ao serializar objeto JSON para string.")
ELAN_DeleteJSONMsg(hJSON)
return
end
-- Deleta a estrutura JSON após a serialização
ELAN_DeleteJSONMsg(hJSON)
ELAN_Trace("Enviando objeto JSON via WebSocket: " .. json_string)
local success = ELAN_SendTCP(socket, json_string, string.len(json_string))
if success == -1 then
ELAN_Trace("Erro ao enviar mensagem JSON via WebSocket")
end
end
-- Função para gerar uma chave Base64 aleatória
local function generate_base64_key(length)
local random_data = ""
for i = 1, length do
random_data = random_data .. string.char(math.random(0, 255))
end
local base64_key = ELAN_Base64Codec(random_data, "ENCODE")
return base64_key
end
-- Monta a mensagem de upgrade HTTP
local function build_http_upgrade_message(host)
local key = generate_base64_key(16)
local upgrade_message = "GET /api/websocket HTTP/1.1\r\n"
.. "Host: " .. host .. "\r\n"
.. "Upgrade: websocket\r\n"
.. "Connection: Upgrade\r\n"
.. "Sec-WebSocket-Key: " .. key .. "\r\n"
.. "Sec-WebSocket-Version: 13\r\n\r\n"
return upgrade_message
end
-- Função de inicialização
function EDRV_Init()
ELAN_Trace("Driver HomeAssistantLighting inicializado")
-- Obtém as configurações do Home Assistant
local haConfig = getHAConfig()
if not haConfig then
ELAN_Trace("Configurações do Home Assistant inválidas. Abortando.")
return
end
ha_host = haConfig.host
ha_port = haConfig.port
ha_token = haConfig.token
socket_id = ELAN_CreateTCPClientSocket(ha_host, ha_port)
if socket_id <= 0 then
ELAN_Trace("Erro ao criar socket TCP: " .. socket_id)
return
end
local connected = ELAN_ConnectTCPSocketAsync(socket_id, 5000)
if not connected then
ELAN_Trace("Erro ao iniciar conexão TCP.")
ELAN_CloseSocket(socket_id)
return
end
end
-- Notificação de conexão TCP assíncrona
function EDRV_OnTCPAsyncConnect(socket, success)
if socket == socket_id and success then
ELAN_Trace("Conexão TCP estabelecida com sucesso.")
local http_upgrade_message = build_http_upgrade_message(ha_host)
ELAN_SendHTTP(socket_id, http_upgrade_message, "", false)
else
ELAN_Trace("Falha ao conectar TCP.")
ELAN_CloseSocket(socket_id)
end
end
-- Recebe dados do WebSocket
function EDRV_RecvTCP(socket, nDataBytes, message)
if socket == socket_id then
ELAN_Trace("Dados recebidos do WebSocket: " .. message)
-- Verifica se a autenticação é necessária e ainda não foi enviada
if message:find("auth_required") and not auth_sent then
ELAN_Trace("Autenticação requerida, enviando credenciais...")
-- Cria a mensagem de autenticação
local hAuthMessage = ELAN_CreateJSONMsg()
ELAN_AddJSONKeyValuePair(hAuthMessage, hAuthMessage, "type", "auth")
ELAN_AddJSONKeyValuePair(hAuthMessage, hAuthMessage, "access_token", ha_token)
send_json(socket_id, hAuthMessage)
auth_sent = true
elseif message:find("auth_ok") then
ELAN_Trace("Autenticação bem-sucedida.")
-- Continua com outros comandos, por exemplo, pegar entidades
local hGetStatesMessage = ELAN_CreateJSONMsg()
ELAN_AddJSONKeyValuePair(hGetStatesMessage, hGetStatesMessage, "id", 1)
ELAN_AddJSONKeyValuePair(hGetStatesMessage, hGetStatesMessage, "type", "get_states")
send_json(socket_id, hGetStatesMessage)
elseif message:find("auth_invalid") then
ELAN_Trace("Autenticação inválida.")
ELAN_CloseSocket(socket_id)
elseif message:find("result") then
ELAN_Trace("Resultado recebido do Home Assistant.")
end
end
end
-- Função para lidar com desconexão TCP
function EDRV_OnTCPDisconnect(socket)
if socket == socket_id then
ELAN_Trace("Conexão TCP desconectada.")
ELAN_CloseSocket(socket_id)
socket_id = nil
auth_sent = false
end
end
function EDRV_ClosedWebSocket(socket, message)
if socket == socket_id then
ELAN_Trace("Conexão WebSocket fechada: " .. message)
ELAN_CloseSocket(socket_id)
socket_id = nil
auth_sent = false
end
end