Files
story/firmware/echoear-platform-agent/main/device_screen_wake.c
T

101 lines
3.9 KiB
C
Raw Normal View History

#include "device_screen_wake.h"
#include "device_power.h"
#include "call_client.h"
#include "esp_random.h"
#include "nvs.h"
#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
typedef struct { uint32_t version; int64_t revision; char id[37]; } wake_event_t;
static wake_event_t pending;
static atomic_bool requested;
static bool awake;
static esp_err_t save_pending(bool keep)
{
nvs_handle_t n;
esp_err_t err = nvs_open("control", NVS_READWRITE, &n);
if (err != ESP_OK) return err;
err = keep ? nvs_set_blob(n, "screen_wake", &pending, sizeof(pending))
: nvs_erase_key(n, "screen_wake");
if (err == ESP_ERR_NVS_NOT_FOUND) err = ESP_OK;
if (err == ESP_OK) err = nvs_commit(n);
nvs_close(n);
return err;
}
esp_err_t device_screen_wake_init(void)
{
memset(&pending, 0, sizeof(pending));
awake = false;
atomic_store(&requested, false);
nvs_handle_t n;
esp_err_t err = nvs_open("control", NVS_READONLY, &n);
if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK;
if (err != ESP_OK) return err;
size_t size = sizeof(pending);
err = nvs_get_blob(n, "screen_wake", &pending, &size);
nvs_close(n);
if (err == ESP_ERR_NVS_NOT_FOUND) { memset(&pending, 0, sizeof(pending)); return ESP_OK; }
if (err != ESP_OK) return err;
if (size != sizeof(pending) || pending.version != 1 || pending.revision <= 0 ||
pending.id[36] != 0 || strlen(pending.id) != 36) return ESP_ERR_INVALID_STATE;
return ESP_OK;
}
void device_screen_wake_request(void) { atomic_store(&requested, true); }
esp_err_t device_screen_wake_poll(int64_t applied_revision)
{
if (atomic_exchange(&requested, false) && !pending.version && call_client_standby()) {
// Retain a tap during initial configuration until its revision is known.
// An offline tap after initialization needs no network access.
if (applied_revision <= 0) { atomic_store(&requested, true); return ESP_OK; }
uint32_t r[4];
esp_fill_random(r, sizeof(r));
pending.version = 1;
pending.revision = applied_revision;
snprintf(pending.id, sizeof(pending.id), "%08lx-%04x-4%03x-%04x-%04x%08lx",
(unsigned long)r[0], (unsigned)(r[1] >> 16), (unsigned)(r[1] & 0xfff),
(unsigned)((r[2] >> 16 & 0x3fff) | 0x8000), (unsigned)(r[2] & 0xffff),
(unsigned long)r[3]);
esp_err_t err = save_pending(true);
if (err != ESP_OK) {
memset(&pending, 0, sizeof(pending));
atomic_store(&requested, true);
return err;
}
}
if (pending.version && !awake) {
esp_err_t err = device_power_apply("active", false);
if (err != ESP_OK) return err;
awake = true;
}
return ESP_OK;
}
void device_screen_wake_add_report(cJSON *report)
{
if (!pending.version || !awake) return;
cJSON *event = cJSON_AddObjectToObject(report, "screen_wake");
cJSON_AddStringToObject(event, "id", pending.id);
cJSON_AddNumberToObject(event, "revision", pending.revision);
}
esp_err_t device_screen_wake_ack(const cJSON *response)
{
if (!pending.version || !awake) return ESP_OK;
const cJSON *event = cJSON_GetObjectItemCaseSensitive(response, "screen_wake");
const cJSON *id = cJSON_GetObjectItemCaseSensitive(event, "id");
const cJSON *revision = cJSON_GetObjectItemCaseSensitive(event, "revision");
const cJSON *result = cJSON_GetObjectItemCaseSensitive(event, "result");
if (!cJSON_IsString(id) || strcmp(id->valuestring, pending.id) ||
!cJSON_IsNumber(revision) || revision->valuedouble != pending.revision ||
!cJSON_IsString(result) || (strcmp(result->valuestring, "applied") &&
strcmp(result->valuestring, "superseded"))) return ESP_ERR_INVALID_RESPONSE;
esp_err_t err = save_pending(false);
if (err == ESP_OK) { memset(&pending, 0, sizeof(pending)); awake = false; }
return err;
}