From 8f5baccffe80be338b9e75e34cf885f37e5c9a3a Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 7 Jan 2025 21:01:16 +0100 Subject: [PATCH] Update structs --- src/NukiBle.cpp | 4 +- src/NukiConstants.h | 6 +- src/NukiLock.cpp | 37 ++++++--- src/NukiLock.h | 9 +++ src/NukiLockConstants.h | 96 ++++++++++++---------- src/NukiLockUtils.cpp | 34 +++++++- src/NukiOpener.cpp | 21 +++-- src/NukiOpener.h | 163 ++++++++++++++++++++------------------ src/NukiOpenerConstants.h | 141 +++++++++++++-------------------- src/NukiOpenerUtils.cpp | 7 +- 10 files changed, 290 insertions(+), 228 deletions(-) diff --git a/src/NukiBle.cpp b/src/NukiBle.cpp index a9c2f88e..e529a067 100644 --- a/src/NukiBle.cpp +++ b/src/NukiBle.cpp @@ -1572,7 +1572,7 @@ void NukiBle::handleReturnMessage(Command returnCode, unsigned char* data, uint1 case Command::AuthorizationEntry : { printBuffer((byte*)data, dataLen, false, "authorizationEntry", debugNukiHexData, logger); AuthorizationEntry authEntry; - memcpy(&authEntry, data, sizeof(authEntry)); + memcpy(&authEntry, data, dataLen); listOfAuthorizationEntries.push_back(authEntry); if (debugNukiReadableData) { NukiLock::logAuthorizationEntry(authEntry, true, logger); @@ -1667,7 +1667,7 @@ void NukiBle::handleReturnMessage(Command returnCode, unsigned char* data, uint1 } case Command::KeypadCode : { KeypadEntry keypadEntry; - memcpy(&keypadEntry, data, sizeof(KeypadEntry)); + memcpy(&keypadEntry, data, dataLen); listOfKeyPadEntries.push_back(keypadEntry); nrOfReceivedKeypadCodes++; diff --git a/src/NukiConstants.h b/src/NukiConstants.h index 82cbb1d4..bdbe4b82 100644 --- a/src/NukiConstants.h +++ b/src/NukiConstants.h @@ -37,14 +37,16 @@ enum class DoorSensorState : uint8_t { enum class BatteryType : uint8_t { Alkali = 0x00, Accumulators = 0x01, - Lithium = 0x02 + Lithium = 0x02, + Unknown = 0xFF }; enum class AdvertisingMode : uint8_t { Automatic = 0x00, Normal = 0x01, Slow = 0x02, - Slowest = 0x03 + Slowest = 0x03, + Unknown = 0xFF }; enum class CommandType { diff --git a/src/NukiLock.cpp b/src/NukiLock.cpp index 8893e854..8debf5f0 100644 --- a/src/NukiLock.cpp +++ b/src/NukiLock.cpp @@ -623,18 +623,35 @@ Nuki::CmdResult NukiLock::retrieveLogEntries(const uint32_t startIndex, const ui } bool NukiLock::isBatteryCritical() { - return ((keyTurnerState.criticalBatteryState & (1 << 0)) != 0); + if(keyTurnerState.criticalBatteryState != 255) { + return ((keyTurnerState.criticalBatteryState & 1) == 1); + } + return false; } bool NukiLock::isKeypadBatteryCritical() { - if ((keyTurnerState.accessoryBatteryState & (1 << 7)) != 0) { - return ((keyTurnerState.accessoryBatteryState & (1 << 6)) != 0); + if(keyTurnerState.accessoryBatteryState != 255) { + if ((keyTurnerState.accessoryBatteryState & 1) == 1) { + return ((keyTurnerState.accessoryBatteryState & 3) == 3); + } + } + return false; +} + +bool NukiLock::isDoorSensorBatteryCritical() { + if(keyTurnerState.accessoryBatteryState != 255) { + if ((keyTurnerState.accessoryBatteryState & 4) == 4) { + return ((keyTurnerState.accessoryBatteryState & 12) == 12); + } } return false; } bool NukiLock::isBatteryCharging() { - return ((keyTurnerState.criticalBatteryState & (1 << 1)) != 0); + if(keyTurnerState.criticalBatteryState != 255) { + return ((keyTurnerState.criticalBatteryState & 2) == 2); + } + return false; } uint8_t NukiLock::getBatteryPerc() { @@ -734,7 +751,7 @@ void NukiLock::handleReturnMessage(Command returnCode, unsigned char* data, uint switch (returnCode) { case Command::KeyturnerStates : { printBuffer((byte*)data, dataLen, false, "keyturnerStates", debugNukiHexData, logger); - memcpy(&keyTurnerState, data, sizeof(keyTurnerState)); + memcpy(&keyTurnerState, data, dataLen); if (debugNukiReadableData) { logKeyturnerState(keyTurnerState, true, logger); } @@ -742,14 +759,14 @@ void NukiLock::handleReturnMessage(Command returnCode, unsigned char* data, uint } case Command::BatteryReport : { printBuffer((byte*)data, dataLen, false, "batteryReport", debugNukiHexData, logger); - memcpy(&batteryReport, data, sizeof(batteryReport)); + memcpy(&batteryReport, data, dataLen); if (debugNukiReadableData) { logBatteryReport(batteryReport, true, logger); } break; } case Command::Config : { - memcpy(&config, data, sizeof(config)); + memcpy(&config, data, dataLen); if (debugNukiReadableData) { logConfig(config, true, logger); } @@ -757,7 +774,7 @@ void NukiLock::handleReturnMessage(Command returnCode, unsigned char* data, uint break; } case Command::AdvancedConfig : { - memcpy(&advancedConfig, data, sizeof(advancedConfig)); + memcpy(&advancedConfig, data, dataLen); if (debugNukiReadableData) { logAdvancedConfig(advancedConfig, true, logger); } @@ -767,14 +784,14 @@ void NukiLock::handleReturnMessage(Command returnCode, unsigned char* data, uint case Command::TimeControlEntry : { printBuffer((byte*)data, dataLen, false, "timeControlEntry", debugNukiHexData, logger); TimeControlEntry timeControlEntry; - memcpy(&timeControlEntry, data, sizeof(timeControlEntry)); + memcpy(&timeControlEntry, data, dataLen); listOfTimeControlEntries.push_back(timeControlEntry); break; } case Command::LogEntry : { printBuffer((byte*)data, dataLen, false, "logEntry", debugNukiHexData, logger); LogEntry logEntry; - memcpy(&logEntry, data, sizeof(logEntry)); + memcpy(&logEntry, data, dataLen); listOfLogEntries.push_back(logEntry); if (debugNukiReadableData) { logLogEntry(logEntry, true, logger); diff --git a/src/NukiLock.h b/src/NukiLock.h index a72a3ea9..5fa5f743 100644 --- a/src/NukiLock.h +++ b/src/NukiLock.h @@ -435,6 +435,15 @@ class NukiLock : public Nuki::NukiBle { * @return true if critical */ bool isBatteryCritical(); + + /** + * @brief Returns door sensor battery critical state in case this is supported + * + * Note that `retrieveOpenerState()` needs to be called first to retrieve the needed data + * + * @return true if critical + */ + bool isDoorSensorBatteryCritical(); /** * @brief Returns keypad battery critical state in case this is supported diff --git a/src/NukiLockConstants.h b/src/NukiLockConstants.h index 6cdefa77..a9c12408 100644 --- a/src/NukiLockConstants.h +++ b/src/NukiLockConstants.h @@ -103,7 +103,8 @@ enum class Trigger : uint8_t { Automatic = 0x03, AutoLock = 0x06, HomeKit = 0xAB, - MQTT = 0xAC + MQTT = 0xAC, + Undefined = 0xFF }; @@ -118,12 +119,14 @@ enum class LockAction : uint8_t { ButtonNoAction = 0x5A, FobAction1 = 0x81, FobAction2 = 0x82, - FobAction3 = 0x83 + FobAction3 = 0x83, + Undefined = 0xFF }; enum class KeypadActionSource : uint8_t { - ArrowKey = 0x00, - Code = 0x01 + ArrowKey = 0x00, + Code = 0x01, + Fingerprint = 0x02 }; enum class KeypadAction : uint8_t { @@ -141,7 +144,8 @@ enum class ButtonPressAction : uint8_t { Lock = 0x03, Unlatch = 0x04, LockNgo = 0x05, - ShowStatus = 0x06 + ShowStatus = 0x06, + Unknown = 0xFF }; enum class CompletionStatus : uint8_t { @@ -172,14 +176,20 @@ struct __attribute__((packed)) KeyTurnerState { uint8_t currentTimeSecond; int16_t timeZoneOffset; uint8_t criticalBatteryState; - uint8_t configUpdateCount; - bool lockNgoTimer; - LockAction lastLockAction; - Trigger lastLockActionTrigger; - CompletionStatus lastLockActionCompletionStatus; + uint8_t configUpdateCount = 255; + uint8_t lockNgoTimer = 255; + LockAction lastLockAction = LockAction::Undefined; + Trigger lastLockActionTrigger = Trigger::Undefined; + CompletionStatus lastLockActionCompletionStatus = CompletionStatus::Unknown; DoorSensorState doorSensorState = DoorSensorState::Unavailable; - uint16_t nightModeActive; - uint8_t accessoryBatteryState; + uint8_t nightModeActive = 255; + uint8_t accessoryBatteryState = 255; + uint8_t network = 255; + uint8_t bleConnectionStrength = 255; + uint8_t wifiConnectionStrength = 255; + uint8_t wifi = 255; + uint8_t mqtt = 255; + uint8_t thread = 255; }; struct __attribute__((packed)) Config { @@ -204,18 +214,18 @@ struct __attribute__((packed)) Config { uint8_t fobAction1; uint8_t fobAction2; uint8_t fobAction3; - uint8_t singleLock; - AdvertisingMode advertisingMode; - uint8_t hasKeypad; - unsigned char firmwareVersion[3]; - unsigned char hardwareRevision[2]; - uint8_t homeKitStatus; - TimeZoneId timeZoneId; - uint8_t deviceType; - uint8_t network; - uint8_t hasKeypadV2; - uint8_t matterStatus; - uint8_t productVariant; + uint8_t singleLock = 255; + AdvertisingMode advertisingMode = AdvertisingMode::Unknown; + uint8_t hasKeypad = 255; + unsigned char firmwareVersion[3] = {0, 0 , 0}; + unsigned char hardwareRevision[2] = {0, 0}; + uint8_t homeKitStatus = 255; + TimeZoneId timeZoneId = TimeZoneId::None; + uint8_t deviceType = 255; + uint8_t network = 255; + uint8_t hasKeypadV2 = 255; + uint8_t matterStatus = 255; + uint8_t productVariant = 255; }; struct __attribute__((packed)) NewConfig { @@ -243,24 +253,26 @@ struct __attribute__((packed)) AdvancedConfig { int16_t lockedPositionOffsetDegrees; int16_t singleLockedPositionOffsetDegrees; int16_t unlockedToLockedTransitionOffsetDegrees; - uint8_t lockNgoTimeout; - ButtonPressAction singleButtonPressAction; - ButtonPressAction doubleButtonPressAction; - uint8_t detachedCylinder; - BatteryType batteryType; - uint8_t automaticBatteryTypeDetection; - uint8_t unlatchDuration; - uint16_t autoLockTimeOut; - uint8_t autoUnLockDisabled; - uint8_t nightModeEnabled; - unsigned char nightModeStartTime[2]; - unsigned char nightModeEndTime[2]; - uint8_t nightModeAutoLockEnabled; - uint8_t nightModeAutoUnlockDisabled; - uint8_t nightModeImmediateLockOnStart; - uint8_t autoLockEnabled; - uint8_t immediateAutoLockEnabled; - uint8_t autoUpdateEnabled; + uint8_t lockNgoTimeout = 255; + ButtonPressAction singleButtonPressAction = ButtonPressAction::Unknown; + ButtonPressAction doubleButtonPressAction = ButtonPressAction::Unknown; + uint8_t detachedCylinder = 255; + BatteryType batteryType = BatteryType::Unknown; + uint8_t automaticBatteryTypeDetection = 255; + uint8_t unlatchDuration = 255; + uint16_t autoLockTimeOut = -1; + uint8_t autoUnLockDisabled = 255; + uint8_t nightModeEnabled = 255; + unsigned char nightModeStartTime[2] = {0, 0}; + unsigned char nightModeEndTime[2] = {0, 0}; + uint8_t nightModeAutoLockEnabled = 255; + uint8_t nightModeAutoUnlockDisabled = 255; + uint8_t nightModeImmediateLockOnStart = 255; + uint8_t autoLockEnabled = 255; + uint8_t immediateAutoLockEnabled = 255; + uint8_t autoUpdateEnabled = 255; + uint8_t speedMode = 255; + uint8_t slowSpeedDuringNightMode = 255; }; struct __attribute__((packed)) NewAdvancedConfig { diff --git a/src/NukiLockUtils.cpp b/src/NukiLockUtils.cpp index 873e63ae..f3e292a3 100644 --- a/src/NukiLockUtils.cpp +++ b/src/NukiLockUtils.cpp @@ -557,6 +557,9 @@ void logKeyturnerState(KeyTurnerState keyTurnerState, bool debug, Print* Log) { logMessageVar("currentTimeSecond: %d", (unsigned int)keyTurnerState.currentTimeSecond, Log, 4); logMessageVar("timeZoneOffset: %d", (unsigned int)keyTurnerState.timeZoneOffset, Log, 4); logMessageVar("criticalBatteryState composed value: %d", (unsigned int)keyTurnerState.criticalBatteryState, Log, 4); + logMessageVar("criticalBatteryState: %d", (unsigned int)(((unsigned int)keyTurnerState.criticalBatteryState) == 1 ? 1 : 0), Log, 4); + logMessageVar("batteryCharging: %d", (unsigned int)(((unsigned int)keyTurnerState.criticalBatteryState & 2) == 2 ? 1 : 0), Log, 4); + logMessageVar("batteryPercent: %d", (unsigned int)((keyTurnerState.criticalBatteryState & 0b11111100) >> 1), Log, 4); logMessageVar("configUpdateCount: %d", (unsigned int)keyTurnerState.configUpdateCount, Log, 4); logMessageVar("lockNgoTimer: %d", (unsigned int)keyTurnerState.lockNgoTimer, Log, 4); logLockAction((LockAction)keyTurnerState.lastLockAction, debug, Log); @@ -564,8 +567,33 @@ void logKeyturnerState(KeyTurnerState keyTurnerState, bool debug, Print* Log) { logCompletionStatus(keyTurnerState.lastLockActionCompletionStatus, debug, Log); logMessageVar("doorSensorState: %d", (unsigned int)keyTurnerState.doorSensorState, Log, 4); logMessageVar("nightModeActive: %d", (unsigned int)keyTurnerState.nightModeActive, Log, 4); - logMessageVar("Keypad bat critical feature supported: %d", (unsigned int)keyTurnerState.accessoryBatteryState & 1, Log, 4); - logMessageVar("Keypad Battery Critical: %d", (unsigned int)keyTurnerState.accessoryBatteryState & 2, Log, 4); + logMessageVar("accessoryBatteryState composed value: %d", (unsigned int)keyTurnerState.accessoryBatteryState, Log, 4); + logMessageVar("Keypad bat critical feature supported: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 1) == 1 ? 1 : 0), Log, 4); + logMessageVar("Keypad Battery Critical: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 3) == 3 ? 1 : 0), Log, 4); + logMessageVar("Doorsensor bat critical feature supported: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 4) == 4 ? 1 : 0), Log, 4); + logMessageVar("Doorsensor Battery Critical: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 12) == 12 ? 1 : 0), Log, 4); + logMessageVar("network composed value: %d", (unsigned int)keyTurnerState.network, Log, 4); + logMessageVar("remoteAccessEnabled: %d", (unsigned int)(((keyTurnerState.network & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("bridgePaired: %d", (unsigned int)((((keyTurnerState.network >> 1) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("sseConnectedViaWifi: %d", (unsigned int)((((keyTurnerState.network >> 2) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("sseConnectionEstablished: %d", (unsigned int)((((keyTurnerState.network >> 3) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("isSseConnectedViaThread: %d", (unsigned int)((((keyTurnerState.network >> 4) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("threadSseUplinkEnabledByUser: %d", (unsigned int)((((keyTurnerState.network >> 5) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("nat64AvailableViaThread: %d", (unsigned int)((((keyTurnerState.network >> 6) & 1) == 1) ? 1 : 0), Log, 4); + logMessageVar("bleConnectionStrength: %d", (unsigned int)keyTurnerState.bleConnectionStrength, Log, 4); + logMessageVar("wifiConnectionStrength: %d", (unsigned int)keyTurnerState.wifiConnectionStrength, Log, 4); + logMessageVar("wifi composed value: %d", (unsigned int)keyTurnerState.wifi, Log, 4); + logMessageVar("wifiStatus: %d", (unsigned int)(keyTurnerState.wifi & 3), Log, 4); + logMessageVar("sseStatus: %d", (unsigned int)((keyTurnerState.wifi >> 2) & 3), Log, 4); + logMessageVar("wifiQuality: %d", (unsigned int)((keyTurnerState.wifi >> 4) & 15), Log, 4); + logMessageVar("mqtt composed value: %d", (unsigned int)keyTurnerState.mqtt, Log, 4); + logMessageVar("mqttStatus: %d", (unsigned int)(keyTurnerState.mqtt & 3), Log, 4); + logMessageVar("mqttConnectionChannel: %d", (unsigned int)((keyTurnerState.mqtt >> 2) & 1), Log, 4); + logMessageVar("thread composed value: %d", (unsigned int)keyTurnerState.thread, Log, 4); + logMessageVar("threadConnectionStatus: %d", (unsigned int)(keyTurnerState.thread & 3), Log, 4); + logMessageVar("threadSseStatus: %d", (unsigned int)((keyTurnerState.thread >> 2) & 3), Log, 4); + logMessageVar("isCommissioningModeActive: %d", (unsigned int)(((unsigned int)keyTurnerState.thread & 16) != 0 ? 1 : 0), Log, 4); + logMessageVar("isWifiDisabledBecauseOfThread: %d", (unsigned int)(((unsigned int)keyTurnerState.thread & 32) != 0 ? 1 : 0), Log, 4); } } @@ -666,6 +694,8 @@ void logAdvancedConfig(AdvancedConfig advancedConfig, bool debug, Print* Log) { logMessageVar("autoLockEnabled :%d", (unsigned int)advancedConfig.autoLockEnabled, Log, 4); logMessageVar("immediateAutoLockEnabled :%d", (unsigned int)advancedConfig.immediateAutoLockEnabled, Log, 4); logMessageVar("autoUpdateEnabled :%d", (unsigned int)advancedConfig.autoUpdateEnabled, Log, 4); + logMessageVar("speedMode :%d", (unsigned int)advancedConfig.speedMode, Log, 4); + logMessageVar("slowSpeedDuringNightMode :%d", (unsigned int)advancedConfig.slowSpeedDuringNightMode, Log, 4); } } diff --git a/src/NukiOpener.cpp b/src/NukiOpener.cpp index 79c1b1e3..a368380d 100644 --- a/src/NukiOpener.cpp +++ b/src/NukiOpener.cpp @@ -579,6 +579,15 @@ bool NukiOpener::isBatteryCritical() { return openerState.criticalBatteryState & 1; } +bool NukiOpener::isKeypadBatteryCritical() { + if (openerState.accessoryBatteryState != 255) { + if ((openerState.accessoryBatteryState & 1) == 1) { + return ((openerState.accessoryBatteryState & 3) == 3); + } + } + return false; +} + const ErrorCode NukiOpener::getLastError() const { return (ErrorCode)errorCode; } @@ -669,7 +678,7 @@ void NukiOpener::handleReturnMessage(Command returnCode, unsigned char* data, ui switch (returnCode) { case Command::KeyturnerStates : { printBuffer((byte*)data, dataLen, false, "keyturnerStates", debugNukiHexData, logger); - memcpy(&openerState, data, sizeof(openerState)); + memcpy(&openerState, data, dataLen); if (debugNukiReadableData) { logKeyturnerState(openerState, true, logger); } @@ -677,14 +686,14 @@ void NukiOpener::handleReturnMessage(Command returnCode, unsigned char* data, ui } case Command::BatteryReport : { printBuffer((byte*)data, dataLen, false, "batteryReport", debugNukiHexData, logger); - memcpy(&batteryReport, data, sizeof(batteryReport)); + memcpy(&batteryReport, data, dataLen); if (debugNukiReadableData) { logBatteryReport(batteryReport, true, logger); } break; } case Command::Config : { - memcpy(&config, data, sizeof(config)); + memcpy(&config, data, dataLen); if (debugNukiReadableData) { logConfig(config, true, logger); } @@ -692,7 +701,7 @@ void NukiOpener::handleReturnMessage(Command returnCode, unsigned char* data, ui break; } case Command::AdvancedConfig : { - memcpy(&advancedConfig, data, sizeof(advancedConfig)); + memcpy(&advancedConfig, data, dataLen); if (debugNukiReadableData) { logAdvancedConfig(advancedConfig, true, logger); } @@ -702,14 +711,14 @@ void NukiOpener::handleReturnMessage(Command returnCode, unsigned char* data, ui case Command::TimeControlEntry : { printBuffer((byte*)data, dataLen, false, "timeControlEntry", debugNukiHexData, logger); TimeControlEntry timeControlEntry; - memcpy(&timeControlEntry, data, sizeof(timeControlEntry)); + memcpy(&timeControlEntry, data, dataLen); listOfTimeControlEntries.push_back(timeControlEntry); break; } case Command::LogEntry : { printBuffer((byte*)data, dataLen, false, "logEntry", debugNukiHexData, logger); LogEntry logEntry; - memcpy(&logEntry, data, sizeof(logEntry)); + memcpy(&logEntry, data, dataLen); listOfLogEntries.push_back(logEntry); if (debugNukiReadableData) { logLogEntry(logEntry, true, logger); diff --git a/src/NukiOpener.h b/src/NukiOpener.h index 14aba8b7..6024a3f6 100644 --- a/src/NukiOpener.h +++ b/src/NukiOpener.h @@ -10,7 +10,7 @@ class NukiOpener : public Nuki::NukiBle { NukiOpener(const std::string& deviceName, const uint32_t deviceId); /** - * @brief Sends lock action cmd via BLE to the lock + * @brief Sends lock action cmd via BLE to the opener * * @param lockAction * @param nukiAppId 0 = App, 1 = Bridge, 2 = Fob, 3 = Keypad @@ -24,7 +24,7 @@ class NukiOpener : public Nuki::NukiBle { /** - * @brief Requests keyturner state from Lock via BLE + * @brief Requests keyturner state from Opener via BLE * * @param state Nuki api based datatype to store the retrieved keyturnerstate */ @@ -39,7 +39,7 @@ class NukiOpener : public Nuki::NukiBle { /** - * @brief Requests battery status from Lock via BLE + * @brief Requests battery status from Opener via BLE * * @param retrievedBatteryReport Nuki api based datatype to store the retrieved battery status */ @@ -47,32 +47,32 @@ class NukiOpener : public Nuki::NukiBle { /** - * @brief Gets the current config from the lock, updates the name parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the name parameter and sends the + * new config to the opener via BLE * * @param name max 32 character name */ Nuki::CmdResult setName(const std::string& name); /** - * @brief Gets the current config from the lock, updates the latitude parameter and sends the new - * config to the lock via BLE + * @brief Gets the current config from the opener, updates the latitude parameter and sends the new + * config to the opener via BLE * * @param degrees the desired latitude */ Nuki::CmdResult setLatitude(const float degrees); /** - * @brief Gets the current config from the lock, updates the longitude parameter and sends the new - * config to the lock via BLE + * @brief Gets the current config from the opener, updates the longitude parameter and sends the new + * config to the opener via BLE * * @param degrees the desired longitude */ Nuki::CmdResult setLongitude(const float degrees); /** - * @brief Gets the current config from the lock, updates the given fob action parameter and sends the new - * config to the lock via BLE + * @brief Gets the current config from the opener, updates the given fob action parameter and sends the new + * config to the opener via BLE * * @param fobActionNr the fob action to change (1 = single press, 2 = double press, 3 = triple press) * @param fobAction the desired fob action setting @@ -80,192 +80,192 @@ class NukiOpener : public Nuki::NukiBle { Nuki::CmdResult setFobAction(const uint8_t fobActionNr, const uint8_t fobAction); /** - * @brief Gets the current config from the lock, updates the operating mode parameter and sends the new - * config to the lock via BLE + * @brief Gets the current config from the opener, updates the operating mode parameter and sends the new + * config to the opener via BLE * * @param opmode the desired operating mode */ Nuki::CmdResult setOperatingMode(const uint8_t opmode); /** - * @brief Gets the current config from the lock, updates the dst parameter and sends the new - * config to the lock via BLE + * @brief Gets the current config from the opener, updates the dst parameter and sends the new + * config to the opener via BLE * * @param enable The desired daylight saving time mode. false disabled, true european */ Nuki::CmdResult enableDst(const bool enable); /** - * @brief Gets the current config from the lock, updates the timezone offset parameter and - * sends the new config to the lock via BLE + * @brief Gets the current config from the opener, updates the timezone offset parameter and + * sends the new config to the opener via BLE * * @param minutes The timezone offset (UTC) in minutes */ Nuki::CmdResult setTimeZoneOffset(const int16_t minutes); /** - * @brief Gets the current config from the lock, updates the timezone id parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the timezone id parameter and sends the + * new config to the opener via BLE * * @param timeZoneId The id of the current timezone or 0xFFFF if timezones are not supported */ Nuki::CmdResult setTimeZoneId(const TimeZoneId timeZoneId); /** - * @brief Gets the current config from the lock, updates the enable button parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the enable button parameter and sends the + * new config to the opener via BLE * * @param enable true if button enabled */ Nuki::CmdResult enableButton(const bool enable); /** - * @brief Gets the current config from the lock, updates the intercom id parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the intercom id parameter and sends the + * new config to the opener via BLE * * @param intercomID the desired database ID of the connected intercom */ Nuki::CmdResult setIntercomID(const uint16_t intercomID); /** - * @brief Gets the current config from the lock, updates the bus mode switch parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the bus mode switch parameter and sends the + * new config to the opener via BLE * * @param busModeSwitch true for analogue mode, false for data mode */ Nuki::CmdResult setBusModeSwitch(const bool busModeSwitch); /** - * @brief Gets the current config from the lock, updates the short circuit duration parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the short circuit duration parameter and sends the + * new config to the opener via BLE * * @param duration the desired duration of the short circuit for BUS mode switching in ms */ Nuki::CmdResult setShortCircuitDuration(const uint16_t duration); /** - * @brief Gets the current config from the lock, updates the electric strike delay parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the electric strike delay parameter and sends the + * new config to the opener via BLE * * @param delay the desired electric strike delay in ms in case of an electric strike actuation by RTO */ Nuki::CmdResult setElectricStrikeDelay(const uint16_t delay); /** - * @brief Gets the current config from the lock, updates the random electric strike delay parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the random electric strike delay parameter and sends the + * new config to the opener via BLE * * @param enable true if random electric strike delay enabled */ Nuki::CmdResult enableRandomElectricStrikeDelay(const bool enable); /** - * @brief Gets the current config from the lock, updates the electric strike duration parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the electric strike duration parameter and sends the + * new config to the opener via BLE * * @param duration the desired duration in ms of electric strike actuation. */ Nuki::CmdResult setElectricStrikeDuration(const uint16_t duration); /** - * @brief Gets the current config from the lock, updates the disable rto after ring parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the disable rto after ring parameter and sends the + * new config to the opener via BLE * * @param disable true if RTO should be disabled after ring */ Nuki::CmdResult disableRtoAfterRing(const bool disable); /** - * @brief Gets the current config from the lock, updates the rto timeout parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the rto timeout parameter and sends the + * new config to the opener via BLE * * @param timeout the desired timeout for RTO in minutes */ Nuki::CmdResult setRtoTimeout(const uint8_t timeout); /** - * @brief Gets the current config from the lock, updates the doorbell suppression parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the doorbell suppression parameter and sends the + * new config to the opener via BLE * * @param suppression the desired setting for doorbell suppression (0 = Off, 1 = CM, 2 = RTO, 3 = CM & RTO, 4 = Ring, 5 = CM & Ring, 6 = RTO & Ring, 7 = CM & RTO & Ring) */ Nuki::CmdResult setDoorbellSuppression(const uint8_t suppression); /** - * @brief Gets the current config from the lock, updates the doorbell suppression duration parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the doorbell suppression duration parameter and sends the + * new config to the opener via BLE * * @param duration the duration in ms of doorbell suppression (only in Operating mode 0x02,0x03,0x04,0x05,0x07,0x08 digital Intercom) */ Nuki::CmdResult setDoorbellSuppressionDuration(const uint16_t duration); /** - * @brief Gets the current config from the lock, updates the sound ring parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the sound ring parameter and sends the + * new config to the opener via BLE * * @param sound the desired sound setting for ring (0 = No Sound, 1 = Sound1, 2 = Sound2, 3 = Sound3) */ Nuki::CmdResult setSoundRing(const uint8_t sound); /** - * @brief Gets the current config from the lock, updates the sound open parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the sound open parameter and sends the + * new config to the opener via BLE * * @param sound the desired sound setting for open (0 = No Sound, 1 = Sound1, 2 = Sound2, 3 = Sound3) */ Nuki::CmdResult setSoundOpen(const uint8_t sound); /** - * @brief Gets the current config from the lock, updates the sound RTO parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the sound RTO parameter and sends the + * new config to the opener via BLE * * @param sound the desired sound setting for RTO (0 = No Sound, 1 = Sound1, 2 = Sound2, 3 = Sound3) */ Nuki::CmdResult setSoundRto(const uint8_t sound); /** - * @brief Gets the current config from the lock, updates the sound continuous mode parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the sound continuous mode parameter and sends the + * new config to the opener via BLE * * @param sound the desired sound setting for continuous mode (0 = No Sound, 1 = Sound1, 2 = Sound2, 3 = Sound3) */ Nuki::CmdResult setSoundCm(const uint8_t sound); /** - * @brief Gets the current config from the lock, updates the enable sound confirmation parameter and sends the - * new config to the lock via BLE + * @brief Gets the current config from the opener, updates the enable sound confirmation parameter and sends the + * new config to the opener via BLE * * @param enable true if sound confirmation enabled */ Nuki::CmdResult enableSoundConfirmation(const bool enable); /** - * @brief Gets the current advanced config from the lock, updates the single button press action - * parameter and sends the new advanced config to the lock via BLE + * @brief Gets the current advanced config from the opener, updates the single button press action + * parameter and sends the new advanced config to the opener via BLE * * @param action the desired action for a single button press */ Nuki::CmdResult setSingleButtonPressAction(const ButtonPressAction action); /** - * @brief Gets the current advanced config from the lock, updates the double button press action - * parameter and sends the new advanced config to the lock via BLE + * @brief Gets the current advanced config from the opener, updates the double button press action + * parameter and sends the new advanced config to the opener via BLE * * @param action the desired action for a double button press */ Nuki::CmdResult setDoubleButtonPressAction(const ButtonPressAction action); /** - * @brief Gets the current advanced config from the lock, updates the battery type parameter and - * sends the new advanced config to the lock via BLE + * @brief Gets the current advanced config from the opener, updates the battery type parameter and + * sends the new advanced config to the opener via BLE * - * @param type The type of the batteries present in the smart lock. + * @param type The type of the batteries present in the smart opener. */ Nuki::CmdResult setBatteryType(const BatteryType type); /** - * @brief Gets the current advanced config from the lock, updates the enable battery type - * detection parameter and sends the new advanced config to the lock via BLE + * @brief Gets the current advanced config from the opener, updates the enable battery type + * detection parameter and sends the new advanced config to the opener via BLE * * @param enable true if the automatic detection of the battery type is enabled */ @@ -273,9 +273,9 @@ class NukiOpener : public Nuki::NukiBle { /** - * @brief Sets the lock ability to pair with other devices (can be used to prevent unauthorized pairing) - * Gets the current config from the lock, updates the pairing parameter and sends the new config to the lock via BLE - * (CAUTION: if pairing is set to false and credentials are deleted a factory reset of the lock needs to be performed + * @brief Sets the opener ability to pair with other devices (can be used to prevent unauthorized pairing) + * Gets the current config from the opener, updates the pairing parameter and sends the new config to the opener via BLE + * (CAUTION: if pairing is set to false and credentials are deleted a factory reset of the opener needs to be performed * before pairing is possible again) * * @param enable true if allowed to pair with other devices @@ -283,23 +283,23 @@ class NukiOpener : public Nuki::NukiBle { Nuki::CmdResult enablePairing(const bool enable); /** - * @brief Gets the current config from the lock, updates the whether or not the flashing - * LED should be enabled to signal an unlocked door. And sends the new config to the lock via BLE + * @brief Gets the current config from the opener, updates the whether or not the flashing + * LED should be enabled to signal an unlocked door. And sends the new config to the opener via BLE * * @param enable true if led enabled */ CmdResult enableLedFlash(const bool enable); /** - * @brief Gets the current config from the lock, and updates the sound level. + * @brief Gets the current config from the opener, and updates the sound level. * * @param enable true if led enabled */ CmdResult setSoundLevel(const uint8_t value); /** - * @brief Gets the current config from the lock, updates the advertising frequency parameter - * and sends the new config to the lock via BLE + * @brief Gets the current config from the opener, updates the advertising frequency parameter + * and sends the new config to the opener via BLE * * @param mode 0x00 Automatic, 0x01 Normal, 0x02 Slow, 0x03 Slowest (~400ms till ~1s) */ @@ -307,7 +307,7 @@ class NukiOpener : public Nuki::NukiBle { /** - * @brief Sends a new time(d) control entry via BLE to the lock. + * @brief Sends a new time(d) control entry via BLE to the opener. * This entry is independant of keypad or authorization entries, it will execute the * defined action at the defined time in the newTimeControlEntry * @@ -316,7 +316,7 @@ class NukiOpener : public Nuki::NukiBle { Nuki::CmdResult addTimeControlEntry(NewTimeControlEntry newTimecontrolEntry); /** - * @brief Sends an updated time(d) control entry via BLE to the lock. + * @brief Sends an updated time(d) control entry via BLE to the opener. * (see addTimeControlEntry()) * * @param TimeControlEntry Nuki api based datatype to send. @@ -325,7 +325,7 @@ class NukiOpener : public Nuki::NukiBle { Nuki::CmdResult updateTimeControlEntry(TimeControlEntry TimeControlEntry); /** - * @brief Deletes a time(d) control entry via BLE to the lock. + * @brief Deletes a time(d) control entry via BLE to the opener. * (see addTimeControlEntry()) * * @param entryId The ID to be deleted, can be retrieved via retrieveTimeControlEntries() @@ -333,7 +333,7 @@ class NukiOpener : public Nuki::NukiBle { Nuki::CmdResult removeTimeControlEntry(uint8_t entryId); /** - * @brief Request the lock via BLE to send the existing time control entries + * @brief Request the opener via BLE to send the existing time control entries * */ Nuki::CmdResult retrieveTimeControlEntries(); @@ -353,25 +353,25 @@ class NukiOpener : public Nuki::NukiBle { void getLogEntries(std::list* requestedLogEntries); /** - * @brief Request the lock via BLE to send the log entries + * @brief Request the opener via BLE to send the log entries * * @param startIndex Startindex of first log msg to be send * @param count The number of log entries to be read, starting at the specified start index. * @param sortOrder The desired sort order - * @param totalCount true if a Log Entry Count is requested from the lock + * @param totalCount true if a Log Entry Count is requested from the opener */ Nuki::CmdResult retrieveLogEntries(const uint32_t startIndex, const uint16_t count, const uint8_t sortOrder, const bool totalCount); /** - * @brief Requests config from Lock via BLE + * @brief Requests config from Opener via BLE * * @param retrievedConfig Nuki api based datatype to store the retrieved config */ Nuki::CmdResult requestConfig(Config* retrievedConfig); /** - * @brief Requests advanced config from Lock via BLE + * @brief Requests advanced config from Opener via BLE * * @param retrievedAdvancedConfig Nuki api based datatype to store the retrieved advanced config */ @@ -388,7 +388,16 @@ class NukiOpener : public Nuki::NukiBle { bool isBatteryCritical(); /** - * @brief Get the Last Error code received from the lock + * @brief Returns keypad battery critical state in case this is supported + * + * Note that `retrieveOpenerState()` needs to be called first to retrieve the needed data + * + * @return true if critical + */ + bool isKeypadBatteryCritical(); + + /** + * @brief Get the Last Error code received from the opener */ const ErrorCode getLastError() const; diff --git a/src/NukiOpenerConstants.h b/src/NukiOpenerConstants.h index ac4e9e85..b599ff17 100644 --- a/src/NukiOpenerConstants.h +++ b/src/NukiOpenerConstants.h @@ -24,16 +24,15 @@ const NimBLEUUID openerGdioUUID = NimBLEUUID("a92ae101-5501-11e4-916c-0800200c9 const NimBLEUUID openerUserDataUUID = NimBLEUUID("a92ae202-5501-11e4-916c-0800200c9a66"); enum class LockAction : uint8_t { - ActivateRTO = 0x01, - DeactivateRTO = 0x02, + ActivateRTO = 0x01, + DeactivateRTO = 0x02, ElectricStrikeActuation = 0x03, - - ActivateCM = 0x04, - DeactivateCM = 0x05, - - FobAction1 = 0x81, - FobAction2 = 0x82, - FobAction3 = 0x83 + ActivateCM = 0x04, + DeactivateCM = 0x05, + FobAction1 = 0x81, + FobAction2 = 0x82, + FobAction3 = 0x83, + Undefined = 0xFF }; @@ -48,49 +47,49 @@ enum class ErrorCode : uint8_t { ERROR_BAD_CRC = 0xFD, ERROR_BAD_LENGTH = 0xFE, ERROR_UNKNOWN = 0xFF, - P_ERROR_NOT_PAIRING = 0x10, + P_ERROR_NOT_PAIRING = 0x10, P_ERROR_BAD_AUTHENTICATOR = 0x11, P_ERROR_BAD_PARAMETER = 0x12, P_ERROR_MAX_USER = 0x13, - K_ERROR_NOT_AUTHORIZED = 0x20, - K_ERROR_BAD_PIN = 0x21, + K_ERROR_NOT_AUTHORIZED = 0x20, + K_ERROR_BAD_PIN = 0x21, K_ERROR_BAD_NONCE = 0x22, K_ERROR_BAD_PARAMETER = 0x23, - K_ERROR_INVALID_AUTH_ID = 0x24, + K_ERROR_INVALID_AUTH_ID = 0x24, K_ERROR_DISABLED = 0x25, - K_ERROR_REMOTE_NOT_ALLOWED = 0x26, + K_ERROR_REMOTE_NOT_ALLOWED = 0x26, K_ERROR_TIME_NOT_ALLOWED = 0x27, K_ERROR_TOO_MANY_PIN_ATTEMPTS = 0x28, K_ERROR_TOO_MANY_ENTRIES = 0x29, - K_ERROR_CODE_ALREADY_EXISTS = 0x2A, + K_ERROR_CODE_ALREADY_EXISTS = 0x2A, K_ERROR_CODE_INVALID = 0x2B, K_ERROR_CODE_INVALID_TIMEOUT_1 = 0x2C, K_ERROR_CODE_INVALID_TIMEOUT_2 = 0x2D, - K_ERROR_CODE_INVALID_TIMEOUT_3 = 0x2E, - K_ERROR_AUTO_UNLOCK_TOO_RECENT = 0x40, + K_ERROR_CODE_INVALID_TIMEOUT_3 = 0x2E, + K_ERROR_AUTO_UNLOCK_TOO_RECENT = 0x40, K_ERROR_POSITION_UNKNOWN = 0x41, K_ERROR_MOTOR_BLOCKED = 0x42, - K_ERROR_CLUTCH_FAILURE = 0x43, + K_ERROR_CLUTCH_FAILURE = 0x43, K_ERROR_MOTOR_TIMEOUT = 0x44, K_ERROR_BUSY = 0x45, K_ERROR_CANCELED = 0x46, - K_ERROR_NOT_CALIBRATED = 0x47, + K_ERROR_NOT_CALIBRATED = 0x47, K_ERROR_MOTOR_POSITION_LIMIT = 0x48, K_ERROR_MOTOR_LOW_VOLTAGE = 0x49, - K_ERROR_MOTOR_POWER_FAILURE = 0x4A, + K_ERROR_MOTOR_POWER_FAILURE = 0x4A, K_ERROR_CLUTCH_POWER_FAILURE = 0x4B, - K_ERROR_VOLTAGE_TOO_LOW = 0x4C, - K_ERROR_FIRMWARE_UPDATE_NEEDED = 0x4D + K_ERROR_VOLTAGE_TOO_LOW = 0x4C, + K_ERROR_FIRMWARE_UPDATE_NEEDED = 0x4D }; enum class CompletionStatus : uint8_t { - Success = 0x00, - Canceled = 0x02, - TooRecent = 0x03, - Busy = 0x04, + Success = 0x00, + Canceled = 0x02, + TooRecent = 0x03, + Busy = 0x04, Incomplete = 0x08, OtherError = 0xFE, - Unknown = 0xFF + Unknown = 0xFF }; @@ -104,29 +103,30 @@ enum class State : uint8_t { enum class LockState : uint8_t { Uncalibrated = 0x00, - Locked = 0x01, - RTOactive = 0x03, - Open = 0x05, - Opening = 0x07, - Undefined = 0xFF + Locked = 0x01, + RTOactive = 0x03, + Open = 0x05, + Opening = 0x07, + Undefined = 0xFF }; enum class Trigger : uint8_t { System = 0x00, Manual = 0x01, Button = 0x02, - Automatic = 0x03 + Automatic = 0x03, + Undefined = 0xFF }; enum class ButtonPressAction : uint8_t { - NoAction = 0x00, - ToggleRTO = 0x01, - ActivateRTO = 0x02, + NoAction = 0x00, + ToggleRTO = 0x01, + ActivateRTO = 0x02, DeactivateRTO = 0x03, - ToggleCM = 0x04, - ActivateCM = 0x05, - DectivateCM = 0x06, - Open = 0x07 + ToggleCM = 0x04, + ActivateCM = 0x05, + DectivateCM = 0x06, + Open = 0x07 }; struct __attribute__((packed)) OpenerState { @@ -141,12 +141,18 @@ struct __attribute__((packed)) OpenerState { uint8_t currentTimeSecond; int16_t timeZoneOffset; uint8_t criticalBatteryState; - uint8_t configUpdateCount; - uint8_t ringToOpenTimer; - LockAction lastLockAction; - Trigger lastLockActionTrigger; - CompletionStatus lastLockActionCompletionStatus; - DoorSensorState doorSensorState = DoorSensorState::Unavailable; + uint8_t configUpdateCount = 255; + uint8_t ringToOpenTimer = 255; + LockAction lastLockAction = LockAction::Undefined; + Trigger lastLockActionTrigger = Trigger::Undefined; + CompletionStatus lastLockActionCompletionStatus = CompletionStatus::Unknown; + uint16_t unused1 = 255; + uint8_t unused2 = 255; + uint8_t unused3 = 255; + uint8_t unused4 = 255; + uint8_t unused5 = 255; + uint8_t unused6 = 255; + uint8_t accessoryBatteryState = 255; }; struct __attribute__((packed)) Config { @@ -173,10 +179,10 @@ struct __attribute__((packed)) Config { uint8_t operatingMode; AdvertisingMode advertisingMode; uint8_t hasKeypad; - unsigned char firmwareVersion[3]; - unsigned char hardwareRevision[2]; + unsigned char firmwareVersion[3] = {0, 0, 0}; + unsigned char hardwareRevision[2] = {0}; TimeZoneId timeZoneId; - uint8_t hasKeypadV2; + uint8_t hasKeypadV2 = 255; }; struct __attribute__((packed)) NewConfig { @@ -219,6 +225,7 @@ struct __attribute__((packed)) AdvancedConfig { ButtonPressAction doubleButtonPressAction; Nuki::BatteryType batteryType; uint8_t automaticBatteryTypeDetection; + uint8_t autoUpdateEnabled = 255; }; struct __attribute__((packed)) NewAdvancedConfig { @@ -403,42 +410,6 @@ inline void completionStatusToString(const CompletionStatus status, char* str) { default: strcpy(str, "undefined"); break; - - } -} - -inline void doorSensorStateToString(const DoorSensorState state, char* str) { - switch (state) { - case DoorSensorState::Unavailable: - strcpy(str, "unavailable"); - break; - case DoorSensorState::Deactivated: - strcpy(str, "deactivated"); - break; - case DoorSensorState::DoorClosed: - strcpy(str, "doorClosed"); - break; - case DoorSensorState::DoorOpened: - strcpy(str, "doorOpened"); - break; - case DoorSensorState::DoorStateUnknown: - strcpy(str, "doorStateUnknown"); - break; - case DoorSensorState::Calibrating: - strcpy(str, "calibrating"); - break; - case DoorSensorState::Uncalibrated: - strcpy(str, "uncalibrated"); - break; - case DoorSensorState::Tampered: - strcpy(str, "tampered"); - break; - case DoorSensorState::Unknown: - strcpy(str, "unknown"); - break; - default: - strcpy(str, "undefined"); - break; } } diff --git a/src/NukiOpenerUtils.cpp b/src/NukiOpenerUtils.cpp index c2099810..a822d2a0 100644 --- a/src/NukiOpenerUtils.cpp +++ b/src/NukiOpenerUtils.cpp @@ -160,6 +160,7 @@ void logConfig(Config config, bool debug, Print* Log) { if (debug) { logMessageVar("nukiId :%d", (unsigned int)config.nukiId, Log, 4); logMessageVar("name :%s", (const char*)config.name, Log, 4); + logMessageVar("capabilities :%s", (unsigned int)config.capabilities, Log, 4); logMessageVar("latitude :%f", (const float)config.latitude, Log, 4); logMessageVar("longitude :%f", (const float)config.longitude, Log, 4); logMessageVar("pairingEnabled :%d", (unsigned int)config.pairingEnabled, Log, 4); @@ -176,6 +177,7 @@ void logConfig(Config config, bool debug, Print* Log) { logMessageVar("fobAction1 :%d", (unsigned int)config.fobAction1, Log, 4); logMessageVar("fobAction2 :%d", (unsigned int)config.fobAction2, Log, 4); logMessageVar("fobAction3 :%d", (unsigned int)config.fobAction3, Log, 4); + logMessageVar("operatingMode :%d", (unsigned int)config.operatingMode, Log, 4); logMessageVar("advertisingMode :%d", (unsigned int)config.advertisingMode, Log, 4); logMessageVar("hasKeypad :%d", (unsigned int)config.hasKeypad, Log, 4); if (Log == nullptr) { @@ -525,8 +527,9 @@ void logKeyturnerState(OpenerState keyTurnerState, bool debug, Print* Log) { logLockAction((LockAction)keyTurnerState.lastLockAction, debug, Log); logMessageVar("lastLockActionTrigger: %d", (unsigned int)keyTurnerState.lastLockActionTrigger, Log, 4); logCompletionStatus(keyTurnerState.lastLockActionCompletionStatus, debug, Log); - logMessageVar("doorSensorState: %d", (unsigned int)keyTurnerState.doorSensorState, Log, 4); - } + logMessageVar("Keypad bat critical feature supported: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 1) == 1 ? 1 : 0), Log, 4); + logMessageVar("Keypad Battery Critical: %d", (unsigned int)(((unsigned int)keyTurnerState.accessoryBatteryState & 3) == 3 ? 1 : 0), Log, 4); + } } void logBatteryReport(BatteryReport batteryReport, bool debug, Print* Log) {