Skip to content

Commit

Permalink
First draft of an OnboardingApp
Browse files Browse the repository at this point in the history
  • Loading branch information
carlhampuswall committed Jan 18, 2024
1 parent 55a964e commit 53d8e98
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 25 deletions.
39 changes: 39 additions & 0 deletions firmware/src/apps/apps.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include "apps.h"
#include "menu.h"
#include "onboarding.h"
#include "settings.h"

#include <typeinfo>
Expand Down Expand Up @@ -132,6 +133,44 @@ void Apps::reload(cJSON *apps_)
cJSON_Delete(apps_);
}

void Apps::createOnboarding()
{
clear();

OnboardingApp *onboarding_app = new OnboardingApp(this->spr_);
uint16_t app_position = 0;

onboarding_app->add_item(
app_position,
OnboardingItem{
"SMART KNOB",
"DEV KIT V0.1",
1,
spr_->color565(255, 255, 255),
nullptr,
nullptr,
"ROTATE TO START",
});

app_position++;

onboarding_app->add_item(
app_position,
OnboardingItem{
"HOME ASSISTANT",
"INTEGRATION",
1,
spr_->color565(255, 255, 255),
nullptr,
nullptr,
"PRESS TO CONTINUE",
});

add(0, onboarding_app);

// setActive(0);
}

