Tasmota/lib/libesp32_div/ESP32-HomeKit/src/esp_hap_char.c

602 lines
16 KiB
C
Raw Normal View History

2021-03-11 11:28:13 +00:00
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <hap_platform_memory.h>
#include <math.h>
#include <string.h>
#include "esp_mfi_debug.h"
#include <esp_hap_main.h>
#include <esp_hap_acc.h>
#include <esp_hap_char.h>
#include <esp_hap_ip_services.h>
#include <esp_hap_database.h>
static QueueHandle_t hap_event_queue;
/**
* @brief get characteristics's value
*/
hap_val_t *esp_mfi_characteristics_get_value(__hap_char_t *_hc)
{
return &_hc->val;
}
/**
* @brief check if characteristics has the specific permission
*/
bool hap_char_has_permission(__hap_char_t *_hc, uint16_t permission)
{
return _hc->permission & permission ? true : false;
}
/**
* @brief compute the mod of a and b: a % b
*/
static double esp_mfi_fmod(double a, double b)
{
if (a < 1e-06 || b < 1e-06)
return 0;
while (!((long long)a) || !((long long)b)) {
a *= 10.0;
b *= 10.0;
}
return fmod(a, b);
}
int hap_event_queue_init()
{
hap_event_queue = xQueueCreate( hap_priv.cfg.max_event_notif_chars,
sizeof(hap_char_t *) );
if (hap_event_queue) {
return HAP_SUCCESS;
} else {
return HAP_FAIL;
}
}
hap_char_t * hap_get_pending_notif_char()
{
hap_char_t *hc;
if (xQueueReceive(hap_event_queue, &hc, 0) != pdTRUE) {
return NULL;
}
return hc;
}
static int hap_queue_event(hap_char_t *hc)
{
int ret;
if (!hap_event_queue) {
return HAP_FAIL;
}
if (xPortInIsrContext() == pdTRUE) {
ret = xQueueSendFromISR(hap_event_queue, &hc, NULL);
} else {
ret = xQueueSend(hap_event_queue, &hc, 0);
}
if (ret == pdTRUE) {
hap_send_event(HAP_INTERNAL_EVENT_TRIGGER_NOTIF);
return HAP_SUCCESS;
}
return HAP_FAIL;
}
/**
* @brief check if characteristics value is at the range
*/
int hap_char_check_val_constraints(__hap_char_t *_hc, hap_val_t *val)
{
if (!_hc->constraint_flags)
return HAP_SUCCESS;
if (_hc->format == HAP_CHAR_FORMAT_INT) {
int value = val->i;
int remainder;
if (value > _hc->max.i
|| value < _hc->min.i)
return HAP_FAIL;
if (!_hc->step.i)
return HAP_SUCCESS;
remainder = (value - _hc->min.i) % _hc->step.i;
if (remainder)
return HAP_FAIL;
} else if (_hc->format == HAP_CHAR_FORMAT_FLOAT) {
float value = val->f;
if (value > _hc->max.f
|| value < _hc->min.f)
return HAP_FAIL;
# if 0
/* Check for step value for floats has a high chance of failure,
* because of precision issues. Hence, better to skip it.
*/
double remainder;
if (_hc->step.f == 0.0)
return HAP_SUCCESS;
remainder = esp_mfi_fmod(value - _hc->min.f, _hc->step.f);
if (remainder != 0.0)
return HAP_FAIL;
#endif
} else if (_hc->format == HAP_CHAR_FORMAT_UINT8
|| _hc->format == HAP_CHAR_FORMAT_UINT16
|| _hc->format == HAP_CHAR_FORMAT_UINT32) {
uint32_t value = val->u;
uint32_t remainder;
if (value > _hc->max.u
|| value < _hc->min.u)
return HAP_FAIL;
if (!_hc->step.u)
return HAP_SUCCESS;
remainder = (value - _hc->min.u) % _hc->step.u;
if (remainder)
return HAP_FAIL;
} else if (_hc->format == HAP_CHAR_FORMAT_UINT64) {
/* TODO: Add support. Currently, there is no particular 64-bit characteristic */
}
return HAP_SUCCESS;
}
/**
* @brief user update characteristics value, preparing for notification
*/
int hap_char_update_val(hap_char_t *hc, hap_val_t *val)
{
if (!hc || !val) {
return HAP_FAIL;
}
__hap_char_t *_hc = (__hap_char_t *)hc;
_hc->update_called = true;
if (hap_char_check_val_constraints(_hc, val) != HAP_SUCCESS)
return HAP_FAIL;
/* Boolean to track if the value has changed.
* This will be later used to decide if an event notification
* is required or not. If the new and old values are same,
* there is no need of a notification
*/
bool value_changed = false;
switch (_hc->format) {
case HAP_CHAR_FORMAT_BOOL:
if (_hc->val.b != val->b) {
_hc->val.b = val->b;
value_changed = true;
}
break;
case HAP_CHAR_FORMAT_INT:
case HAP_CHAR_FORMAT_UINT8:
case HAP_CHAR_FORMAT_UINT16:
case HAP_CHAR_FORMAT_UINT32:
if (_hc->val.i != val->i) {
_hc->val.i = val->i;
value_changed = true;
}
break;
case HAP_CHAR_FORMAT_FLOAT:
if (_hc->val.f != val->f) {
_hc->val.f = val->f;
value_changed = true;
}
break;
case HAP_CHAR_FORMAT_STRING:
/* Not checking all combinations to find if value has changed,
* since generally we do not expect dynamic string values
*
* Eg.
* Both (old and new) values being NULL
* Old value NULL, New non-NULL
* Old value non-NULL, new NULL
*/
if (_hc->val.s && val->s && !strcmp(_hc->val.s, val->s))
value_changed = false;
else
value_changed = true;
if (_hc->val.s) {
hap_platform_memory_free(_hc->val.s);
_hc->val.s = NULL;
}
if (val->s) {
_hc->val.s = strdup(val->s);
if (!_hc->val.s)
return HAP_FAIL;
}
break;
case HAP_CHAR_FORMAT_DATA:
case HAP_CHAR_FORMAT_TLV8: {
_hc->val.d.buf = val->d.buf;
_hc->val.d.buflen = val->d.buflen;
value_changed = true;
}
break;
default:
break;
}
if (value_changed || (_hc->permission & HAP_CHAR_PERM_SPECIAL_READ)) {
ESP_MFI_DEBUG_INTR(ESP_MFI_DEBUG_INFO, "Value Changed");
hap_queue_event(hc);
} else {
/* If there is no value change, reset the owner flag here itself, as no notification
* is being sent anyways. In the absence of this, if there is a GET /characteristics
* followed by some value change from hardware, the owner_ctrl stays assigned to a
* stale value, and so the controller misses a notification.
*/
_hc->owner_ctrl = 0;
}
return HAP_SUCCESS;
}
const hap_val_t *hap_char_get_val(hap_char_t *hc)
{
if (!hc)
return NULL;
return &((__hap_char_t *)hc)->val;
}
/**
* @brief Get the minimum value of characteristic
*/
const hap_val_t *hap_char_get_min_val(hap_char_t *hc)
{
if (hc) {
if(((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MIN_FLAG) {
return &((__hap_char_t *)hc)->min;
}
}
return NULL;
}
/**
* @brief Get the maximum value of characteristic
*/
const hap_val_t *hap_char_get_max_val(hap_char_t *hc)
{
if (hc) {
if(((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MAX_FLAG || ((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_MAXLEN_FLAG) {
return &((__hap_char_t *)hc)->max;
}
}
return NULL;
}
/**
* @brief Get the step value of characteristic
*/
const hap_val_t *hap_char_get_step_val(hap_char_t *hc)
{
if (hc) {
if (((__hap_char_t *)hc)->constraint_flags & HAP_CHAR_STEP_FLAG) {
return &((__hap_char_t *)hc)->step;
}
}
return NULL;
}
/**
* @brief HAP create a characteristics
*/
static hap_char_t *hap_char_create(char *type_uuid, uint32_t permission, hap_char_format_t format, hap_val_t val)
{
__hap_char_t *new_ch;
ESP_MFI_ASSERT(type_uuid);
if (HAP_CHAR_FORMAT_STRING == format && val.s) {
if (strlen(val.s) > HAP_CHAR_STRING_MAX_LEN)
return NULL;
}
new_ch = hap_platform_memory_calloc(1, sizeof(__hap_char_t));
if (!new_ch) {
return NULL;
}
new_ch->val = val;
new_ch->type_uuid = type_uuid;
new_ch->format = format;
new_ch->permission = permission;
return (hap_char_t *) new_ch;
}
hap_char_t *hap_char_bool_create(char *type_uuid, uint16_t perms, bool b)
{
hap_val_t val = {.b = b};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_BOOL, val);
}
hap_char_t *hap_char_uint8_create(char *type_uuid, uint16_t perms, uint8_t u8)
{
hap_val_t val = {.u = u8};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT8, val);
}
hap_char_t *hap_char_uint16_create(char *type_uuid, uint16_t perms, uint16_t u16)
{
hap_val_t val = {.u = u16};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT16, val);
}
hap_char_t *hap_char_uint32_create(char *type_uuid, uint16_t perms, uint32_t u32)
{
hap_val_t val = {.u = u32};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT32, val);
}
hap_char_t *hap_char_uint64_create(char *type_uuid, uint16_t perms, uint64_t u64)
{
hap_val_t val = {.i64 = u64};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_UINT64, val);
}
hap_char_t *hap_char_int_create(char *type_uuid, uint16_t perms, int i32)
{
hap_val_t val = {.i = i32};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_INT, val);
}
hap_char_t *hap_char_float_create(char *type_uuid, uint16_t perms, float f)
{
hap_val_t val = {.f = f};
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_FLOAT, val);
}
hap_char_t *hap_char_string_create(char *type_uuid, uint16_t perms, char *s)
{
hap_val_t val;
if (s)
val.s = strdup(s);
else
val.s = NULL;
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_STRING, val);
}
hap_char_t *hap_char_data_create(char *type_uuid, uint16_t perms, hap_data_val_t *d)
{
hap_val_t val = {0};
if (d) {
val.d = *d;
}
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_DATA, val);
}
hap_char_t *hap_char_tlv8_create(char *type_uuid, uint16_t perms, hap_tlv8_val_t *t)
{
hap_val_t val = {0};
if (t) {
val.t = *t;
}
return hap_char_create(type_uuid, perms, HAP_CHAR_FORMAT_TLV8, val);
}
/**
* @brief HAP get target characteristics IID
*/
uint32_t hap_char_get_iid(hap_char_t *hc)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
return tmp->iid;
}
/**
* @brief HAP get target characteristics type UUID
*/
const char * hap_char_get_type_uuid(hap_char_t *hc)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
return tmp->type_uuid;
}
uint16_t hap_char_get_perm(hap_char_t *hc)
{
if (!hc)
return 0;
__hap_char_t *tmp = (__hap_char_t *)hc;
return tmp->permission;
}
hap_char_format_t hap_char_get_format(hap_char_t *hc)
{
if (!hc)
return 0;
__hap_char_t *tmp = (__hap_char_t *)hc;
return tmp->format;
}
/**
* @brief HAP delete target characteristics
*/
void hap_char_delete(hap_char_t *hc)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *_hc = (__hap_char_t *)hc;
if (_hc->format == HAP_CHAR_FORMAT_STRING) {
if (_hc->val.s) {
hap_platform_memory_free(_hc->val.s);
}
}
if (_hc->valid_vals) {
hap_platform_memory_free(_hc->valid_vals);
}
if (_hc->valid_vals_range) {
hap_platform_memory_free(_hc->valid_vals_range);
}
hap_platform_memory_free(_hc);
}
/**
* @brief HAP configure the characteristics's value description
*/
void hap_char_int_set_constraints(hap_char_t *hc, int min, int max, int step)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
tmp->min.i = min;
tmp->max.i = max;
tmp->step.i = step;
if (step) {
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG | HAP_CHAR_STEP_FLAG);
} else {
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG);
}
}
void hap_char_float_set_constraints(hap_char_t *hc, float min, float max, float step)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
tmp->min.f = min;
tmp->max.f = max;
tmp->step.f = step;
if (step) {
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG | HAP_CHAR_STEP_FLAG);
} else {
tmp->constraint_flags |= (HAP_CHAR_MIN_FLAG | HAP_CHAR_MAX_FLAG);
}
}
void hap_char_string_set_maxlen(hap_char_t *hc, int maxlen)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
if (maxlen > HAP_CHAR_STRING_MAX_LEN) {
maxlen = HAP_CHAR_STRING_MAX_LEN;
ESP_MFI_DEBUG(ESP_MFI_DEBUG_WARN, "Characteristic string length larger than maximum value(%d), falling back to the maximum value.", HAP_CHAR_STRING_MAX_LEN);
}
tmp->max.i = maxlen;
tmp->constraint_flags |= HAP_CHAR_MAXLEN_FLAG;
}
void hap_char_add_description(hap_char_t *hc, const char *description)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
tmp->description = (char *)description;
}
void hap_char_add_unit(hap_char_t *hc, const char *unit)
{
ESP_MFI_ASSERT(hc);
__hap_char_t *tmp = (__hap_char_t *)hc;
tmp->unit = (char *)unit;
}
hap_char_t *hap_char_get_next(hap_char_t *hc)
{
return ((__hap_char_t *)hc)->next_char;
}
hap_serv_t *hap_char_get_parent(hap_char_t *hc)
{
return ((__hap_char_t *)hc)->parent;
}
#define set_bit(val, index) ((val) |= (1 << index))
#define reset_bit(val, index) ((val) &= ~(1 << index))
void hap_char_manage_notification(hap_char_t *hc, int index, bool ev)
{
__hap_char_t *_hc = (__hap_char_t *)hc;
if (ev)
set_bit(_hc->ev_ctrls, index);
else
reset_bit(_hc->ev_ctrls, index);
}
bool hap_char_is_ctrl_subscribed(hap_char_t *hc, int index)
{
__hap_char_t *_hc = (__hap_char_t *)hc;
return (_hc->ev_ctrls & (1 << index)) ? true : false;
}
void hap_char_set_owner_ctrl(hap_char_t *hc, int index)
{
__hap_char_t *_hc = (__hap_char_t *)hc;
_hc->owner_ctrl = 0;
set_bit(_hc->owner_ctrl, index);
}
bool hap_char_is_ctrl_owner(hap_char_t *hc, int index)
{
__hap_char_t *_hc = (__hap_char_t *)hc;
return (_hc->owner_ctrl & (1 << index)) ? true : false;
}
void hap_char_set_iid(hap_char_t *hc, int32_t iid)
{
if (hc) {
((__hap_char_t *)hc)->iid = iid;
}
}
void hap_disable_all_char_notif(int index)
{
/* Just loop through all characteristic objects and reset the
* bit indicating event notifications. This is the simplest way
*/
hap_acc_t *ha;
hap_serv_t *hs;
hap_char_t *hc;
for (ha = hap_get_first_acc(); ha; ha = hap_acc_get_next(ha)) {
for (hs = hap_acc_get_first_serv(ha); hs; hs = hap_serv_get_next(hs)) {
for (hc = hap_serv_get_first_char(hs); hc; hc = hap_char_get_next(hc)) {
reset_bit(((__hap_char_t *)hc)->ev_ctrls, index);
}
}
}
}
void hap_char_add_valid_vals(hap_char_t *hc, const uint8_t *valid_vals, size_t valid_val_cnt)
{
if (!hc)
return;
__hap_char_t *_hc = (__hap_char_t *)hc;
_hc->valid_vals = hap_platform_memory_malloc(valid_val_cnt);
if (_hc->valid_vals) {
memcpy(_hc->valid_vals, valid_vals, valid_val_cnt);
_hc->valid_vals_cnt = valid_val_cnt;
}
}
void hap_char_add_valid_vals_range(hap_char_t *hc, uint8_t start_val, uint8_t end_val)
{
if (!hc)
return;
__hap_char_t *_hc = (__hap_char_t *)hc;
_hc->valid_vals_range = hap_platform_memory_malloc(sizeof(uint8_t));
if (_hc->valid_vals_range) {
_hc->valid_vals_range[0] = start_val;
_hc->valid_vals_range[1] = end_val;
}
}