-- 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