Files
linuxSmartie/src/hass.cpp
2025-12-28 13:07:22 +01:00

160 lines
4.7 KiB
C++

#include <libwebsockets.h>
#include <nlohmann/json.hpp>
#include <hass.hpp>
#include <log.hpp>
#include <string>
HomeAssistant::HomeAssistant(std::string addr, std::string ent) {
address = addr;
entity = ent;
}
HomeAssistant::~HomeAssistant() {
disconnect();
}
void HomeAssistant::event_handler(struct lws* wsi, enum lws_callback_reasons reason, char* in) {
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
log(LOG::ERR, "websocket conn error: {}", in ? in : "(null)");
return;
}
case LWS_CALLBACK_CLIENT_RECEIVE: {
nlohmann::json json = nlohmann::json::parse(in);
std::string ha_type = json["type"].get<std::string>();
if (ha_type == "auth_required") {
json.clear();
json["type"] = "auth";
json["access_token"] = HASS_TOKEN;
tx = json.dump();
break;
}
if (ha_type == "auth_ok") {
log(LOG::INF, "subscribing to entity \"{}\"", entity);
json.clear();
json["id"] = id++;
json["type"] = "subscribe_entities";
json["entity_ids"][0] = entity.c_str();
tx = json.dump();
break;
}
if (ha_type == "result") {
if (json["success"].get<bool>() != true)
log(LOG::ERR, "api said success was \"false\" on message id {}", id - 1);
break;
}
if (ha_type == "event") {
json = json["event"];
if(json.contains("a"))
state = std::stof(json["a"][entity]["s"].get<std::string>());
if(json.contains("c"))
state = std::stof(json["c"][entity]["+"]["s"].get<std::string>());
break;
}
log(LOG::WRN, "unhandled event: {}", (char*)in);
break;
}
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
log(LOG::INF, "Websocket connected");
break;
}
case LWS_CALLBACK_CLIENT_CLOSED: {
log(LOG::INF, "Websocket closed");
return;
}
case LWS_CALLBACK_CLIENT_WRITEABLE: {
/* skip whole callback if there's no tx to send */
if (tx.size() > 0) {
size_t msgLen = LWS_PRE + tx.size();
unsigned char* buf = (unsigned char*)malloc(msgLen);
if (buf) {
memcpy(&buf[LWS_PRE], tx.c_str(), tx.size());
lws_write(wsi, &buf[LWS_PRE], tx.size(), LWS_WRITE_TEXT);
tx = "";
free(buf);
}
}
break;
}
default:
break;
}
}
int HomeAssistant::callback(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t) {
HomeAssistant* self = static_cast<HomeAssistant*>(user);
self->event_handler(wsi, reason, (char*)in);
return 0;
}
int HomeAssistant::connect() {
struct lws_client_connect_info i;
struct lws_context_creation_info info;
lws_set_log_level(LLL_ERR | LLL_WARN, lwsl_emit_syslog);
memset(&i, 0, sizeof(i));
memset(&info, 0, sizeof info);
static const struct lws_protocols protocols[] = {
{"lws-minimal-client", callback, sizeof(void*), 0, 0, NULL, 0},
LWS_PROTOCOL_LIST_TERM};
static const struct lws_extension extensions[] = {
{"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
"; client_no_context_takeover"
"; client_max_window_bits"},
{NULL, NULL, NULL}};
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
info.fd_limit_per_thread = 1 + 1 + 1;
info.extensions = extensions;
context = lws_create_context(&info);
if (!context) {
log(LOG::ERR, "error creating context");
return -1;
}
i.context = context;
i.port = 443;
i.address = address.c_str();
i.path = "/api/websocket";
i.host = i.address;
i.origin = i.address;
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_PRIORITIZE_READS;
i.protocol = NULL;
i.userdata = this;
i.local_protocol_name = "lws-minimal-client";
websock = lws_client_connect_via_info(&i);
if (!websock) {
log(LOG::ERR, "error creating lws client");
return -1;
}
return 0;
}
void HomeAssistant::service(int timeout_ms) {
lws_callback_on_writable(websock);
lws_service(context, timeout_ms);
}
void HomeAssistant::disconnect() {
lws_context_destroy(context);
}