Something went wrong. Try again.
Live STIB-MIVB departures on the Pebble Time 2. It covers the bus, tram, and metro network of Brussels
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297#include "msg.h"#include "model.h"#include "line_window.h"#include "departures_window.h"
static void (*s_on_update)(uint8_t);static int16_t s_last_min1 = -1;static char s_last_stop_id[ID_LEN];static char s_last_line[LINE_LEN];
static void maybe_vibrate(void) { DepartureRef ref; if (model_departures(PAGE_TRANSIT, &ref, 1) == 0) { s_last_min1 = -1; return; } Stop *stop = model_stop_at(PAGE_TRANSIT, ref.stop); Row *row = &stop->rows[ref.row]; int16_t now = row->min1; if (strcmp(stop->id, s_last_stop_id) != 0 || strcmp(row->line, s_last_line) != 0) { strncpy(s_last_stop_id, stop->id, ID_LEN - 1); s_last_stop_id[ID_LEN - 1] = '\0'; strncpy(s_last_line, row->line, LINE_LEN - 1); s_last_line[LINE_LEN - 1] = '\0'; s_last_min1 = now; return; } if (model_vibrate() && s_last_min1 > 0 && now == 0) { vibes_short_pulse(); } s_last_min1 = now;}
#define WIRE_VERSION 2#define ROW_BLOB_ENTRY 57#define STRIP_BLOB_ENTRY 33
static bool blob_count(const Tuple *t, uint16_t entry_size, uint8_t cap, uint8_t *out) { if (!t || t->length < 2) { return false; } const uint8_t *d = t->value->data; if (d[0] != WIRE_VERSION || d[1] > cap) { return false; } if (t->length != (uint16_t)(2 + d[1] * entry_size)) { return false; } *out = d[1]; return true;}
static int16_t blob_i16(const uint8_t *d) { return (int16_t)(d[0] | ((uint16_t)d[1] << 8));}
static void unpack_row(Row *row, const uint8_t *d) { memcpy(row->line, d, LINE_LEN); row->line[LINE_LEN - 1] = '\0'; row->kind = d[8] & 7; row->flags = (d[8] >> 3) & 3; row->color = (GColor){ .argb = d[9] }; row->text_color = (GColor){ .argb = d[10] }; memcpy(row->destination, d + 11, NAME_LEN); row->destination[NAME_LEN - 1] = '\0'; row->min1 = blob_i16(d + 43); row->min2 = blob_i16(d + 45); memcpy(row->platform, d + 47, PLATFORM_LEN); row->platform[PLATFORM_LEN - 1] = '\0'; row->delay_seconds = blob_i16(d + 55); row->w_dest = -1; row->w_dest_bold = -1;}
static void fill_stop(DictionaryIterator *it, Page page) { if (page != model_requested_page()) { return; } Tuple *index = dict_find(it, MESSAGE_KEY_STOP_INDEX); Tuple *count = dict_find(it, MESSAGE_KEY_STOP_COUNT); if (!index) { return; } uint8_t index_value = (uint8_t)index->value->int32; Tuple *rows = dict_find(it, MESSAGE_KEY_BOARD_ROWS); uint8_t row_n = 0; if (rows && !blob_count(rows, ROW_BLOB_ENTRY, MAX_ROWS, &row_n)) { return; } if (index_value == 0) { if (!count) { return; } model_begin(page, (uint8_t)count->value->int32); } Stop *stop = model_staging_stop_at(index_value); if (!stop) { return; } Tuple *t; if ((t = dict_find(it, MESSAGE_KEY_STOP_ID))) { strncpy(stop->id, t->value->cstring, ID_LEN - 1); stop->id[ID_LEN - 1] = '\0'; } if ((t = dict_find(it, MESSAGE_KEY_STOP_NAME))) { strncpy(stop->name, t->value->cstring, NAME_LEN - 1); stop->name[NAME_LEN - 1] = '\0'; stop->w_name = -1; } if ((t = dict_find(it, MESSAGE_KEY_STOP_DISTANCE))) { stop->distance_m = (uint16_t)t->value->int32; } if (rows) { stop->row_count = row_n; for (uint8_t j = 0; j < row_n; j++) { unpack_row(&stop->rows[j], rows->value->data + 2 + j * ROW_BLOB_ENTRY); } } if ((t = dict_find(it, MESSAGE_KEY_VILLO_MECHANICAL))) { stop->bikes_mechanical = (int16_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_VILLO_ELECTRICAL))) { stop->bikes_electrical = (int16_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_VILLO_DOCKS))) { stop->docks = (int16_t)t->value->int32; } model_staging_note(index_value);}
static void fill_line_header(DictionaryIterator *it) { model_line_begin(); LineDetail *detail = model_line_detail(); detail->header_received = true; Tuple *t; if ((t = dict_find(it, MESSAGE_KEY_ROW_LINE))) { strncpy(detail->line, t->value->cstring, LINE_LEN - 1); detail->line[LINE_LEN - 1] = '\0'; } if ((t = dict_find(it, MESSAGE_KEY_ROW_KIND))) { detail->kind = (uint8_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_ROW_COLOR))) { detail->color = (GColor){ .argb = (uint8_t)t->value->int32 }; } if ((t = dict_find(it, MESSAGE_KEY_ROW_TEXT_COLOR))) { detail->text_color = (GColor){ .argb = (uint8_t)t->value->int32 }; } if ((t = dict_find(it, MESSAGE_KEY_ROW_DEST))) { strncpy(detail->destination, t->value->cstring, NAME_LEN - 1); detail->destination[NAME_LEN - 1] = '\0'; } if ((t = dict_find(it, MESSAGE_KEY_ROW_MIN1))) { detail->min1 = (int16_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_ROW_MIN2))) { detail->min2 = (int16_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_LINE_DISRUPTION))) { strncpy(detail->disruption, t->value->cstring, sizeof(detail->disruption) - 1); detail->disruption[sizeof(detail->disruption) - 1] = '\0'; } if ((t = dict_find(it, MESSAGE_KEY_LINE_STRIPS))) { uint8_t n; if (blob_count(t, STRIP_BLOB_ENTRY, MAX_STRIP, &n)) { detail->strip_count = n; detail->strip_received = n; for (uint8_t i = 0; i < n; i++) { const uint8_t *d = t->value->data + 2 + i * STRIP_BLOB_ENTRY; memcpy(detail->strip[i].name, d, NAME_LEN); detail->strip[i].name[NAME_LEN - 1] = '\0'; detail->strip[i].marker = d[32]; } } } if ((t = dict_find(it, MESSAGE_KEY_LINE_STATUS))) { detail->status = (uint8_t)t->value->int32; } if ((t = dict_find(it, MESSAGE_KEY_LINE_SHAPE))) { uint16_t len = t->length > MAX_SHAPE_BYTES ? MAX_SHAPE_BYTES : t->length; memcpy(detail->shape, t->value->data, len); detail->shape_len = (uint8_t)len; } s_on_update(MSG_UPDATE_LINE);}
static Page inbox_page(DictionaryIterator *it) { Tuple *page = dict_find(it, MESSAGE_KEY_PAGE); if (!page) { return PAGE_TRANSIT; } int32_t value = page->value->int32; return value >= 0 && value < PAGE_COUNT ? (Page)value : PAGE_TRANSIT;}
static void inbox(DictionaryIterator *it, void *context) { Tuple *kind = dict_find(it, MESSAGE_KEY_KIND); if (!kind) { return; } Page page = inbox_page(it); switch ((uint8_t)kind->value->int32) { case 1: fill_stop(it, page); break; case 3: { model_note_data_arrival(page, time(NULL)); bool committed = model_commit(page); if (page != model_requested_page()) { break; } Tuple *err = dict_find(it, MESSAGE_KEY_ERROR); uint8_t code = err ? (uint8_t)err->value->int32 : 0; if (code == 4) { model_begin(page, 0); committed = model_commit(page); } if (code != 0 || committed) { model_set_state(code == 4 ? STATE_EMPTY : code == 3 ? STATE_STALE : STATE_OK); } maybe_vibrate(); s_on_update(MSG_UPDATE_BOARD); line_window_poll(); break; } case 4: { Tuple *err = dict_find(it, MESSAGE_KEY_ERROR); if (err) { model_set_state((uint8_t)err->value->int32 == 1 ? STATE_GEO_ERROR : STATE_NET_ERROR); } line_window_fail(); s_on_update(MSG_UPDATE_BOARD | MSG_UPDATE_LINE); break; } case 5: { Tuple *vib = dict_find(it, MESSAGE_KEY_CFG_VIBRATE); Tuple *lang = dict_find(it, MESSAGE_KEY_CFG_LANG); model_set_config(vib ? (uint8_t)vib->value->int32 == 1 : model_vibrate(), lang ? (uint8_t)lang->value->int32 : model_lang()); s_on_update(MSG_UPDATE_CONFIG); break; } case 6: fill_line_header(it); break; case 8: { Tuple *result = dict_find(it, MESSAGE_KEY_FAV_RESULT); if (result) { departures_window_fav_result((uint8_t)result->value->int32); } break; } }}
void msg_init(void (*on_update)(uint8_t mask)) { s_on_update = on_update; app_message_register_inbox_received(inbox); app_message_open(1024, 128);}
void msg_request_refresh(Page page) { model_note_request(page); DictionaryIterator *it; if (app_message_outbox_begin(&it) != APP_MSG_OK) { return; } dict_write_uint8(it, MESSAGE_KEY_KIND, 10); dict_write_uint8(it, MESSAGE_KEY_REQ_PAGE, (uint8_t)page); app_message_outbox_send();}
void msg_request_line(const char *stop_id, const char *line, const char *dest) { DictionaryIterator *it; if (app_message_outbox_begin(&it) != APP_MSG_OK) { return; } dict_write_uint8(it, MESSAGE_KEY_KIND, 11); dict_write_cstring(it, MESSAGE_KEY_REQ_STOP_ID, stop_id); dict_write_cstring(it, MESSAGE_KEY_REQ_LINE, line); if (dest && dest[0]) { dict_write_cstring(it, MESSAGE_KEY_REQ_DEST, dest); } app_message_outbox_send();}
void msg_request_fav_toggle(const char *stop_id, const char *stop_name) { DictionaryIterator *it; if (app_message_outbox_begin(&it) != APP_MSG_OK) { return; } dict_write_uint8(it, MESSAGE_KEY_KIND, 12); dict_write_cstring(it, MESSAGE_KEY_REQ_STOP_ID, stop_id); dict_write_cstring(it, MESSAGE_KEY_STOP_NAME, stop_name); app_message_outbox_send();}