From e545fea9c0219adfd68cd23e9c91c12e8f4a4a22 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Sun, 7 Apr 2024 19:47:18 +0200 Subject: [PATCH 1/5] add UICamera, displays local test image --- meson.build | 1 + src/HAEntity.hpp | 1 + src/front-lvgl.cpp | 1 + src/front-lvgl.hpp | 3 +++ src/lv_conf.h | 8 ++++---- src/uicomponents/UICamera.cpp | 33 +++++++++++++++++++++++++++++++++ src/uicomponents/UICamera.hpp | 16 ++++++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/uicomponents/UICamera.cpp create mode 100644 src/uicomponents/UICamera.hpp diff --git a/meson.build b/meson.build index bcc14990..1cb30c88 100644 --- a/meson.build +++ b/meson.build @@ -198,6 +198,7 @@ if get_option('front-lvgl').enabled() 'src/uicomponents/imgs/colortemp24.c', 'src/uicomponents/imgs/brightness24.c', 'src/uicomponents/imgs/white24.c', + 'src/uicomponents/UICamera.cpp', 'src/uicomponents/uirgblight.cpp', 'src/uicomponents/UILogBox.cpp', 'src/uicomponents/UIComponents.cpp', diff --git a/src/HAEntity.hpp b/src/HAEntity.hpp index cfa695b2..993fb98b 100644 --- a/src/HAEntity.hpp +++ b/src/HAEntity.hpp @@ -21,6 +21,7 @@ enum class EntityType Light, Switch, Fan, + Camera, OTHER, }; diff --git a/src/front-lvgl.cpp b/src/front-lvgl.cpp index c670aec5..4393b176 100644 --- a/src/front-lvgl.cpp +++ b/src/front-lvgl.cpp @@ -191,6 +191,7 @@ void uithread(HABackend& _backend, int _argc, char* _argv[]) {EntityType::Light, makeUIElement}, {EntityType::Switch, makeUIElement}, {EntityType::Fan, makeUIElement}, + {EntityType::Camera, makeUIElement}, {EntityType::OTHER, makeUIElement}}; auto entities = _backend.getEntitiesByPattern(entity_command.get("pattern")); diff --git a/src/front-lvgl.hpp b/src/front-lvgl.hpp index 099393d8..9d69d08d 100644 --- a/src/front-lvgl.hpp +++ b/src/front-lvgl.hpp @@ -24,8 +24,11 @@ #include #include #include "sdl/sdl.h" + +// FIXME the includes for all the components should maybe be in uicomponents.hpp itself? #include "uicomponents/UIComponents.hpp" #include "uicomponents/uirgblight.hpp" +#include "uicomponents/UICamera.hpp" #include using std::string; diff --git a/src/lv_conf.h b/src/lv_conf.h index 37341dec..5c403435 100644 --- a/src/lv_conf.h +++ b/src/lv_conf.h @@ -609,9 +609,9 @@ /*File system interfaces for common APIs */ /*API for fopen, fread, etc*/ -#define LV_USE_FS_STDIO 0 +#define LV_USE_FS_STDIO 1 #if LV_USE_FS_STDIO - #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_STDIO_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif @@ -647,14 +647,14 @@ #endif /*PNG decoder library*/ -#define LV_USE_PNG 0 +#define LV_USE_PNG 1 /*BMP decoder library*/ #define LV_USE_BMP 0 /* JPG + split JPG decoder library. * Split JPG is a custom format optimized for embedded systems. */ -#define LV_USE_SJPG 0 +#define LV_USE_SJPG 1 /*GIF decoder library*/ #define LV_USE_GIF 0 diff --git a/src/uicomponents/UICamera.cpp b/src/uicomponents/UICamera.cpp new file mode 100644 index 00000000..0d61a165 --- /dev/null +++ b/src/uicomponents/UICamera.cpp @@ -0,0 +1,33 @@ +#include "UICamera.hpp" + +// FIXME: we do a whole lot of json parsing in this file, that we should be doing somewhere else. + +UICamera::UICamera(std::shared_ptr _entity, lv_obj_t* _parent) : + UIEntity(_entity, _parent) +{ + auto state = entity->getJsonState(); + std::cerr << "INITIAL STATE FOR " << _entity->name << ":" << state.dump(2) << std::endl; + + if (!state.contains("attributes")) { + throw std::runtime_error("Camera can't operate with a state that has no attributes"); + } + + auto attributes = state["attributes"]; + + // We generate a UI based on 'supported_color_modes'. color_mode then tells us which mode to use. Color_mode should be in update() + imgpanel = lv_img_create(_parent); + lv_img_set_src(imgpanel, "A:test.png"); + // lv_obj_align(imgpanel, LV_ALIGN_LEFT_MID, 20, 0); + + update(); +} + +void UICamera::update() +{ + auto state = entity->getJsonState(); + std::cerr << "UPDATED STATE FOR " << entity->name << ":" << state.dump(2) << std::endl; + + { + std::unique_lock lvlock(g_lvgl_updatelock); + } +} diff --git a/src/uicomponents/UICamera.hpp b/src/uicomponents/UICamera.hpp new file mode 100644 index 00000000..69c31739 --- /dev/null +++ b/src/uicomponents/UICamera.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "UIComponents.hpp" +#include +#include + +class UICamera : public UIEntity +{ +public: + UICamera(std::shared_ptr _entity, lv_obj_t* _parent); + + void update() override; + +private: + // FIXME: we never free() the lv_obj_t*'s in code + lv_obj_t* imgpanel; +}; From ac36580648f15ea4a47c5fbeae8c03f4aaeba306 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Sun, 7 Apr 2024 20:31:34 +0200 Subject: [PATCH 2/5] broken: we now download the image (blocking!) but LVGL does not like it --- src/uicomponents/UICamera.cpp | 100 ++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/uicomponents/UICamera.cpp b/src/uicomponents/UICamera.cpp index 0d61a165..97e41efb 100644 --- a/src/uicomponents/UICamera.cpp +++ b/src/uicomponents/UICamera.cpp @@ -1,4 +1,6 @@ #include "UICamera.hpp" +#include "logger.hpp" +#include // FIXME: we do a whole lot of json parsing in this file, that we should be doing somewhere else. @@ -22,6 +24,37 @@ UICamera::UICamera(std::shared_ptr _entity, lv_obj_t* _parent) : update(); } +// code from https://curl.se/libcurl/c/getinmemory.html, modified + +// FIXME: use std::array? +struct MemoryStruct { + char *memory; + size_t size; +}; + +static size_t +writeMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + char *ptr = static_cast(realloc(mem->memory, mem->size + realsize + 1)); + if(!ptr) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +// end code from + void UICamera::update() { auto state = entity->getJsonState(); @@ -29,5 +62,72 @@ void UICamera::update() { std::unique_lock lvlock(g_lvgl_updatelock); + auto url = "http://grace.7bits.nl:8123" + state["attributes"]["entity_picture"].get(); + g_log << "camera URL: " << url << std::endl; + +// code from as above, modified + CURL *curl_handle; + CURLcode res; + + struct MemoryStruct chunk; + chunk.memory = static_cast(malloc(1)); /* grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + + curl_global_init(CURL_GLOBAL_ALL); + + /* init the curl session */ + curl_handle = curl_easy_init(); + + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + + /* some servers do not like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + /* get it! */ + res = curl_easy_perform(curl_handle); + + /* check for errors */ + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + } + else { + /* + * Now, our chunk.memory points to a memory block that is chunk.size + * bytes big and contains the remote file. + * + * Do something nice with it! + */ + + printf("%lu bytes retrieved\n", (unsigned long)chunk.size); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); +// end code from + + lv_img_dsc_t img = { + .header = { + .cf = LV_IMG_CF_RAW_ALPHA, + .always_zero = 0, + .w = 50, + .h = 50, + }, + }; + + img.data_size = chunk.size; + img.data = (uint8_t*)(chunk.memory); + + lv_img_set_src(imgpanel, &img); + + free(chunk.memory); } } From cd82fd1906a06b17f5bdf43fb60a423e00694f27 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Sun, 7 Apr 2024 21:01:00 +0200 Subject: [PATCH 3/5] show image fetched from HA --- src/uicomponents/UICamera.cpp | 8 ++++---- src/uicomponents/UICamera.hpp | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/uicomponents/UICamera.cpp b/src/uicomponents/UICamera.cpp index 97e41efb..41e3d86d 100644 --- a/src/uicomponents/UICamera.cpp +++ b/src/uicomponents/UICamera.cpp @@ -114,12 +114,12 @@ void UICamera::update() curl_easy_cleanup(curl_handle); // end code from - lv_img_dsc_t img = { + img = { .header = { .cf = LV_IMG_CF_RAW_ALPHA, .always_zero = 0, - .w = 50, - .h = 50, + .w = 0, + .h = 0, }, }; @@ -128,6 +128,6 @@ void UICamera::update() lv_img_set_src(imgpanel, &img); - free(chunk.memory); + // free(chunk.memory); // FIXME this leaks terribly } } diff --git a/src/uicomponents/UICamera.hpp b/src/uicomponents/UICamera.hpp index 69c31739..17e57d13 100644 --- a/src/uicomponents/UICamera.hpp +++ b/src/uicomponents/UICamera.hpp @@ -13,4 +13,5 @@ class UICamera : public UIEntity private: // FIXME: we never free() the lv_obj_t*'s in code lv_obj_t* imgpanel; + lv_img_dsc_t img; }; From 2d55aa7140130070106a9e42f46c1e97bbb26e48 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Tue, 9 Apr 2024 19:26:29 +0200 Subject: [PATCH 4/5] enable lvgl image cache --- src/lv_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lv_conf.h b/src/lv_conf.h index 5c403435..0c910c5f 100644 --- a/src/lv_conf.h +++ b/src/lv_conf.h @@ -144,7 +144,7 @@ *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. *However the opened images might consume additional RAM. *0: to disable caching*/ -#define LV_IMG_CACHE_DEF_SIZE 0 +#define LV_IMG_CACHE_DEF_SIZE 10 /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ From d99c5cfb782eac92af8323c9fcee396ce386e004 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Tue, 9 Apr 2024 19:26:33 +0200 Subject: [PATCH 5/5] flow --- src/uicomponents/UICamera.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/uicomponents/UICamera.cpp b/src/uicomponents/UICamera.cpp index 41e3d86d..d84df848 100644 --- a/src/uicomponents/UICamera.cpp +++ b/src/uicomponents/UICamera.cpp @@ -19,6 +19,14 @@ UICamera::UICamera(std::shared_ptr _entity, lv_obj_t* _parent) : // We generate a UI based on 'supported_color_modes'. color_mode then tells us which mode to use. Color_mode should be in update() imgpanel = lv_img_create(_parent); lv_img_set_src(imgpanel, "A:test.png"); + // lv_obj_set_width(imgpanel, uiEntityWidth); + // lv_obj_set_height(imgpanel, 380); + lv_obj_set_style_pad_all(imgpanel, 5, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_align(imgpanel, LV_ALIGN_CENTER); + lv_obj_set_flex_flow(imgpanel, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(imgpanel, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START); + + // lv_obj_align(imgpanel, LV_ALIGN_LEFT_MID, 20, 0); update();