Compare commits

...

4 Commits

Author SHA1 Message Date
Marc Brooks 598687915d
Merge 13d348c89e into 7955ee9d35 2025-10-29 23:10:28 +01:00
Marc Brooks 7955ee9d35
Improves OTA update reporting and process (#838) 2025-10-29 23:10:23 +01:00
Marc Brooks 13d348c89e
Hide MAC and show hostname when IP address is available 2025-10-27 18:01:55 -05:00
Marc Brooks 3538d15025
feat/Add active hostname to device display
Also regularized up the layouts
2025-10-27 16:28:02 -05:00
30 changed files with 2644 additions and 1691 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ node_modules
#internal/native/lib #internal/native/lib
ui/reports ui/reports
*.eez-project-ui-state

View File

@ -41,9 +41,15 @@ func switchToMainScreen() {
func updateDisplay() { func updateDisplay() {
if networkManager != nil { if networkManager != nil {
nativeInstance.UpdateLabelIfChanged("home_info_ipv4_addr", networkManager.IPv4String()) nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv4_addr", networkManager.IPv4String())
nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv6_addr", networkManager.IPv6String()) nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv6_addr", networkManager.IPv6String())
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkManager.MACString()) nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkManager.MACString())
nativeInstance.UpdateLabelIfChanged("home_info_hostname", networkManager.Hostname())
// we either show the MAC address (if no IP yet) or the hostname (if either IPv4 or IPv6 are available)
hasIP := networkManager.IPv4Ready() || networkManager.IPv6Ready()
nativeInstance.ChangeVisibility("home_info_mac_addr", !hasIP)
nativeInstance.ChangeVisibility("home_info_hostname", hasIP)
} }
_, _ = nativeInstance.UIObjHide("menu_btn_network") _, _ = nativeInstance.UIObjHide("menu_btn_network")
@ -70,6 +76,7 @@ func updateDisplay() {
nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Disconnected") nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Disconnected")
_, _ = nativeInstance.UIObjClearState("hdmi_status_label", "LV_STATE_CHECKED") _, _ = nativeInstance.UIObjClearState("hdmi_status_label", "LV_STATE_CHECKED")
} }
nativeInstance.UpdateLabelIfChanged("cloud_status_label", fmt.Sprintf("%d active", actionSessions)) nativeInstance.UpdateLabelIfChanged("cloud_status_label", fmt.Sprintf("%d active", actionSessions))
if networkManager != nil && networkManager.IsUp() { if networkManager != nil && networkManager.IsUp() {

View File

@ -117,14 +117,18 @@ func (n *Native) UpdateLabelIfChanged(objName string, newText string) {
// UpdateLabelAndChangeVisibility updates the label and changes the visibility of the object // UpdateLabelAndChangeVisibility updates the label and changes the visibility of the object
func (n *Native) UpdateLabelAndChangeVisibility(objName string, newText string) { func (n *Native) UpdateLabelAndChangeVisibility(objName string, newText string) {
n.UpdateLabelIfChanged(objName, newText) n.UpdateLabelIfChanged(objName, newText)
n.ChangeVisibility(objName, newText != "")
}
// ChangeVisibility shows or hides an object AND the container it is in
func (n *Native) ChangeVisibility(objName string, show bool) {
containerName := objName + "_container" containerName := objName + "_container"
if newText == "" { if show {
_, _ = n.UIObjHide(objName)
_, _ = n.UIObjHide(containerName)
} else {
_, _ = n.UIObjShow(objName) _, _ = n.UIObjShow(objName)
_, _ = n.UIObjShow(containerName) _, _ = n.UIObjShow(containerName)
} else {
_, _ = n.UIObjHide(objName)
_, _ = n.UIObjHide(containerName)
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ extern "C" {
#endif #endif
extern const lv_font_t ui_font_font_bold30; extern const lv_font_t ui_font_font_bold30;
extern const lv_font_t ui_font_font_bold24;
extern const lv_font_t ui_font_font_book16; extern const lv_font_t ui_font_font_book16;
extern const lv_font_t ui_font_font_book18; extern const lv_font_t ui_font_font_book18;
extern const lv_font_t ui_font_font_book20; extern const lv_font_t ui_font_font_book20;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 30 px * Size: 30 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 30 --no-compress --font ../../Downloads/jetkvm-lvgl-ui 2/assets/font-bold.ttf --range 32-127 --format lvgl * Opts: --bpp 4 --size 30 --no-compress --font ../fonts/font-bold.ttf --range 32-127 --format lvgl
******************************************************************************/ ******************************************************************************/
#ifdef __has_include #ifdef __has_include

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 16 px * Size: 16 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 16 --no-compress --font ../../Downloads/jetkvm-lvgl-ui 2/assets/font-book.ttf --range 32-127 --format lvgl * Opts: --bpp 4 --size 16 --no-compress --font ../fonts/font-book.ttf --range 32-127 --format lvgl
******************************************************************************/ ******************************************************************************/
#ifdef __has_include #ifdef __has_include

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 18 px * Size: 18 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 18 --no-compress --font ../../Downloads/jetkvm-lvgl-ui 2/assets/font-book.ttf --range 32-127 --format lvgl * Opts: --bpp 4 --size 18 --no-compress --font ../fonts/font-book.ttf --range 32-127 --format lvgl
******************************************************************************/ ******************************************************************************/
#ifdef __has_include #ifdef __has_include

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 20 px * Size: 20 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 20 --no-compress --font ../../Downloads/jetkvm-lvgl-ui 2/assets/font-book.ttf --range 32-127 --format lvgl * Opts: --bpp 4 --size 20 --no-compress --font ../fonts/font-book.ttf --range 32-127 --format lvgl
******************************************************************************/ ******************************************************************************/
#ifdef __has_include #ifdef __has_include

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 24 px * Size: 24 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 24 --no-compress --font ../../Downloads/jetkvm-lvgl-ui 2/assets/font-book.ttf --range 32-127 --format lvgl * Opts: --bpp 4 --size 24 --no-compress --font ../fonts/font-book.ttf --range 32-127 --format lvgl
******************************************************************************/ ******************************************************************************/
#ifdef __has_include #ifdef __has_include

View File

@ -152,8 +152,8 @@ void create_screen_no_network_screen() {
// NoNetworkContentContainer // NoNetworkContentContainer
lv_obj_t *obj = lv_obj_create(parent_obj); lv_obj_t *obj = lv_obj_create(parent_obj);
objects.no_network_content_container = obj; objects.no_network_content_container = obj;
lv_obj_set_pos(obj, 0, 20); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(75)); lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -167,7 +167,6 @@ void create_screen_no_network_screen() {
lv_obj_set_style_flex_main_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_flex_main_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_flex_track_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_flex_track_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_margin_top(obj, 200, LV_PART_MAIN | LV_STATE_CHECKED);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -327,11 +326,11 @@ void create_screen_home_screen() {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
objects.home_info_ipv6_addr = obj; objects.home_info_ipv6_addr = obj;
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_PCT(98), 17); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_label_set_long_mode(obj, LV_LABEL_LONG_DOT); lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
add_style_label_font16(obj); add_style_label_font16(obj);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_font_book16, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, -1, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "fe80::ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); lv_label_set_text(obj, "fe80::ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
} }
{ {
@ -341,8 +340,22 @@ void create_screen_home_screen() {
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0)); lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_label_font16(obj); add_style_label_font16(obj);
lv_obj_set_style_pad_top(obj, -1, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "01:23:45:67:89:ab"); lv_label_set_text(obj, "01:23:45:67:89:ab");
} }
{
// HomeInfoHostname
lv_obj_t *obj = lv_label_create(parent_obj);
objects.home_info_hostname = obj;
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_label_set_long_mode(obj, LV_LABEL_LONG_DOT);
add_style_label_font16(obj);
lv_obj_set_style_pad_bottom(obj, 2, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, -1, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "JetKVM");
}
} }
} }
{ {
@ -799,7 +812,7 @@ void create_screen_menu_advanced_screen() {
lv_obj_t *obj = lv_obj_create(parent_obj); lv_obj_t *obj = lv_obj_create(parent_obj);
objects.menu_items_container_1 = obj; objects.menu_items_container_1 = obj;
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80)); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -950,6 +963,7 @@ void create_screen_menu_network_screen() {
lv_obj_set_size(obj, 300, 240); lv_obj_set_size(obj, 300, 240);
lv_obj_add_event_cb(obj, action_menu_advanced_screen_gesture, LV_EVENT_GESTURE, (void *)0); lv_obj_add_event_cb(obj, action_menu_advanced_screen_gesture, LV_EVENT_GESTURE, (void *)0);
add_style_flex_screen_menu(obj); add_style_flex_screen_menu(obj);
lv_obj_set_style_pad_right(obj, 24, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1601,7 +1615,7 @@ void create_screen_status_screen() {
lv_obj_t *obj = lv_obj_create(parent_obj); lv_obj_t *obj = lv_obj_create(parent_obj);
objects.status_items_container = obj; objects.status_items_container = obj;
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80)); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -1638,13 +1652,13 @@ void create_screen_status_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1673,6 +1687,7 @@ void create_screen_status_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -1680,7 +1695,6 @@ void create_screen_status_screen() {
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1709,13 +1723,13 @@ void create_screen_status_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1820,7 +1834,7 @@ void create_screen_reset_config_screen() {
lv_obj_t *obj = lv_obj_create(parent_obj); lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_container = obj; objects.reset_config_container = obj;
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80)); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -1840,13 +1854,13 @@ void create_screen_reset_config_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1874,7 +1888,7 @@ void create_screen_reset_config_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
add_style_info_content_label(obj); add_style_info_content_label(obj);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Press and hold for\n10 seconds"); lv_label_set_text(obj, "Press and hold\nfor 10 seconds");
} }
} }
} }
@ -1915,13 +1929,13 @@ void create_screen_reset_config_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -1934,15 +1948,15 @@ void create_screen_reset_config_screen() {
lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_RELEASED, (void *)0); lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_RELEASED, (void *)0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_menu_button_label(obj);
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Reset configuration"); lv_label_set_text(obj, "Reset configuration");
} }
} }
@ -2087,7 +2101,7 @@ void create_screen_reboot_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
add_style_info_content_label(obj); add_style_info_content_label(obj);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Press and hold for\n5 seconds"); lv_label_set_text(obj, "Press and hold\nfor 5 seconds");
} }
} }
} }
@ -2099,13 +2113,13 @@ void create_screen_reboot_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -2116,17 +2130,17 @@ void create_screen_reboot_screen() {
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSED, (void *)0); lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSED, (void *)0);
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSING, (void *)0); lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSING, (void *)0);
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_RELEASED, (void *)0); lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_RELEASED, (void *)0);
add_style_menu_button(obj);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
lv_obj_t *obj = lv_label_create(parent_obj); lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_menu_button_label(obj);
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Hold to reboot"); lv_label_set_text(obj, "Hold to reboot");
} }
} }
@ -2290,6 +2304,7 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80)); lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
@ -2298,7 +2313,6 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_set_scroll_dir(obj, LV_DIR_VER); lv_obj_set_scroll_dir(obj, LV_DIR_VER);
lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_START); lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_START);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -2307,13 +2321,13 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -2341,7 +2355,7 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
add_style_info_content_label(obj); add_style_info_content_label(obj);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Press and hold for\n5 seconds"); lv_label_set_text(obj, "Press and hold\nfor 5 seconds");
} }
} }
} }
@ -2382,13 +2396,13 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj); add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -2399,9 +2413,9 @@ void create_screen_switch_dhcp_client_screen() {
lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_PRESSED, (void *)0); lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_PRESSED, (void *)0);
lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_PRESSING, (void *)0); lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_PRESSING, (void *)0);
lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_RELEASED, (void *)0); lv_obj_add_event_cb(obj, action_dhcpc, LV_EVENT_RELEASED, (void *)0);
add_style_menu_button(obj);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT);
{ {
lv_obj_t *parent_obj = obj; lv_obj_t *parent_obj = obj;
{ {
@ -2410,8 +2424,8 @@ void create_screen_switch_dhcp_client_screen() {
objects.dhcp_client_change_label = obj; objects.dhcp_client_change_label = obj;
lv_obj_set_pos(obj, 0, 0); lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_menu_button_label(obj);
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Switch to udhcpc"); lv_label_set_text(obj, "Switch to udhcpc");
} }
} }

View File

@ -35,6 +35,7 @@ typedef struct _objects_t {
lv_obj_t *home_info_ipv4_addr; lv_obj_t *home_info_ipv4_addr;
lv_obj_t *home_info_ipv6_addr; lv_obj_t *home_info_ipv6_addr;
lv_obj_t *home_info_mac_addr; lv_obj_t *home_info_mac_addr;
lv_obj_t *home_info_hostname;
lv_obj_t *divider; lv_obj_t *divider;
lv_obj_t *home_status_container; lv_obj_t *home_status_container;
lv_obj_t *usb_status; lv_obj_t *usb_status;

View File

@ -76,8 +76,6 @@ void remove_style_flex_center(lv_obj_t *obj) {
void init_style_flex_start_MAIN_DEFAULT(lv_style_t *style) { void init_style_flex_start_MAIN_DEFAULT(lv_style_t *style) {
init_style_flex_center_MAIN_DEFAULT(style); init_style_flex_center_MAIN_DEFAULT(style);
lv_style_set_layout(style, LV_LAYOUT_FLEX);
lv_style_set_flex_flow(style, LV_FLEX_FLOW_COLUMN);
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START);
@ -110,10 +108,8 @@ void remove_style_flex_start(lv_obj_t *obj) {
void init_style_flow_row_space_between_MAIN_DEFAULT(lv_style_t *style) { void init_style_flow_row_space_between_MAIN_DEFAULT(lv_style_t *style) {
init_style_flex_center_MAIN_DEFAULT(style); init_style_flex_center_MAIN_DEFAULT(style);
lv_style_set_layout(style, LV_LAYOUT_FLEX);
lv_style_set_flex_flow(style, LV_FLEX_FLOW_ROW); lv_style_set_flex_flow(style, LV_FLEX_FLOW_ROW);
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_SPACE_BETWEEN); lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_CENTER);
lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START);
}; };
@ -144,11 +140,7 @@ void remove_style_flow_row_space_between(lv_obj_t *obj) {
void init_style_flow_row_start_center_MAIN_DEFAULT(lv_style_t *style) { void init_style_flow_row_start_center_MAIN_DEFAULT(lv_style_t *style) {
init_style_flow_row_space_between_MAIN_DEFAULT(style); init_style_flow_row_space_between_MAIN_DEFAULT(style);
lv_style_set_layout(style, LV_LAYOUT_FLEX);
lv_style_set_flex_flow(style, LV_FLEX_FLOW_ROW);
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_CENTER);
lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START);
}; };
lv_style_t *get_style_flow_row_start_center_MAIN_DEFAULT() { lv_style_t *get_style_flow_row_start_center_MAIN_DEFAULT() {
@ -180,9 +172,9 @@ void init_style_flex_column_start_MAIN_DEFAULT(lv_style_t *style) {
lv_style_set_layout(style, LV_LAYOUT_FLEX); lv_style_set_layout(style, LV_LAYOUT_FLEX);
lv_style_set_flex_flow(style, LV_FLEX_FLOW_COLUMN); lv_style_set_flex_flow(style, LV_FLEX_FLOW_COLUMN);
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START); lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START);
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_SPACE_EVENLY);
}; };
lv_style_t *get_style_flex_column_start_MAIN_DEFAULT() { lv_style_t *get_style_flex_column_start_MAIN_DEFAULT() {
@ -302,6 +294,37 @@ void remove_style_label_font16(lv_obj_t *obj) {
lv_obj_remove_style(obj, get_style_label_font16_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_remove_style(obj, get_style_label_font16_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
}; };
//
// Style: LabelFontBold24
//
void init_style_label_font_bold24_MAIN_DEFAULT(lv_style_t *style) {
init_style_label_font16_MAIN_DEFAULT(style);
lv_style_set_text_font(style, &ui_font_font_bold24);
lv_style_set_length(style, 0);
};
lv_style_t *get_style_label_font_bold24_MAIN_DEFAULT() {
static lv_style_t *style;
if (!style) {
style = lv_malloc(sizeof(lv_style_t));
lv_style_init(style);
init_style_label_font_bold24_MAIN_DEFAULT(style);
}
return style;
};
void add_style_label_font_bold24(lv_obj_t *obj) {
(void)obj;
lv_obj_add_style(obj, get_style_label_font_bold24_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
};
void remove_style_label_font_bold24(lv_obj_t *obj) {
(void)obj;
lv_obj_remove_style(obj, get_style_label_font_bold24_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
};
// //
// Style: LabelFontBold30 // Style: LabelFontBold30
// //
@ -342,7 +365,6 @@ void init_style_header_link_MAIN_DEFAULT(lv_style_t *style) {
lv_style_set_text_color(style, lv_color_hex(0xff1d4ed8)); lv_style_set_text_color(style, lv_color_hex(0xff1d4ed8));
lv_style_set_text_opa(style, 255); lv_style_set_text_opa(style, 255);
lv_style_set_text_font(style, &ui_font_font_book20); lv_style_set_text_font(style, &ui_font_font_book20);
lv_style_set_text_align(style, LV_TEXT_ALIGN_CENTER);
}; };
lv_style_t *get_style_header_link_MAIN_DEFAULT() { lv_style_t *get_style_header_link_MAIN_DEFAULT() {
@ -536,6 +558,7 @@ void add_style(lv_obj_t *obj, int32_t styleIndex) {
add_style_flex_screen, add_style_flex_screen,
add_style_flex_screen_menu, add_style_flex_screen_menu,
add_style_label_font16, add_style_label_font16,
add_style_label_font_bold24,
add_style_label_font_bold30, add_style_label_font_bold30,
add_style_header_link, add_style_header_link,
add_style_menu_button, add_style_menu_button,
@ -559,6 +582,7 @@ void remove_style(lv_obj_t *obj, int32_t styleIndex) {
remove_style_flex_screen, remove_style_flex_screen,
remove_style_flex_screen_menu, remove_style_flex_screen_menu,
remove_style_label_font16, remove_style_label_font16,
remove_style_label_font_bold24,
remove_style_label_font_bold30, remove_style_label_font_bold30,
remove_style_header_link, remove_style_header_link,
remove_style_menu_button, remove_style_menu_button,

View File

@ -52,6 +52,11 @@ lv_style_t *get_style_label_font16_MAIN_DEFAULT();
void add_style_label_font16(lv_obj_t *obj); void add_style_label_font16(lv_obj_t *obj);
void remove_style_label_font16(lv_obj_t *obj); void remove_style_label_font16(lv_obj_t *obj);
// Style: LabelFontBold24
lv_style_t *get_style_label_font_bold24_MAIN_DEFAULT();
void add_style_label_font_bold24(lv_obj_t *obj);
void remove_style_label_font_bold24(lv_obj_t *obj);
// Style: LabelFontBold30 // Style: LabelFontBold30
lv_style_t *get_style_label_font_bold30_MAIN_DEFAULT(); lv_style_t *get_style_label_font_bold30_MAIN_DEFAULT();
void add_style_label_font_bold30(lv_obj_t *obj); void add_style_label_font_bold30(lv_obj_t *obj);

Binary file not shown.

Binary file not shown.

27
main.go
View File

@ -14,6 +14,7 @@ import (
var appCtx context.Context var appCtx context.Context
func Main() { func Main() {
logger.Log().Msg("JetKVM Starting Up")
LoadConfig() LoadConfig()
var cancel context.CancelFunc var cancel context.CancelFunc
@ -79,16 +80,16 @@ func Main() {
startVideoSleepModeTicker() startVideoSleepModeTicker()
go func() { go func() {
// wait for 15 minutes before starting auto-update checks
// this is to avoid interfering with initial setup processes
// and to ensure the system is stable before checking for updates
time.Sleep(15 * time.Minute) time.Sleep(15 * time.Minute)
for {
logger.Debug().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("UPDATING")
if !config.AutoUpdateEnabled {
return
}
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() { for {
logger.Debug().Msg("system time is not synced, will retry in 30 seconds") logger.Info().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("auto-update check")
time.Sleep(30 * time.Second) if !config.AutoUpdateEnabled {
logger.Debug().Msg("auto-update disabled")
time.Sleep(5 * time.Minute) // we'll check if auto-updates are enabled in five minutes
continue continue
} }
@ -98,6 +99,12 @@ func Main() {
continue continue
} }
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() {
logger.Debug().Msg("system time is not synced, will retry in 30 seconds")
time.Sleep(30 * time.Second)
continue
}
includePreRelease := config.IncludePreRelease includePreRelease := config.IncludePreRelease
err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease) err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease)
if err != nil { if err != nil {
@ -107,6 +114,7 @@ func Main() {
time.Sleep(1 * time.Hour) time.Sleep(1 * time.Hour)
} }
}() }()
//go RunFuseServer() //go RunFuseServer()
go RunWebServer() go RunWebServer()
@ -123,7 +131,8 @@ func Main() {
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs <-sigs
logger.Info().Msg("JetKVM Shutting Down")
logger.Log().Msg("JetKVM Shutting Down")
//if fuseServer != nil { //if fuseServer != nil {
// err := setMassStorageImage(" ") // err := setMassStorageImage(" ")
// if err != nil { // if err != nil {

40
ota.go
View File

@ -176,7 +176,7 @@ func downloadFile(ctx context.Context, path string, url string, downloadProgress
if nr > 0 { if nr > 0 {
nw, ew := file.Write(buf[0:nr]) nw, ew := file.Write(buf[0:nr])
if nw < nr { if nw < nr {
return fmt.Errorf("short write: %d < %d", nw, nr) return fmt.Errorf("short file write: %d < %d", nw, nr)
} }
written += int64(nw) written += int64(nw)
if ew != nil { if ew != nil {
@ -240,7 +240,7 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
if nr > 0 { if nr > 0 {
nw, ew := hash.Write(buf[0:nr]) nw, ew := hash.Write(buf[0:nr])
if nw < nr { if nw < nr {
return fmt.Errorf("short write: %d < %d", nw, nr) return fmt.Errorf("short hash write: %d < %d", nw, nr)
} }
verified += int64(nw) verified += int64(nw)
if ew != nil { if ew != nil {
@ -260,11 +260,16 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
} }
} }
hashSum := hash.Sum(nil) // close the file so we can rename below
scopedLogger.Info().Str("path", path).Str("hash", hex.EncodeToString(hashSum)).Msg("SHA256 hash of") if err := fileToHash.Close(); err != nil {
return fmt.Errorf("error closing file: %w", err)
}
if hex.EncodeToString(hashSum) != expectedHash { hashSum := hex.EncodeToString(hash.Sum(nil))
return fmt.Errorf("hash mismatch: %x != %s", hashSum, expectedHash) scopedLogger.Info().Str("path", path).Str("hash", hashSum).Msg("SHA256 hash of")
if hashSum != expectedHash {
return fmt.Errorf("hash mismatch: %s != %s", hashSum, expectedHash)
} }
if err := os.Rename(unverifiedPath, path); err != nil { if err := os.Rename(unverifiedPath, path); err != nil {
@ -313,7 +318,7 @@ func triggerOTAStateUpdate() {
func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error { func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error {
scopedLogger := otaLogger.With(). scopedLogger := otaLogger.With().
Str("deviceId", deviceId). Str("deviceId", deviceId).
Str("includePreRelease", fmt.Sprintf("%v", includePreRelease)). Bool("includePreRelease", includePreRelease).
Logger() Logger()
scopedLogger.Info().Msg("Trying to update...") scopedLogger.Info().Msg("Trying to update...")
@ -362,8 +367,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error downloading app update: %v", err) otaState.Error = fmt.Sprintf("Error downloading app update: %v", err)
scopedLogger.Error().Err(err).Msg("Error downloading app update") scopedLogger.Error().Err(err).Msg("Error downloading app update")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return err return fmt.Errorf("error downloading app update: %w", err)
} }
downloadFinished := time.Now() downloadFinished := time.Now()
otaState.AppDownloadFinishedAt = &downloadFinished otaState.AppDownloadFinishedAt = &downloadFinished
otaState.AppDownloadProgress = 1 otaState.AppDownloadProgress = 1
@ -379,17 +385,21 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err) otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err)
scopedLogger.Error().Err(err).Msg("Error verifying app update hash") scopedLogger.Error().Err(err).Msg("Error verifying app update hash")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return err return fmt.Errorf("error verifying app update: %w", err)
} }
verifyFinished := time.Now() verifyFinished := time.Now()
otaState.AppVerifiedAt = &verifyFinished otaState.AppVerifiedAt = &verifyFinished
otaState.AppVerificationProgress = 1 otaState.AppVerificationProgress = 1
triggerOTAStateUpdate()
otaState.AppUpdatedAt = &verifyFinished otaState.AppUpdatedAt = &verifyFinished
otaState.AppUpdateProgress = 1 otaState.AppUpdateProgress = 1
triggerOTAStateUpdate() triggerOTAStateUpdate()
scopedLogger.Info().Msg("App update downloaded") scopedLogger.Info().Msg("App update downloaded")
rebootNeeded = true rebootNeeded = true
triggerOTAStateUpdate()
} else { } else {
scopedLogger.Info().Msg("App is up to date") scopedLogger.Info().Msg("App is up to date")
} }
@ -405,8 +415,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error downloading system update: %v", err) otaState.Error = fmt.Sprintf("Error downloading system update: %v", err)
scopedLogger.Error().Err(err).Msg("Error downloading system update") scopedLogger.Error().Err(err).Msg("Error downloading system update")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return err return fmt.Errorf("error downloading system update: %w", err)
} }
downloadFinished := time.Now() downloadFinished := time.Now()
otaState.SystemDownloadFinishedAt = &downloadFinished otaState.SystemDownloadFinishedAt = &downloadFinished
otaState.SystemDownloadProgress = 1 otaState.SystemDownloadProgress = 1
@ -422,8 +433,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err) otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err)
scopedLogger.Error().Err(err).Msg("Error verifying system update hash") scopedLogger.Error().Err(err).Msg("Error verifying system update hash")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return err return fmt.Errorf("error verifying system update: %w", err)
} }
scopedLogger.Info().Msg("System update downloaded") scopedLogger.Info().Msg("System update downloaded")
verifyFinished := time.Now() verifyFinished := time.Now()
otaState.SystemVerifiedAt = &verifyFinished otaState.SystemVerifiedAt = &verifyFinished
@ -439,8 +451,10 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
if err != nil { if err != nil {
otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err) otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err)
scopedLogger.Error().Err(err).Msg("Error starting rk_ota command") scopedLogger.Error().Err(err).Msg("Error starting rk_ota command")
triggerOTAStateUpdate()
return fmt.Errorf("error starting rk_ota command: %w", err) return fmt.Errorf("error starting rk_ota command: %w", err)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -475,13 +489,15 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
Str("output", output). Str("output", output).
Int("exitCode", cmd.ProcessState.ExitCode()). Int("exitCode", cmd.ProcessState.ExitCode()).
Msg("Error executing rk_ota command") Msg("Error executing rk_ota command")
triggerOTAStateUpdate()
return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output) return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output)
} }
scopedLogger.Info().Str("output", output).Msg("rk_ota success") scopedLogger.Info().Str("output", output).Msg("rk_ota success")
otaState.SystemUpdateProgress = 1 otaState.SystemUpdateProgress = 1
otaState.SystemUpdatedAt = &verifyFinished otaState.SystemUpdatedAt = &verifyFinished
triggerOTAStateUpdate()
rebootNeeded = true rebootNeeded = true
triggerOTAStateUpdate()
} else { } else {
scopedLogger.Info().Msg("System is up to date") scopedLogger.Info().Msg("System is up to date")
} }

View File

@ -212,7 +212,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
Button.displayName = "Button"; Button.displayName = "Button";
type LinkPropsType = Pick<LinkProps, "to"> & type LinkPropsType = Pick<LinkProps, "to"> &
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean }; React.ComponentProps<typeof ButtonContent> & { disabled?: boolean, reloadDocument?: boolean };
export const LinkButton = ({ to, ...props }: LinkPropsType) => { export const LinkButton = ({ to, ...props }: LinkPropsType) => {
const classes = cx( const classes = cx(
"group outline-hidden", "group outline-hidden",
@ -230,7 +230,7 @@ export const LinkButton = ({ to, ...props }: LinkPropsType) => {
); );
} else { } else {
return ( return (
<Link to={to} className={classes}> <Link to={to} reloadDocument={props.reloadDocument} className={classes}>
<ButtonContent {...props} /> <ButtonContent {...props} />
</Link> </Link>
); );

View File

@ -573,14 +573,18 @@ export interface OtaState {
export interface UpdateState { export interface UpdateState {
isUpdatePending: boolean; isUpdatePending: boolean;
setIsUpdatePending: (isPending: boolean) => void; setIsUpdatePending: (isPending: boolean) => void;
updateDialogHasBeenMinimized: boolean; updateDialogHasBeenMinimized: boolean;
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
otaState: OtaState; otaState: OtaState;
setOtaState: (state: OtaState) => void; setOtaState: (state: OtaState) => void;
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
modalView: UpdateModalViews modalView: UpdateModalViews
setModalView: (view: UpdateModalViews) => void; setModalView: (view: UpdateModalViews) => void;
setUpdateErrorMessage: (errorMessage: string) => void;
updateErrorMessage: string | null; updateErrorMessage: string | null;
setUpdateErrorMessage: (errorMessage: string) => void;
} }
export const useUpdateStore = create<UpdateState>(set => ({ export const useUpdateStore = create<UpdateState>(set => ({
@ -611,8 +615,10 @@ export const useUpdateStore = create<UpdateState>(set => ({
updateDialogHasBeenMinimized: false, updateDialogHasBeenMinimized: false,
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) =>
set({ updateDialogHasBeenMinimized: hasBeenMinimized }), set({ updateDialogHasBeenMinimized: hasBeenMinimized }),
modalView: "loading", modalView: "loading",
setModalView: (view: UpdateModalViews) => set({ modalView: view }), setModalView: (view: UpdateModalViews) => set({ modalView: view }),
updateErrorMessage: null, updateErrorMessage: null,
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }), setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
})); }));

View File

@ -73,10 +73,10 @@ export async function checkDeviceAuth() {
.GET(`${DEVICE_API}/device/status`) .GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>); .then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) return redirect("/welcome"); if (!res.isSetup) throw redirect("/welcome");
const deviceRes = await api.GET(`${DEVICE_API}/device`); const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.status === 401) return redirect("/login-local"); if (deviceRes.status === 401) throw redirect("/login-local");
if (deviceRes.ok) { if (deviceRes.ok) {
const device = (await deviceRes.json()) as LocalDevice; const device = (await deviceRes.json()) as LocalDevice;
return { authMode: device.authMode }; return { authMode: device.authMode };
@ -86,7 +86,7 @@ export async function checkDeviceAuth() {
} }
export async function checkAuth() { export async function checkAuth() {
return import.meta.env.MODE === "device" ? checkDeviceAuth() : checkCloudAuth(); return isOnDevice ? checkDeviceAuth() : checkCloudAuth();
} }
let router; let router;

View File

@ -58,7 +58,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
return { device, user }; return { device, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { devices: [] }; return { user };
} }
}; };

View File

@ -54,7 +54,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
return { device, user }; return { device, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { devices: [] }; return { user };
} }
}; };

