Compare commits
4 Commits
dc40acfd06
...
501c2de874
| Author | SHA1 | Date | |
|---|---|---|---|
|
501c2de874
|
|||
|
b39a3be956
|
|||
|
3ec7bf7acb
|
|||
|
a12dfe7760
|
@@ -11,7 +11,9 @@ idf_component_register(SRCS
|
|||||||
esp_netif
|
esp_netif
|
||||||
esp_event
|
esp_event
|
||||||
json
|
json
|
||||||
|
led-manager
|
||||||
simulator
|
simulator
|
||||||
persistence-manager
|
persistence-manager
|
||||||
message-manager
|
message-manager
|
||||||
|
simulator
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
|
|
||||||
|
void common_init(void);
|
||||||
cJSON *create_light_status_json(void);
|
cJSON *create_light_status_json(void);
|
||||||
|
|
||||||
#endif // COMMON_H
|
#endif // COMMON_H
|
||||||
|
|||||||
@@ -746,7 +746,7 @@ esp_err_t api_static_file_handler(httpd_req_t *req)
|
|||||||
const char *uri = req->uri;
|
const char *uri = req->uri;
|
||||||
wifi_mode_t mode = 0;
|
wifi_mode_t mode = 0;
|
||||||
esp_wifi_get_mode(&mode);
|
esp_wifi_get_mode(&mode);
|
||||||
// Im AP-Modus immer captive.html ausliefern
|
// Always serve captive.html in AP mode
|
||||||
if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA)
|
if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA)
|
||||||
{
|
{
|
||||||
if (strcmp(uri, "/") == 0 || strcmp(uri, "/index.html") == 0)
|
if (strcmp(uri, "/") == 0 || strcmp(uri, "/index.html") == 0)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "api_handlers.h"
|
#include "api_handlers.h"
|
||||||
#include "websocket_handler.h"
|
#include "websocket_handler.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@@ -86,6 +87,9 @@ static esp_err_t start_webserver(void)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common initialization
|
||||||
|
common_init();
|
||||||
|
|
||||||
ESP_LOGI(TAG, "HTTP server started successfully");
|
ESP_LOGI(TAG, "HTTP server started successfully");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,39 @@
|
|||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "api_server.h"
|
||||||
|
#include "color.h"
|
||||||
|
#include "message_manager.h"
|
||||||
#include "persistence_manager.h"
|
#include "persistence_manager.h"
|
||||||
|
#include "simulator.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
const char *system_time = NULL;
|
||||||
|
rgb_t color = {0, 0, 0};
|
||||||
|
|
||||||
|
static void on_message_received(const message_t *msg)
|
||||||
|
{
|
||||||
|
if (msg->type == MESSAGE_TYPE_SIMULATION)
|
||||||
|
{
|
||||||
|
system_time = msg->data.simulation.time;
|
||||||
|
color.red = msg->data.simulation.red;
|
||||||
|
color.green = msg->data.simulation.green;
|
||||||
|
color.blue = msg->data.simulation.blue;
|
||||||
|
|
||||||
|
cJSON *json = create_light_status_json();
|
||||||
|
cJSON_AddStringToObject(json, "type", "status");
|
||||||
|
char *response = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
api_server_ws_broadcast(response);
|
||||||
|
free(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_init(void)
|
||||||
|
{
|
||||||
|
message_manager_register_listener(on_message_received);
|
||||||
|
}
|
||||||
|
|
||||||
// Gibt ein cJSON-Objekt with dem aktuellen Lichtstatus zurück
|
// Gibt ein cJSON-Objekt with dem aktuellen Lichtstatus zurück
|
||||||
cJSON *create_light_status_json(void)
|
cJSON *create_light_status_json(void)
|
||||||
@@ -28,14 +60,20 @@ cJSON *create_light_status_json(void)
|
|||||||
}
|
}
|
||||||
cJSON_AddStringToObject(json, "mode", mode_str);
|
cJSON_AddStringToObject(json, "mode", mode_str);
|
||||||
|
|
||||||
cJSON_AddStringToObject(json, "schema", "schema_03.csv");
|
int variant = persistence_manager_get_int(&pm, "light_variant", 3);
|
||||||
cJSON *color = cJSON_CreateObject();
|
char schema_filename[20];
|
||||||
cJSON_AddNumberToObject(color, "r", 255);
|
snprintf(schema_filename, sizeof(schema_filename), "schema_%02d.csv", variant);
|
||||||
cJSON_AddNumberToObject(color, "g", 240);
|
cJSON_AddStringToObject(json, "schema", schema_filename);
|
||||||
cJSON_AddNumberToObject(color, "b", 220);
|
|
||||||
cJSON_AddItemToObject(json, "color", color);
|
|
||||||
|
|
||||||
persistence_manager_deinit(&pm);
|
persistence_manager_deinit(&pm);
|
||||||
|
|
||||||
|
cJSON *c = cJSON_CreateObject();
|
||||||
|
cJSON_AddNumberToObject(c, "r", color.red);
|
||||||
|
cJSON_AddNumberToObject(c, "g", color.green);
|
||||||
|
cJSON_AddNumberToObject(c, "b", color.blue);
|
||||||
|
cJSON_AddItemToObject(json, "color", c);
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(json, "clock", system_time);
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void ClockScreenSaver::getCurrentTimeString(char *buffer, size_t bufferSize) con
|
|||||||
char *simulated_time = get_time();
|
char *simulated_time = get_time();
|
||||||
if (simulated_time != nullptr)
|
if (simulated_time != nullptr)
|
||||||
{
|
{
|
||||||
strncpy(buffer, simulated_time, bufferSize);
|
snprintf(buffer, bufferSize, "%s Uhr", simulated_time);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ extern "C"
|
|||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
MESSAGE_TYPE_SETTINGS,
|
MESSAGE_TYPE_SETTINGS,
|
||||||
MESSAGE_TYPE_BUTTON
|
MESSAGE_TYPE_BUTTON,
|
||||||
|
MESSAGE_TYPE_SIMULATION
|
||||||
} message_type_t;
|
} message_type_t;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@@ -48,12 +49,21 @@ extern "C"
|
|||||||
} value;
|
} value;
|
||||||
} settings_message_t;
|
} settings_message_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char time[6];
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
} simulation_message_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
message_type_t type;
|
message_type_t type;
|
||||||
union {
|
union {
|
||||||
settings_message_t settings;
|
settings_message_t settings;
|
||||||
button_message_t button;
|
button_message_t button;
|
||||||
|
simulation_message_t simulation;
|
||||||
} data;
|
} data;
|
||||||
} message_t;
|
} message_t;
|
||||||
|
|
||||||
|
|||||||
@@ -17,21 +17,29 @@ static QueueHandle_t message_queue = NULL;
|
|||||||
static message_listener_t message_listeners[MAX_MESSAGE_LISTENERS] = {0};
|
static message_listener_t message_listeners[MAX_MESSAGE_LISTENERS] = {0};
|
||||||
static size_t message_listener_count = 0;
|
static size_t message_listener_count = 0;
|
||||||
|
|
||||||
void message_manager_register_listener(message_listener_t listener) {
|
void message_manager_register_listener(message_listener_t listener)
|
||||||
if (listener && message_listener_count < MAX_MESSAGE_LISTENERS) {
|
{
|
||||||
|
if (listener && message_listener_count < MAX_MESSAGE_LISTENERS)
|
||||||
|
{
|
||||||
// Doppelte Registrierung vermeiden
|
// Doppelte Registrierung vermeiden
|
||||||
for (size_t i = 0; i < message_listener_count; ++i) {
|
for (size_t i = 0; i < message_listener_count; ++i)
|
||||||
if (message_listeners[i] == listener) return;
|
{
|
||||||
|
if (message_listeners[i] == listener)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
message_listeners[message_listener_count++] = listener;
|
message_listeners[message_listener_count++] = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void message_manager_unregister_listener(message_listener_t listener) {
|
void message_manager_unregister_listener(message_listener_t listener)
|
||||||
for (size_t i = 0; i < message_listener_count; ++i) {
|
{
|
||||||
if (message_listeners[i] == listener) {
|
for (size_t i = 0; i < message_listener_count; ++i)
|
||||||
|
{
|
||||||
|
if (message_listeners[i] == listener)
|
||||||
|
{
|
||||||
// Nachfolgende Listener nach vorne schieben
|
// Nachfolgende Listener nach vorne schieben
|
||||||
for (size_t j = i; j < message_listener_count - 1; ++j) {
|
for (size_t j = i; j < message_listener_count - 1; ++j)
|
||||||
|
{
|
||||||
message_listeners[j] = message_listeners[j + 1];
|
message_listeners[j] = message_listeners[j + 1];
|
||||||
}
|
}
|
||||||
message_listeners[--message_listener_count] = NULL;
|
message_listeners[--message_listener_count] = NULL;
|
||||||
@@ -70,17 +78,23 @@ static void message_manager_task(void *param)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
persistence_manager_deinit(&pm);
|
persistence_manager_deinit(&pm);
|
||||||
ESP_LOGI(TAG, "Setting written: %s", msg.data.settings.key);
|
ESP_LOGD(TAG, "Setting written: %s", msg.data.settings.key);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MESSAGE_TYPE_BUTTON:
|
case MESSAGE_TYPE_BUTTON:
|
||||||
ESP_LOGI(TAG, "Button event: id=%d, type=%d", msg.data.button.button_id, msg.data.button.event_type);
|
ESP_LOGD(TAG, "Button event: id=%d, type=%d", msg.data.button.button_id, msg.data.button.event_type);
|
||||||
// TODO: Weiterverarbeitung/Callback für Button-Events
|
break;
|
||||||
|
case MESSAGE_TYPE_SIMULATION:
|
||||||
|
/// just logging
|
||||||
|
ESP_LOGD(TAG, "Simulation event: time=%s, color=(%d,%d,%d)", msg.data.simulation.time,
|
||||||
|
msg.data.simulation.red, msg.data.simulation.green, msg.data.simulation.blue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Observer Pattern: Listener benachrichtigen
|
// Observer Pattern: Listener benachrichtigen
|
||||||
for (size_t i = 0; i < message_listener_count; ++i) {
|
for (size_t i = 0; i < message_listener_count; ++i)
|
||||||
if (message_listeners[i]) {
|
{
|
||||||
|
if (message_listeners[i])
|
||||||
|
{
|
||||||
message_listeners[i](&msg);
|
message_listeners[i](&msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +115,6 @@ bool message_manager_post(const message_t *msg, TickType_t timeout)
|
|||||||
{
|
{
|
||||||
if (!message_queue)
|
if (!message_queue)
|
||||||
return false;
|
return false;
|
||||||
ESP_LOGI(TAG, "Post: type=%d", msg->type);
|
ESP_LOGD(TAG, "Post: type=%d", msg->type);
|
||||||
return xQueueSend(message_queue, msg, timeout) == pdTRUE;
|
return xQueueSend(message_queue, msg, timeout) == pdTRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_na
|
|||||||
if (err == ESP_OK)
|
if (err == ESP_OK)
|
||||||
{
|
{
|
||||||
pm->initialized = true;
|
pm->initialized = true;
|
||||||
ESP_LOGI(TAG, "Initialized with namespace: %s", pm->nvs_namespace);
|
ESP_LOGD(TAG, "Initialized with namespace: %s", pm->nvs_namespace);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ idf_component_register(SRCS
|
|||||||
PRIV_REQUIRES
|
PRIV_REQUIRES
|
||||||
led-manager
|
led-manager
|
||||||
persistence-manager
|
persistence-manager
|
||||||
|
message-manager
|
||||||
spiffs
|
spiffs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "led_strip_ws2812.h"
|
#include "led_strip_ws2812.h"
|
||||||
|
#include "message_manager.h"
|
||||||
#include "persistence_manager.h"
|
#include "persistence_manager.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include <esp_heap_caps.h>
|
#include <esp_heap_caps.h>
|
||||||
@@ -15,12 +16,12 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static const char *TAG = "simulator";
|
static const char *TAG = "simulator";
|
||||||
static char *time;
|
static char *time = NULL;
|
||||||
|
|
||||||
static char *time_to_string(int hhmm)
|
static char *time_to_string(int hhmm)
|
||||||
{
|
{
|
||||||
static char buffer[20];
|
static char buffer[20];
|
||||||
snprintf(buffer, sizeof(buffer), "%02d:%02d Uhr", hhmm / 100, hhmm % 100);
|
snprintf(buffer, sizeof(buffer), "%02d:%02d", hhmm / 100, hhmm % 100);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +236,17 @@ static light_item_node_t *find_next_light_item_for_time(int hhmm)
|
|||||||
return next_item;
|
return next_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_simulation_message(const char *time, rgb_t color)
|
||||||
|
{
|
||||||
|
message_t msg = {};
|
||||||
|
msg.type = MESSAGE_TYPE_SIMULATION;
|
||||||
|
strncpy(msg.data.simulation.time, time, sizeof(msg.data.simulation.time) - 1);
|
||||||
|
msg.data.simulation.red = color.red;
|
||||||
|
msg.data.simulation.green = color.green;
|
||||||
|
msg.data.simulation.blue = color.blue;
|
||||||
|
message_manager_post(&msg, pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
void start_simulate_day(void)
|
void start_simulate_day(void)
|
||||||
{
|
{
|
||||||
initialize_light_items();
|
initialize_light_items();
|
||||||
@@ -242,8 +254,9 @@ void start_simulate_day(void)
|
|||||||
light_item_node_t *current_item = find_best_light_item_for_time(1200);
|
light_item_node_t *current_item = find_best_light_item_for_time(1200);
|
||||||
if (current_item != NULL)
|
if (current_item != NULL)
|
||||||
{
|
{
|
||||||
led_strip_update(LED_STATE_DAY,
|
rgb_t color = {.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||||
(rgb_t){.red = current_item->red, .green = current_item->green, .blue = current_item->blue});
|
led_strip_update(LED_STATE_DAY, color);
|
||||||
|
send_simulation_message("12:00", color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,8 +267,9 @@ void start_simulate_night(void)
|
|||||||
light_item_node_t *current_item = find_best_light_item_for_time(0);
|
light_item_node_t *current_item = find_best_light_item_for_time(0);
|
||||||
if (current_item != NULL)
|
if (current_item != NULL)
|
||||||
{
|
{
|
||||||
led_strip_update(LED_STATE_NIGHT,
|
rgb_t color = {.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||||
(rgb_t){.red = current_item->red, .green = current_item->green, .blue = current_item->blue});
|
led_strip_update(LED_STATE_NIGHT, color);
|
||||||
|
send_simulation_message("00:00", color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,45 +310,51 @@ void simulate_cycle(void *args)
|
|||||||
light_item_node_t *current_item = find_best_light_item_for_time(hhmm);
|
light_item_node_t *current_item = find_best_light_item_for_time(hhmm);
|
||||||
light_item_node_t *next_item = find_next_light_item_for_time(hhmm);
|
light_item_node_t *next_item = find_next_light_item_for_time(hhmm);
|
||||||
|
|
||||||
if (current_item != NULL && next_item != NULL)
|
if (current_item != NULL)
|
||||||
{
|
{
|
||||||
int current_item_time_min = (atoi(current_item->time) / 100) * 60 + (atoi(current_item->time) % 100);
|
rgb_t color = {0, 0, 0};
|
||||||
int next_item_time_min = (atoi(next_item->time) / 100) * 60 + (atoi(next_item->time) % 100);
|
|
||||||
|
|
||||||
if (next_item_time_min < current_item_time_min)
|
// Use head as fallback if next_item is NULL
|
||||||
|
next_item = next_item ? next_item : head;
|
||||||
|
if (next_item != NULL)
|
||||||
{
|
{
|
||||||
next_item_time_min += total_minutes_in_day;
|
int current_item_time_min = (atoi(current_item->time) / 100) * 60 + (atoi(current_item->time) % 100);
|
||||||
}
|
int next_item_time_min = (atoi(next_item->time) / 100) * 60 + (atoi(next_item->time) % 100);
|
||||||
|
|
||||||
int minutes_since_current_item_start = current_minute_of_day - current_item_time_min;
|
if (next_item_time_min < current_item_time_min)
|
||||||
if (minutes_since_current_item_start < 0)
|
{
|
||||||
|
next_item_time_min += total_minutes_in_day;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minutes_since_current_item_start = current_minute_of_day - current_item_time_min;
|
||||||
|
if (minutes_since_current_item_start < 0)
|
||||||
|
{
|
||||||
|
minutes_since_current_item_start += total_minutes_in_day;
|
||||||
|
}
|
||||||
|
|
||||||
|
int interval_duration = next_item_time_min - current_item_time_min;
|
||||||
|
if (interval_duration == 0)
|
||||||
|
{
|
||||||
|
interval_duration = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float interpolation_factor = (float)minutes_since_current_item_start / (float)interval_duration;
|
||||||
|
|
||||||
|
// Prepare colors for interpolation
|
||||||
|
rgb_t start_rgb = {.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||||
|
rgb_t end_rgb = {.red = next_item->red, .green = next_item->green, .blue = next_item->blue};
|
||||||
|
|
||||||
|
// Use the interpolation function
|
||||||
|
color = interpolate_color(start_rgb, end_rgb, interpolation_factor);
|
||||||
|
led_strip_update(LED_STATE_SIMULATION, color);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
minutes_since_current_item_start += total_minutes_in_day;
|
// No next_item and no head, use only current
|
||||||
|
color = (rgb_t){.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
||||||
|
led_strip_update(LED_STATE_SIMULATION, color);
|
||||||
}
|
}
|
||||||
|
send_simulation_message(time, color);
|
||||||
int interval_duration = next_item_time_min - current_item_time_min;
|
|
||||||
if (interval_duration == 0)
|
|
||||||
{
|
|
||||||
interval_duration = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float interpolation_factor = (float)minutes_since_current_item_start / (float)interval_duration;
|
|
||||||
|
|
||||||
// Prepare colors for interpolation
|
|
||||||
rgb_t start_rgb = {.red = current_item->red, .green = current_item->green, .blue = current_item->blue};
|
|
||||||
rgb_t end_rgb = {.red = next_item->red, .green = next_item->green, .blue = next_item->blue};
|
|
||||||
|
|
||||||
// Use the interpolation function
|
|
||||||
rgb_t final_rgb = interpolate_color(start_rgb, end_rgb, interpolation_factor);
|
|
||||||
|
|
||||||
led_strip_update(LED_STATE_SIMULATION, final_rgb);
|
|
||||||
}
|
|
||||||
else if (current_item != NULL)
|
|
||||||
{
|
|
||||||
// No next item, just use current
|
|
||||||
led_strip_update(
|
|
||||||
LED_STATE_SIMULATION,
|
|
||||||
(rgb_t){.red = current_item->red, .green = current_item->green, .blue = current_item->blue});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ uint8_t received_signal;
|
|||||||
std::shared_ptr<Widget> m_widget;
|
std::shared_ptr<Widget> m_widget;
|
||||||
std::vector<std::shared_ptr<Widget>> m_history;
|
std::vector<std::shared_ptr<Widget>> m_history;
|
||||||
std::unique_ptr<InactivityTracker> m_inactivityTracker;
|
std::unique_ptr<InactivityTracker> m_inactivityTracker;
|
||||||
// Persistence Manager für C-API
|
// Persistence Manager for C-API
|
||||||
persistence_manager_t g_persistence_manager;
|
persistence_manager_t g_persistence_manager;
|
||||||
|
|
||||||
extern QueueHandle_t buttonQueue;
|
extern QueueHandle_t buttonQueue;
|
||||||
@@ -196,10 +196,10 @@ void app_task(void *args)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display initialisieren, damit Info angezeigt werden kann
|
// Initialize display so that info can be shown
|
||||||
setup_screen();
|
setup_screen();
|
||||||
|
|
||||||
// BACK-Button prüfen und ggf. Einstellungen löschen (mit Countdown)
|
// Check BACK button and delete settings if necessary (with countdown)
|
||||||
gpio_config_t io_conf = {};
|
gpio_config_t io_conf = {};
|
||||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
io_conf.mode = GPIO_MODE_INPUT;
|
io_conf.mode = GPIO_MODE_INPUT;
|
||||||
@@ -224,7 +224,7 @@ void app_task(void *args)
|
|||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
if (gpio_get_level(BUTTON_BACK) != 0)
|
if (gpio_get_level(BUTTON_BACK) != 0)
|
||||||
{
|
{
|
||||||
// Button losgelassen, abbrechen
|
// Button released, abort
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i == 1)
|
if (i == 1)
|
||||||
|
|||||||
@@ -557,6 +557,11 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item.visible {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-item:last-child {
|
.status-item:last-child {
|
||||||
|
|||||||
@@ -92,18 +92,18 @@
|
|||||||
<div class="control-section">
|
<div class="control-section">
|
||||||
<h3 data-i18n="control.status.title">Aktueller Status</h3>
|
<h3 data-i18n="control.status.title">Aktueller Status</h3>
|
||||||
<div class="status-display">
|
<div class="status-display">
|
||||||
<div class="status-item">
|
<div class="status-item visible">
|
||||||
<span class="status-label" data-i18n="control.status.mode">Modus</span>
|
<span class="status-label" data-i18n="control.status.mode">Modus</span>
|
||||||
<span class="status-value" id="current-mode" data-i18n="mode.simulation">Simulation</span>
|
<span class="status-value" id="current-mode" data-i18n="mode.simulation">Simulation</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item visible">
|
||||||
<span class="status-label" data-i18n="control.status.schema">Schema</span>
|
|
||||||
<span class="status-value" id="current-schema">Schema 1</span>
|
|
||||||
</div>
|
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label" data-i18n="control.status.color">Aktuelle Farbe</span>
|
<span class="status-label" data-i18n="control.status.color">Aktuelle Farbe</span>
|
||||||
<div class="current-color-preview" id="current-color"></div>
|
<div class="current-color-preview" id="current-color"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="status-label" data-i18n="control.status.clock">Uhrzeit</span>
|
||||||
|
<span class="status-value" id="current-clock">--:-- Uhr</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ document.addEventListener('touchend', (e) => {
|
|||||||
lastTouchEnd = now;
|
lastTouchEnd = now;
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
// Initialization
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
initI18n();
|
initI18n();
|
||||||
initTheme();
|
initTheme();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const translations = {
|
|||||||
'control.status.mode': 'Modus',
|
'control.status.mode': 'Modus',
|
||||||
'control.status.schema': 'Schema',
|
'control.status.schema': 'Schema',
|
||||||
'control.status.color': 'Aktuelle Farbe',
|
'control.status.color': 'Aktuelle Farbe',
|
||||||
|
'control.status.clock': 'Uhrzeit',
|
||||||
|
|
||||||
// Common
|
// Common
|
||||||
'common.on': 'AN',
|
'common.on': 'AN',
|
||||||
@@ -174,7 +175,8 @@ const translations = {
|
|||||||
// General
|
// General
|
||||||
'loading': 'Laden...',
|
'loading': 'Laden...',
|
||||||
'error': 'Fehler',
|
'error': 'Fehler',
|
||||||
'success': 'Erfolg'
|
'success': 'Erfolg',
|
||||||
|
'clock.suffix': 'Uhr'
|
||||||
},
|
},
|
||||||
|
|
||||||
en: {
|
en: {
|
||||||
@@ -216,6 +218,7 @@ const translations = {
|
|||||||
'control.status.mode': 'Mode',
|
'control.status.mode': 'Mode',
|
||||||
'control.status.schema': 'Schema',
|
'control.status.schema': 'Schema',
|
||||||
'control.status.color': 'Current Color',
|
'control.status.color': 'Current Color',
|
||||||
|
'control.status.clock': "Time",
|
||||||
|
|
||||||
// Common
|
// Common
|
||||||
'common.on': 'ON',
|
'common.on': 'ON',
|
||||||
@@ -349,7 +352,8 @@ const translations = {
|
|||||||
// General
|
// General
|
||||||
'loading': 'Loading...',
|
'loading': 'Loading...',
|
||||||
'error': 'Error',
|
'error': 'Error',
|
||||||
'success': 'Success'
|
'success': 'Success',
|
||||||
|
'clock.suffix': "o'clock"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,20 @@ function updateSimulationOptions() {
|
|||||||
} else {
|
} else {
|
||||||
options.classList.remove('visible');
|
options.classList.remove('visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
'control.status.clock'
|
||||||
|
].forEach(i18nKey => {
|
||||||
|
const label = document.querySelector(`.status-item .status-label[data-i18n="${i18nKey}"]`);
|
||||||
|
const item = label ? label.closest('.status-item') : null;
|
||||||
|
if (item) {
|
||||||
|
if (currentMode === 'simulation') {
|
||||||
|
item.classList.add('visible');
|
||||||
|
} else {
|
||||||
|
item.classList.remove('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setActiveSchema() {
|
async function setActiveSchema() {
|
||||||
@@ -173,8 +187,6 @@ async function loadLightStatus() {
|
|||||||
// Update schema
|
// Update schema
|
||||||
if (status.schema) {
|
if (status.schema) {
|
||||||
document.getElementById('active-schema').value = status.schema;
|
document.getElementById('active-schema').value = status.schema;
|
||||||
const schemaNum = status.schema.replace('schema_0', '').replace('.csv', '');
|
|
||||||
document.getElementById('current-schema').textContent = t(`schema.name.${schemaNum}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current color
|
// Update current color
|
||||||
@@ -184,6 +196,15 @@ async function loadLightStatus() {
|
|||||||
colorPreview.style.backgroundColor = `rgb(${status.color.r}, ${status.color.g}, ${status.color.b})`;
|
colorPreview.style.backgroundColor = `rgb(${status.color.r}, ${status.color.g}, ${status.color.b})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update clock/time
|
||||||
|
if (status.clock) {
|
||||||
|
const clockEl = document.getElementById('current-clock');
|
||||||
|
if (clockEl) {
|
||||||
|
// Use one translation key for the suffix, language is handled by t()
|
||||||
|
clockEl.textContent = status.clock + ' ' + t('clock.suffix');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Light status not available');
|
console.log('Light status not available');
|
||||||
|
|||||||
@@ -65,18 +65,32 @@ function updateStatusFromData(status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status.schema) {
|
if (status.schema) {
|
||||||
document.getElementById('active-schema').value = status.schema;
|
const activeSchemaEl = document.getElementById('active-schema');
|
||||||
|
if (activeSchemaEl) {
|
||||||
|
activeSchemaEl.value = status.schema;
|
||||||
|
}
|
||||||
const schemaNames = {
|
const schemaNames = {
|
||||||
'schema_01.csv': 'Schema 1',
|
'schema_01.csv': 'Schema 1',
|
||||||
'schema_02.csv': 'Schema 2',
|
'schema_02.csv': 'Schema 2',
|
||||||
'schema_03.csv': 'Schema 3'
|
'schema_03.csv': 'Schema 3'
|
||||||
};
|
};
|
||||||
document.getElementById('current-schema').textContent = schemaNames[status.schema] || status.schema;
|
const currentSchemaEl = document.getElementById('current-schema');
|
||||||
|
if (currentSchemaEl) {
|
||||||
|
currentSchemaEl.textContent = schemaNames[status.schema] || status.schema;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.color) {
|
if (status.color) {
|
||||||
updateColorPreview(status.color.r, status.color.g, status.color.b);
|
updateColorPreview(status.color.r, status.color.g, status.color.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update clock/time
|
||||||
|
if (status.clock) {
|
||||||
|
const clockEl = document.getElementById('current-clock');
|
||||||
|
if (clockEl) {
|
||||||
|
clockEl.textContent = status.clock + ' ' + (typeof t === 'function' ? t('clock.suffix') : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateColorPreview(r, g, b) {
|
function updateColorPreview(r, g, b) {
|
||||||
|
|||||||
Reference in New Issue
Block a user