101 lines
3.9 KiB
C
101 lines
3.9 KiB
C
#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;
|
||
|
|
}
|