Mercurial > louis > mq > lightsd
changeset 241:aaa04a77415b
wip^2
author | Louis Opter <kalessin@kalessin.fr> |
---|---|
date | Tue, 11 Aug 2015 00:37:46 -0700 |
parents | fde704d2089a |
children | d23d6fbb50df |
files | implement_some_metadata_packet_types.h implement_some_metadata_packet_types.patch series |
diffstat | 3 files changed, 1270 insertions(+), 1180 deletions(-) [+] |
line wrap: on
line diff
--- a/implement_some_metadata_packet_types.h Sun Aug 09 17:06:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1179 +0,0 @@ -# HG changeset patch -# Parent 0955e5e7ff9f454c303a1314cfec54d3c3708cd5 -Handle various informational packet types - -- MCU state & firmware info; -- WiFi state & firmware info; -- device/runtime info. - -diff --git a/core/lightsd.h b/core/lightsd.h ---- a/core/lightsd.h -+++ b/core/lightsd.h -@@ -24,10 +24,33 @@ - #define LGTD_ABS(v) ((v) >= 0 ? (v) : (v) * -1) - #define LGTD_MIN(a, b) ((a) < (b) ? (a) : (b)) - #define LGTD_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) --#define LGTD_MSECS_TO_TIMEVAL(v) { \ -+#define LGTD_MSECS_TO_TIMEVAL(v) { \ - .tv_sec = (v) / 1000, \ - .tv_usec = ((v) % 1000) * 1000 \ - } -+#define LGTD_NSECS_TO_USECS(v) ((v) / (unsigned int)10E6) -+#define LGTD_NSECS_TO_SECS(v) ((v) / (unsigned int)10E9) -+#define LGTD_SECS_TO_NSECS(v) ((v) * (unsigned int)10E9) -+#define LGTD_TM_TO_ISOTIME(tm, sbuf, bufsz, usec) do { \ -+ /* '2015-01-02T10:13:16.132222+00:00' */ \ -+ if ((usec)) { \ -+ snprintf( \ -+ (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", \ -+ 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ -+ (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, (intmax_t)usec, \ -+ (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ -+ LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ -+ ); \ -+ } else { \ -+ snprintf( \ -+ (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d%c%02ld:%02ld", \ -+ 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ -+ (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, \ -+ (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ -+ LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ -+ ); \ -+ } \ -+} while (0) - - enum lgtd_verbosity { - LGTD_DEBUG = 0, -@@ -51,6 +74,11 @@ - void lgtd_sockaddrtoa(const struct sockaddr_storage *, char *buf, int buflen); - short lgtd_sockaddrport(const struct sockaddr_storage *); - -+void lgtd_print_duration(uint64_t, char *, int); -+#define LGTD_PRINT_DURATION(secs, arr) do { \ -+ lgtd_print_duration((secs), (arr), sizeof((arr))); \ -+} while (0) -+ - void _lgtd_err(void (*)(int, const char *, ...), int, const char *, ...) - __attribute__((format(printf, 3, 4))); - #define lgtd_err(eval, fmt, ...) _lgtd_err(err, (eval), (fmt), ##__VA_ARGS__); -diff --git a/core/log.c b/core/log.c ---- a/core/log.c -+++ b/core/log.c -@@ -52,14 +52,7 @@ - if (!localtime_r(&now.tv_sec, &tm_now)) { - goto error; - } -- // '2015-01-02T10:13:16.132222+00:00' -- snprintf( -- strbuf, bufsz, "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", -- 1900 + tm_now.tm_year, 1 + tm_now.tm_mon, tm_now.tm_mday, -- tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, -- (intmax_t)now.tv_usec, tm_now.tm_gmtoff >= 0 ? '+' : '-', // %+02ld doesn't work -- LGTD_ABS(tm_now.tm_gmtoff / 60 / 60), tm_now.tm_gmtoff % (60 * 60) -- ); -+ LGTD_TM_TO_ISOTIME(&tm_now, strbuf, bufsz, now.tv_usec); - return; - error: - strbuf[0] = '\0'; -@@ -110,6 +103,26 @@ - } - - void -+lgtd_print_duration(uint64_t secs, char *buf, int bufsz) -+{ -+ assert(buf); -+ assert(bufsz > 0); -+ -+ int days = secs / (60 * 60 * 24); -+ int minutes = secs / 60; -+ int hours = minutes / 60; -+ hours = hours % 24; -+ minutes = minutes % 60; -+ -+ int i = 0; -+ if (days) { -+ int n = snprintf(buf, bufsz, "%d days ", days); -+ i = LGTD_MIN(i + n, bufsz); -+ } -+ snprintf(&buf[i], bufsz - i, "%02d:%02d", hours, minutes); -+} -+ -+void - _lgtd_err(void (*errfn)(int, const char *, ...), - int eval, - const char *fmt, -diff --git a/core/proto.c b/core/proto.c ---- a/core/proto.c -+++ b/core/proto.c -@@ -196,6 +196,39 @@ - } - - static const char *state_fmt = ("{" -+ "\"_lifx\":{" -+ "\"gateway\":{" -+ "\"url\":\"tcp://%s:[%hu]\"," -+ "\"latency\":%d" -+ "}," -+ "\"mcu\":{" -+ "\"firmware_built_at\":\"%s\"," -+ "\"firmware_installed_at\":\"%s\"," -+ "\"firmware_version\":%u," -+ "\"signal_strength\":%u," -+ "\"tx_bytes\":%u," -+ "\"rx_bytes\":%u," -+ "\"unknown\":%u" -+ "}," -+ "\"wifi\":{" -+ "\"firmware_built_at\":\"%s\"," -+ "\"firmware_installed_at\":\"%s\"," -+ "\"signal_strength\":%u," -+ "\"tx_bytes\":%u," -+ "\"rx_bytes\":%u," -+ "\"unknown\":%u" -+ "}," -+ "\"product_info\":{" -+ "\"vendor_id\":%#x," -+ "\"product_id\":%#x," -+ "\"version\":%u" -+ "}," -+ "\"runtime_info\":{" -+ "\"time\":\"%s\"," -+ "\"uptime\":%ju," -+ "\"downtime\":%ju" -+ "}" -+ "}," - "\"hsbk\":[%s,%s,%s,%hu]," - "\"power\":%s," - "\"label\":\"%s\"," -@@ -206,6 +239,21 @@ - (src), (start), (stop), (dst), sizeof((dst)) \ - ) - -+#define PRINT_LIFX_FW_TIMESTAMPS(fw_info, built_at_buf, installed_at_buf) \ -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP((fw_info)->built_at, (built_at_buf)); \ -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP( \ -+ (fw_info)->installed_at, (installed_at_buf) \ -+ ) -+ -+#define LIFX_IP_STATUS_ARGS(b, ip) \ -+ ip##_fw_built_at, \ -+ ip##_fw_installed_at, \ -+ (b)->ip##_fw_info.version, \ -+ (b)->ip##_state.signal_strength, \ -+ (b)->ip##_state.tx_bytes, \ -+ (b)->ip##_state.rx_bytes, \ -+ (b)->ip##_state.reserved -+ - lgtd_client_start_send_response(client); - lgtd_client_write_string(client, "["); - struct lgtd_router_device *device; -@@ -217,9 +265,33 @@ - PRINT_COMPONENT(bulb->state.saturation, s, 0, 1); - PRINT_COMPONENT(bulb->state.brightness, b, 0, 1); - -- char buf[3072]; -+ char mcu_fw_built_at[64], mcu_fw_installed_at[64], bulb_time[64]; -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(bulb->runtime_info.time, bulb_time); -+ PRINT_LIFX_FW_TIMESTAMPS( -+ &bulb->mcu_fw_info, mcu_fw_built_at, mcu_fw_installed_at -+ ); -+ -+ char wifi_fw_built_at[64], wifi_fw_installed_at[64]; -+ PRINT_LIFX_FW_TIMESTAMPS( -+ &bulb->wifi_fw_info, wifi_fw_built_at, wifi_fw_installed_at -+ ); -+ -+ char buf[4096]; - int written = snprintf( - buf, sizeof(buf), state_fmt, -+ // _lifx.gateway: -+ bulb->gw->ip_addr, bulb->gw->port, -+ LGTD_LIFX_GATEWAY_LATENCY(bulb->gw), -+ // _lifx.{mcu,wifi}: -+ LIFX_IP_STATUS_ARGS(bulb, mcu), LIFX_IP_STATUS_ARGS(bulb, wifi), -+ // _lifx.product_info: -+ bulb->product_info.vendor_id, bulb->product_info.product_id, -+ bulb->product_info.version, -+ // _lifx.runtime_info: -+ bulb_time, -+ LGTD_NSECS_TO_SECS(bulb->runtime_info.uptime), -+ LGTD_NSECS_TO_SECS(bulb->runtime_info.downtime), -+ // bulb state: - h, s, b, bulb->state.kelvin, - bulb->state.power == LGTD_LIFX_POWER_ON ? "true" : "false", - bulb->state.label[0] ? bulb->state.label : lgtd_addrtoa(bulb->addr) -diff --git a/lifx/bulb.c b/lifx/bulb.c ---- a/lifx/bulb.c -+++ b/lifx/bulb.c -@@ -154,3 +154,69 @@ - - bulb->state.tags = tags; - } -+ -+void -+lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_ip_state *state, -+ lgtd_time_mono_t received_at) -+{ -+ assert(bulb); -+ assert(state); -+ -+ bulb->last_mcu_state_at = received_at; -+ memcpy(&bulb->mcu_state, state, sizeof(bulb->mcu_state)); -+} -+ -+void -+lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_ip_firmware_info *info) -+{ -+ assert(bulb); -+ assert(info); -+ -+ memcpy(&bulb->mcu_fw_info, info, sizeof(bulb->mcu_fw_info)); -+} -+ -+void -+lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_ip_state *state, -+ lgtd_time_mono_t received_at) -+{ -+ assert(bulb); -+ assert(state); -+ -+ bulb->last_wifi_state_at = received_at; -+ memcpy(&bulb->wifi_state, state, sizeof(bulb->wifi_state)); -+} -+ -+void -+lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_ip_firmware_info *info) -+{ -+ assert(bulb); -+ assert(info); -+ -+ memcpy(&bulb->wifi_fw_info, info, sizeof(bulb->wifi_fw_info)); -+} -+ -+void -+lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_product_info *info) -+{ -+ assert(bulb); -+ assert(info); -+ -+ memcpy(&bulb->product_info, info, sizeof(bulb->product_info)); -+} -+ -+void -+lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *bulb, -+ const struct lgtd_lifx_runtime_info *info, -+ lgtd_time_mono_t received_at) -+{ -+ assert(bulb); -+ assert(info); -+ -+ bulb->last_runtime_info_at = received_at; -+ memcpy(&bulb->runtime_info, info, sizeof(bulb->runtime_info)); -+} -diff --git a/lifx/bulb.h b/lifx/bulb.h ---- a/lifx/bulb.h -+++ b/lifx/bulb.h -@@ -30,17 +30,51 @@ - char label[LGTD_LIFX_LABEL_SIZE]; - uint64_t tags; - }; -+ -+struct lgtd_lifx_ip_state { -+ float signal_strength; // mW -+ uint32_t tx_bytes; -+ uint32_t rx_bytes; -+ uint16_t reserved; // Temperature? -+}; -+ -+struct lgtd_lifx_ip_firmware_info { -+ uint64_t built_at; // ns since epoch -+ uint64_t installed_at; // ns since epoch -+ uint32_t version; -+}; -+ -+struct lgtd_lifx_product_info { -+ uint32_t vendor_id; -+ uint32_t product_id; -+ uint32_t version; -+}; -+ -+struct lgtd_lifx_runtime_info { -+ uint64_t time; // ns since epoch -+ uint64_t uptime; // ns -+ uint64_t downtime; // ns, last power off period duration -+}; - #pragma pack(pop) - - struct lgtd_lifx_bulb { -- RB_ENTRY(lgtd_lifx_bulb) link; -- SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; -- struct lgtd_lifx_gateway *gw; -- uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; -- struct lgtd_lifx_light_state state; -- lgtd_time_mono_t last_light_state_at; -- lgtd_time_mono_t dirty_at; -- uint16_t expected_power_on; -+ RB_ENTRY(lgtd_lifx_bulb) link; -+ SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; -+ lgtd_time_mono_t last_light_state_at; -+ lgtd_time_mono_t last_mcu_state_at; -+ lgtd_time_mono_t last_wifi_state_at; -+ lgtd_time_mono_t last_runtime_info_at; -+ lgtd_time_mono_t dirty_at; -+ uint16_t expected_power_on; -+ uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; -+ struct lgtd_lifx_gateway *gw; -+ struct lgtd_lifx_light_state state; -+ struct lgtd_lifx_ip_state mcu_state; -+ struct lgtd_lifx_ip_state wifi_state; -+ struct lgtd_lifx_ip_firmware_info mcu_fw_info; -+ struct lgtd_lifx_ip_firmware_info wifi_fw_info; -+ struct lgtd_lifx_product_info product_info; -+ struct lgtd_lifx_runtime_info runtime_info; - }; - RB_HEAD(lgtd_lifx_bulb_map, lgtd_lifx_bulb); - SLIST_HEAD(lgtd_lifx_bulb_list, lgtd_lifx_bulb); -@@ -69,3 +103,19 @@ - lgtd_time_mono_t); - void lgtd_lifx_bulb_set_power_state(struct lgtd_lifx_bulb *, uint16_t); - void lgtd_lifx_bulb_set_tags(struct lgtd_lifx_bulb *, uint64_t); -+ -+void lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_state *, -+ lgtd_time_mono_t); -+void lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_firmware_info *); -+void lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_state *, -+ lgtd_time_mono_t); -+void lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_firmware_info *); -+void lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_product_info *); -+void lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_runtime_info *, -+ lgtd_time_mono_t); -diff --git a/lifx/gateway.c b/lifx/gateway.c ---- a/lifx/gateway.c -+++ b/lifx/gateway.c -@@ -489,12 +489,8 @@ - (uintmax_t)pkt->tags - ); - -- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( -- gw, hdr->target.device_addr -- ); -- if (!b) { -- return; -- } -+ struct lgtd_lifx_bulb *b; -+ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); - - assert(sizeof(*pkt) == sizeof(b->state)); - lgtd_lifx_bulb_set_light_state( -@@ -519,7 +515,7 @@ - } - } - -- int latency = gw->last_pkt_at - gw->last_req_at; -+ int latency = LGTD_LIFX_GATEWAY_LATENCY(gw); - if (latency < LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS) { - if (!event_pending(gw->refresh_ev, EV_TIMEOUT, NULL)) { - int timeout = LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS - latency; -@@ -559,14 +555,9 @@ - gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), pkt->power - ); - -- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( -- gw, hdr->target.device_addr -+ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( -+ gw, hdr->target.device_addr, lgtd_lifx_bulb_set_power_state, pkt->power - ); -- if (!b) { -- return; -- } -- -- lgtd_lifx_bulb_set_power_state(b, pkt->power); - } - - int -@@ -673,9 +664,10 @@ - } - } - --void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_tags *pkt) -+void -+lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_tags *pkt) - { - assert(gw && hdr && pkt); - -@@ -685,12 +677,8 @@ - (uintmax_t)pkt->tags - ); - -- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( -- gw, hdr->target.device_addr -- ); -- if (!b) { -- return; -- } -+ struct lgtd_lifx_bulb *b; -+ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); - - int tag_id; - LGTD_LIFX_WIRE_FOREACH_TAG_ID(tag_id, pkt->tags) { -@@ -707,3 +695,142 @@ - - lgtd_lifx_bulb_set_tags(b, pkt->tags); - } -+ -+void -+lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_ip_state *pkt) -+{ -+ assert(gw && hdr && pkt); -+ -+ const char *type; -+ void (*bulb_fn)(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_state *, -+ lgtd_time_mono_t); -+ -+ switch (hdr->packet_type) { -+ case LGTD_LIFX_MESH_INFO: -+ type = "MCU_STATE"; -+ bulb_fn = lgtd_lifx_bulb_set_mcu_state; -+ break; -+ case LGTD_LIFX_WIFI_INFO: -+ type = "WIFI_STATE"; -+ bulb_fn = lgtd_lifx_bulb_set_wifi_state; -+ break; -+ default: -+ lgtd_info("invalid ip state packet_type %#hx", hdr->packet_type); -+#ifndef NDEBUG -+ abort(); -+#endif -+ return; -+ } -+ -+ lgtd_debug( -+ "%s <-- [%s]:%hu - %s " -+ "signal_strength=%f, rx_bytes=%u, tx_bytes=%u, reserved=%hu", -+ type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), -+ pkt->signal_strength, pkt->rx_bytes, pkt->tx_bytes, pkt->reserved -+ ); -+ -+ LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( -+ gw, -+ hdr->target.device_addr, -+ bulb_fn, -+ (const struct lgtd_lifx_ip_state *)pkt -+ ); -+} -+ -+void -+lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_ip_firmware_info *pkt) -+{ -+ assert(gw && hdr && pkt); -+ -+ const char *type; -+ void (*bulb_fn)(struct lgtd_lifx_bulb *, -+ const struct lgtd_lifx_ip_firmware_info *); -+ -+ switch (hdr->packet_type) { -+ case LGTD_LIFX_MESH_FIRMWARE: -+ type = "MCU_FIRMWARE_INFO"; -+ bulb_fn = lgtd_lifx_bulb_set_mcu_firmware_info; -+ break; -+ case LGTD_LIFX_WIFI_FIRMWARE_STATE: -+ type = "WIFI_FIRMWARE_INFO"; -+ bulb_fn = lgtd_lifx_bulb_set_wifi_firmware_info; -+ break; -+ default: -+ lgtd_info("invalid ip firmware packet_type %#hx", hdr->packet_type); -+#ifndef NDEBUG -+ abort(); -+#endif -+ return; -+ } -+ -+ char built_at[64], installed_at[64]; -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->built_at, built_at); -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->installed_at, installed_at); -+ -+ lgtd_debug( -+ "%s <-- [%s]:%hu - %s " -+ "built_at=%s, installed_at=%s, version=%u", -+ type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), -+ built_at, installed_at, pkt->version -+ ); -+ -+ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( -+ gw, -+ hdr->target.device_addr, -+ bulb_fn, -+ (const struct lgtd_lifx_ip_firmware_info *)pkt -+ ); -+} -+ -+void -+lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_product_info *pkt) -+{ -+ assert(gw && hdr && pkt); -+ -+ lgtd_debug( -+ "PRODUCT_INFO <-- [%s]:%hu - %s " -+ "vendor_id=%#x, product_id=%#x, version=%u", -+ gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), -+ pkt->vendor_id, pkt->product_id, pkt->version -+ ); -+ -+ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( -+ gw, -+ hdr->target.device_addr, -+ lgtd_lifx_bulb_set_product_info, -+ (const struct lgtd_lifx_product_info *)pkt -+ ); -+} -+ -+void -+lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_runtime_info *pkt) -+{ -+ assert(gw && hdr && pkt); -+ -+ char device_time[64], uptime[64], downtime[64]; -+ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->time, device_time); -+ LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->uptime), uptime); -+ LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->downtime), downtime); -+ -+ lgtd_debug( -+ "PRODUCT_INFO <-- [%s]:%hu - %s time=%s, uptime=%s, downtime=%s", -+ gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), -+ device_time, uptime, downtime -+ ); -+ -+ LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( -+ gw, -+ hdr->target.device_addr, -+ lgtd_lifx_bulb_set_runtime_info, -+ (const struct lgtd_lifx_runtime_info *)pkt -+ ); -+} -diff --git a/lifx/gateway.h b/lifx/gateway.h ---- a/lifx/gateway.h -+++ b/lifx/gateway.h -@@ -36,6 +36,12 @@ - struct lgtd_lifx_gateway { - LIST_ENTRY(lgtd_lifx_gateway) link; - struct lgtd_lifx_bulb_list bulbs; -+#define LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr) do { \ -+ (b) = lgtd_lifx_gateway_get_or_open_bulb((gw), (bulb_addr)); \ -+ if (!(b)) { \ -+ return; \ -+ } \ -+} while (0) - // Multiple gateways can share the same site (that happens when bulbs are - // far away enough that ZigBee can't be used). Moreover the SET_PAN_GATEWAY - // packet doesn't include the device address in the header (i.e: site and -@@ -60,6 +66,7 @@ - lgtd_time_mono_t last_req_at; - lgtd_time_mono_t next_req_at; - lgtd_time_mono_t last_pkt_at; -+#define LGTD_LIFX_GATEWAY_LATENCY(gw) ((gw)->last_pkt_at - (gw)->last_req_at) - struct lgtd_lifx_message pkt_ring[LGTD_LIFX_GATEWAY_PACKET_RING_SIZE]; - #define LGTD_LIFX_GATEWAY_INC_MESSAGE_RING_INDEX(idx) do { \ - (idx) += 1; \ -@@ -77,6 +84,18 @@ - - extern struct lgtd_lifx_gateway_list lgtd_lifx_gateways; - -+#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR(gw, bulb_addr, bulb_fn, payload) do { \ -+ struct lgtd_lifx_bulb *b; \ -+ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ -+ (bulb_fn)(b, (payload)); \ -+} while (0) -+ -+#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT(gw, bulb_addr, bulb_fn, payload) do { \ -+ struct lgtd_lifx_bulb *b; \ -+ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ -+ (bulb_fn)(b, (payload), (gw)->last_pkt_at); \ -+} while (0) -+ - struct lgtd_lifx_gateway *lgtd_lifx_gateway_get(const struct sockaddr_storage *); - struct lgtd_lifx_gateway *lgtd_lifx_gateway_open(const struct sockaddr_storage *, - ev_socklen_t, -@@ -119,3 +138,15 @@ - void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *, - const struct lgtd_lifx_packet_header *, - const struct lgtd_lifx_packet_tags *); -+void lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *, -+ const struct lgtd_lifx_packet_header *, -+ const struct lgtd_lifx_packet_ip_state *); -+void lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *, -+ const struct lgtd_lifx_packet_header *, -+ const struct lgtd_lifx_packet_ip_firmware_info *); -+void lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *, -+ const struct lgtd_lifx_packet_header *, -+ const struct lgtd_lifx_packet_product_info *); -+void lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *, -+ const struct lgtd_lifx_packet_header *, -+ const struct lgtd_lifx_packet_runtime_info *); -diff --git a/lifx/wire_proto.c b/lifx/wire_proto.c ---- a/lifx/wire_proto.c -+++ b/lifx/wire_proto.c -@@ -24,8 +24,10 @@ - #include <stdarg.h> - #include <stdbool.h> - #include <stdint.h> -+#include <stdio.h> - #include <stdlib.h> - #include <string.h> -+#include <time.h> - - #include <event2/util.h> - -@@ -74,12 +76,32 @@ - (void)pkt; - } - -+static void -+lgtd_lifx_wire_enosys_packet_handler(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const void *pkt) -+{ -+ (void)pkt; -+ -+ const struct lgtd_lifx_packet_info *pkt_info; -+ pkt_info = lgtd_lifx_wire_get_packet_info(hdr->packet_type); -+ bool addressable = hdr->protocol & LGTD_LIFX_PROTOCOL_ADDRESSABLE; -+ bool tagged = hdr->protocol & LGTD_LIFX_PROTOCOL_TAGGED; -+ unsigned int protocol = hdr->protocol & LGTD_LIFX_PROTOCOL_VERSION_MASK; -+ lgtd_info( -+ "%s <-- [%s]:%hu - (Unimplemented, header info: " -+ "addressable=%d, tagged=%d, protocol=%d)", -+ pkt_info->name, gw->ip_addr, gw->port, -+ addressable, tagged, protocol -+ ); -+} -+ - void - lgtd_lifx_wire_load_packet_info_map(void) - { - #define DECODER(x) ((void (*)(void *))(x)) - #define ENCODER(x) ((void (*)(void *))(x)) --#define HANDLER(x) \ -+#define HANDLER(x) \ - ((void (*)(struct lgtd_lifx_gateway *, \ - const struct lgtd_lifx_packet_header *, \ - const void *))(x)) -@@ -90,6 +112,10 @@ - #define REQUEST_ONLY \ - .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ - .handle = lgtd_lifx_wire_null_packet_handler -+#define UNIMPLEMENTED \ -+ .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ -+ .encode = lgtd_lifx_wire_null_packet_encoder_decoder, \ -+ .handle = lgtd_lifx_wire_enosys_packet_handler - - static struct lgtd_lifx_packet_info packet_table[] = { - // Gateway packets: -@@ -187,6 +213,211 @@ - .size = sizeof(struct lgtd_lifx_packet_tags), - .decode = DECODER(lgtd_lifx_wire_decode_tags), - .handle = HANDLER(lgtd_lifx_gateway_handle_tags) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_MESH_INFO", -+ .type = LGTD_LIFX_GET_MESH_INFO -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "MESH_INFO", -+ .type = LGTD_LIFX_MESH_INFO, -+ .size = sizeof(struct lgtd_lifx_packet_ip_state), -+ .decode = DECODER(lgtd_lifx_wire_decode_ip_state), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_MESH_FIRMWARE", -+ .type = LGTD_LIFX_GET_MESH_FIRMWARE -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "MESH_FIRMWARE", -+ .type = LGTD_LIFX_MESH_FIRMWARE, -+ .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), -+ .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_WIFI_INFO", -+ .type = LGTD_LIFX_GET_WIFI_INFO, -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "WIFI_INFO", -+ .type = LGTD_LIFX_WIFI_INFO, -+ .size = sizeof(struct lgtd_lifx_packet_ip_state), -+ .decode = DECODER(lgtd_lifx_wire_decode_ip_state), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_WIFI_FIRMWARE_STATE", -+ .type = LGTD_LIFX_GET_WIFI_FIRMWARE_STATE -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "WIFI_FIRMWARE_STATE", -+ .type = LGTD_LIFX_WIFI_FIRMWARE_STATE, -+ .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), -+ .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_VERSION", -+ .type = LGTD_LIFX_GET_VERSION -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "VERSION_STATE", -+ .type = LGTD_LIFX_VERSION_STATE, -+ .size = sizeof(struct lgtd_lifx_packet_product_info), -+ .decode = DECODER(lgtd_lifx_wire_decode_product_info), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_product_info) -+ }, -+ { -+ REQUEST_ONLY, -+ NO_PAYLOAD, -+ .name = "GET_INFO", -+ .type = LGTD_LIFX_GET_INFO -+ }, -+ { -+ RESPONSE_ONLY, -+ .name = "INFO_STATE", -+ .type = LGTD_LIFX_INFO_STATE, -+ .size = sizeof(struct lgtd_lifx_packet_runtime_info), -+ .decode = DECODER(lgtd_lifx_wire_decode_runtime_info), -+ .handle = HANDLER(lgtd_lifx_gateway_handle_runtime_info) -+ }, -+ // Unimplemented but "known" packets -+ { -+ UNIMPLEMENTED, -+ .name = "GET_TIME", -+ .type = LGTD_LIFX_GET_TIME -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_TIME", -+ .type = LGTD_LIFX_SET_TIME -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "TIME_STATE", -+ .type = LGTD_LIFX_TIME_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "GET_RESET_SWITCH_STATE", -+ .type = LGTD_LIFX_GET_RESET_SWITCH_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "RESET_SWITCH_STATE", -+ .type = LGTD_LIFX_RESET_SWITCH_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "GET_BULB_LABEL", -+ .type = LGTD_LIFX_GET_BULB_LABEL -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_BULB_LABEL", -+ .type = LGTD_LIFX_SET_BULB_LABEL -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "BULB_LABEL", -+ .type = LGTD_LIFX_BULB_LABEL -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "GET_MCU_RAIL_VOLTAGE", -+ .type = LGTD_LIFX_GET_MCU_RAIL_VOLTAGE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "MCU_RAIL_VOLTAGE", -+ .type = LGTD_LIFX_MCU_RAIL_VOLTAGE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "REBOOT", -+ .type = LGTD_LIFX_REBOOT -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_FACTORY_TEST_MODE", -+ .type = LGTD_LIFX_SET_FACTORY_TEST_MODE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "DISABLE_FACTORY_TEST_MODE", -+ .type = LGTD_LIFX_DISABLE_FACTORY_TEST_MODE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "ACK", -+ .type = LGTD_LIFX_ACK -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "ECHO_REQUEST", -+ .type = LGTD_LIFX_ECHO_REQUEST -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "ECHO_RESPONSE", -+ .type = LGTD_LIFX_ECHO_RESPONSE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_DIM_ABSOLUTE", -+ .type = LGTD_LIFX_SET_DIM_ABSOLUTE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_DIM_RELATIVE", -+ .type = LGTD_LIFX_SET_DIM_RELATIVE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "GET_WIFI_STATE", -+ .type = LGTD_LIFX_GET_WIFI_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_WIFI_STATE", -+ .type = LGTD_LIFX_SET_WIFI_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "WIFI_STATE", -+ .type = LGTD_LIFX_WIFI_STATE -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "GET_ACCESS_POINTS", -+ .type = LGTD_LIFX_GET_ACCESS_POINTS -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "SET_ACCESS_POINTS", -+ .type = LGTD_LIFX_SET_ACCESS_POINTS -+ }, -+ { -+ UNIMPLEMENTED, -+ .name = "ACCESS_POINT", -+ .type = LGTD_LIFX_ACCESS_POINT - } - }; - -@@ -234,6 +465,24 @@ - return LGTD_LIFX_WAVEFORM_INVALID; - } - -+void -+lgtd_lifx_wire_print_nsec_timestamp(uint64_t nsec_ts, char *buf, int bufsz) -+{ -+ assert(buf); -+ assert(bufsz > 0); -+ -+ time_t ts = LGTD_NSECS_TO_SECS(nsec_ts); -+ -+ struct tm tm_utc; -+ if (gmtime_r(&ts, &tm_utc)) { -+ int64_t usecs = LGTD_NSECS_TO_USECS(nsec_ts - LGTD_SECS_TO_NSECS(ts)); -+ LGTD_TM_TO_ISOTIME(&tm_utc, buf, bufsz, usecs); -+ return; -+ } -+ -+ buf[0] = '\0'; -+} -+ - static void - lgtd_lifx_wire_encode_header(struct lgtd_lifx_packet_header *hdr, int flags) - { -@@ -423,3 +672,44 @@ - - pkt->tags = le64toh(pkt->tags); - } -+ -+void -+lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *pkt) -+{ -+ assert(pkt); -+ -+ pkt->signal_strength = lgtd_lifx_wire_lefloattoh(pkt->signal_strength); -+ pkt->tx_bytes = le32toh(pkt->tx_bytes); -+ pkt->rx_bytes = le32toh(pkt->rx_bytes); -+ pkt->reserved = le16toh(pkt->reserved); -+} -+ -+void -+lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *pkt) -+{ -+ assert(pkt); -+ -+ pkt->built_at = le64toh(pkt->built_at); -+ pkt->installed_at = le64toh(pkt->installed_at); -+ pkt->version = le32toh(pkt->version); -+} -+ -+void -+lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *pkt) -+{ -+ assert(pkt); -+ -+ pkt->vendor_id = le32toh(pkt->vendor_id); -+ pkt->product_id = le32toh(pkt->product_id); -+ pkt->version = le32toh(pkt->version); -+} -+ -+void -+lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *pkt) -+{ -+ assert(pkt); -+ -+ pkt->time = le64toh(pkt->time); -+ pkt->uptime = le64toh(pkt->uptime); -+ pkt->downtime = le64toh(pkt->downtime); -+} -diff --git a/lifx/wire_proto.h b/lifx/wire_proto.h ---- a/lifx/wire_proto.h -+++ b/lifx/wire_proto.h -@@ -28,7 +28,7 @@ - static inline floatle_t - lgtd_lifx_wire_htolefloat(float f) - { -- union { float f; uint32_t i; } u = { .f = f }; -+ union { float f; uint32_t i; } u = { .f = f }; - htole32(u.i); - return u.f; - } -@@ -36,7 +36,7 @@ - static inline floatle_t - lgtd_lifx_wire_lefloattoh(float f) - { -- union { float f; uint32_t i; } u = { .f = f }; -+ union { float f; uint32_t i; } u = { .f = f }; - le32toh(u.i); - return u.f; - } -@@ -249,6 +249,30 @@ - char label[LGTD_LIFX_LABEL_SIZE]; - }; - -+struct lgtd_lifx_packet_ip_state { -+ floatle_t signal_strength; -+ uint32le_t tx_bytes; -+ uint32le_t rx_bytes; -+ uint16le_t reserved; -+}; -+ -+struct lgtd_lifx_packet_ip_firmware_info { -+ uint64le_t built_at; -+ uint64le_t installed_at; -+ uint32le_t version; -+}; -+ -+struct lgtd_lifx_packet_product_info { -+ uint32le_t vendor_id; -+ uint32le_t product_id; -+ uint32le_t version; -+}; -+ -+struct lgtd_lifx_packet_runtime_info { -+ uint64le_t time; -+ uint64le_t uptime; -+ uint64le_t downtime; -+}; - #pragma pack(pop) - - enum lgtd_lifx_header_flags { -@@ -330,8 +354,11 @@ - (tag_id_varname) != -1; \ - (tag_id_varname) = lgtd_lifx_wire_next_tag_id((tag_id_varname), (tags))) - -- - enum lgtd_lifx_waveform_type lgtd_lifx_wire_waveform_string_id_to_type(const char *, int); -+void lgtd_lifx_wire_print_nsec_timestamp(uint64_t, char *, int); -+#define LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(ts, arr) do { \ -+ lgtd_lifx_wire_print_nsec_timestamp((ts), (arr), sizeof((arr))); \ -+} while (0) - - const struct lgtd_lifx_packet_info *lgtd_lifx_wire_get_packet_info(enum lgtd_lifx_packet_type); - void lgtd_lifx_wire_load_packet_info_map(void); -@@ -356,3 +383,8 @@ - void lgtd_lifx_wire_decode_tags(struct lgtd_lifx_packet_tags *); - void lgtd_lifx_wire_encode_tag_labels(struct lgtd_lifx_packet_tag_labels *); - void lgtd_lifx_wire_decode_tag_labels(struct lgtd_lifx_packet_tag_labels *); -+ -+void lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *); -+void lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *); -+void lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *); -+void lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *); -diff --git a/tests/lifx/mock_gateway.h b/tests/lifx/mock_gateway.h ---- a/tests/lifx/mock_gateway.h -+++ b/tests/lifx/mock_gateway.h -@@ -129,3 +129,51 @@ - (void)pkt_tags; - } - #endif -+ -+#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_STATE -+void -+lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_ip_state *pkt) -+{ -+ (void)gw; -+ (void)hdr; -+ (void)pkt; -+} -+#endif -+ -+#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_FIRMWARE_INFO -+void -+lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_ip_firmware_info *pkt) -+{ -+ (void)gw; -+ (void)hdr; -+ (void)pkt; -+} -+#endif -+ -+#ifndef LGTD_LIFX_GATEWAY_HANDLE_PRODUCT_INFO -+void -+lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_product_info *pkt) -+{ -+ (void)gw; -+ (void)hdr; -+ (void)pkt; -+} -+#endif -+ -+#ifndef LGTD_LIFX_GATEWAY_HANDLE_RUNTIME_INFO -+void -+lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, -+ const struct lgtd_lifx_packet_header *hdr, -+ const struct lgtd_lifx_packet_runtime_info *pkt) -+{ -+ (void)gw; -+ (void)hdr; -+ (void)pkt; -+} -+#endif -diff --git a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c ---- a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c -+++ b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c -@@ -2,7 +2,7 @@ - - #include "wire_proto.c" - --#include "test_wire_proto_utils.h" -+#include "mock_gateway.h" - - int - main(void) -diff --git a/tests/lifx/wire_proto/test_wire_proto_utils.h b/tests/lifx/wire_proto/test_wire_proto_utils.h -deleted file mode 100644 ---- a/tests/lifx/wire_proto/test_wire_proto_utils.h -+++ /dev/null -@@ -1,46 +0,0 @@ --#pragma once -- --void lgtd_lifx_gateway_handle_pan_gateway(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_pan_gateway *pkt) --{ -- (void)gw; -- (void)hdr; -- (void)pkt; --} -- --void lgtd_lifx_gateway_handle_light_status(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_light_status *pkt) --{ -- (void)gw; -- (void)hdr; -- (void)pkt; --} -- --void lgtd_lifx_gateway_handle_power_state(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_power_state *pkt) --{ -- (void)gw; -- (void)hdr; -- (void)pkt; --} -- --void lgtd_lifx_gateway_handle_tag_labels(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_tag_labels *pkt) --{ -- (void)gw; -- (void)hdr; -- (void)pkt; --} -- --void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, -- const struct lgtd_lifx_packet_header *hdr, -- const struct lgtd_lifx_packet_tags *pkt) --{ -- (void)gw; -- (void)hdr; -- (void)pkt; --} -diff --git a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c ---- a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c -+++ b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c -@@ -1,6 +1,6 @@ - #include "wire_proto.c" - --#include "test_wire_proto_utils.h" -+#include "mock_gateway.h" - - int - main(void)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/implement_some_metadata_packet_types.patch Tue Aug 11 00:37:46 2015 -0700 @@ -0,0 +1,1269 @@ +# HG changeset patch +# Parent 42808e1e42e717f87163cdaf63d9c72303da2d9f +Handle various informational packet types + +- MCU state & firmware info; +- WiFi state & firmware info; +- device/runtime info. + +diff --git a/core/daemon.c b/core/daemon.c +--- a/core/daemon.c ++++ b/core/daemon.c +@@ -106,10 +106,9 @@ + char title[LGTD_DAEMON_TITLE_SIZE] = { 0 }; + int i = 0; + +-#define TITLE_APPEND(fmt, ...) do { \ +- int n = snprintf((&title[i]), (sizeof(title) - i), (fmt), __VA_ARGS__); \ +- i = LGTD_MIN(i + n, (int)sizeof(title)); \ +-} while (0) ++#define TITLE_APPEND(fmt, ...) LGTD_SNPRINTF_APPEND( \ ++ title, i, (int)sizeof(title), (fmt), __VA_ARGS__ \ ++) + + #define PREFIX(fmt, ...) TITLE_APPEND( \ + "%s" fmt, (i && title[i - 1] == ')' ? "; " : ""), __VA_ARGS__ \ +diff --git a/core/lightsd.h b/core/lightsd.h +--- a/core/lightsd.h ++++ b/core/lightsd.h +@@ -24,10 +24,37 @@ + #define LGTD_ABS(v) ((v) >= 0 ? (v) : (v) * -1) + #define LGTD_MIN(a, b) ((a) < (b) ? (a) : (b)) + #define LGTD_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +-#define LGTD_MSECS_TO_TIMEVAL(v) { \ ++#define LGTD_MSECS_TO_TIMEVAL(v) { \ + .tv_sec = (v) / 1000, \ + .tv_usec = ((v) % 1000) * 1000 \ + } ++#define LGTD_NSECS_TO_USECS(v) ((v) / (unsigned int)10E6) ++#define LGTD_NSECS_TO_SECS(v) ((v) / (unsigned int)10E9) ++#define LGTD_SECS_TO_NSECS(v) ((v) * (unsigned int)10E9) ++#define LGTD_TM_TO_ISOTIME(tm, sbuf, bufsz, usec) do { \ ++ /* '2015-01-02T10:13:16.132222+00:00' */ \ ++ if ((usec)) { \ ++ snprintf( \ ++ (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", \ ++ 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ ++ (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, (intmax_t)usec, \ ++ (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ ++ LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ ++ ); \ ++ } else { \ ++ snprintf( \ ++ (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d%c%02ld:%02ld", \ ++ 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ ++ (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, \ ++ (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ ++ LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ ++ ); \ ++ } \ ++} while (0) ++#define LGTD_SNPRINTF_APPEND(buf, i, bufsz, ...) do { \ ++ int n = snprintf(&(buf)[(i)], bufsz - i, __VA_ARGS__); \ ++ (i) = LGTD_MIN((i) + n, bufsz); \ ++while (0) + + enum lgtd_verbosity { + LGTD_DEBUG = 0, +@@ -51,6 +78,11 @@ + void lgtd_sockaddrtoa(const struct sockaddr_storage *, char *buf, int buflen); + short lgtd_sockaddrport(const struct sockaddr_storage *); + ++void lgtd_print_duration(uint64_t, char *, int); ++#define LGTD_PRINT_DURATION(secs, arr) do { \ ++ lgtd_print_duration((secs), (arr), sizeof((arr))); \ ++} while (0) ++ + void _lgtd_err(void (*)(int, const char *, ...), int, const char *, ...) + __attribute__((format(printf, 3, 4))); + #define lgtd_err(eval, fmt, ...) _lgtd_err(err, (eval), (fmt), ##__VA_ARGS__); +diff --git a/core/log.c b/core/log.c +--- a/core/log.c ++++ b/core/log.c +@@ -52,14 +52,7 @@ + if (!localtime_r(&now.tv_sec, &tm_now)) { + goto error; + } +- // '2015-01-02T10:13:16.132222+00:00' +- snprintf( +- strbuf, bufsz, "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", +- 1900 + tm_now.tm_year, 1 + tm_now.tm_mon, tm_now.tm_mday, +- tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, +- (intmax_t)now.tv_usec, tm_now.tm_gmtoff >= 0 ? '+' : '-', // %+02ld doesn't work +- LGTD_ABS(tm_now.tm_gmtoff / 60 / 60), tm_now.tm_gmtoff % (60 * 60) +- ); ++ LGTD_TM_TO_ISOTIME(&tm_now, strbuf, bufsz, now.tv_usec); + return; + error: + strbuf[0] = '\0'; +@@ -110,6 +103,26 @@ + } + + void ++lgtd_print_duration(uint64_t secs, char *buf, int bufsz) ++{ ++ assert(buf); ++ assert(bufsz > 0); ++ ++ int days = secs / (60 * 60 * 24); ++ int minutes = secs / 60; ++ int hours = minutes / 60; ++ hours = hours % 24; ++ minutes = minutes % 60; ++ ++ int i = 0; ++ if (days) { ++ int n = snprintf(buf, bufsz, "%d days ", days); ++ i = LGTD_MIN(i + n, bufsz); ++ } ++ snprintf(&buf[i], bufsz - i, "%02d:%02d", hours, minutes); ++} ++ ++void + _lgtd_err(void (*errfn)(int, const char *, ...), + int eval, + const char *fmt, +diff --git a/core/proto.c b/core/proto.c +--- a/core/proto.c ++++ b/core/proto.c +@@ -196,6 +196,40 @@ + } + + static const char *state_fmt = ("{" ++ "\"_lifx\":{" ++ "\"gateway\":{" ++ "\"url\":\"tcp://%s:[%hu]\"," ++ "\"latency\":%d" ++ "}," ++ "\"mcu\":{" ++ "\"firmware_built_at\":\"%s\"," ++ "\"firmware_installed_at\":\"%s\"," ++ "\"firmware_version\":%u," ++ "\"signal_strength\":%u," ++ "\"tx_bytes\":%u," ++ "\"rx_bytes\":%u," ++ "\"unknown\":%u" ++ "}," ++ "\"wifi\":{" ++ "\"firmware_built_at\":\"%s\"," ++ "\"firmware_installed_at\":\"%s\"," ++ "\"firmware_version\":%u," ++ "\"signal_strength\":%u," ++ "\"tx_bytes\":%u," ++ "\"rx_bytes\":%u," ++ "\"unknown\":%u" ++ "}," ++ "\"product_info\":{" ++ "\"vendor_id\":%#x," ++ "\"product_id\":%#x," ++ "\"version\":%u" ++ "}," ++ "\"runtime_info\":{" ++ "\"time\":\"%s\"," ++ "\"uptime\":%ju," ++ "\"downtime\":%ju" ++ "}" ++ "}," + "\"hsbk\":[%s,%s,%s,%hu]," + "\"power\":%s," + "\"label\":\"%s\"," +@@ -206,6 +240,12 @@ + (src), (start), (stop), (dst), sizeof((dst)) \ + ) + ++#define PRINT_LIFX_FW_TIMESTAMPS(fw_info, built_at_buf, installed_at_buf) \ ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP((fw_info)->built_at, (built_at_buf)); \ ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP( \ ++ (fw_info)->installed_at, (installed_at_buf) \ ++ ) ++ + lgtd_client_start_send_response(client); + lgtd_client_write_string(client, "["); + struct lgtd_router_device *device; +@@ -217,14 +257,91 @@ + PRINT_COMPONENT(bulb->state.saturation, s, 0, 1); + PRINT_COMPONENT(bulb->state.brightness, b, 0, 1); + +- char buf[3072]; +- int written = snprintf( +- buf, sizeof(buf), state_fmt, ++ char mcu_fw_built_at[64], mcu_fw_installed_at[64], bulb_time[64]; ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(bulb->runtime_info.time, bulb_time); ++ PRINT_LIFX_FW_TIMESTAMPS( ++ &bulb->mcu_fw_info, mcu_fw_built_at, mcu_fw_installed_at ++ ); ++ ++ char wifi_fw_built_at[64], wifi_fw_installed_at[64]; ++ PRINT_LIFX_FW_TIMESTAMPS( ++ &bulb->wifi_fw_info, wifi_fw_built_at, wifi_fw_installed_at ++ ); ++ ++ char buf[2048]; ++ int i = 0; ++ ++ LGTD_SNPRINTF_APPEND( ++ buf, i, (int)sizeof(buf), "{" ++ "\"_lifx\":{" ++ "\"gateway\":{" ++ "\"url\":\"tcp://%s:[%hu]\"," ++ "\"latency\":%d" ++ "},", ++ bulb->gw->ip_addr, bulb->gw->port, ++ LGTD_LIFX_GATEWAY_LATENCY(bulb->gw) ++ ); ++ ++ for (int ip = 0; ip != LGTD_LIFX_BULB_IP_COUNT; ip++) { ++ LGTD_SNPRINTF_APPEND( ++ buf, i, (int)sizeof(buf), ++ "\"%s\":{" ++ "\"firmware_built_at\":\"%s\"," ++ "\"firmware_installed_at\":\"%s\"," ++ "\"firmware_version\":%u," ++ "\"signal_strength\":%u," ++ "\"tx_bytes\":%u," ++ "\"rx_bytes\":%u," ++ "\"unknown\":%u" ++ "},", ++ lgtd_lifx_bulb_ip_names[ip], ++ bulb->ips[ip].fw_info.built_at, ++ bulb->ips[ip].fw_info.installed_at, ++ bulb->ips[ip].fw_info.version, ++ bulb->ips[ip].state.signal_strength, ++ bulb->ips[ip].state.tx_bytes, ++ bulb->ips[ip].state.rx_bytes, ++ bulb->ips[ip].state.reserved ++ ); ++ } ++ ++ LGTD_SNPRINTF_APPEND( ++ buf, i, (int)sizeof(buf), ++ "\"product_info\":{" ++ "\"vendor_id\":%#x," ++ "\"product_id\":%#x," ++ "\"version\":%u" ++ "},", ++ bulb->product_info.vendor_id, ++ bulb->product_info.product_id, ++ bulb->product_info.version, ++ ); ++ ++ LGTD_SNPRINTF_APPEND( ++ buf, i, (int)sizeof(buf), ++ "\"runtime_info\":{" ++ "\"time\":\"%s\"," ++ "\"uptime\":%ju," ++ "\"downtime\":%ju" ++ "}" ++ "}," ++ bulb_time, ++ LGTD_NSECS_TO_SECS(bulb->runtime_info.uptime), ++ LGTD_NSECS_TO_SECS(bulb->runtime_info.downtime), ++ ); ++ ++ LGTD_SNPRINTF_APPEND( ++ buf, i, (int)sizeof(buf), ++ "\"hsbk\":[%s,%s,%s,%hu]," ++ "\"power\":%s," ++ "\"label\":\"%s\"," ++ "\"tags\":[", + h, s, b, bulb->state.kelvin, + bulb->state.power == LGTD_LIFX_POWER_ON ? "true" : "false", + bulb->state.label[0] ? bulb->state.label : lgtd_addrtoa(bulb->addr) + ); +- if (written >= (int)sizeof(buf)) { ++ ++ if (i == (int)sizeof(buf)) { + lgtd_warnx( + "can't send state of bulb %s (%s) to client " + "[%s]:%hu: output buffer to small", +diff --git a/lifx/bulb.c b/lifx/bulb.c +--- a/lifx/bulb.c ++++ b/lifx/bulb.c +@@ -39,6 +39,8 @@ + struct lgtd_lifx_bulb_map lgtd_lifx_bulbs_table = + RB_INITIALIZER(&lgtd_lifx_bulbs_table); + ++const const char *lgtd_lifx_bulb_ip_names[] = { "mcu", "wifi" }; ++ + struct lgtd_lifx_bulb * + lgtd_lifx_bulb_get(const uint8_t *addr) + { +@@ -154,3 +156,69 @@ + + bulb->state.tags = tags; + } ++ ++void ++lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_ip_state *state, ++ lgtd_time_mono_t received_at) ++{ ++ assert(bulb); ++ assert(state); ++ ++ bulb->last_mcu_state_at = received_at; ++ memcpy(&bulb->mcu_state, state, sizeof(bulb->mcu_state)); ++} ++ ++void ++lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_ip_firmware_info *info) ++{ ++ assert(bulb); ++ assert(info); ++ ++ memcpy(&bulb->mcu_fw_info, info, sizeof(bulb->mcu_fw_info)); ++} ++ ++void ++lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_ip_state *state, ++ lgtd_time_mono_t received_at) ++{ ++ assert(bulb); ++ assert(state); ++ ++ bulb->last_wifi_state_at = received_at; ++ memcpy(&bulb->wifi_state, state, sizeof(bulb->wifi_state)); ++} ++ ++void ++lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_ip_firmware_info *info) ++{ ++ assert(bulb); ++ assert(info); ++ ++ memcpy(&bulb->wifi_fw_info, info, sizeof(bulb->wifi_fw_info)); ++} ++ ++void ++lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_product_info *info) ++{ ++ assert(bulb); ++ assert(info); ++ ++ memcpy(&bulb->product_info, info, sizeof(bulb->product_info)); ++} ++ ++void ++lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *bulb, ++ const struct lgtd_lifx_runtime_info *info, ++ lgtd_time_mono_t received_at) ++{ ++ assert(bulb); ++ assert(info); ++ ++ bulb->last_runtime_info_at = received_at; ++ memcpy(&bulb->runtime_info, info, sizeof(bulb->runtime_info)); ++} +diff --git a/lifx/bulb.h b/lifx/bulb.h +--- a/lifx/bulb.h ++++ b/lifx/bulb.h +@@ -30,17 +30,58 @@ + char label[LGTD_LIFX_LABEL_SIZE]; + uint64_t tags; + }; ++ ++struct lgtd_lifx_ip_state { ++ float signal_strength; // mW ++ uint32_t tx_bytes; ++ uint32_t rx_bytes; ++ uint16_t reserved; // Temperature? ++}; ++ ++struct lgtd_lifx_ip_firmware_info { ++ uint64_t built_at; // ns since epoch ++ uint64_t installed_at; // ns since epoch ++ uint32_t version; ++}; ++ ++struct lgtd_lifx_product_info { ++ uint32_t vendor_id; ++ uint32_t product_id; ++ uint32_t version; ++}; ++ ++struct lgtd_lifx_runtime_info { ++ uint64_t time; // ns since epoch ++ uint64_t uptime; // ns ++ uint64_t downtime; // ns, last power off period duration ++}; + #pragma pack(pop) + ++enum lgtd_lifx_bulb_ips { ++ LGTD_LIFX_BULB_MCU_IP = 0, ++ LGTD_LIFX_BULB_WIFI_IP = 1, ++ LGTD_LIFX_BULB_IP_COUNT = 2 ++}; ++ ++extern const char *lgtd_lifx_bulb_ip_names[]; ++ + struct lgtd_lifx_bulb { +- RB_ENTRY(lgtd_lifx_bulb) link; +- SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; +- struct lgtd_lifx_gateway *gw; +- uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; +- struct lgtd_lifx_light_state state; +- lgtd_time_mono_t last_light_state_at; +- lgtd_time_mono_t dirty_at; +- uint16_t expected_power_on; ++ RB_ENTRY(lgtd_lifx_bulb) link; ++ SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; ++ lgtd_time_mono_t last_light_state_at; ++ lgtd_time_mono_t last_runtime_info_at; ++ lgtd_time_mono_t dirty_at; ++ uint16_t expected_power_on; ++ uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; ++ struct lgtd_lifx_gateway *gw; ++ struct lgtd_lifx_light_state state; ++ struct { ++ lgtd_time_mono_t updated_at; ++ struct lgtd_lifx_ip_state state; ++ struct lgtd_lifx_ip_firmware_info fw_info; ++ } ips[LGTD_LIFX_BULB_IP_COUNT]; ++ struct lgtd_lifx_product_info product_info; ++ struct lgtd_lifx_runtime_info runtime_info; + }; + RB_HEAD(lgtd_lifx_bulb_map, lgtd_lifx_bulb); + SLIST_HEAD(lgtd_lifx_bulb_list, lgtd_lifx_bulb); +@@ -69,3 +110,19 @@ + lgtd_time_mono_t); + void lgtd_lifx_bulb_set_power_state(struct lgtd_lifx_bulb *, uint16_t); + void lgtd_lifx_bulb_set_tags(struct lgtd_lifx_bulb *, uint64_t); ++ ++void lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_state *, ++ lgtd_time_mono_t); ++void lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_firmware_info *); ++void lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_state *, ++ lgtd_time_mono_t); ++void lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_firmware_info *); ++void lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_product_info *); ++void lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_runtime_info *, ++ lgtd_time_mono_t); +diff --git a/lifx/gateway.c b/lifx/gateway.c +--- a/lifx/gateway.c ++++ b/lifx/gateway.c +@@ -489,12 +489,8 @@ + (uintmax_t)pkt->tags + ); + +- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( +- gw, hdr->target.device_addr +- ); +- if (!b) { +- return; +- } ++ struct lgtd_lifx_bulb *b; ++ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); + + assert(sizeof(*pkt) == sizeof(b->state)); + lgtd_lifx_bulb_set_light_state( +@@ -519,7 +515,7 @@ + } + } + +- int latency = gw->last_pkt_at - gw->last_req_at; ++ int latency = LGTD_LIFX_GATEWAY_LATENCY(gw); + if (latency < LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS) { + if (!event_pending(gw->refresh_ev, EV_TIMEOUT, NULL)) { + int timeout = LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS - latency; +@@ -559,14 +555,9 @@ + gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), pkt->power + ); + +- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( +- gw, hdr->target.device_addr ++ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( ++ gw, hdr->target.device_addr, lgtd_lifx_bulb_set_power_state, pkt->power + ); +- if (!b) { +- return; +- } +- +- lgtd_lifx_bulb_set_power_state(b, pkt->power); + } + + int +@@ -673,9 +664,10 @@ + } + } + +-void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_tags *pkt) ++void ++lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_tags *pkt) + { + assert(gw && hdr && pkt); + +@@ -685,12 +677,8 @@ + (uintmax_t)pkt->tags + ); + +- struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( +- gw, hdr->target.device_addr +- ); +- if (!b) { +- return; +- } ++ struct lgtd_lifx_bulb *b; ++ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); + + int tag_id; + LGTD_LIFX_WIRE_FOREACH_TAG_ID(tag_id, pkt->tags) { +@@ -707,3 +695,142 @@ + + lgtd_lifx_bulb_set_tags(b, pkt->tags); + } ++ ++void ++lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_ip_state *pkt) ++{ ++ assert(gw && hdr && pkt); ++ ++ const char *type; ++ void (*bulb_fn)(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_state *, ++ lgtd_time_mono_t); ++ ++ switch (hdr->packet_type) { ++ case LGTD_LIFX_MESH_INFO: ++ type = "MCU_STATE"; ++ bulb_fn = lgtd_lifx_bulb_set_mcu_state; ++ break; ++ case LGTD_LIFX_WIFI_INFO: ++ type = "WIFI_STATE"; ++ bulb_fn = lgtd_lifx_bulb_set_wifi_state; ++ break; ++ default: ++ lgtd_info("invalid ip state packet_type %#hx", hdr->packet_type); ++#ifndef NDEBUG ++ abort(); ++#endif ++ return; ++ } ++ ++ lgtd_debug( ++ "%s <-- [%s]:%hu - %s " ++ "signal_strength=%f, rx_bytes=%u, tx_bytes=%u, reserved=%hu", ++ type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), ++ pkt->signal_strength, pkt->rx_bytes, pkt->tx_bytes, pkt->reserved ++ ); ++ ++ LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( ++ gw, ++ hdr->target.device_addr, ++ bulb_fn, ++ (const struct lgtd_lifx_ip_state *)pkt ++ ); ++} ++ ++void ++lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_ip_firmware_info *pkt) ++{ ++ assert(gw && hdr && pkt); ++ ++ const char *type; ++ void (*bulb_fn)(struct lgtd_lifx_bulb *, ++ const struct lgtd_lifx_ip_firmware_info *); ++ ++ switch (hdr->packet_type) { ++ case LGTD_LIFX_MESH_FIRMWARE: ++ type = "MCU_FIRMWARE_INFO"; ++ bulb_fn = lgtd_lifx_bulb_set_mcu_firmware_info; ++ break; ++ case LGTD_LIFX_WIFI_FIRMWARE_STATE: ++ type = "WIFI_FIRMWARE_INFO"; ++ bulb_fn = lgtd_lifx_bulb_set_wifi_firmware_info; ++ break; ++ default: ++ lgtd_info("invalid ip firmware packet_type %#hx", hdr->packet_type); ++#ifndef NDEBUG ++ abort(); ++#endif ++ return; ++ } ++ ++ char built_at[64], installed_at[64]; ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->built_at, built_at); ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->installed_at, installed_at); ++ ++ lgtd_debug( ++ "%s <-- [%s]:%hu - %s " ++ "built_at=%s, installed_at=%s, version=%u", ++ type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), ++ built_at, installed_at, pkt->version ++ ); ++ ++ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( ++ gw, ++ hdr->target.device_addr, ++ bulb_fn, ++ (const struct lgtd_lifx_ip_firmware_info *)pkt ++ ); ++} ++ ++void ++lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_product_info *pkt) ++{ ++ assert(gw && hdr && pkt); ++ ++ lgtd_debug( ++ "PRODUCT_INFO <-- [%s]:%hu - %s " ++ "vendor_id=%#x, product_id=%#x, version=%u", ++ gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), ++ pkt->vendor_id, pkt->product_id, pkt->version ++ ); ++ ++ LGTD_LIFX_GATEWAY_SET_BULB_ATTR( ++ gw, ++ hdr->target.device_addr, ++ lgtd_lifx_bulb_set_product_info, ++ (const struct lgtd_lifx_product_info *)pkt ++ ); ++} ++ ++void ++lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_runtime_info *pkt) ++{ ++ assert(gw && hdr && pkt); ++ ++ char device_time[64], uptime[64], downtime[64]; ++ LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->time, device_time); ++ LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->uptime), uptime); ++ LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->downtime), downtime); ++ ++ lgtd_debug( ++ "PRODUCT_INFO <-- [%s]:%hu - %s time=%s, uptime=%s, downtime=%s", ++ gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), ++ device_time, uptime, downtime ++ ); ++ ++ LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( ++ gw, ++ hdr->target.device_addr, ++ lgtd_lifx_bulb_set_runtime_info, ++ (const struct lgtd_lifx_runtime_info *)pkt ++ ); ++} +diff --git a/lifx/gateway.h b/lifx/gateway.h +--- a/lifx/gateway.h ++++ b/lifx/gateway.h +@@ -36,6 +36,12 @@ + struct lgtd_lifx_gateway { + LIST_ENTRY(lgtd_lifx_gateway) link; + struct lgtd_lifx_bulb_list bulbs; ++#define LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr) do { \ ++ (b) = lgtd_lifx_gateway_get_or_open_bulb((gw), (bulb_addr)); \ ++ if (!(b)) { \ ++ return; \ ++ } \ ++} while (0) + // Multiple gateways can share the same site (that happens when bulbs are + // far away enough that ZigBee can't be used). Moreover the SET_PAN_GATEWAY + // packet doesn't include the device address in the header (i.e: site and +@@ -60,6 +66,7 @@ + lgtd_time_mono_t last_req_at; + lgtd_time_mono_t next_req_at; + lgtd_time_mono_t last_pkt_at; ++#define LGTD_LIFX_GATEWAY_LATENCY(gw) ((gw)->last_pkt_at - (gw)->last_req_at) + struct lgtd_lifx_message pkt_ring[LGTD_LIFX_GATEWAY_PACKET_RING_SIZE]; + #define LGTD_LIFX_GATEWAY_INC_MESSAGE_RING_INDEX(idx) do { \ + (idx) += 1; \ +@@ -77,6 +84,18 @@ + + extern struct lgtd_lifx_gateway_list lgtd_lifx_gateways; + ++#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR(gw, bulb_addr, bulb_fn, payload) do { \ ++ struct lgtd_lifx_bulb *b; \ ++ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ ++ (bulb_fn)(b, (payload)); \ ++} while (0) ++ ++#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT(gw, bulb_addr, bulb_fn, payload) do { \ ++ struct lgtd_lifx_bulb *b; \ ++ LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ ++ (bulb_fn)(b, (payload), (gw)->last_pkt_at); \ ++} while (0) ++ + struct lgtd_lifx_gateway *lgtd_lifx_gateway_get(const struct sockaddr_storage *); + struct lgtd_lifx_gateway *lgtd_lifx_gateway_open(const struct sockaddr_storage *, + ev_socklen_t, +@@ -119,3 +138,15 @@ + void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *, + const struct lgtd_lifx_packet_header *, + const struct lgtd_lifx_packet_tags *); ++void lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *, ++ const struct lgtd_lifx_packet_header *, ++ const struct lgtd_lifx_packet_ip_state *); ++void lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *, ++ const struct lgtd_lifx_packet_header *, ++ const struct lgtd_lifx_packet_ip_firmware_info *); ++void lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *, ++ const struct lgtd_lifx_packet_header *, ++ const struct lgtd_lifx_packet_product_info *); ++void lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *, ++ const struct lgtd_lifx_packet_header *, ++ const struct lgtd_lifx_packet_runtime_info *); +diff --git a/lifx/wire_proto.c b/lifx/wire_proto.c +--- a/lifx/wire_proto.c ++++ b/lifx/wire_proto.c +@@ -24,8 +24,10 @@ + #include <stdarg.h> + #include <stdbool.h> + #include <stdint.h> ++#include <stdio.h> + #include <stdlib.h> + #include <string.h> ++#include <time.h> + + #include <event2/util.h> + +@@ -74,12 +76,32 @@ + (void)pkt; + } + ++static void ++lgtd_lifx_wire_enosys_packet_handler(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const void *pkt) ++{ ++ (void)pkt; ++ ++ const struct lgtd_lifx_packet_info *pkt_info; ++ pkt_info = lgtd_lifx_wire_get_packet_info(hdr->packet_type); ++ bool addressable = hdr->protocol & LGTD_LIFX_PROTOCOL_ADDRESSABLE; ++ bool tagged = hdr->protocol & LGTD_LIFX_PROTOCOL_TAGGED; ++ unsigned int protocol = hdr->protocol & LGTD_LIFX_PROTOCOL_VERSION_MASK; ++ lgtd_info( ++ "%s <-- [%s]:%hu - (Unimplemented, header info: " ++ "addressable=%d, tagged=%d, protocol=%d)", ++ pkt_info->name, gw->ip_addr, gw->port, ++ addressable, tagged, protocol ++ ); ++} ++ + void + lgtd_lifx_wire_load_packet_info_map(void) + { + #define DECODER(x) ((void (*)(void *))(x)) + #define ENCODER(x) ((void (*)(void *))(x)) +-#define HANDLER(x) \ ++#define HANDLER(x) \ + ((void (*)(struct lgtd_lifx_gateway *, \ + const struct lgtd_lifx_packet_header *, \ + const void *))(x)) +@@ -90,6 +112,10 @@ + #define REQUEST_ONLY \ + .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ + .handle = lgtd_lifx_wire_null_packet_handler ++#define UNIMPLEMENTED \ ++ .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ ++ .encode = lgtd_lifx_wire_null_packet_encoder_decoder, \ ++ .handle = lgtd_lifx_wire_enosys_packet_handler + + static struct lgtd_lifx_packet_info packet_table[] = { + // Gateway packets: +@@ -187,6 +213,211 @@ + .size = sizeof(struct lgtd_lifx_packet_tags), + .decode = DECODER(lgtd_lifx_wire_decode_tags), + .handle = HANDLER(lgtd_lifx_gateway_handle_tags) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_MESH_INFO", ++ .type = LGTD_LIFX_GET_MESH_INFO ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "MESH_INFO", ++ .type = LGTD_LIFX_MESH_INFO, ++ .size = sizeof(struct lgtd_lifx_packet_ip_state), ++ .decode = DECODER(lgtd_lifx_wire_decode_ip_state), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_MESH_FIRMWARE", ++ .type = LGTD_LIFX_GET_MESH_FIRMWARE ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "MESH_FIRMWARE", ++ .type = LGTD_LIFX_MESH_FIRMWARE, ++ .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), ++ .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_WIFI_INFO", ++ .type = LGTD_LIFX_GET_WIFI_INFO, ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "WIFI_INFO", ++ .type = LGTD_LIFX_WIFI_INFO, ++ .size = sizeof(struct lgtd_lifx_packet_ip_state), ++ .decode = DECODER(lgtd_lifx_wire_decode_ip_state), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_WIFI_FIRMWARE_STATE", ++ .type = LGTD_LIFX_GET_WIFI_FIRMWARE_STATE ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "WIFI_FIRMWARE_STATE", ++ .type = LGTD_LIFX_WIFI_FIRMWARE_STATE, ++ .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), ++ .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_VERSION", ++ .type = LGTD_LIFX_GET_VERSION ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "VERSION_STATE", ++ .type = LGTD_LIFX_VERSION_STATE, ++ .size = sizeof(struct lgtd_lifx_packet_product_info), ++ .decode = DECODER(lgtd_lifx_wire_decode_product_info), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_product_info) ++ }, ++ { ++ REQUEST_ONLY, ++ NO_PAYLOAD, ++ .name = "GET_INFO", ++ .type = LGTD_LIFX_GET_INFO ++ }, ++ { ++ RESPONSE_ONLY, ++ .name = "INFO_STATE", ++ .type = LGTD_LIFX_INFO_STATE, ++ .size = sizeof(struct lgtd_lifx_packet_runtime_info), ++ .decode = DECODER(lgtd_lifx_wire_decode_runtime_info), ++ .handle = HANDLER(lgtd_lifx_gateway_handle_runtime_info) ++ }, ++ // Unimplemented but "known" packets ++ { ++ UNIMPLEMENTED, ++ .name = "GET_TIME", ++ .type = LGTD_LIFX_GET_TIME ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_TIME", ++ .type = LGTD_LIFX_SET_TIME ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "TIME_STATE", ++ .type = LGTD_LIFX_TIME_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "GET_RESET_SWITCH_STATE", ++ .type = LGTD_LIFX_GET_RESET_SWITCH_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "RESET_SWITCH_STATE", ++ .type = LGTD_LIFX_RESET_SWITCH_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "GET_BULB_LABEL", ++ .type = LGTD_LIFX_GET_BULB_LABEL ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_BULB_LABEL", ++ .type = LGTD_LIFX_SET_BULB_LABEL ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "BULB_LABEL", ++ .type = LGTD_LIFX_BULB_LABEL ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "GET_MCU_RAIL_VOLTAGE", ++ .type = LGTD_LIFX_GET_MCU_RAIL_VOLTAGE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "MCU_RAIL_VOLTAGE", ++ .type = LGTD_LIFX_MCU_RAIL_VOLTAGE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "REBOOT", ++ .type = LGTD_LIFX_REBOOT ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_FACTORY_TEST_MODE", ++ .type = LGTD_LIFX_SET_FACTORY_TEST_MODE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "DISABLE_FACTORY_TEST_MODE", ++ .type = LGTD_LIFX_DISABLE_FACTORY_TEST_MODE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "ACK", ++ .type = LGTD_LIFX_ACK ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "ECHO_REQUEST", ++ .type = LGTD_LIFX_ECHO_REQUEST ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "ECHO_RESPONSE", ++ .type = LGTD_LIFX_ECHO_RESPONSE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_DIM_ABSOLUTE", ++ .type = LGTD_LIFX_SET_DIM_ABSOLUTE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_DIM_RELATIVE", ++ .type = LGTD_LIFX_SET_DIM_RELATIVE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "GET_WIFI_STATE", ++ .type = LGTD_LIFX_GET_WIFI_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_WIFI_STATE", ++ .type = LGTD_LIFX_SET_WIFI_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "WIFI_STATE", ++ .type = LGTD_LIFX_WIFI_STATE ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "GET_ACCESS_POINTS", ++ .type = LGTD_LIFX_GET_ACCESS_POINTS ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "SET_ACCESS_POINTS", ++ .type = LGTD_LIFX_SET_ACCESS_POINTS ++ }, ++ { ++ UNIMPLEMENTED, ++ .name = "ACCESS_POINT", ++ .type = LGTD_LIFX_ACCESS_POINT + } + }; + +@@ -234,6 +465,24 @@ + return LGTD_LIFX_WAVEFORM_INVALID; + } + ++void ++lgtd_lifx_wire_print_nsec_timestamp(uint64_t nsec_ts, char *buf, int bufsz) ++{ ++ assert(buf); ++ assert(bufsz > 0); ++ ++ time_t ts = LGTD_NSECS_TO_SECS(nsec_ts); ++ ++ struct tm tm_utc; ++ if (gmtime_r(&ts, &tm_utc)) { ++ int64_t usecs = LGTD_NSECS_TO_USECS(nsec_ts - LGTD_SECS_TO_NSECS(ts)); ++ LGTD_TM_TO_ISOTIME(&tm_utc, buf, bufsz, usecs); ++ return; ++ } ++ ++ buf[0] = '\0'; ++} ++ + static void + lgtd_lifx_wire_encode_header(struct lgtd_lifx_packet_header *hdr, int flags) + { +@@ -423,3 +672,44 @@ + + pkt->tags = le64toh(pkt->tags); + } ++ ++void ++lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *pkt) ++{ ++ assert(pkt); ++ ++ pkt->signal_strength = lgtd_lifx_wire_lefloattoh(pkt->signal_strength); ++ pkt->tx_bytes = le32toh(pkt->tx_bytes); ++ pkt->rx_bytes = le32toh(pkt->rx_bytes); ++ pkt->reserved = le16toh(pkt->reserved); ++} ++ ++void ++lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *pkt) ++{ ++ assert(pkt); ++ ++ pkt->built_at = le64toh(pkt->built_at); ++ pkt->installed_at = le64toh(pkt->installed_at); ++ pkt->version = le32toh(pkt->version); ++} ++ ++void ++lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *pkt) ++{ ++ assert(pkt); ++ ++ pkt->vendor_id = le32toh(pkt->vendor_id); ++ pkt->product_id = le32toh(pkt->product_id); ++ pkt->version = le32toh(pkt->version); ++} ++ ++void ++lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *pkt) ++{ ++ assert(pkt); ++ ++ pkt->time = le64toh(pkt->time); ++ pkt->uptime = le64toh(pkt->uptime); ++ pkt->downtime = le64toh(pkt->downtime); ++} +diff --git a/lifx/wire_proto.h b/lifx/wire_proto.h +--- a/lifx/wire_proto.h ++++ b/lifx/wire_proto.h +@@ -28,7 +28,7 @@ + static inline floatle_t + lgtd_lifx_wire_htolefloat(float f) + { +- union { float f; uint32_t i; } u = { .f = f }; ++ union { float f; uint32_t i; } u = { .f = f }; + htole32(u.i); + return u.f; + } +@@ -36,7 +36,7 @@ + static inline floatle_t + lgtd_lifx_wire_lefloattoh(float f) + { +- union { float f; uint32_t i; } u = { .f = f }; ++ union { float f; uint32_t i; } u = { .f = f }; + le32toh(u.i); + return u.f; + } +@@ -249,6 +249,30 @@ + char label[LGTD_LIFX_LABEL_SIZE]; + }; + ++struct lgtd_lifx_packet_ip_state { ++ floatle_t signal_strength; ++ uint32le_t tx_bytes; ++ uint32le_t rx_bytes; ++ uint16le_t reserved; ++}; ++ ++struct lgtd_lifx_packet_ip_firmware_info { ++ uint64le_t built_at; ++ uint64le_t installed_at; ++ uint32le_t version; ++}; ++ ++struct lgtd_lifx_packet_product_info { ++ uint32le_t vendor_id; ++ uint32le_t product_id; ++ uint32le_t version; ++}; ++ ++struct lgtd_lifx_packet_runtime_info { ++ uint64le_t time; ++ uint64le_t uptime; ++ uint64le_t downtime; ++}; + #pragma pack(pop) + + enum lgtd_lifx_header_flags { +@@ -330,8 +354,11 @@ + (tag_id_varname) != -1; \ + (tag_id_varname) = lgtd_lifx_wire_next_tag_id((tag_id_varname), (tags))) + +- + enum lgtd_lifx_waveform_type lgtd_lifx_wire_waveform_string_id_to_type(const char *, int); ++void lgtd_lifx_wire_print_nsec_timestamp(uint64_t, char *, int); ++#define LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(ts, arr) do { \ ++ lgtd_lifx_wire_print_nsec_timestamp((ts), (arr), sizeof((arr))); \ ++} while (0) + + const struct lgtd_lifx_packet_info *lgtd_lifx_wire_get_packet_info(enum lgtd_lifx_packet_type); + void lgtd_lifx_wire_load_packet_info_map(void); +@@ -356,3 +383,8 @@ + void lgtd_lifx_wire_decode_tags(struct lgtd_lifx_packet_tags *); + void lgtd_lifx_wire_encode_tag_labels(struct lgtd_lifx_packet_tag_labels *); + void lgtd_lifx_wire_decode_tag_labels(struct lgtd_lifx_packet_tag_labels *); ++ ++void lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *); ++void lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *); ++void lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *); ++void lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *); +diff --git a/tests/lifx/mock_gateway.h b/tests/lifx/mock_gateway.h +--- a/tests/lifx/mock_gateway.h ++++ b/tests/lifx/mock_gateway.h +@@ -129,3 +129,51 @@ + (void)pkt_tags; + } + #endif ++ ++#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_STATE ++void ++lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_ip_state *pkt) ++{ ++ (void)gw; ++ (void)hdr; ++ (void)pkt; ++} ++#endif ++ ++#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_FIRMWARE_INFO ++void ++lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_ip_firmware_info *pkt) ++{ ++ (void)gw; ++ (void)hdr; ++ (void)pkt; ++} ++#endif ++ ++#ifndef LGTD_LIFX_GATEWAY_HANDLE_PRODUCT_INFO ++void ++lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_product_info *pkt) ++{ ++ (void)gw; ++ (void)hdr; ++ (void)pkt; ++} ++#endif ++ ++#ifndef LGTD_LIFX_GATEWAY_HANDLE_RUNTIME_INFO ++void ++lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, ++ const struct lgtd_lifx_packet_header *hdr, ++ const struct lgtd_lifx_packet_runtime_info *pkt) ++{ ++ (void)gw; ++ (void)hdr; ++ (void)pkt; ++} ++#endif +diff --git a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c +--- a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c ++++ b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c +@@ -2,7 +2,7 @@ + + #include "wire_proto.c" + +-#include "test_wire_proto_utils.h" ++#include "mock_gateway.h" + + int + main(void) +diff --git a/tests/lifx/wire_proto/test_wire_proto_utils.h b/tests/lifx/wire_proto/test_wire_proto_utils.h +deleted file mode 100644 +--- a/tests/lifx/wire_proto/test_wire_proto_utils.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-#pragma once +- +-void lgtd_lifx_gateway_handle_pan_gateway(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_pan_gateway *pkt) +-{ +- (void)gw; +- (void)hdr; +- (void)pkt; +-} +- +-void lgtd_lifx_gateway_handle_light_status(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_light_status *pkt) +-{ +- (void)gw; +- (void)hdr; +- (void)pkt; +-} +- +-void lgtd_lifx_gateway_handle_power_state(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_power_state *pkt) +-{ +- (void)gw; +- (void)hdr; +- (void)pkt; +-} +- +-void lgtd_lifx_gateway_handle_tag_labels(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_tag_labels *pkt) +-{ +- (void)gw; +- (void)hdr; +- (void)pkt; +-} +- +-void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, +- const struct lgtd_lifx_packet_header *hdr, +- const struct lgtd_lifx_packet_tags *pkt) +-{ +- (void)gw; +- (void)hdr; +- (void)pkt; +-} +diff --git a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c +--- a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c ++++ b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c +@@ -1,6 +1,6 @@ + #include "wire_proto.c" + +-#include "test_wire_proto_utils.h" ++#include "mock_gateway.h" + + int + main(void)