Author: Andy Green Date: Wed May 14 07:40:58 2025 +0100 power: tasmota on diff --git a/src/power/CMakeLists.txt b/src/power/CMakeLists.txt index 176494d..3948ef5 100644 --- a/src/power/CMakeLists.txt +++ b/src/power/CMakeLists.txt @@ -5,6 +5,7 @@ set(SRCS p-sai.c p-conf.c p-comms.c + p-smartplug.c ) set(requirements 1) @@ -13,6 +14,8 @@ require_lws_config(LWS_WITH_SPAWN 1 requirements) require_lws_config(LWS_WITH_STRUCT_JSON 1 requirements) require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) require_lws_config(LWS_WITH_DIR 1 requirements) +require_lws_config(LWS_WITH_WOL 1 requirements) + if (requirements) add_executable(${SUB} ${SRCS}) diff --git a/src/power/p-comms.c b/src/power/p-comms.c index ee72344..8b64718 100644 --- a/src/power/p-comms.c +++ b/src/power/p-comms.c @@ -38,14 +38,68 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) { saip_server_link_t *pss = (saip_server_link_t *)userobj; saip_server_t *sps = (saip_server_t *)lws_ss_opaque_from_user(pss); + const char *p = (const char *)buf, *end = (const char *)buf + len; + char plat[128]; + size_t n; - lwsl_notice("%s: len %d, flags: %d\n", __func__, (int)len, flags); + lwsl_notice("%s: len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps); lwsl_hexdump_notice(buf, len); + while (p < end) { + n = 0; + while (p < end && *p != ',') + if (n < sizeof(plat) - 1) + plat[n++] = *p++; + + plat[n] = '\0'; + if (p < end && *p == ',') + p++; + + /* + * Does this server list this platform? + */ + + lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) { + saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list); + + if (!strcmp(sp->name, plat)) { + /* + * Server said this platform has pending jobs. + * sai-power config says this builder can do + * jobs on that platform. Let's make sure it + * is powered on. + */ + if (!strcmp(sp->power_on_type, "wol")) { + uint8_t mac[LWS_ETHER_ADDR_LEN]; + + if (lws_parse_mac(sp->power_on_mac, mac)) { + lwsl_user("Failed to parse mac '%s'\n", sp->power_on_mac); + } else + + if (!lws_wol(lws_ss_cx_from_user(pss), + NULL, mac)) { + lwsl_user("Failed to WOL '%s'\n", sp->power_on_mac); + } else + lwsl_user("Sent WOL to '%s'\n", sp->power_on_mac); + } + + if (!strcmp(sp->power_on_type, "tasmota")) { + + if (lws_ss_create(lws_ss_cx_from_user(pss), + 0, &ssi_saip_smartplug_t, + sp, NULL, NULL, NULL)) { + lwsl_err("%s: failed to create smartplug secure stream\n", + __func__); + } + } + } + + } lws_end_foreach_dll(px); + } + (void)sps; -// if (saip_ws_json_rx_power(sps, buf, len)) -// return 1; + return 0; } @@ -103,7 +157,7 @@ saip_m_state(void *userobj, void *sh, lws_ss_constate_t state, pq = sps->url; n = (int)strlen(pq); } - +#if 0 sps->name = sps->url + strlen(sps->url) + 1; memcpy((char *)sps->name, pq, (unsigned int)n); ((char *)sps->name)[n] = '\0'; @@ -112,7 +166,7 @@ saip_m_state(void *userobj, void *sh, lws_ss_constate_t state, *strchr(sps->name, '.') = '_'; while (strchr(sps->name, '/')) *strchr(sps->name, '/') = '_'; - +#endif break; case LWSSSCS_DESTROYING: diff --git a/src/power/p-private.h b/src/power/p-private.h index 247f1d7..dae86da 100644 --- a/src/power/p-private.h +++ b/src/power/p-private.h @@ -114,7 +114,7 @@ struct jpargs { }; extern struct sai_power power; -extern const lws_ss_info_t ssi_saip_server_link_t; +extern const lws_ss_info_t ssi_saip_server_link_t, ssi_saip_smartplug_t; extern const struct lws_protocols protocol_com_warmcat_sai, protocol_ws_power; int saip_config_global(struct sai_power *power, const char *d); diff --git a/src/power/p-sai.c b/src/power/p-sai.c index 472d665..8bf3775 100644 --- a/src/power/p-sai.c +++ b/src/power/p-sai.c @@ -132,6 +132,20 @@ static const char * const default_ss_policy = "{\"method\": \"\"}," "{\"mime\": \"\"}" "]" + "}}," + /* + * Http operations to smartplugs + */ + "{\"sai_power_smartplug\": {" + "\"endpoint\":" "\"${url}\"," + "\"port\":" "80," + "\"protocol\":" "\"h1\"," + "\"http_url\":" "\"\"," /* filled in by url */ + "\"tls\":" "false," + "\"retry\":" "\"default\"," + "\"metadata\": [" + "{\"url\": \"\"}" + "]" "}}" "]}" ; diff --git a/src/power/p-smartplug.c b/src/power/p-smartplug.c new file mode 100644 index 0000000..f97a113 --- /dev/null +++ b/src/power/p-smartplug.c @@ -0,0 +1,74 @@ +/* + * sai-power com-warmcat-sai client protocol implementation + * + * Copyright (C) 2019 - 2025 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include + +#include "p-private.h" + +LWS_SS_USER_TYPEDEF + char payload[200]; + size_t size; + size_t pos; +} saip_smartplug_t; + +static lws_ss_state_return_t +saip_spc_state(void *userobj, void *sh, lws_ss_constate_t state, + lws_ss_tx_ordinal_t ack) +{ + saip_smartplug_t *pss = (saip_smartplug_t *)userobj; + saip_server_plat_t *sp = (saip_server_plat_t *)lws_ss_opaque_from_user(pss); + + lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state), + (unsigned int)ack); + + switch (state) { + + case LWSSSCS_CREATING: + + lwsl_notice("%s: binding ss to %s\n", __func__, sp->power_on_url); + + if (lws_ss_set_metadata(lws_ss_from_user(pss), + "url", sp->power_on_url, + strlen(sp->power_on_url))) + lwsl_warn("%s: unable to set metadata\n", __func__); + + return lws_ss_client_connect(lws_ss_from_user(pss)); + + case LWSSSCS_QOS_ACK_REMOTE: + lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__); + break; + + default: + break; + } + + return LWSSSSRET_OK; +} + +LWS_SS_INFO("smartplug_control", saip_smartplug_t) +// .rx = saip_spc_rx, +// .tx = saip_spc_tx, + .state = saip_spc_state, + .user_alloc = sizeof(saip_smartplug_t), + .streamtype = "sai_power_smartplug" +};