View File

@ -98,7 +98,7 @@ export default function SettingsAccessIndexRoute() {
} }
getCloudState(); getCloudState();
// In cloud mode, we need to navigate to the device overview page, as we don't a connection anymore // In cloud mode, we need to navigate to the device overview page, as we don't have a connection anymore
if (!isOnDevice) navigate("/"); if (!isOnDevice) navigate("/");
return; return;
}); });

View File

@ -9,11 +9,17 @@ export default function SettingsGeneralRebootRoute() {
const navigate = useNavigate(); const navigate = useNavigate();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => {
navigate(".."); // back to the devices.$id.settings page
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]);
const onConfirmUpdate = useCallback(() => { const onConfirmUpdate = useCallback(() => {
send("reboot", { force: true}); send("reboot", { force: true});
}, [send]); }, [send]);
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />; return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />;
} }
export function Dialog({ export function Dialog({

View File

@ -21,6 +21,11 @@ export default function SettingsGeneralUpdateRoute() {
const { setModalView, otaState } = useUpdateStore(); const { setModalView, otaState } = useUpdateStore();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => {
navigate(".."); // back to the devices.$id.settings page
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]);
const onConfirmUpdate = useCallback(() => { const onConfirmUpdate = useCallback(() => {
send("tryUpdate", {}); send("tryUpdate", {});
setModalView("updating"); setModalView("updating");
@ -36,9 +41,9 @@ export default function SettingsGeneralUpdateRoute() {
} else { } else {
setModalView("loading"); setModalView("loading");
} }
}, [otaState.updating, otaState.error, setModalView, updateSuccess]); }, [otaState.error, otaState.updating, setModalView, updateSuccess]);
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />; return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />;
} }
export function Dialog({ export function Dialog({

View File

@ -1,7 +1,6 @@
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { import {
Outlet, Outlet,
redirect,
useLoaderData, useLoaderData,
useLocation, useLocation,
useNavigate, useNavigate,
@ -16,7 +15,7 @@ import { motion, AnimatePresence } from "framer-motion";
import useWebSocket from "react-use-websocket"; import useWebSocket from "react-use-websocket";
import { cx } from "@/cva.config"; import { cx } from "@/cva.config";
import { CLOUD_API, DEVICE_API } from "@/ui.config"; import { CLOUD_API } from "@/ui.config";
import api from "@/api"; import api from "@/api";
import { checkAuth, isInCloud, isOnDevice } from "@/main"; import { checkAuth, isInCloud, isOnDevice } from "@/main";
import { import {
@ -51,9 +50,14 @@ import {
RebootingOverlay, RebootingOverlay,
} from "@components/VideoOverlay"; } from "@components/VideoOverlay";
import { FeatureFlagProvider } from "@providers/FeatureFlagProvider"; import { FeatureFlagProvider } from "@providers/FeatureFlagProvider";
import { DeviceStatus } from "@routes/welcome-local";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
export type AuthMode = "password" | "noPassword" | null;
interface LocalLoaderResp {
authMode: AuthMode;
}
interface CloudLoaderResp { interface CloudLoaderResp {
deviceName: string; deviceName: string;
user: User | null; user: User | null;
@ -62,35 +66,20 @@ interface CloudLoaderResp {
} | null; } | null;
} }
export type AuthMode = "password" | "noPassword" | null;
export interface LocalDevice { export interface LocalDevice {
authMode: AuthMode; authMode: AuthMode;
deviceId: string; deviceId: string;
} }
const deviceLoader = async () => { const deviceLoader = async () => {
const res = await api const device = await checkAuth();
.GET(`${DEVICE_API}/device/status`) return { authMode: device.authMode } as LocalLoaderResp;
.then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) return redirect("/welcome");
const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.status === 401) return redirect("/login-local");
if (deviceRes.ok) {
const device = (await deviceRes.json()) as LocalDevice;
return { authMode: device.authMode };
}
throw new Error("Error fetching device");
}; };
const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => { const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => {
const user = await checkAuth(); const user = await checkAuth();
const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`); const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`);
const iceConfig = await iceResp.json(); const iceConfig = await iceResp.json();
const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`); const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`);
if (!deviceResp.ok) { if (!deviceResp.ok) {
@ -105,11 +94,11 @@ const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> =>
device: { id: string; name: string; user: { googleId: string } }; device: { id: string; name: string; user: { googleId: string } };
}; };
return { user, iceConfig, deviceName: device.name || device.id }; return { user, iceConfig, deviceName: device.name || device.id } as CloudLoaderResp;
}; };
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => { const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
return import.meta.env.MODE === "device" ? deviceLoader() : cloudLoader(params); return isOnDevice ? deviceLoader() : cloudLoader(params);
}; };
export default function KvmIdRoute() { export default function KvmIdRoute() {
@ -185,7 +174,7 @@ export default function KvmIdRoute() {
try { try {
await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription)); await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription));
console.log("[setRemoteSessionDescription] Remote description set successfully"); console.log("[setRemoteSessionDescription] Remote description set successfully to: " + remoteDescription.sdp);
setLoadingMessage(m.establishing_secure_connection()); setLoadingMessage(m.establishing_secure_connection());
} catch (error) { } catch (error) {
console.error( console.error(
@ -230,9 +219,14 @@ export default function KvmIdRoute() {
const ignoreOffer = useRef(false); const ignoreOffer = useRef(false);
const isSettingRemoteAnswerPending = useRef(false); const isSettingRemoteAnswerPending = useRef(false);
const makingOffer = useRef(false); const makingOffer = useRef(false);
const reconnectAttemptsRef = useRef(2000);
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const reconnectInterval = (attempt: number) => {
// Exponential backoff with a max of 10 seconds between attempts
return Math.min(500 * 2 ** attempt, 10000);
}
const { sendMessage, getWebSocket } = useWebSocket( const { sendMessage, getWebSocket } = useWebSocket(
isOnDevice isOnDevice
? `${wsProtocol}//${window.location.host}/webrtc/signaling/client` ? `${wsProtocol}//${window.location.host}/webrtc/signaling/client`
@ -240,10 +234,10 @@ export default function KvmIdRoute() {
{ {
heartbeat: true, heartbeat: true,
retryOnError: true, retryOnError: true,
reconnectAttempts: 2000, reconnectAttempts: reconnectAttemptsRef.current,
reconnectInterval: 1000, reconnectInterval: reconnectInterval,
onReconnectStop: (numAttempts: number) => { onReconnectStop: (numAttempts: number) => {
console.debug("Reconnect stopped", numAttempts); console.debug("Reconnect stopped after ", numAttempts, "attempts");
cleanupAndStopReconnecting(); cleanupAndStopReconnecting();
}, },
@ -261,6 +255,7 @@ export default function KvmIdRoute() {
console.error("[Websocket] onError", event); console.error("[Websocket] onError", event);
// We don't want to close everything down, we wait for the reconnect to stop instead // We don't want to close everything down, we wait for the reconnect to stop instead
}, },
onOpen() { onOpen() {
console.debug("[Websocket] onOpen"); console.debug("[Websocket] onOpen");
// We want to clear the reboot state when the websocket connection is opened // We want to clear the reboot state when the websocket connection is opened
@ -293,6 +288,7 @@ export default function KvmIdRoute() {
*/ */
const parsedMessage = JSON.parse(message.data); const parsedMessage = JSON.parse(message.data);
if (parsedMessage.type === "device-metadata") { if (parsedMessage.type === "device-metadata") {
const { deviceVersion } = parsedMessage.data; const { deviceVersion } = parsedMessage.data;
console.debug("[Websocket] Received device-metadata message"); console.debug("[Websocket] Received device-metadata message");
@ -309,10 +305,12 @@ export default function KvmIdRoute() {
console.log("[Websocket] Device is using new signaling"); console.log("[Websocket] Device is using new signaling");
isLegacySignalingEnabled.current = false; isLegacySignalingEnabled.current = false;
} }
setupPeerConnection(); setupPeerConnection();
} }
if (!peerConnection) return; if (!peerConnection) return;
if (parsedMessage.type === "answer") { if (parsedMessage.type === "answer") {
console.debug("[Websocket] Received answer"); console.debug("[Websocket] Received answer");
const readyForOffer = const readyForOffer =

View File

@ -30,7 +30,7 @@ const loader: LoaderFunction = async ()=> {
return { devices, user }; return { devices, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { devices: [] }; return { devices: [], user };
} }
}; };