void Apps::updateMenu()
{
// re - generate new menu based on loaded apps
Expand Down
7 changes: 6 additions & 1 deletion firmware/src/apps/apps.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
#include "climate.h"
#include "light_dimmer.h"
#include "light_switch.h"
#include "menu.h"
#include "music.h"
#include "settings.h"
#include "stopwatch.h"

// include all "menu" apps
#include "menu.h"
#include "onboarding.h"

// TODO: generate menu based on items in the map
class Apps
{
Expand All @@ -31,7 +34,9 @@ class Apps
void setSprite(TFT_eSprite *spr_);
void loadApp(uint8_t position, std::string app_slug, std::string app_id, std::string friendly_name);
void updateMenu();

void reload(cJSON *apps_);
void createOnboarding();

private:
QueueHandle_t mutex;
Expand Down
144 changes: 144 additions & 0 deletions firmware/src/apps/onboarding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include "onboarding.h"

OnboardingApp::OnboardingApp(TFT_eSprite *spr_) : App(spr_)
{
// sprintf(room, "%s", "");

motor_config = PB_SmartKnobConfig{
0,
0,
0,
0,
-1, // max position < min position indicates no bounds
25 * PI / 180,
2,
1,
0.55,
"SKDEMO_Menu", // TODO: clean this
0,
{},
0,
20,
};
}

EntityStateUpdate OnboardingApp::updateStateFromKnob(PB_SmartKnobState state)
{
// TODO: cache menu size

int32_t position_for_onboarding_calc = state.current_position;

// needed to next reload of App
motor_config.position_nonce = position_for_onboarding_calc;
motor_config.position = position_for_onboarding_calc;

if (state.current_position < 0)
{
position_for_onboarding_calc = items.size() * 10000 + state.current_position;
}

current_onboarding_position = position_for_onboarding_calc % items.size();

return EntityStateUpdate{};
}

void OnboardingApp::updateStateFromSystem(AppState state) {}

uint8_t OnboardingApp::navigationNext()
{
return current_onboarding_position + 1; // +1 to shift from 0 position which is menu itself
}

TFT_eSprite *OnboardingApp::render()
{
OnboardingItem current_item = find_item(current_onboarding_position);
OnboardingItem prev_item;
OnboardingItem next_item;
if (current_onboarding_position == 0)
{
// prev_item = find_item(items.size() - 1);
next_item = find_item(current_onboarding_position + 1);
}
else if (current_onboarding_position == items.size() - 1)
{
prev_item = find_item(current_onboarding_position - 1);
// next_item = find_item(0);
}
else
{
prev_item = find_item(current_onboarding_position - 1);
next_item = find_item(current_onboarding_position + 1);
}
render_onboarding_screen(current_item, prev_item, next_item);
return this->spr_;
}

void OnboardingApp::add_item(uint8_t id, OnboardingItem item)
{
items[id] = item;
onboarding_items_count++;
}

// TODO: add protection, could cause panic
OnboardingItem OnboardingApp::find_item(uint8_t id)
{
return (*items.find(id)).second;
}

void OnboardingApp::render_onboarding_screen(OnboardingItem current, OnboardingItem prev, OnboardingItem next)
{
uint32_t color_active = current.color;
uint32_t color_inactive = spr_->color565(150, 150, 150);
uint32_t label_color = color_inactive;
uint32_t background = spr_->color565(0, 0, 0);

uint16_t center_h = TFT_WIDTH / 2;
uint16_t center_v = TFT_WIDTH / 2;

int8_t screen_name_label_w = 100;
int8_t screen_name_label_h = spr_->fontHeight(1);
int8_t label_vertical_offset = 25;

uint8_t icon_size_active = 80;
uint8_t icon_size_inactive = 40;

spr_->setTextDatum(CC_DATUM);
spr_->setTextSize(1);
spr_->setFreeFont(&NDS1210pt7b);

// spr_->fillRect(center_h - room_lable_w / 2, label_vertical_offset, room_lable_w, room_lable_h + 1, label_color); // +1 for height to draw circle right
// spr_->fillCircle(center_h - room_lable_w / 2, label_vertical_offset + room_lable_h / 2, room_lable_h / 2, label_color);
// spr_->fillCircle(center_h + room_lable_w / 2, label_vertical_offset + room_lable_h / 2, room_lable_h / 2, label_color);

if (current.big_icon == nullptr)
{
if (current.small_icon == nullptr)
{
spr_->setTextColor(color_active);
spr_->drawString(current.screen_name, center_v, center_h - screen_name_label_h * 2, 1);
spr_->drawString(current.screen_description, center_v, center_h - screen_name_label_h, 1);

spr_->setTextColor(spr_->color565(128, 255, 80));
spr_->drawString(current.call_to_action, center_v, center_v + icon_size_active / 2, 1);
}
else
{
spr_->setTextColor(color_active);
spr_->drawString(current.screen_name, center_v, label_vertical_offset + screen_name_label_h / 2 - 1, 1);

spr_->setTextColor(spr_->color565(128, 255, 80));
spr_->drawString(current.call_to_action, center_v, center_v + icon_size_active / 2 + 30, 1);
}
}

// spr_->drawString(room, center_h, label_vertical_offset + room_lable_h / 2 - 1, 1);

// spr_->drawBitmap(center_h - icon_size_active / 2, center_v - icon_size_active / 2, current.big_icon, icon_size_active, icon_size_active, color_active, background);
// ESP_LOGD("menu.cpp", "%s", current.screen_name);

// left one
// spr_->drawBitmap(center_h - icon_size_active / 2 - 20 - icon_size_inactive, center_v - icon_size_inactive / 2, prev.small_icon, icon_size_inactive, icon_size_inactive, color_inactive, background);

// right one
// spr_->drawBitmap(center_h + icon_size_active / 2 + 20, center_v - icon_size_inactive / 2, next.small_icon, icon_size_inactive, icon_size_inactive, color_inactive, background);
};
36 changes: 36 additions & 0 deletions firmware/src/apps/onboarding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include "app.h"
#include "font/NDS1210pt7b.h"

#include <map>

struct OnboardingItem
{
const char *screen_name;
const char *screen_description;
uint16_t app_id;
uint32_t color;
const unsigned char *small_icon;
const unsigned char *big_icon;
const char *call_to_action;
};

class OnboardingApp : public App
{
public:
OnboardingApp(TFT_eSprite *spr_);
TFT_eSprite *render();
EntityStateUpdate updateStateFromKnob(PB_SmartKnobState state);
void updateStateFromSystem(AppState state);
uint8_t navigationNext();
void add_item(uint8_t id, OnboardingItem item);
OnboardingItem find_item(uint8_t id);

private:
std::map<uint8_t, OnboardingItem> items;
uint8_t onboarding_items_count = 0;
uint8_t current_onboarding_position = 0;

void render_onboarding_screen(OnboardingItem current, OnboardingItem prev, OnboardingItem next);
};
50 changes: 26 additions & 24 deletions firmware/src/display_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,45 @@ void DisplayTask::run()
}
spr_.setTextColor(0xFFFF, TFT_BLACK);

std::string apps_config = "[{\"app_slug\":\"stopwatch\",\"app_id\":\"stopwatch.office\",\"friendly_name\":\"Stopwatch\",\"area\":\"office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_switch\",\"app_id\":\"light.ceiling\",\"friendly_name\":\"Ceiling\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_dimmer\",\"app_id\":\"light.workbench\",\"friendly_name\":\"Workbench\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"thermostat\",\"app_id\":\"climate.office\",\"friendly_name\":\"Climate\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"3d_printer\",\"app_id\":\"3d_printer.office\",\"friendly_name\":\"3D Printer\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"blinds\",\"app_id\":\"blinds.office\",\"friendly_name\":\"Shades\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"music\",\"app_id\":\"music.office\",\"friendly_name\":\"Music\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"}]";
// std::string apps_config = "[{\"app_slug\":\"stopwatch\",\"app_id\":\"stopwatch-office\",\"friendly_name\":\"Stopwatch\",\"area\":\"office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_switch\",\"app_id\":\"light.ceiling\",\"friendly_name\":\"Ceiling\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"}]";
// std::string apps_config = "[{\"app_slug\":\"stopwatch\",\"app_id\":\"stopwatch.office\",\"friendly_name\":\"Stopwatch\",\"area\":\"office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_switch\",\"app_id\":\"light.ceiling\",\"friendly_name\":\"Ceiling\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_dimmer\",\"app_id\":\"light.workbench\",\"friendly_name\":\"Workbench\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"thermostat\",\"app_id\":\"climate.office\",\"friendly_name\":\"Climate\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"3d_printer\",\"app_id\":\"3d_printer.office\",\"friendly_name\":\"3D Printer\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"blinds\",\"app_id\":\"blinds.office\",\"friendly_name\":\"Shades\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"music\",\"app_id\":\"music.office\",\"friendly_name\":\"Music\",\"area\":\"Office\",\"menu_color\":\"#ffffff\"}]";
// // std::string apps_config = "[{\"app_slug\":\"stopwatch\",\"app_id\":\"stopwatch-office\",\"friendly_name\":\"Stopwatch\",\"area\":\"office\",\"menu_color\":\"#ffffff\"},{\"app_slug\":\"light_switch\",\"app_id\":\"light.ceiling\",\"friendly_name\":\"Ceiling\",\"area\":\"Kitchen\",\"menu_color\":\"#ffffff\"}]";

