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 <andy@warmcat.com>
+ *
+ * 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 <libwebsockets.h>
+#include <string.h>
+#include <signal.h>
+
+#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"
+};