in zcl_power_config_commands.c battery alarms don't work properly due to a typo in
in zcl_power_config_commands.c battery alarms don't work properly due to a typo in
Hi,
Apologies for the long wait. We've had Easter holidays in Norway for the past week meaning there has been little to no activity on Devzone.
Thank you for reporting this. I will relay this to the Zigbee team and get back to you if it is relevant.
Kind regards,
Andreas
Hi,
It looks indeed to be a bug from DSR's side. We've created a ticket to patch this from their end. jira.dsr-corporation.com/.../ZOINOR-11
Kind regards,
Andreas
Hi again,
I can also add that the fix will be implemented in NCS 2.7.0, and the fix which will be outside of the ZBOSS library can be seen in the attached file.
I assume that you have your own fix for this, but if you're waiting you can use the fix below if you want to
/* ZBOSS Zigbee software protocol stack * * Copyright (c) 2012-2020 DSR Corporation, Denver CO, USA. * www.dsr-zboss.com * www.dsr-corporation.com * All rights reserved. * * This is unpublished proprietary source code of DSR Corporation * The copyright notice does not evidence any actual or intended * publication of such source code. * * ZBOSS is a registered trademark of Data Storage Research LLC d/b/a DSR * Corporation * * Commercial Usage * Licensees holding valid DSR Commercial licenses may use * this file in accordance with the DSR Commercial License * Agreement provided with the Software or, alternatively, in accordance * with the terms contained in a written agreement between you and * DSR. */ /* PURPOSE: ZCL Power Configuration cluster specific commands handling */ #define ZB_TRACE_FILE_ID 2079 #include "zb_common.h" #if defined (ZB_ZCL_SUPPORT_CLUSTER_POWER_CONFIG) #include "zb_time.h" #include "zb_zdo.h" #include "zb_zcl.h" #include "zcl/zb_zcl_power_config.h" #include "zcl/zb_zcl_alarms.h" /** @internal @{ */ zb_ret_t check_value_power_config_server(zb_uint16_t attr_id, zb_uint8_t endpoint, zb_uint8_t *value); void zb_zcl_power_config_write_attr_hook_server(zb_uint8_t endpoint, zb_uint16_t attr_id, zb_uint8_t *new_value, zb_uint16_t manuf_code); void zb_zcl_power_config_init_server() { zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, check_value_power_config_server, zb_zcl_power_config_write_attr_hook_server, (zb_zcl_cluster_handler_t)NULL); } void zb_zcl_power_config_init_client() { zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_CLIENT_ROLE, (zb_zcl_cluster_check_value_t)NULL, (zb_zcl_cluster_write_attr_hook_t)NULL, (zb_zcl_cluster_handler_t)NULL); } zb_ret_t check_value_power_config_server(zb_uint16_t attr_id, zb_uint8_t endpoint, zb_uint8_t *value) { ZVUNUSED(attr_id); ZVUNUSED(value); ZVUNUSED(endpoint); /* All values for mandatory attributes are allowed, extra check for * optional attributes is needed */ return RET_OK; } /*Subtrahend to cast battery voltage attribute IDs to the simplified calculation view*/ #define ZB_ZCL_POWER_CONFIG_BATTERY_VOLTAGE_ATTR_SUBTRAHEND 0x20 /*Subtrahend to cast battery percentage remaining attribute IDs to the simplified calculation view*/ #define ZB_ZCL_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ATTR_SUBTRAHEND 0x21 /*The addend is needed for calculation alarm value from normalized attribute Id*/ #define ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_CODES_ADDEND 0x20 /*Alarm State attribute shifts (ZCL spec Rev 7 3.3.2.2.4.11)*/ #define ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_LSHIFT 0 #define ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_2_LSHIFT 10 #define ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_3_LSHIFT 20 /*The minuend for getting alarm mask value from normalized attribute ID*/ #define ZB_ZCL_POWER_CONFIG_ALARM_MASKS_MINUEND 16 #define ZB_ZCL_CHECK_THRESHOLD_EXISTENCE(ep, attr_set, is_percentage) \ { \ zb_zcl_attr_t *attr_desc_local; \ attr_desc_local = zb_zcl_get_attr_desc_a( \ ep, \ ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ (is_percentage) ? \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_MIN_THRESHOLD_ID): \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_MIN_THRESHOLD_ID)); \ if (!attr_desc_local) \ { \ val &= ~ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_MIN_THRESHOLD; \ } \ attr_desc_local = zb_zcl_get_attr_desc_a( \ ep, \ ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ (is_percentage) ? \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD1_ID): \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD1_ID)); \ if (!attr_desc_local) \ { \ val &= ~ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE1; \ } \ attr_desc_local = zb_zcl_get_attr_desc_a( \ ep, \ ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ (is_percentage) ? \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD2_ID): \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD2_ID)); \ if (!attr_desc_local) \ { \ val &= ~ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE2; \ } \ attr_desc_local = zb_zcl_get_attr_desc_a( \ ep, \ ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ (is_percentage) ? \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD3_ID): \ (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD3_ID)); \ if (!attr_desc_local) \ { \ val &= ~ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE3; \ } \ } void fill_alarm_mask_bitmap(zb_uint8_t ep, enum zb_zcl_power_config_battery_alarm_state_e value, zb_zcl_attr_t *attr_desc, zb_uint16_t attr_set, zb_bool_t is_percentage) { if (attr_desc) { zb_uint8_t val = ZB_ZCL_POWER_CONFIG_ALARM_MASKS_MINUEND - value; ZB_ZCL_CHECK_THRESHOLD_EXISTENCE(ep, attr_set, is_percentage); ZB_ZCL_SET_DIRECTLY_ATTR_VAL8(attr_desc, val); } } void fill_alarm_state_bitmap(zb_uint8_t ep, enum zb_zcl_power_config_battery_alarm_state_e value, zb_zcl_attr_t *attr_desc, zb_uint16_t attr_set, zb_bool_t is_percentage) { if (attr_desc) { zb_uint32_t attr_val = 0; zb_uint8_t val = ZB_ZCL_POWER_CONFIG_ALARM_MASKS_MINUEND - value; ZB_ZCL_CHECK_THRESHOLD_EXISTENCE(ep, attr_set, is_percentage); if (attr_set == ZB_ZCL_POWER_CONFIG_BATTERY_ATTRIBUTE_SET) { attr_val = (zb_uint32_t)val << ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_LSHIFT; } else if (attr_set == ZB_ZCL_POWER_CONFIG_BATTERY_SOURCE_2_ATTRIBUTE_SET) { attr_val = (zb_uint32_t)val << ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_2_LSHIFT; } else if (attr_set == ZB_ZCL_POWER_CONFIG_BATTERY_SOURCE_3_ATTRIBUTE_SET) { attr_val = (zb_uint32_t)val << ZB_ZCL_POWER_CONFIG_ALARM_STATE_ATTR_BATTERY_SOURCE_3_LSHIFT; } ZB_ZCL_SET_DIRECTLY_ATTR_VAL32(attr_desc, attr_val); } } /* performs comparison taking into account hysteresis. Returns TRUE * if voltage value goes lower then threshold */ zb_bool_t zcl_pwr_value_under_threshold(zb_uint8_t voltage, zb_uint8_t threshold) { zb_bool_t ret = ZB_FALSE; TRACE_MSG(TRACE_ZCL1, "> zcl_pwr_value_under_threshold voltage %hd threshold %hd", (FMT__H_H, voltage, threshold)); /* TODO: implement comparison taking into account hysteresis */ if ((voltage < threshold) && (threshold != 0xff)) { ret = ZB_TRUE; } TRACE_MSG(TRACE_ZCL1, "< zcl_pwr_value_under_threshold ret %hd", (FMT__H, ret)); return ret; } void send_alarm(zb_uint8_t param) { zb_bool_t ret = ZB_TRUE; zb_uint16_t addr = 0; zb_uint16_t *user_param = ZB_BUF_GET_PARAM(param, zb_uint16_t); zb_uint8_t ep = *user_param; zb_uint8_t alarm_code = *user_param >> 8; /* AN: For Mains Voltage it needs to check current value just before alarm sending one more time. * Maybe Voltage has come back to the [Min Threshold; Max Threshold] range */ if ((alarm_code == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD_ALARM_CODE) || (alarm_code == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MAX_THRESHOLD_ALARM_CODE)) { zb_zcl_attr_t *attr_desc; zb_uint16_t val,threshold; attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_ID); val = ZB_ZCL_GET_ATTRIBUTE_VAL_16(attr_desc); attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (alarm_code == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD_ALARM_CODE) ? ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD : ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_MAX_THRESHOLD); threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_16(attr_desc); if (((alarm_code == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD_ALARM_CODE) && (val >= threshold)) || ((alarm_code == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MAX_THRESHOLD_ALARM_CODE) && (val <= threshold)) || (threshold == ZB_ZCL_POWER_CONFIG_THRESHOLD_ALARM_OMISSION_VALUE)) { ret = ZB_FALSE; } } if (ret) { ZB_ZCL_ALARMS_SEND_ALARM_RES( param, addr, ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT, 0, ep, ZB_AF_HA_PROFILE_ID, NULL, alarm_code, ZB_ZCL_CLUSTER_ID_POWER_CONFIG); } else { zb_buf_free(param); } } void get_send_alarm_buf(zb_uint8_t param, zb_uint16_t user_param) { if (!param) { zb_buf_get_out_delayed_ext(get_send_alarm_buf, user_param, 0); } else { zb_uint16_t dwell_period = 0; zb_uint16_t *u_param = ZB_BUF_GET_PARAM(param, zb_uint16_t); zb_zcl_attr_t *attr_desc = NULL; *u_param = user_param; attr_desc = zb_zcl_get_attr_desc_a( (zb_uint8_t)user_param, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_POWER_CONFIG_MAINS_DWELL_TRIP_POINT); if (attr_desc && ((user_param >> 8 == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD_ALARM_CODE) || (user_param >> 8 == ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_MAX_THRESHOLD_ALARM_CODE))) { dwell_period = ZB_ZCL_GET_ATTRIBUTE_VAL_16(attr_desc); } zb_schedule_alarm(send_alarm, param, ZB_TIME_ONE_SECOND*dwell_period); } } void zcl_pwr_cfg_check_mains_voltage(zb_uint8_t ep, zb_uint16_t val) { zb_bool_t ret = ZB_FALSE; zb_zcl_attr_t *attr_desc; zb_zcl_attr_t *attr_desc_mask; zb_uint16_t threshold; attr_desc_mask = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_POWER_CONFIG_MAINS_ALARM_MASK_ID); TRACE_MSG(TRACE_ZCL1, "> zcl_pwr_cfg_check_mains_voltage_value ep %hd, val %d", (FMT__H_D, ep, val)); if (attr_desc_mask) { attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_MIN_THRESHOLD); if (attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_16(attr_desc); if (val < threshold && threshold != ZB_ZCL_POWER_CONFIG_THRESHOLD_ALARM_OMISSION_VALUE) { ZB_ZCL_SET_DIRECTLY_ATTR_VAL8(attr_desc_mask, ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_ALARM_CODE_MIN_THRESHOLD); ret = ZB_TRUE; } } if (!ret) { attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_MAX_THRESHOLD); if (attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_16(attr_desc); if (val > threshold && threshold != ZB_ZCL_POWER_CONFIG_THRESHOLD_ALARM_OMISSION_VALUE) { ZB_ZCL_SET_DIRECTLY_ATTR_VAL8(attr_desc_mask, ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_ALARM_CODE_MAX_THRESHOLD); ret = ZB_TRUE; } } } if (ret) { zb_af_endpoint_desc_t* endpoint_desc = zb_af_get_endpoint_desc(ep); zb_zcl_cluster_desc_t *cluster_desc = get_cluster_desc(endpoint_desc, ZB_ZCL_CLUSTER_ID_ALARMS, ZB_ZCL_CLUSTER_SERVER_ROLE); if (cluster_desc) { zb_uint8_t alarm_mask = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc_mask); zb_uint16_t u_param = ( ((alarm_mask & ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_ALARM_CODE_MAX_THRESHOLD) ? ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_ALARM_CODE_MAX_THRESHOLD : ZB_ZCL_POWER_CONFIG_MAINS_VOLTAGE_ALARM_CODE_MIN_THRESHOLD)<< 8) | ep; zb_buf_get_out_delayed_ext(get_send_alarm_buf, u_param, 0); } } } } /* HA version of Power Configuration declares 3 sets of * BatteryInformation and BatterySettings attr sets. This function * is used to check new BATTERY_VOLTAGE attribute or BATTERY_PERCENTAGE_REMAINING * value in attribute set defined by attr_set value and set BatteryAlarmState * if any alarm appears (BatteryVoltageMinThreshold or other threshold check) */ void zcl_pwr_cfg_check_battery_voltage_or_percentage_value(zb_uint8_t ep, zb_uint8_t val, zb_uint16_t attr_set, zb_bool_t is_percentage) { zb_bool_t ret = ZB_FALSE; zb_zcl_attr_t *attr_desc; zb_zcl_attr_t *attr_desc_state; zb_zcl_attr_t *attr_desc_mask; zb_uint8_t threshold; TRACE_MSG(TRACE_ZCL1, "> zcl_pwr_cfg_check_val_value ep %hd, val %hd, attr_set %d", (FMT__H_H_D, ep, val, attr_set)); attr_desc_state = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_ALARM_STATE_ID)); attr_desc_mask = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_ALARM_MASK_ID)); if (attr_desc_state) { /*** Check Min threshold value ***/ attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (is_percentage) ? (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_MIN_THRESHOLD_ID): (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_MIN_THRESHOLD_ID)); if(attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc); if (zcl_pwr_value_under_threshold(val, threshold)) { fill_alarm_state_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_MIN_THRESHOLD, attr_desc_mask, attr_set, is_percentage); if (attr_desc_mask) { fill_alarm_mask_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_MIN_THRESHOLD, attr_desc_mask, attr_set, is_percentage); } ret = ZB_TRUE; } } /*** Check Threshold1 value ***/ if (!ret) { attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (is_percentage) ? (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD1_ID): (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD1_ID)); if(attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc); if (zcl_pwr_value_under_threshold(val, threshold)) { fill_alarm_state_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE1, attr_desc_mask, attr_set, is_percentage); if (attr_desc_mask) { fill_alarm_mask_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE1, attr_desc_mask, attr_set, is_percentage); } ret = ZB_TRUE; } } } /*** Check Threshold2 value ***/ if (!ret) { attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (is_percentage) ? (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD2_ID): (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD2_ID)); if(attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc); if (zcl_pwr_value_under_threshold(val, threshold)) { fill_alarm_state_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE2, attr_desc_mask, attr_set, is_percentage); if (attr_desc_mask) { fill_alarm_mask_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE2, attr_desc_mask, attr_set, is_percentage); } ret = ZB_TRUE; } } } /*** Check Threshold3 value ***/ if (!ret) { attr_desc = zb_zcl_get_attr_desc_a( ep, ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ZB_ZCL_CLUSTER_SERVER_ROLE, (is_percentage) ? (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_THRESHOLD3_ID): (zb_uint16_t)(attr_set+ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_THRESHOLD3_ID)); if(attr_desc) { threshold = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc); if (zcl_pwr_value_under_threshold(val, threshold)) { fill_alarm_state_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE3, attr_desc_mask, attr_set, is_percentage); if (attr_desc_mask) { fill_alarm_mask_bitmap(ep, ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE3, attr_desc_mask, attr_set, is_percentage); } ret = ZB_TRUE; } } } if (ret) { if (attr_desc_mask) { zb_af_endpoint_desc_t* endpoint_desc = zb_af_get_endpoint_desc(ep); zb_zcl_cluster_desc_t *cluster_desc = get_cluster_desc(endpoint_desc, ZB_ZCL_CLUSTER_ID_ALARMS, ZB_ZCL_CLUSTER_SERVER_ROLE); if (cluster_desc) { zb_uint8_t alarm_mask = ZB_ZCL_GET_ATTRIBUTE_VAL_8(attr_desc_mask); /* AN: Looks like, that we can send only one "the most significant" alarm. * ZCL spec strongly requires (p. 3.3.2.2.4.8 BatteryVoltageThreshold 1-3 Attributes) * that BatteryVoltageThresholdN+1 has to be higher than the BatteryVoltageThresholdN. * * ms-alarm - subtrahend with range [0;4]. It is needed for Alarm code "threshold" nibble (10*[0,1,2] + 4 - ms_alarm) */ zb_uint8_t ms_alarm = (alarm_mask & ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_MIN_THRESHOLD) ? 4 : ( (alarm_mask & ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE1) ? 3 : ( (alarm_mask & ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE2) ? 2 : ( (alarm_mask & ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_STATE_SOURCE1_VOLTAGE3) ? 1 : 0 ))); zb_uint16_t u_param = (((attr_set+ZB_ZCL_POWER_CONFIG_BATTERY_ALARM_CODES_ADDEND)/2 + (4-ms_alarm)) << 8) | ep; zb_buf_get_out_delayed_ext(get_send_alarm_buf, u_param, 0); } } } } TRACE_MSG(TRACE_ZCL1, "< zcl_pwr_cfg_check_val_value ret %hd", (FMT__H, ret)); } void zb_zcl_power_config_write_attr_hook_server(zb_uint8_t endpoint, zb_uint16_t attr_id, zb_uint8_t *new_value, zb_uint16_t manuf_code) { TRACE_MSG(TRACE_ZCL1, "> zb_zcl_power_config_write_attr_hook endpoint %hx attr_id 0x%x, manuf_code 0x%x", (FMT__H_D_D, endpoint, attr_id, manuf_code)); ZVUNUSED(manuf_code); if (attr_id == ZB_ZCL_ATTR_POWER_CONFIG_MAINS_VOLTAGE_ID) { zb_uint16_t new_value_16 = ZB_ZCL_ATTR_GET16(new_value); zcl_pwr_cfg_check_mains_voltage(endpoint, new_value_16); } if (attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_ID || attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY2_VOLTAGE_ID || attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY3_VOLTAGE_ID) { zcl_pwr_cfg_check_battery_voltage_or_percentage_value(endpoint, *new_value, attr_id-ZB_ZCL_POWER_CONFIG_BATTERY_VOLTAGE_ATTR_SUBTRAHEND, ZB_FALSE); } else if (attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID || attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY2_PERCENTAGE_REMAINING_ID || attr_id == ZB_ZCL_ATTR_POWER_CONFIG_BATTERY3_PERCENTAGE_REMAINING_ID) { zcl_pwr_cfg_check_battery_voltage_or_percentage_value(endpoint, *new_value, attr_id-ZB_ZCL_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ATTR_SUBTRAHEND, ZB_TRUE); } TRACE_MSG(TRACE_ZCL1, "< zb_zcl_power_config_write_attr_hook", (FMT__0)); } /** * @} internal */ #endif /* ZB_ZCL_SUPPORT_CLUSTER_POWER_CONFIG */
Kind regards,
Andreas