#include #include #include #include #include #define HASS_ADDRESS "home.drath.win" #define HASS_TOKEN "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI0MzUxMTZiY2VjNTg0MTI3OGI4YmU0NTY4ZGQ5MzIyYiIsImlhdCI6MTc1NzQzMDI0MCwiZXhwIjoyMDcyNzkwMjQwfQ.6wKTlxEA0FLWfv-qW35vewUSvTxdVrZWqFwHT5Khu6E" static bool bExit; std::string tx; struct lws_context *context = NULL; lws *websock = NULL; uint16_t id = 2; static int callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); static const struct lws_protocols protocols[] = { {"lws-minimal-client", callback, 0, 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}}; static int callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { ArduinoJson::JsonDocument json; std::string entity = "sensor.listwa_moc"; switch (reason) { case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { std::println("[HomeAssistant] websocket conn error: {}", in ? (char *)in : "(null)"); return -1; } case LWS_CALLBACK_CLIENT_RECEIVE: { ArduinoJson::deserializeJson(json, (char *)in); std::string ha_type = json["type"].as(); // std::println("[HomeAssistant] got event type: {} ", ha_type); if (ha_type == "auth_required") { json.clear(); json["type"] = "auth"; json["access_token"] = HASS_TOKEN; ArduinoJson::serializeJson(json, tx); break; } if (ha_type == "auth_ok") { std::println("[HomeAssistant] subscribing to entity \"{}\"", entity); json.clear(); json["id"] = id++; json["type"] = "subscribe_entities"; json["entity_ids"][0] = entity; ArduinoJson::serializeJson(json, tx); break; } if (ha_type == "result") { if (json["success"].as() == "false") std::println("[HomeAssistant] api said success was \"false\" on message id {}", id - 1); break; } if (ha_type == "event") { float state = json["event"]["c"][entity]["+"]["s"].as(); std::println("{}", state); break; } std::println("[HomeAssistant] unhandled event: {}", (char *)in); break; } case LWS_CALLBACK_CLIENT_ESTABLISHED: { std::println("[HomeAssistant] Websocket connected"); break; } case LWS_CALLBACK_CLIENT_CLOSED: { std::println("[HomeAssistant] Websocket closed"); return -1; } case LWS_CALLBACK_CLIENT_WRITEABLE: { /* skip whole callback if there's no tx to send */ if (tx.size() > 0) { // std::println("[HomeAssistant] sending {}", tx); 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()); if(!lws_write(wsi, &buf[LWS_PRE], tx.size(), LWS_WRITE_TEXT)); tx = ""; free(buf); } } break; } default: break; } return 0; } static void handle_sigint(int sig) { bExit = true; } int hass_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); 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) { std::println("[HomeAssistant] error creating context"); return -1; } i.context = context; i.port = 443; i.address = HASS_ADDRESS; 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.local_protocol_name = "lws-minimal-client"; websock = lws_client_connect_via_info(&i); if (!websock) { std::println("[HomeAssistant] error creating lws client"); return -1; } return 0; } void hass_poll() { lws_service(context, 0); lws_callback_on_writable(websock); } void hass_disconnect() { lws_context_destroy(context); } int main(int argc, const char **argv) { signal(SIGINT, handle_sigint); hass_connect(); while (!bExit) hass_poll(); hass_disconnect(); return 0; }