238 lines
6.3 KiB
C
238 lines
6.3 KiB
C
#include "rpc.h"
|
|
#include "main.h"
|
|
#include "mqtt.h"
|
|
#include "timespec.h"
|
|
|
|
static void rpc_log(struct mg_rpc_request_info *ri, struct mg_str args) {
|
|
LOG(LL_INFO, ("tag=%.*s src=%.*s method=%.*s args='%.*s'",
|
|
ri->tag.len, ri->tag.p, ri->src.len, ri->src.p,
|
|
ri->method.len, ri->method.p, args.len, args.p));
|
|
// TODO(pim): log to MQTT
|
|
}
|
|
|
|
// Grab the idx from args and resolve to a GPIO, return true if found, false if not.
|
|
// If we return false, return_idx and return_gpio are not usable.
|
|
static bool rpc_args_to_idx_and_gpio(struct mg_rpc_request_info *ri, struct mg_str args, int *return_idx, uint8_t *return_gpio) {
|
|
int idx;
|
|
int gpio;
|
|
|
|
if (json_scanf(args.p, args.len, ri->args_fmt, &idx) != 1) {
|
|
mg_rpc_send_errorf(ri, 400, "idx is required");
|
|
ri = NULL;
|
|
return false;
|
|
}
|
|
|
|
if (idx < 0 || idx >= channel_get_total()) {
|
|
mg_rpc_send_errorf(ri, 400, "idx must be between 0 and %d", channel_get_total() - 1);
|
|
ri = NULL;
|
|
return false;
|
|
}
|
|
|
|
gpio = channel_gpio_by_idx(idx);
|
|
if (gpio == GPIO_INVALID) {
|
|
mg_rpc_send_errorf(ri, 400, "No GPIO for idx");
|
|
ri = NULL;
|
|
return false;
|
|
}
|
|
|
|
*return_gpio = gpio;
|
|
*return_idx = idx;
|
|
return true;
|
|
}
|
|
|
|
static void rpc_channel_toggle_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
uint8_t gpio;
|
|
int idx;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
if (!rpc_args_to_idx_and_gpio(ri, args, &idx, &gpio)) {
|
|
return;
|
|
}
|
|
|
|
channel_handler(gpio, NULL);
|
|
mg_rpc_send_responsef(ri, "{idx: %d, relay_state: %d}", idx, channel_get(idx));
|
|
ri = NULL;
|
|
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
static void rpc_channel_get_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
uint8_t gpio;
|
|
int idx;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
if (!rpc_args_to_idx_and_gpio(ri, args, &idx, &gpio)) {
|
|
return;
|
|
}
|
|
|
|
mg_rpc_send_responsef(ri, "{idx: %d, relay_state: %d}", idx, channel_get(idx));
|
|
ri = NULL;
|
|
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
static void rpc_channel_set_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
uint8_t gpio;
|
|
int idx = -1;
|
|
int value = -1;
|
|
int duration = -1;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
json_scanf(args.p, args.len, ri->args_fmt, &idx, &value, &duration);
|
|
|
|
if (idx < 0 || idx >= channel_get_total()) {
|
|
mg_rpc_send_errorf(ri, 400, "idx must be between 0 and %d", channel_get_total() - 1);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
if (value < 0 || value > 1) {
|
|
mg_rpc_send_errorf(ri, 400, "value must be 0 or 1");
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
if (duration > 60) {
|
|
mg_rpc_send_errorf(ri, 400, "duration, if set, must be between 1 and 60 seconds");
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
gpio = channel_gpio_by_idx(idx);
|
|
if (gpio == GPIO_INVALID) {
|
|
mg_rpc_send_errorf(ri, 400, "No GPIO for idx");
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
if (duration > 0) {
|
|
channel_set_duration(idx, (bool)value, duration);
|
|
} else {
|
|
channel_set(idx, (bool)value);
|
|
}
|
|
channel_override_set(idx);
|
|
mg_rpc_send_responsef(ri, "{idx: %d, relay_state: %d}", idx, channel_get(idx));
|
|
ri = NULL;
|
|
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
static void rpc_timespec_get_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
struct mgos_timespec *ts;
|
|
char buf[200];
|
|
int idx;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
json_scanf(args.p, args.len, ri->args_fmt, &idx);
|
|
if (idx < 0 || idx >= channel_get_total()) {
|
|
mg_rpc_send_errorf(ri, 400, "idx must be between 0 and %d", channel_get_total() - 1);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
ts = channel_get_timespec(idx);
|
|
if (!ts) {
|
|
mg_rpc_send_errorf(ri, 400, "timespec on channel %d isn't set", idx);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
if (!timespec_get_spec(ts, buf, sizeof(buf))) {
|
|
mg_rpc_send_errorf(ri, 400, "Could not get timespec on channel %d", idx);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
mg_rpc_send_responsef(ri, "{idx: %d, spec: %Q}", idx, buf);
|
|
ri = NULL;
|
|
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
static void rpc_timespec_clear_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
struct mgos_timespec *ts;
|
|
int idx;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
json_scanf(args.p, args.len, ri->args_fmt, &idx);
|
|
if (idx < 0 || idx >= channel_get_total()) {
|
|
mg_rpc_send_errorf(ri, 400, "idx must be between 0 and %d", channel_get_total() - 1);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
ts = channel_get_timespec(idx);
|
|
if (!ts) {
|
|
mg_rpc_send_errorf(ri, 400, "timespec on channel %d isn't set", idx);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
|
|
if (!timespec_clear_spec(ts)) {
|
|
mg_rpc_send_errorf(ri, 400, "Failed to clear timespec on channel %d", idx);
|
|
ri = NULL;
|
|
return;
|
|
}
|
|
channel_override_set(idx);
|
|
|
|
mg_rpc_send_responsef(ri, "{idx: %d}", idx);
|
|
ri = NULL;
|
|
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
static void rpc_timespec_add_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
|
struct mgos_timespec *ts;
|
|
int idx;
|
|
char *spec = NULL;
|
|
|
|
rpc_log(ri, args);
|
|
|
|
json_scanf(args.p, args.len, ri->args_fmt, &idx, &spec);
|
|
if (idx < 0 || idx >= channel_get_total()) {
|
|
mg_rpc_send_errorf(ri, 400, "idx must be between 0 and %d", channel_get_total() - 1);
|
|
ri = NULL;
|
|
goto exit;
|
|
}
|
|
ts = channel_get_timespec(idx);
|
|
if (!ts) {
|
|
mg_rpc_send_errorf(ri, 400, "timespec on channel %d isn't set", idx);
|
|
ri = NULL;
|
|
goto exit;
|
|
}
|
|
if (!timespec_add_spec(ts, spec)) {
|
|
mg_rpc_send_errorf(ri, 400, "timespec '%Q' is malformed", spec);
|
|
ri = NULL;
|
|
goto exit;
|
|
}
|
|
channel_override_set(idx);
|
|
|
|
mg_rpc_send_responsef(ri, "{idx: %d}", idx);
|
|
ri = NULL;
|
|
|
|
exit:
|
|
if (spec) {
|
|
free(spec);
|
|
}
|
|
(void)cb_arg;
|
|
(void)fi;
|
|
}
|
|
|
|
void rpc_init() {
|
|
struct mg_rpc *c = mgos_rpc_get_global();
|
|
|
|
mg_rpc_add_handler(c, "Channel.Toggle", "{idx: %d}", rpc_channel_toggle_handler, NULL);
|
|
mg_rpc_add_handler(c, "Channel.Get", "{idx: %d}", rpc_channel_get_handler, NULL);
|
|
mg_rpc_add_handler(c, "Channel.Set", "{idx: %d, value: %d, duration: %d}", rpc_channel_set_handler, NULL);
|
|
mg_rpc_add_handler(c, "Timespec.Get", "{idx: %d}", rpc_timespec_get_handler, NULL);
|
|
mg_rpc_add_handler(c, "Timespec.Clear", "{idx: %d}", rpc_timespec_clear_handler, NULL);
|
|
mg_rpc_add_handler(c, "Timespec.Add", "{idx: %d, spec: %Q}", rpc_timespec_add_handler, NULL);
|
|
}
|