diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp index f2806926e9c537..4cf693727aa8a1 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -61,77 +62,123 @@ DoorLockServer & DoorLockServer::Instance() */ void DoorLockServer::InitServer(chip::EndpointId endpointId) { - emberAfDoorLockClusterPrintln("Door Lock cluster initialized at %d", endpointId); + emberAfDoorLockClusterPrintln("Door Lock cluster initialized at endpoint #%" PRIu16, endpointId); } bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLockState) { - auto lockState = static_cast(newLockState); + auto lockState = chip::to_underlying(newLockState); - emberAfDoorLockClusterPrintln("Setting Lock State to '%hhu'", lockState); + emberAfDoorLockClusterPrintln("Setting LockState to '%" PRIu8 "'", lockState); + EmberAfStatus status = Attributes::LockState::Set(endpointId, newLockState); - bool status = (Attributes::LockState::Set(endpointId, newLockState) == EMBER_ZCL_STATUS_SUCCESS); - if (!status) + if (EMBER_ZCL_STATUS_SUCCESS != status) { - ChipLogError(Zcl, "Unable to set the Lock State to %hhu: internal error", lockState); + ChipLogError(Zcl, "Unable to set LockState attribute: status=0x%" PRIx8, status); } - return status; + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetActuatorEnabled(chip::EndpointId endpointId, bool newActuatorState) { auto actuatorState = static_cast(newActuatorState); - emberAfDoorLockClusterPrintln("Setting Actuator State to '%hhu'", actuatorState); + emberAfDoorLockClusterPrintln("Setting ActuatorEnabled to '%" PRIu8 "'", actuatorState); + EmberAfStatus status = Attributes::ActuatorEnabled::Set(endpointId, newActuatorState); - bool status = (Attributes::ActuatorEnabled::Set(endpointId, newActuatorState) == EMBER_ZCL_STATUS_SUCCESS); - if (!status) + if (EMBER_ZCL_STATUS_SUCCESS != status) { - ChipLogError(Zcl, "Unable to set the Actuator State to %hhu: internal error", actuatorState); + ChipLogError(Zcl, "Unable to set ActuatorEnabled attribute: status=0x%" PRIx8, status); } - return false; + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetDoorState(chip::EndpointId endpointId, DlDoorState newDoorState) { - auto doorState = static_cast(newDoorState); + auto doorState = chip::to_underlying(newDoorState); - emberAfDoorLockClusterPrintln("Setting Door State to '%hhu'", doorState); - bool status = (Attributes::DoorState::Set(endpointId, newDoorState) == EMBER_ZCL_STATUS_SUCCESS); + emberAfDoorLockClusterPrintln("Setting DoorState to '%" PRIu8 "'", doorState); + EmberAfStatus status = Attributes::DoorState::Set(endpointId, newDoorState); - if (!status) + if (EMBER_ZCL_STATUS_SUCCESS != status) { - ChipLogError(Zcl, "Unable to set the Door State to %hhu: internal error", doorState); + ChipLogError(Zcl, "Unable to set DoorState attribute: status=0x%" PRIx8, status); } - return false; + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetLanguage(chip::EndpointId endpointId, const char * newLanguage) { - return true; + auto lang = chip::CharSpan(newLanguage, strlen(newLanguage)); + + emberAfDoorLockClusterPrintln("Setting Language to '%s'", newLanguage); + EmberAfStatus status = Attributes::Language::Set(endpointId, lang); + + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogError(Zcl, "Unable to set Language attribute: status=0x%" PRIx8, status); + } + + return (EMBER_ZCL_STATUS_SUCCESS == status); } -bool DoorLockServer::SetAutoRelockTime(chip::EndpointId, uint32_t newAutoRelockTimeSec) +bool DoorLockServer::SetAutoRelockTime(chip::EndpointId endpointId, uint32_t newAutoRelockTimeSec) { - return true; + emberAfDoorLockClusterPrintln("Setting AutoRelockTime to '%" PRIu32 "'", newAutoRelockTimeSec); + EmberAfStatus status = Attributes::AutoRelockTime::Set(endpointId, newAutoRelockTimeSec); + + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogError(Zcl, "Unable to set AutoRelockTime attribute: status=0x%" PRIx8, status); + } + + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetSoundVolume(chip::EndpointId endpointId, uint8_t newSoundVolume) { - return true; + emberAfDoorLockClusterPrintln("Setting SoundVolume to '%" PRIu8 "'", newSoundVolume); + EmberAfStatus status = Attributes::SoundVolume::Set(endpointId, newSoundVolume); + + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogError(Zcl, "Unable to set SoundVolume attribute: status=0x%" PRIx8, status); + } + + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetOneTouchLocking(chip::EndpointId endpointId, bool isEnabled) { - return true; + auto enable = static_cast(isEnabled); + + emberAfDoorLockClusterPrintln("Setting EnableOneTouchLocking to '%" PRIu8 "'", enable); + EmberAfStatus status = Attributes::EnableOneTouchLocking::Set(endpointId, isEnabled); + + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogError(Zcl, "Unable to set EnableOneTouchLocking attribute: status=0x%" PRIx8, status); + } + + return (EMBER_ZCL_STATUS_SUCCESS == status); } bool DoorLockServer::SetPrivacyModeButton(chip::EndpointId endpointId, bool isEnabled) { - return true; + auto enable = static_cast(isEnabled); + + emberAfDoorLockClusterPrintln("Setting EnablePrivacyModeButton to '%" PRIu8 "'", enable); + EmberAfStatus status = Attributes::EnablePrivacyModeButton::Set(endpointId, isEnabled); + + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogError(Zcl, "Unable to set EnablePrivacyModeButton attribute: status=0x%" PRIx8, status); + } + + return (EMBER_ZCL_STATUS_SUCCESS == status); } // ======================================================= @@ -143,7 +190,6 @@ bool emberAfDoorLockClusterLockDoorCallback(chip::app::CommandHandler * commandO emberAfDoorLockClusterPrintln("Received Lock Door command (not implemented)"); // TODO: Implement door locking by calling emberAfPluginDoorLockOnDoorLockCommand - emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; } @@ -229,8 +275,109 @@ chip::Protocols::InteractionModel::Status MatterDoorLockClusterServerPreAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath, EmberAfAttributeType attributeType, uint16_t size, uint8_t * value) { - // TODO: Implement attribute changes - return chip::Protocols::InteractionModel::Status::Success; + chip::Protocols::InteractionModel::Status res; + + switch (attributePath.mAttributeId) + { + case chip::app::Clusters::DoorLock::Attributes::Language::Id: + if (value[0] <= 3) + { + char lang[3 + 1] = { 0 }; + memcpy(lang, &value[1], value[0]); + res = emberAfPluginDoorLockOnLanguageChange(attributePath.mEndpointId, lang); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::AutoRelockTime::Id: + if (sizeof(uint32_t) == size) + { + uint32_t newRelockTime = *(reinterpret_cast(value)); + res = emberAfPluginDoorLockOnAutoRelockTimeChange(attributePath.mEndpointId, newRelockTime); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::SoundVolume::Id: + if (sizeof(uint8_t) == size) + { + res = emberAfPluginDoorLockOnSoundVolumeChange(attributePath.mEndpointId, *value); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::OperatingMode::Id: + if (sizeof(uint8_t) == size) + { + res = emberAfPluginDoorLockOnOperatingModeChange(attributePath.mEndpointId, *value); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::EnableOneTouchLocking::Id: + if (sizeof(bool) == size) + { + bool enable = *reinterpret_cast(value); + res = emberAfPluginDoorLockOnEnableOneTouchLockingChange(attributePath.mEndpointId, enable); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::EnablePrivacyModeButton::Id: + if (sizeof(bool) == size) + { + bool enable = *reinterpret_cast(value); + res = emberAfPluginDoorLockOnEnablePrivacyModeButtonChange(attributePath.mEndpointId, enable); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::WrongCodeEntryLimit::Id: + if (sizeof(uint8_t) == size) + { + res = emberAfPluginDoorLockOnWrongCodeEntryLimitChange(attributePath.mEndpointId, *value); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + case chip::app::Clusters::DoorLock::Attributes::UserCodeTemporaryDisableTime::Id: + if (sizeof(uint8_t) == size) + { + res = emberAfPluginDoorLockOnUserCodeTemporaryDisableTimeChange(attributePath.mEndpointId, *value); + } + else + { + res = chip::Protocols::InteractionModel::Status::InvalidValue; + } + break; + + default: + res = emberAfPluginDoorLockOnUnhandledAttributeChange(attributePath.mEndpointId, attributeType, size, value); + break; + } + + return res; } void emberAfPluginDoorLockServerLockoutEventHandler(void) {} @@ -243,3 +390,62 @@ void MatterDoorLockPluginServerInitCallback() } void MatterDoorLockClusterServerAttributeChangedCallback(const app::ConcreteAttributePath & attributePath) {} + +// ============================================================================= +// Pre-change callbacks for cluster attributes +// ============================================================================= + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnLanguageChange(chip::EndpointId EndpointId, const char * newLanguage) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnAutoRelockTimeChange(chip::EndpointId EndpointId, uint32_t newTime) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnSoundVolumeChange(chip::EndpointId EndpointId, uint8_t newVolume) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnOperatingModeChange(chip::EndpointId EndpointId, uint8_t newMode) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnEnableOneTouchLockingChange(chip::EndpointId EndpointId, bool enable) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnEnablePrivacyModeButtonChange(chip::EndpointId EndpointId, bool enable) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnWrongCodeEntryLimitChange(chip::EndpointId EndpointId, uint8_t newLimit) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnUserCodeTemporaryDisableTimeChange(chip::EndpointId EndpointId, uint8_t newTime) +{ + return chip::Protocols::InteractionModel::Status::Success; +} + +chip::Protocols::InteractionModel::Status __attribute__((weak)) +emberAfPluginDoorLockOnUnhandledAttributeChange(chip::EndpointId EndpointId, EmberAfAttributeType attrType, uint16_t attrSize, + uint8_t * attrValue) +{ + return chip::Protocols::InteractionModel::Status::Success; +} diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index d9af7c8d8eab76..9e853a6c2bd214 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -43,7 +43,7 @@ class DoorLockServer bool SetDoorState(chip::EndpointId endpointId, chip::app::Clusters::DoorLock::DlDoorState newDoorState); bool SetLanguage(chip::EndpointId endpointId, const char * newLanguage); - bool SetAutoRelockTime(chip::EndpointId, uint32_t newAutoRelockTimeSec); + bool SetAutoRelockTime(chip::EndpointId endpointId, uint32_t newAutoRelockTimeSec); bool SetSoundVolume(chip::EndpointId endpointId, uint8_t newSoundVolume); bool SetOneTouchLocking(chip::EndpointId endpointId, bool isEnabled); @@ -55,3 +55,109 @@ class DoorLockServer bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const char * PINCOde); bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const char * PINCode); + +// ============================================================================= +// Pre-change callbacks for cluster attributes +// ============================================================================= + +/** @brief 'Language' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newLanguage language to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnLanguageChange(chip::EndpointId EndpointId, + const char * newLanguage); + +/** @brief 'AutoRelockTime' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newTime relock time value to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnAutoRelockTimeChange(chip::EndpointId EndpointId, + uint32_t newTime); + +/** @brief 'SoundVolume' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newVolume volume level to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnSoundVolumeChange(chip::EndpointId EndpointId, uint8_t newVolume); + +/** @brief 'OperatingMode' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newMode operating mode to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnOperatingModeChange(chip::EndpointId EndpointId, uint8_t newMode); + +/** @brief 'EnableOneTouchLocking' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param enable true to enable one touch locking, false otherwise + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnEnableOneTouchLockingChange(chip::EndpointId EndpointId, + bool enable); + +/** @brief 'EnablePrivacyModeButton' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param enable true to enable privacy mode button, false otherwise + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnEnablePrivacyModeButtonChange(chip::EndpointId EndpointId, + bool enable); + +/** @brief 'WrongCodeEntryLimit' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newLimit new limit for the number of incorrect PIN attempts to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnWrongCodeEntryLimitChange(chip::EndpointId EndpointId, + uint8_t newLimit); + +/** @brief 'UserCodeTemporaryDisableTime' attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param newTime new number of seconds for which lock will be shut down due to wrong code entry + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnUserCodeTemporaryDisableTimeChange(chip::EndpointId EndpointId, + uint8_t newTime); + +/** @note This callback is called for any cluster attribute that has no predefined callback above + * + * @brief Cluster attribute pre-change callback + * + * @param EndpointId endpoint for which attribute is changing + * @param attrType attribute that is going to be changed + * @param attrSize attribute value storage size + * @param attrValue attribute value to set + * + * @retval InteractionModel::Status::Success if attribute change is possible + * @retval any other InteractionModel::Status value to forbid attribute change + */ +chip::Protocols::InteractionModel::Status emberAfPluginDoorLockOnUnhandledAttributeChange(chip::EndpointId EndpointId, + EmberAfAttributeType attrType, + uint16_t attrSize, uint8_t * attrValue); diff --git a/src/app/zap-templates/templates/app/helper.js b/src/app/zap-templates/templates/app/helper.js index 504cc2a48a943f..c9c2d6ba42330f 100644 --- a/src/app/zap-templates/templates/app/helper.js +++ b/src/app/zap-templates/templates/app/helper.js @@ -109,7 +109,7 @@ var endpointClusterWithInit = [ 'Thermostat', ]; var endpointClusterWithAttributeChanged = [ 'Identify', 'Door Lock', 'Pump Configuration and Control' ]; -var endpointClusterWithPreAttribute = [ 'IAS Zone', 'Thermostat User Interface Configuration' ]; +var endpointClusterWithPreAttribute = [ 'IAS Zone', 'Door Lock', 'Thermostat User Interface Configuration' ]; var endpointClusterWithMessageSent = [ 'IAS Zone' ]; /** diff --git a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h index ddd4992624990f..bd13c3ed4a04c9 100644 --- a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h +++ b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h @@ -2317,6 +2317,7 @@ }; \ const EmberAfGenericClusterFunction chipFuncArrayDoorLockServer[] = { \ (EmberAfGenericClusterFunction) MatterDoorLockClusterServerAttributeChangedCallback, \ + (EmberAfGenericClusterFunction) MatterDoorLockClusterServerPreAttributeChangedCallback, \ }; \ const EmberAfGenericClusterFunction chipFuncArrayPumpConfigurationAndControlServer[] = { \ (EmberAfGenericClusterFunction) emberAfPumpConfigurationAndControlClusterServerInitCallback, \ @@ -2495,7 +2496,8 @@ ZAP_ATTRIBUTE_INDEX(276), \ 19, \ 29, \ - ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ + ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION) | \ + ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayDoorLockServer }, /* Endpoint: 1, Cluster: Door Lock (server) */ \ { \ 0x0102, ZAP_ATTRIBUTE_INDEX(295), 20, 35, ZAP_CLUSTER_MASK(SERVER), NULL \ diff --git a/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h b/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h index fafb00330d795f..ec908383db22f5 100644 --- a/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h +++ b/zzz_generated/tv-casting-app/zap-generated/endpoint_config.h @@ -1451,6 +1451,7 @@ }; \ const EmberAfGenericClusterFunction chipFuncArrayDoorLockServer[] = { \ (EmberAfGenericClusterFunction) MatterDoorLockClusterServerAttributeChangedCallback, \ + (EmberAfGenericClusterFunction) MatterDoorLockClusterServerPreAttributeChangedCallback, \ }; \ const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \ (EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \ @@ -1576,7 +1577,8 @@ ZAP_ATTRIBUTE_INDEX(232), \ 19, \ 29, \ - ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ + ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION) | \ + ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayDoorLockServer }, /* Endpoint: 1, Cluster: Door Lock (server) */ \ { \ 0x0102, ZAP_ATTRIBUTE_INDEX(251), 19, 31, ZAP_CLUSTER_MASK(SERVER), NULL \