#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); }