Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions generated/Kconfig.kv_keys
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ config KV_STORE_KEY_BLUETOOTH_THROUGHPUT_LIMIT
help
Request connected Bluetooth peers to limit throughtput

config KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE
bool "Enable KV key LED_DISABLE_DAILY_TIME_RANGE"
help
Disable LEDs between two UTC times daily

config KV_STORE_KEY_GRAVITY_REFERENCE
bool "Enable KV key GRAVITY_REFERENCE"
help
Expand Down
28 changes: 28 additions & 0 deletions generated/include/infuse/fs/kv_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ struct kv_range_u8 {
uint8_t upper;
} __packed;

/** UTC Hour-Minute-Second */
struct kv_utc_hms {
uint8_t hour;
uint8_t minute;
uint8_t second;
} __packed;

/**
* @}
*/
Expand Down Expand Up @@ -333,6 +340,14 @@ struct kv_bluetooth_throughput_limit {
uint16_t limit_kbps;
} __packed;

/** Disable LEDs between two UTC times daily */
struct kv_led_disable_daily_time_range {
/** Disable LEDs at this time */
struct kv_utc_hms disable_start;
/** Re-enable LEDs at this time */
struct kv_utc_hms disable_end;
} __packed;

/** Reference gravity vector for tilt calculations */
struct kv_gravity_reference {
/** X axis component of gravity vector */
Expand Down Expand Up @@ -466,6 +481,8 @@ enum kv_builtin_id {
KV_KEY_LORA_CONFIG = 51,
/** Request connected Bluetooth peers to limit throughtput */
KV_KEY_BLUETOOTH_THROUGHPUT_LIMIT = 52,
/** Disable LEDs between two UTC times daily */
KV_KEY_LED_DISABLE_DAILY_TIME_RANGE = 53,
/** Reference gravity vector for tilt calculations */
KV_KEY_GRAVITY_REFERENCE = 60,
/** Array of points defining a closed polygon */
Expand Down Expand Up @@ -514,6 +531,7 @@ enum kv_builtin_size {
_KV_KEY_BLUETOOTH_PEER_SIZE = sizeof(struct kv_bluetooth_peer),
_KV_KEY_LORA_CONFIG_SIZE = sizeof(struct kv_lora_config),
_KV_KEY_BLUETOOTH_THROUGHPUT_LIMIT_SIZE = sizeof(struct kv_bluetooth_throughput_limit),
_KV_KEY_LED_DISABLE_DAILY_TIME_RANGE_SIZE = sizeof(struct kv_led_disable_daily_time_range),
_KV_KEY_GRAVITY_REFERENCE_SIZE = sizeof(struct kv_gravity_reference),
_KV_KEY_TASK_SCHEDULES_DEFAULT_ID_SIZE = sizeof(struct kv_task_schedules_default_id),
};
Expand Down Expand Up @@ -544,6 +562,7 @@ enum kv_builtin_size {
#define _KV_KEY_BLUETOOTH_PEER_TYPE struct kv_bluetooth_peer
#define _KV_KEY_LORA_CONFIG_TYPE struct kv_lora_config
#define _KV_KEY_BLUETOOTH_THROUGHPUT_LIMIT_TYPE struct kv_bluetooth_throughput_limit
#define _KV_KEY_LED_DISABLE_DAILY_TIME_RANGE_TYPE struct kv_led_disable_daily_time_range
#define _KV_KEY_GRAVITY_REFERENCE_TYPE struct kv_gravity_reference
#define _KV_KEY_GEOFENCE_TYPE struct kv_geofence
#define _KV_KEY_TASK_SCHEDULES_DEFAULT_ID_TYPE struct kv_task_schedules_default_id
Expand Down Expand Up @@ -598,6 +617,8 @@ enum kv_builtin_size {
(1 +)) \
IF_ENABLED(CONFIG_KV_STORE_KEY_BLUETOOTH_THROUGHPUT_LIMIT, \
(1 +)) \
IF_ENABLED(CONFIG_KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE, \
(1 +)) \
IF_ENABLED(CONFIG_KV_STORE_KEY_GRAVITY_REFERENCE, \
(1 +)) \
IF_ENABLED(CONFIG_KV_STORE_KEY_GEOFENCE, \
Expand Down Expand Up @@ -810,6 +831,13 @@ static struct key_value_slot_definition _KV_SLOTS_ARRAY_DEFINE[] = {
.flags = KV_FLAGS_REFLECT,
},
#endif /* CONFIG_KV_STORE_KEY_BLUETOOTH_THROUGHPUT_LIMIT */
#ifdef CONFIG_KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE
{
.key = KV_KEY_LED_DISABLE_DAILY_TIME_RANGE,
.range = 1,
.flags = KV_FLAGS_REFLECT,
},
#endif /* CONFIG_KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE */
#ifdef CONFIG_KV_STORE_KEY_GRAVITY_REFERENCE
{
.key = KV_KEY_GRAVITY_REFERENCE,
Expand Down
2 changes: 2 additions & 0 deletions include/infuse/states.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ enum infuse_state {
INFUSE_STATE_DEVICE_STARTED_MOVING = 6,
/* Device stopped moving */
INFUSE_STATE_DEVICE_STOPPED_MOVING = 7,
/* Suppress LED activity */
INFUSE_STATE_LED_SUPPRESS = 8,
/* Start of application-specific state range */
INFUSE_STATES_APP_START = 128,
INFUSE_STATES_END = UINT8_MAX
Expand Down
1 change: 1 addition & 0 deletions lib/auto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_BATTERY_CHARGE_ACCUMULATOR charge_accumulator.c)
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_BLUETOOTH_CONN_LOG bluetooth_conn_log.c)
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_CHARGER_TEMPERATURE_CONTROL charger_control.c)
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_KV_STATE_OBSERVER kv_state_observer.c)
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_TIME_SYNC_LOG time_sync_log.c)
zephyr_sources_ifdef(CONFIG_INFUSE_AUTO_WIFI_CONN_LOG wifi_conn_log.c)
7 changes: 7 additions & 0 deletions lib/auto/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ config INFUSE_AUTO_TIME_SYNC_LOG
depends on TDF_DATA_LOGGER
default y

config INFUSE_AUTO_KV_STATE_OBSERVER
bool "Automatically set application states based on KV"
depends on INFUSE_EPOCH_TIME
depends on INFUSE_APPLICATION_STATES
depends on KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE
default y

config INFUSE_AUTO_WIFI_CONN_LOG
bool "Automatically log WiFi connection events"
depends on WIFI
Expand Down
147 changes: 147 additions & 0 deletions lib/auto/kv_state_observer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* @file
* @copyright 2025 Embeint Holdings Pty Ltd
* @author Jordan Yates <[email protected]>
*
* SPDX-License-Identifier: FSL-1.1-ALv2
*/

#include <zephyr/init.h>
#include <zephyr/kernel.h>

#include <infuse/fs/kv_store.h>
#include <infuse/fs/kv_types.h>
#include <infuse/states.h>
#include <infuse/time/epoch.h>

static void kv_state_obs_value_changed(uint16_t key, const void *data, size_t data_len,
void *user_ctx);
static void reference_time_updated(enum epoch_time_source source, struct timeutil_sync_instant old,
struct timeutil_sync_instant new, void *user_ctx);

static struct k_work_delayable led_delayable;
static struct kv_store_cb kv_observer_cb = {
.value_changed = kv_state_obs_value_changed,
};
static struct epoch_time_cb epoch_observer_cb = {
.reference_time_updated = reference_time_updated,
};
static uint32_t disable_daily_seconds_start;
static uint32_t disable_daily_seconds_end;
static bool has_disable_daily;

static uint32_t utc_seconds_from_hms(const struct kv_utc_hms *hms)
{
return (hms->hour * SEC_PER_HOUR) + (hms->minute * SEC_PER_MIN) + hms->second;
}

static void led_disable_delayable(struct k_work *work)
{
uint32_t utc_seconds;
struct kv_utc_hms hms;
uint32_t boundary;
uint64_t now;
struct tm c;

/* If we don't know the time or have a KV, we can't suppress */
if (!has_disable_daily || !epoch_time_trusted_source(epoch_time_get_source(), true)) {
infuse_state_clear(INFUSE_STATE_LED_SUPPRESS);
return;
}

/* Get current time */
now = epoch_time_now();
epoch_time_unix_calendar(now, &c);
hms.hour = c.tm_hour;
hms.minute = c.tm_min;
hms.second = c.tm_sec;
utc_seconds = utc_seconds_from_hms(&hms);

/* Handle current time vs windows */
if (disable_daily_seconds_start < disable_daily_seconds_end) {
if (utc_seconds < disable_daily_seconds_start) {
/* Before start window */
boundary = disable_daily_seconds_start - utc_seconds;
infuse_state_clear(INFUSE_STATE_LED_SUPPRESS);
} else if (utc_seconds > disable_daily_seconds_end) {
/* After end window */
boundary = disable_daily_seconds_start + (SEC_PER_DAY - utc_seconds);
infuse_state_clear(INFUSE_STATE_LED_SUPPRESS);
} else {
/* In range */
boundary = disable_daily_seconds_end - utc_seconds;
infuse_state_set(INFUSE_STATE_LED_SUPPRESS);
}
} else {
if (utc_seconds <= disable_daily_seconds_end) {
/* In range */
boundary = disable_daily_seconds_end - utc_seconds;
infuse_state_set(INFUSE_STATE_LED_SUPPRESS);
} else if (utc_seconds >= disable_daily_seconds_start) {
/* In range */
boundary = (SEC_PER_DAY - utc_seconds) + disable_daily_seconds_end;
infuse_state_set(INFUSE_STATE_LED_SUPPRESS);
} else {
/* Before start window */
boundary = disable_daily_seconds_start - utc_seconds;
infuse_state_clear(INFUSE_STATE_LED_SUPPRESS);
}
}

/* Set reschedule time */
boundary = MAX(1, boundary);
k_work_reschedule(&led_delayable, K_SECONDS(boundary));
}

static void kv_state_obs_value_changed(uint16_t key, const void *data, size_t data_len,
void *user_ctx)
{
const struct kv_led_disable_daily_time_range *disable_daily;

if (key == KV_KEY_LED_DISABLE_DAILY_TIME_RANGE) {
if (data == NULL) {
/* Slot has been deleted, cancel any suppression */
has_disable_daily = false;
k_work_cancel_delayable(&led_delayable);
infuse_state_clear(INFUSE_STATE_LED_SUPPRESS);
} else {
disable_daily = data;
/* Cache the current value */
disable_daily_seconds_start =
utc_seconds_from_hms(&disable_daily->disable_start);
disable_daily_seconds_end =
utc_seconds_from_hms(&disable_daily->disable_end);
has_disable_daily = true;
/* Re-evaluate immediately */
k_work_reschedule(&led_delayable, K_NO_WAIT);
}
}
}

static void reference_time_updated(enum epoch_time_source source, struct timeutil_sync_instant old,
struct timeutil_sync_instant new, void *user_ctx)
{
/* Re-evaluate immediately */
k_work_reschedule(&led_delayable, K_NO_WAIT);
}

static int kv_state_observer_init(void)
{
struct kv_led_disable_daily_time_range disable_daily;

epoch_time_register_callback(&epoch_observer_cb);
kv_store_register_callback(&kv_observer_cb);
k_work_init_delayable(&led_delayable, led_disable_delayable);
/* Initialise the cached values */
if (KV_STORE_READ(KV_KEY_LED_DISABLE_DAILY_TIME_RANGE, &disable_daily) ==
sizeof(disable_daily)) {
disable_daily_seconds_start = utc_seconds_from_hms(&disable_daily.disable_start);
disable_daily_seconds_end = utc_seconds_from_hms(&disable_daily.disable_end);
has_disable_daily = true;
}
/* Evaluate immediately */
k_work_schedule(&led_delayable, K_NO_WAIT);
return 0;
}

SYS_INIT(kv_state_observer_init, APPLICATION, 0);
17 changes: 17 additions & 0 deletions scripts/west_commands/cloud_definitions/kv_store.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
{"name": "lower", "type": "uint8_t"},
{"name": "upper", "type": "uint8_t"}
]
},
"kv_utc_hms": {
"description": "UTC Hour-Minute-Second",
"fields": [
{"name": "hour", "type": "uint8_t"},
{"name": "minute", "type": "uint8_t"},
{"name": "second", "type": "uint8_t"}
]
}
},
"definitions": {
Expand Down Expand Up @@ -272,6 +280,15 @@
{"name": "limit_kbps", "type": "uint16_t", "description": "Requested throughput limit (kbps)"}
]
},
"53": {
"name": "LED_DISABLE_DAILY_TIME_RANGE",
"description": "Disable LEDs between two UTC times daily",
"reflect": true,
"fields": [
{"name": "disable_start", "type": "struct kv_utc_hms", "description": "Disable LEDs at this time"},
{"name": "disable_end", "type": "struct kv_utc_hms", "description": "Re-enable LEDs at this time"}
]
},
"60": {
"name": "GRAVITY_REFERENCE",
"description": "Reference gravity vector for tilt calculations",
Expand Down
9 changes: 9 additions & 0 deletions tests/lib/auto/kv_state_observer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(kv_state_observer)

target_sources(app PRIVATE
src/main.c
)
5 changes: 5 additions & 0 deletions tests/lib/auto/kv_state_observer/boards/native_sim.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/ {
chosen {
infuse,kv-partition = &storage_partition;
};
};
11 changes: 11 additions & 0 deletions tests/lib/auto/kv_state_observer/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONFIG_ZTEST=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_KV_STORE=y
CONFIG_INFUSE_EPOCH_TIME=y
CONFIG_INFUSE_APPLICATION_STATES=y
CONFIG_KV_STORE_KEY_LED_DISABLE_DAILY_TIME_RANGE=y
CONFIG_INFUSE_AUTO_KV_STATE_OBSERVER=y
CONFIG_INFUSE_EPOCH_TIME_PRINT_REF_ON_SYNC=n
Loading