cJSON *json_root = cJSON_Parse(apps_config.c_str());
// cJSON *json_root = cJSON_Parse(apps_config.c_str());

if (json_root == NULL)
{
ESP_LOGE("display_task.cpp", "failed to parse JSON");
}
// if (json_root == NULL)
// {
// ESP_LOGE("display_task.cpp", "failed to parse JSON");
// }

apps.setSprite(&spr_);

cJSON *json_app = NULL;
// cJSON *json_app = NULL;

uint16_t app_position = 1;
// uint16_t app_position = 1;

cJSON_ArrayForEach(json_app, json_root)
{
cJSON *json_app_slug = cJSON_GetObjectItemCaseSensitive(json_app, "app_slug");
cJSON *json_app_id = cJSON_GetObjectItemCaseSensitive(json_app, "app_id");
cJSON *json_friendly_name = cJSON_GetObjectItemCaseSensitive(json_app, "friendly_name");
snprintf(buf_, sizeof(buf_), "fromJSON > app_slug=%s", json_app_slug->valuestring);
log(buf_);
// ESP_LOGD("display_task.cpp", "%s", buf_);
// cJSON_ArrayForEach(json_app, json_root)
// {
// cJSON *json_app_slug = cJSON_GetObjectItemCaseSensitive(json_app, "app_slug");
// cJSON *json_app_id = cJSON_GetObjectItemCaseSensitive(json_app, "app_id");
// cJSON *json_friendly_name = cJSON_GetObjectItemCaseSensitive(json_app, "friendly_name");
// snprintf(buf_, sizeof(buf_), "fromJSON > app_slug=%s", json_app_slug->valuestring);
// log(buf_);
// // ESP_LOGD("display_task.cpp", "%s", buf_);

apps.loadApp(app_position, std::string(json_app_slug->valuestring), std::string(json_app_id->valuestring), json_friendly_name->valuestring);
// apps.loadApp(app_position, std::string(json_app_slug->valuestring), std::string(json_app_id->valuestring), json_friendly_name->valuestring);

app_position++;
}
// app_position++;
// }

cJSON_Delete(json_root);
// cJSON_Delete(json_root);

SettingsApp *settings_app = new SettingsApp(&spr_);
apps.add(app_position, settings_app);
// SettingsApp *settings_app = new SettingsApp(&spr_);
// apps.add(app_position, settings_app);

// generate menu from apps list
apps.updateMenu();
// apps.updateMenu();

apps.createOnboarding();

AppState app_state;

Expand Down

0 comments on commit 53d8e98

Please sign in to comment.