General_view.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. #include "UnitempViews.h"
  2. #include "unitemp_icons.h"
  3. #include <assets_icons.h>
  4. static View* view;
  5. typedef enum general_views {
  6. G_NO_SENSORS_VIEW, //Нет датчиков
  7. G_LIST_VIEW, //Вид в ввиде списка
  8. G_CAROUSEL_VIEW, //Карусель
  9. } general_view;
  10. typedef enum carousel_info {
  11. CAROUSEL_VALUES, //Отображение значений датчиков
  12. CAROUSEL_INFO, //Отображение информации о датчике
  13. } carousel_info;
  14. static uint8_t sensor_index = 0;
  15. static bool selector = false;
  16. static uint32_t lastSelectTime = 0;
  17. static const uint16_t selector_timeout = 2000;
  18. static carousel_info carousel_info_selector = CAROUSEL_VALUES;
  19. static general_view current_view;
  20. static void _draw_temperature(Canvas* canvas, Sensor* sensor, uint8_t x, uint8_t y, Color color) {
  21. //Рисование рамки
  22. canvas_draw_rframe(canvas, x, y, 54, 20, 3);
  23. if(color == ColorBlack) {
  24. canvas_draw_rbox(canvas, x, y, 54, 19, 3);
  25. canvas_invert_color(canvas);
  26. } else {
  27. canvas_draw_rframe(canvas, x, y, 54, 19, 3);
  28. }
  29. int16_t temp_int = sensor->temp;
  30. int8_t temp_dec = abs((int16_t)(sensor->temp * 10) % 10);
  31. //Рисование иконки
  32. canvas_draw_icon(
  33. canvas, x + 3, y + 3, (app->settings.unit == CELSIUS ? &I_temp_C_11x14 : &I_temp_F_11x14));
  34. if((int16_t)sensor->temp == -128 || sensor->status == UT_TIMEOUT) {
  35. snprintf(app->buff, BUFF_SIZE, "--");
  36. canvas_set_font(canvas, FontBigNumbers);
  37. canvas_draw_str_aligned(canvas, x + 27, y + 10, AlignCenter, AlignCenter, app->buff);
  38. snprintf(app->buff, BUFF_SIZE, ". -");
  39. canvas_set_font(canvas, FontPrimary);
  40. canvas_draw_str_aligned(canvas, x + 50, y + 10 + 3, AlignRight, AlignCenter, app->buff);
  41. if(color == ColorBlack) canvas_invert_color(canvas);
  42. return;
  43. }
  44. //Целая часть температуры
  45. snprintf(app->buff, BUFF_SIZE, "%d", temp_int);
  46. canvas_set_font(canvas, FontBigNumbers);
  47. canvas_draw_str_aligned(
  48. canvas, x + 27 + ((temp_int <= -10) ? 5 : 0), y + 10, AlignCenter, AlignCenter, app->buff);
  49. //Печать дробной части температуры в диапазоне от -9 до 99 (когда два знака в числе)
  50. if(temp_int > -10 && temp_int <= 99) {
  51. uint8_t int_len = canvas_string_width(canvas, app->buff);
  52. snprintf(app->buff, BUFF_SIZE, ".%d", temp_dec);
  53. canvas_set_font(canvas, FontPrimary);
  54. canvas_draw_str(canvas, x + 27 + int_len / 2 + 2, y + 10 + 7, app->buff);
  55. }
  56. if(color == ColorBlack) canvas_invert_color(canvas);
  57. }
  58. static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) {
  59. //Рисование рамки
  60. canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3);
  61. canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3);
  62. //Рисование иконки
  63. canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 2, &I_hum_9x15);
  64. if((int8_t)sensor->hum == -128 || sensor->status == UT_TIMEOUT) {
  65. snprintf(app->buff, BUFF_SIZE, "--");
  66. canvas_set_font(canvas, FontBigNumbers);
  67. canvas_draw_str_aligned(
  68. canvas, pos[0] + 27, pos[1] + 10, AlignCenter, AlignCenter, app->buff);
  69. snprintf(app->buff, BUFF_SIZE, ". -");
  70. canvas_set_font(canvas, FontPrimary);
  71. canvas_draw_str_aligned(
  72. canvas, pos[0] + 50, pos[1] + 10 + 3, AlignRight, AlignCenter, app->buff);
  73. return;
  74. }
  75. //Целая часть влажности
  76. snprintf(app->buff, BUFF_SIZE, "%d", (uint8_t)sensor->hum);
  77. canvas_set_font(canvas, FontBigNumbers);
  78. canvas_draw_str_aligned(canvas, pos[0] + 27, pos[1] + 10, AlignCenter, AlignCenter, app->buff);
  79. uint8_t int_len = canvas_string_width(canvas, app->buff);
  80. //Единица измерения
  81. canvas_set_font(canvas, FontPrimary);
  82. canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%");
  83. }
  84. static void _draw_pressure(Canvas* canvas, Sensor* sensor) {
  85. const uint8_t x = 29, y = 39;
  86. //Рисование рамки
  87. canvas_draw_rframe(canvas, x, y, 69, 20, 3);
  88. canvas_draw_rframe(canvas, x, y, 69, 19, 3);
  89. //Рисование иконки
  90. canvas_draw_icon(canvas, x + 3, y + 4, &I_pressure_7x13);
  91. //Давление
  92. snprintf(app->buff, BUFF_SIZE, "%d", (uint16_t)sensor->pressure);
  93. canvas_set_font(canvas, FontBigNumbers);
  94. canvas_draw_str_aligned(canvas, x + 30, y + 10, AlignCenter, AlignCenter, app->buff);
  95. //Единица измерения
  96. canvas_draw_icon(canvas, x + 50, y + 3, &I_mm_hg_17x15);
  97. }
  98. static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos[2], Color color) {
  99. canvas_set_font(canvas, FontPrimary);
  100. const uint8_t max_width = 61;
  101. char sensor_name[12] = {0};
  102. memcpy(sensor_name, sensor->name, 10);
  103. if(canvas_string_width(canvas, sensor_name) > max_width) {
  104. uint8_t i = 10;
  105. while((canvas_string_width(canvas, sensor_name) > max_width - 6) && (i != 0)) {
  106. sensor_name[i--] = '\0';
  107. }
  108. sensor_name[++i] = '.';
  109. sensor_name[++i] = '.';
  110. }
  111. canvas_draw_str_aligned(
  112. canvas, pos[0] + 27, pos[1] + 3, AlignCenter, AlignCenter, sensor_name);
  113. _draw_temperature(canvas, sensor, pos[0], pos[1] + 8, color);
  114. }
  115. static void _draw_view_noSensors(Canvas* canvas) {
  116. canvas_draw_icon(canvas, 7, 17, &I_sherlok_53x55);
  117. //Рисование рамки
  118. canvas_draw_rframe(canvas, 0, 0, 128, 63, 7);
  119. canvas_draw_rframe(canvas, 0, 0, 128, 64, 7);
  120. canvas_set_font(canvas, FontPrimary);
  121. canvas_draw_str_aligned(canvas, 63, 10, AlignCenter, AlignCenter, "No sensors found");
  122. canvas_set_font(canvas, FontSecondary);
  123. const uint8_t x = 65, y = 32;
  124. canvas_draw_rframe(canvas, x - 4, y - 11, 54, 33, 3);
  125. canvas_draw_rframe(canvas, x - 4, y - 11, 54, 34, 3);
  126. canvas_draw_str(canvas, x, y, "To add the");
  127. canvas_draw_str(canvas, x, y + 9, "new sensor");
  128. canvas_draw_str(canvas, x, y + 18, "press OK");
  129. canvas_draw_icon(canvas, x + 37, y + 10, &I_Ok_btn_9x9);
  130. }
  131. static void _draw_view_sensorsList(Canvas* canvas) {
  132. //Текущая страница
  133. uint8_t page = sensor_index / 4;
  134. //Количество датчиков, которые будут отображаться на странице
  135. uint8_t page_sensors_count;
  136. if((unitemp_sensors_getActiveCount() - page * 4) / 4) {
  137. page_sensors_count = 4;
  138. } else {
  139. page_sensors_count = (unitemp_sensors_getActiveCount() - page * 4) % 4;
  140. }
  141. //Количество страниц
  142. uint8_t pages =
  143. unitemp_sensors_getActiveCount() / 4 + (unitemp_sensors_getActiveCount() % 4 ? 1 : 0);
  144. //Стрелка вверх
  145. if(page > 0) {
  146. canvas_draw_icon(canvas, 60, 2, &I_arrow_up_5x9);
  147. }
  148. //Стрелка вниз
  149. if(pages > 0 && page < pages - 1) {
  150. canvas_draw_icon(canvas, 60, 56, &I_arrow_down_5x9);
  151. }
  152. //Включение/выключение селектора
  153. if(furi_get_tick() - lastSelectTime > selector_timeout) {
  154. selector = false;
  155. } else {
  156. selector = true;
  157. }
  158. const uint8_t value_positions[][4][2] = {
  159. {{36, 18}}, //1 датчик
  160. {{4, 18}, {70, 18}}, //2 датчика
  161. {{4, 3}, {70, 3}, {37, 33}}, //3 датчика
  162. {{4, 3}, {70, 3}, {4, 33}, {70, 33}}}; //4 датчика
  163. //Рисование рамки
  164. canvas_draw_rframe(canvas, 0, 0, 128, 63, 7);
  165. canvas_draw_rframe(canvas, 0, 0, 128, 64, 7);
  166. for(uint8_t i = 0; i < page_sensors_count; i++) {
  167. _draw_singleSensor(
  168. canvas,
  169. unitemp_sensor_getActive(page * 4 + i),
  170. value_positions[page_sensors_count - 1][i],
  171. ((i == sensor_index % 4) && selector ? ColorBlack : ColorWhite));
  172. }
  173. }
  174. static void _draw_carousel_values(Canvas* canvas) {
  175. if(unitemp_sensor_getActive(sensor_index)->status == UT_TIMEOUT) {
  176. const Icon* frames[] = {&I_happy_2_78x46, &I_happy_78x46, &I_sad_78x46};
  177. canvas_draw_icon(canvas, 24, 15, frames[furi_get_tick() % 2250 / 750]);
  178. return;
  179. }
  180. static const uint8_t temp_positions[3][2] = {{37, 23}, {37, 16}, {9, 16}};
  181. static const uint8_t hum_positions[2][2] = {{37, 38}, {65, 16}};
  182. //Селектор значений для отображения
  183. switch(unitemp_sensor_getActive(sensor_index)->type->datatype) {
  184. case UT_DATA_TYPE_TEMP:
  185. _draw_temperature(
  186. canvas,
  187. unitemp_sensor_getActive(sensor_index),
  188. temp_positions[0][0],
  189. temp_positions[0][1],
  190. ColorWhite);
  191. break;
  192. case UT_DATA_TYPE_TEMP_HUM:
  193. _draw_temperature(
  194. canvas,
  195. unitemp_sensor_getActive(sensor_index),
  196. temp_positions[1][0],
  197. temp_positions[1][1],
  198. ColorWhite);
  199. _draw_humidity(canvas, unitemp_sensor_getActive(sensor_index), hum_positions[0]);
  200. break;
  201. case UT_DATA_TYPE_TEMP_PRESS:
  202. _draw_temperature(
  203. canvas,
  204. unitemp_sensor_getActive(sensor_index),
  205. temp_positions[1][0],
  206. temp_positions[1][1],
  207. ColorWhite);
  208. _draw_pressure(canvas, unitemp_sensor_getActive(sensor_index));
  209. break;
  210. case UT_DATA_TYPE_TEMP_HUM_PRESS:
  211. _draw_temperature(
  212. canvas,
  213. unitemp_sensor_getActive(sensor_index),
  214. temp_positions[2][0],
  215. temp_positions[2][1],
  216. ColorWhite);
  217. _draw_humidity(canvas, unitemp_sensor_getActive(sensor_index), hum_positions[1]);
  218. _draw_pressure(canvas, unitemp_sensor_getActive(sensor_index));
  219. break;
  220. }
  221. }
  222. static void _draw_carousel_info(Canvas* canvas) {
  223. canvas_set_font(canvas, FontPrimary);
  224. canvas_draw_str(canvas, 10, 23, "Type:");
  225. if(unitemp_sensor_getActive(sensor_index)->type->interface == &ONE_WIRE) {
  226. OneWireSensor* s = unitemp_sensor_getActive(sensor_index)->instance;
  227. canvas_set_font(canvas, FontPrimary);
  228. canvas_draw_str(canvas, 10, 35, "GPIO:");
  229. canvas_draw_str(canvas, 10, 47, "ID:");
  230. canvas_set_font(canvas, FontSecondary);
  231. canvas_draw_str(
  232. canvas,
  233. 41,
  234. 23,
  235. unitemp_onewire_sensor_getModel(unitemp_sensor_getActive(sensor_index)));
  236. canvas_draw_str(canvas, 41, 35, s->bus->gpio->name);
  237. snprintf(
  238. app->buff,
  239. BUFF_SIZE,
  240. "%02X%02X%02X%02X%02X%02X%02X%02X",
  241. s->deviceID[0],
  242. s->deviceID[1],
  243. s->deviceID[2],
  244. s->deviceID[3],
  245. s->deviceID[4],
  246. s->deviceID[5],
  247. s->deviceID[6],
  248. s->deviceID[7]);
  249. canvas_draw_str(canvas, 24, 47, app->buff);
  250. }
  251. if(unitemp_sensor_getActive(sensor_index)->type->interface == &SINGLE_WIRE) {
  252. canvas_set_font(canvas, FontPrimary);
  253. canvas_draw_str(canvas, 10, 35, "GPIO:");
  254. canvas_set_font(canvas, FontSecondary);
  255. canvas_draw_str(canvas, 41, 23, unitemp_sensor_getActive(sensor_index)->type->typename);
  256. canvas_draw_str(
  257. canvas,
  258. 41,
  259. 35,
  260. ((SingleWireSensor*)unitemp_sensor_getActive(sensor_index)->instance)->gpio->name);
  261. }
  262. if(unitemp_sensor_getActive(sensor_index)->type->interface == &I2C) {
  263. canvas_set_font(canvas, FontPrimary);
  264. canvas_draw_str(canvas, 10, 35, "I2C addr:");
  265. canvas_set_font(canvas, FontSecondary);
  266. canvas_draw_str(canvas, 41, 23, unitemp_sensor_getActive(sensor_index)->type->typename);
  267. snprintf(
  268. app->buff,
  269. BUFF_SIZE,
  270. "0x%02X",
  271. ((I2CSensor*)unitemp_sensor_getActive(sensor_index)->instance)->currentI2CAdr);
  272. canvas_draw_str(canvas, 57, 35, app->buff);
  273. }
  274. }
  275. static void _draw_view_sensorsCarousel(Canvas* canvas) {
  276. //Рисование рамки
  277. canvas_draw_rframe(canvas, 0, 0, 128, 63, 7);
  278. canvas_draw_rframe(canvas, 0, 0, 128, 64, 7);
  279. //Печать имени
  280. canvas_set_font(canvas, FontPrimary);
  281. canvas_draw_str_aligned(
  282. canvas, 64, 7, AlignCenter, AlignCenter, unitemp_sensor_getActive(sensor_index)->name);
  283. //Подчёркивание
  284. uint8_t line_len =
  285. canvas_string_width(canvas, unitemp_sensor_getActive(sensor_index)->name) + 2;
  286. canvas_draw_line(canvas, 64 - line_len / 2, 12, 64 + line_len / 2, 12);
  287. //Стрелка вправо
  288. if(unitemp_sensors_getTypesCount() > 0 &&
  289. sensor_index < unitemp_sensors_getActiveCount() - 1) {
  290. canvas_draw_icon(canvas, 120, 28, &I_arrow_right_5x9);
  291. }
  292. //Стрелка влево
  293. if(sensor_index > 0) {
  294. canvas_draw_icon(canvas, 3, 28, &I_arrow_left_5x9);
  295. }
  296. switch(carousel_info_selector) {
  297. case CAROUSEL_VALUES:
  298. _draw_carousel_values(canvas);
  299. break;
  300. case CAROUSEL_INFO:
  301. _draw_carousel_info(canvas);
  302. break;
  303. }
  304. }
  305. static void _draw_callback(Canvas* canvas, void* _model) {
  306. UNUSED(_model);
  307. app->sensors_ready = true;
  308. uint8_t sensors_count = unitemp_sensors_getActiveCount();
  309. if(sensor_index + 1 > sensors_count) sensor_index = 0;
  310. if(sensors_count == 0) {
  311. current_view = G_NO_SENSORS_VIEW;
  312. _draw_view_noSensors(canvas);
  313. } else {
  314. if(sensors_count == 1) current_view = G_CAROUSEL_VIEW;
  315. if(current_view == G_NO_SENSORS_VIEW) current_view = G_LIST_VIEW;
  316. if(current_view == G_LIST_VIEW) _draw_view_sensorsList(canvas);
  317. if(current_view == G_CAROUSEL_VIEW) _draw_view_sensorsCarousel(canvas);
  318. }
  319. }
  320. static bool _input_callback(InputEvent* event, void* context) {
  321. Unitemp* app = context;
  322. //Обработка короткого нажатия "ок"
  323. if(event->key == InputKeyOk && event->type == InputTypeShort) {
  324. //Меню добавления датчика при их отсутствии
  325. if(current_view == G_NO_SENSORS_VIEW) {
  326. app->sensors_ready = false;
  327. unitemp_SensorsList_switch();
  328. } else if(current_view == G_LIST_VIEW) {
  329. if(selector) {
  330. //Переход в карусель
  331. current_view = G_CAROUSEL_VIEW;
  332. carousel_info_selector = CAROUSEL_VALUES;
  333. } else {
  334. //Переход в главное меню при выключенном селекторе
  335. app->sensors_ready = false;
  336. unitemp_MainMenu_switch();
  337. }
  338. } else if(current_view == G_CAROUSEL_VIEW) {
  339. app->sensors_ready = false;
  340. unitemp_SensorActions_switch(unitemp_sensor_getActive(sensor_index));
  341. }
  342. }
  343. //Обработка короткого нажатия "вниз"
  344. if(event->key == InputKeyDown && event->type == InputTypeShort) {
  345. //Листание селектора вниз в режиме списка
  346. if(current_view == G_LIST_VIEW) {
  347. lastSelectTime = furi_get_tick();
  348. if(selector) sensor_index++;
  349. if(sensor_index >= unitemp_sensors_getActiveCount()) sensor_index = 0;
  350. }
  351. if(current_view == G_CAROUSEL_VIEW) {
  352. carousel_info_selector = !carousel_info_selector;
  353. }
  354. }
  355. //Обработка короткого нажатия "вверх"
  356. if(event->key == InputKeyUp && event->type == InputTypeShort) {
  357. //Листание селектора вверх в режиме списка
  358. if(current_view == G_LIST_VIEW) {
  359. lastSelectTime = furi_get_tick();
  360. if(selector) sensor_index--;
  361. if(sensor_index >= unitemp_sensors_getActiveCount())
  362. sensor_index = unitemp_sensors_getActiveCount() - 1;
  363. }
  364. if(current_view == G_CAROUSEL_VIEW) {
  365. carousel_info_selector = !carousel_info_selector;
  366. }
  367. }
  368. //Обработка короткого нажатия "вправо"
  369. if(event->key == InputKeyRight && event->type == InputTypeShort) {
  370. //Пролистывание карусели вперёд
  371. if(current_view == G_CAROUSEL_VIEW) {
  372. carousel_info_selector = CAROUSEL_VALUES;
  373. if(++sensor_index >= unitemp_sensors_getActiveCount()) sensor_index = 0;
  374. }
  375. //Переход в карусель
  376. if(current_view == G_LIST_VIEW) current_view = G_CAROUSEL_VIEW;
  377. }
  378. //Обработка короткого нажатия "влево"
  379. if(event->key == InputKeyLeft && event->type == InputTypeShort) {
  380. //Пролистывание карусели назад
  381. if(current_view == G_CAROUSEL_VIEW) {
  382. carousel_info_selector = CAROUSEL_VALUES;
  383. if(--sensor_index >= unitemp_sensors_getActiveCount())
  384. sensor_index = unitemp_sensors_getActiveCount() - 1;
  385. }
  386. //Переход в карусель
  387. if(current_view == G_LIST_VIEW) current_view = G_CAROUSEL_VIEW;
  388. }
  389. //Обработка короткого нажатия "назад"
  390. if(event->key == InputKeyBack && event->type == InputTypeShort) {
  391. //Выход из приложения при листе или отсутствии датчиков
  392. if(current_view == G_LIST_VIEW || current_view == G_NO_SENSORS_VIEW ||
  393. ((current_view == G_CAROUSEL_VIEW) && (unitemp_sensors_getActiveCount() == 1)))
  394. app->processing = false;
  395. //Переход в список датчиков
  396. if((current_view == G_CAROUSEL_VIEW) && (unitemp_sensors_getActiveCount() != 1))
  397. current_view = G_LIST_VIEW;
  398. }
  399. return true;
  400. }
  401. void unitemp_General_alloc(void) {
  402. view = view_alloc();
  403. view_set_context(view, app);
  404. view_set_draw_callback(view, _draw_callback);
  405. view_set_input_callback(view, _input_callback);
  406. view_dispatcher_add_view(app->view_dispatcher, VIEW_GENERAL, view);
  407. }
  408. void unitemp_General_switch(void) {
  409. app->sensors_ready = true;
  410. view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_GENERAL);
  411. }
  412. void unitemp_General_free(void) {
  413. view_free(view);
  414. }