Skip to content

Commit

Permalink
Merge pull request #133 from Habbie/icons
Browse files Browse the repository at this point in the history
add icon to UISensor
  • Loading branch information
Habbie authored Dec 15, 2024
2 parents ed16c8a + 38cc136 commit 573b597
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/Backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ class HABackend : Backend
std::mutex domainslock;
};

#endif
#endif
2 changes: 2 additions & 0 deletions src/HAEntity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class HAEntity : public ISubject
string domain;
string fullname;
string id;
string platform;
string translation_key;

// HAEntity(json _state);
HAEntity(json _state, std::shared_ptr<HADomain> _hadomain, HABackend* _backend);
Expand Down
65 changes: 63 additions & 2 deletions src/front-lvgl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace lvgl
lv_style_t b612style;
lv_font_t* mdifont;
lv_style_t mdistyle;
map<iconkey, string> iconmap; // will need a lock eventually
json iconcomponentmap; // lock?
}
}

Expand Down Expand Up @@ -82,11 +84,13 @@ void renderCard(std::vector<std::unique_ptr<UIEntity>>& uielements, nlohmann::ba
auto objs = card["entities"];
for (auto ent : objs) {
string entityname;
string icon;
if (ent.type() == json::value_t::string) {
entityname = ent;
}
else {
entityname = ent["entity"];
icon = ent.value("icon", "");
}
std::shared_ptr<HAEntity> entity = HABackend::getInstance().getEntityByName(entityname);
if (entity->getEntityType() == EntityType::Light) {
Expand All @@ -98,7 +102,7 @@ void renderCard(std::vector<std::unique_ptr<UIEntity>>& uielements, nlohmann::ba
uielements.push_back(std::move(btn));
}
else if (entity->getEntityType() == EntityType::Sensor) {
std::unique_ptr<UIEntity> sensor = std::make_unique<UISensor>(entity, cont_row);
std::unique_ptr<UIEntity> sensor = std::make_unique<UISensor>(entity, cont_row, icon);
uielements.push_back(std::move(sensor));
}
else {
Expand Down Expand Up @@ -243,7 +247,7 @@ void uithread(int _argc, char* _argv[])
lv_style_init(&voorkant::lvgl::b612style);
lv_style_set_text_font(&voorkant::lvgl::b612style, voorkant::lvgl::b612font);

voorkant::lvgl::mdifont = lv_tiny_ttf_create_data_ex(mdi_ttf, mdi_ttf_len, 16, LV_FONT_KERNING_NORMAL, 1024);
voorkant::lvgl::mdifont = lv_tiny_ttf_create_data_ex(mdi_ttf, mdi_ttf_len, 30, LV_FONT_KERNING_NORMAL, 1024);
lv_style_init(&voorkant::lvgl::mdistyle);
lv_style_set_text_font(&voorkant::lvgl::mdistyle, voorkant::lvgl::mdifont);

Expand Down Expand Up @@ -299,6 +303,63 @@ void uithread(int _argc, char* _argv[])
lv_log_register_print_cb(lvLogCallback);

std::vector<std::unique_ptr<UIEntity>> uielements;
if (program.is_subcommand_used(entity_command) || program.is_subcommand_used(dashboard_command)) {
// need to collect some data used in both cases

auto& ha = HABackend::getInstance();

// FIXME: -cli, -ftxui don't need list_for_display or get_icons
json list_for_display = ha.doCommand("config/entity_registry/list_for_display", {});

cerr << list_for_display.dump(2) << endl;
std::set<string> integrations;
for (auto ent : list_for_display["result"]["entities"]) {
auto entity_id = ent["ei"].get<std::string>();
auto platform = ent.value("pl", "");
auto translation_key = ent.value("tk", "");
integrations.insert(platform);

auto entity = ha.getEntityByName(entity_id);
entity->platform = platform;
entity->translation_key = translation_key;
}
json get_icons_req;
get_icons_req["category"] = "entity"; // wonder what other categories there are. platform? integration? need to find frank somewhere

json get_icons = ha.doCommand("frontend/get_icons", get_icons_req);
// inside "resources":
// "sun": {
// "sensor": {
// "next_dawn": {
// "default": "mdi:sun-clock"
// },
// "next_dusk": {
// "default": "mdi:sun-clock"
// },

cerr << get_icons.dump(2) << endl;

for (auto [platform, data] : get_icons["result"]["resources"].items()) {
// FIXME: this if skips a lot
if (data.count("sensor")) {
for (auto& [key, data2] : data["sensor"].items()) {
// key: next_dawn, data2: "default": " ... ""
auto icon = data2.value("default", "");
if (!icon.empty()) {
// platform: sun, key: next_dawn, icon: mdi-sun-clock
voorkant::lvgl::iconmap[{platform, key}] = icon;
cerr << "set iconmap[" << platform << "," << key << "]=" << icon << endl;
}
}
}
}

json get_icons_component_req;
get_icons_component_req["category"] = "entity_component";

voorkant::lvgl::iconcomponentmap = ha.doCommand("frontend/get_icons", get_icons_component_req)["result"]["resources"];
}

if (program.is_subcommand_used(entity_command)) {
// FIXME: does this actually need unique_ptr? I guess it might save some copying

Expand Down
81 changes: 76 additions & 5 deletions src/uicomponents/UIComponents.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "UIComponents.hpp"
#include "mdimap.hpp"
#include "logger.hpp"
#include <src/core/lv_obj_pos.h>
#include <src/core/lv_obj_style_gen.h>
#include <src/misc/lv_area.h>
#include <src/misc/lv_text.h>

lv_obj_t* UIComponent::createLabel(lv_obj_t* _parent, std::string _text)
{
Expand Down Expand Up @@ -178,32 +181,100 @@ void UIDummy::update()
}
};

UISensor::UISensor(std::shared_ptr<HAEntity> _entity, lv_obj_t* _parent) :
// if _icon is passed, we got one from the dashboard, use that
string getIconFor(std::shared_ptr<HAEntity> _entity, std::string _icon) // for now, this function -always- returns something that starts with mdi:
{
if (!_icon.empty()) {
return _icon;
}
json state = _entity->getJsonState();

// 1. see if the state simply contains an icon - user might have set it explicitly
string icon = state["attributes"].value("icon", "");

if (!icon.empty()) {
return icon;
}

if (state["attributes"].count("entity_picture")) {
// there is an icon, but it is not in a format we support yet (like SVG)
return "mdi:border-none-variant";
}
// 2. see if we can find one for platform+translation key
voorkant::lvgl::iconkey key = {_entity->platform, _entity->translation_key};
if (voorkant::lvgl::iconmap.count(key)) {
return voorkant::lvgl::iconmap.at(key);
}

// 3. maybe we can find one by domain plus device_class?
auto& domain = _entity->domain;
auto device_class = state["attributes"].value("device_class", "_");

if (voorkant::lvgl::iconcomponentmap.count(domain)) {
auto& domaindata = voorkant::lvgl::iconcomponentmap[domain];
if (domaindata.count(device_class)) {
if (domaindata[device_class].count("default")) { // FIXME: handle state-dependent variants too
return domaindata[device_class]["default"];
}
}
}

return "mdi:border-none";
}

UISensor::UISensor(std::shared_ptr<HAEntity> _entity, lv_obj_t* _parent, std::string _icon) :
UIEntity(_entity, _parent)
{
lv_obj_t* flowpanel = lv_obj_create(_parent);
lv_obj_set_width(flowpanel, uiEntityWidth);
lv_obj_set_height(flowpanel, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(flowpanel, 5, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_align(flowpanel, LV_ALIGN_CENTER);
lv_obj_set_flex_flow(flowpanel, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_flow(flowpanel, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(flowpanel, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_add_event_cb(flowpanel, UISensor::clickCB, LV_EVENT_CLICKED, reinterpret_cast<void*>(&entity));

lv_obj_t* label = createLabel(flowpanel, entity->name);
lv_obj_t* iconpart = lv_label_create(flowpanel);
lv_obj_set_width(iconpart, 30);
lv_obj_set_height(iconpart, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(iconpart, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(iconpart, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
string icon = getIconFor(_entity, _icon);
// cerr << "iconmap[" << _entity->platform << "," << _entity->translation_key << "]=" << voorkant::lvgl::iconmap[{_entity->platform, _entity->translation_key}] << endl;

if (icon.substr(0, 4) == "mdi:") {
icon = icon.substr(4);
}
else {
icon = "help"; // as getIconFor currently promises something mdi:, we should never get here
}

lv_label_set_text(iconpart, voorkant::mdi::name2id(icon).data());
lv_obj_add_style(iconpart, &voorkant::lvgl::mdistyle, 0);
lv_obj_set_style_text_align(iconpart, LV_TEXT_ALIGN_CENTER, 0);

lv_obj_t* textpart = lv_obj_create(flowpanel);
lv_obj_set_width(textpart, uiEntityWidth - 55);
lv_obj_set_height(textpart, LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(textpart, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(textpart, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_flex_flow(textpart, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(textpart, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);

lv_obj_t* label = createLabel(textpart, entity->name);
lv_obj_set_width(label, LV_PCT(100));
lv_obj_set_align(label, LV_ALIGN_LEFT_MID);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN);

extratext2 = createLabel(flowpanel, "State:");
extratext2 = createLabel(textpart, "State:");
lv_obj_set_width(extratext2, LV_PCT(100));
lv_obj_set_align(extratext2, LV_ALIGN_RIGHT_MID);
lv_obj_set_style_text_align(extratext2, LV_TEXT_ALIGN_RIGHT, LV_PART_MAIN);

const auto& services = _entity->getServices();
for (const auto& service : services) {
string txt = "Service: ";
lv_obj_t* servicelabel = createLabel(flowpanel, txt.append(service->name));
lv_obj_t* servicelabel = createLabel(textpart, txt.append(service->name));
lv_obj_set_width(servicelabel, LV_PCT(100));
lv_obj_set_align(servicelabel, LV_ALIGN_LEFT_MID);
}
Expand Down
5 changes: 4 additions & 1 deletion src/uicomponents/UIComponents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ namespace lvgl
extern lv_style_t b612style;
extern lv_font_t* mdifont;
extern lv_style_t mdistyle;
typedef std::pair<string, string> iconkey; // platform, translation_key
extern map<iconkey, string> iconmap; // will need a lock eventually
extern json iconcomponentmap;
}
}

Expand Down Expand Up @@ -82,7 +85,7 @@ class UIDummy : public UIEntity
class UISensor : public UIEntity
{
public:
UISensor(std::shared_ptr<HAEntity> _entity, lv_obj_t* _parent);
UISensor(std::shared_ptr<HAEntity> _entity, lv_obj_t* _parent, std::string _icon = "");
void update() override;

private:
Expand Down

0 comments on commit 573b597

Please sign in to comment.