General_view.c 14 KB

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