Files
nuki_hub/src/NukiOfficial.cpp
2025-08-13 17:04:20 +02:00

397 lines
12 KiB
C++

#include <cstring>
#include "NukiOfficial.h"
#include "Logger.h"
#include "PreferencesKeys.h"
#include "../lib/nuki_ble/src/NukiLockUtils.h"
#include <stdlib.h>
#include <ctype.h>
NukiOfficial::NukiOfficial(Preferences *preferences)
{
offEnabled = preferences->getBool(preference_official_hybrid_enabled, false);
_disableNonJSON = preferences->getBool(preference_disable_non_json, false);
}
void NukiOfficial::setUid(const uint32_t& uid)
{
char uidString[20];
itoa(uid, uidString, 16);
for(char* c=uidString; *c=toupper(*c); ++c);
strcpy(mqttPath, "nuki/");
strcat(mqttPath, uidString);
offTopics.reserve(10);
//_offTopics.push_back(mqtt_topic_official_mode);
offTopics.push_back((char*)mqtt_topic_official_state);
offTopics.push_back((char*)mqtt_topic_official_batteryCritical);
offTopics.push_back((char*)mqtt_topic_official_batteryChargeState);
offTopics.push_back((char*)mqtt_topic_official_batteryCharging);
offTopics.push_back((char*)mqtt_topic_official_keypadBatteryCritical);
offTopics.push_back((char*)mqtt_topic_official_doorsensorState);
offTopics.push_back((char*)mqtt_topic_official_doorsensorBatteryCritical);
offTopics.push_back((char*)mqtt_topic_official_connected);
offTopics.push_back((char*)mqtt_topic_official_commandResponse);
offTopics.push_back((char*)mqtt_topic_official_lockActionEvent);
}
void NukiOfficial::setPublisher(NukiPublisher *publisher)
{
_publisher = publisher;
}
const char *NukiOfficial::getMqttPath() const
{
return mqttPath;
}
void NukiOfficial::buildMqttPath(const char *path, char *outPath)
{
int offset = 0;
char inPath[181] = {0};
memcpy(inPath, mqttPath, sizeof(mqttPath));
for(const char& c : inPath)
{
if(c == 0x00)
{
break;
}
outPath[offset] = c;
++offset;
}
int i=0;
while(outPath[i] != 0x00)
{
outPath[offset] = path[i];
++i;
++offset;
}
outPath[i+1] = 0x00;
}
bool NukiOfficial::comparePrefixedPath(const char *fullPath, const char *subPath)
{
char prefixedPath[500];
buildMqttPath(subPath, prefixedPath);
return strcmp(fullPath, prefixedPath) == 0;
}
void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value)
{
char str[50];
bool publishBatteryJson = false;
memset(&str, 0, sizeof(str));
Log->println("Official Nuki change received");
Log->print("Topic: ");
Log->println(topic);
Log->print("Value: ");
Log->println(value);
if(strcmp(topic, mqtt_topic_official_connected) == 0)
{
Log->print("Connected: ");
Log->println(strcmp(value, "true") == 0 ? 1 : 0);
offConnected = (strcmp(value, "true") == 0 ? 1 : 0);
_publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true);
}
else if(strcmp(topic, mqtt_topic_official_state) == 0)
{
offState = atoi(value);
_statusUpdated = true;
Log->println("Lock: Updating status on Hybrid state change");
_publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true);
NukiLock::lockstateToString((NukiLock::LockState)offState, str);
_publisher->publishString(mqtt_topic_lock_state, str, true);
Log->print("Lockstate: ");
Log->println(str);
_offStateToPublish = (NukiLock::LockState)offState;
_hasOffStateToPublish = true;
}
else if(strcmp(topic, mqtt_topic_official_doorsensorState) == 0)
{
offDoorsensorState = atoi(value);
_statusUpdated = true;
Log->println("Lock: Updating status on Hybrid door sensor state change");
_publisher->publishBool(mqtt_topic_lock_status_updated, _statusUpdated, true);
NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)offDoorsensorState, str);
Log->print("Doorsensor state: ");
Log->println(str);
_publisher->publishString(mqtt_topic_lock_door_sensor_state, str, true);
}
else if(strcmp(topic, mqtt_topic_official_batteryCritical) == 0)
{
offCritical = (strcmp(value, "true") == 0 ? 1 : 0);
Log->print("Battery critical: ");
Log->println(offCritical);
if(!_disableNonJSON)
{
_publisher->publishBool(mqtt_topic_battery_critical, offCritical, true);
}
publishBatteryJson = true;
}
else if(strcmp(topic, mqtt_topic_official_batteryCharging) == 0)
{
offCharging = (strcmp(value, "true") == 0 ? 1 : 0);
Log->print("Battery charging: ");
Log->println(offCharging);
if(!_disableNonJSON)
{
_publisher->publishBool(mqtt_topic_battery_charging, offCharging, true);
}
publishBatteryJson = true;
}
else if(strcmp(topic, mqtt_topic_official_batteryChargeState) == 0)
{
offChargeState = atoi(value);
Log->print("Battery level: ");
Log->println(offChargeState);
if(!_disableNonJSON)
{
_publisher->publishInt(mqtt_topic_battery_level, offChargeState, true);
}
publishBatteryJson = true;
}
else if(strcmp(topic, mqtt_topic_official_keypadBatteryCritical) == 0)
{
offKeypadCritical = (strcmp(value, "true") == 0 ? 1 : 0);
if(!_disableNonJSON)
{
_publisher->publishBool(mqtt_topic_battery_keypad_critical, offKeypadCritical, true);
}
publishBatteryJson = true;
}
else if(strcmp(topic, mqtt_topic_official_doorsensorBatteryCritical) == 0)
{
offDoorsensorCritical = (strcmp(value, "true") == 0 ? 1 : 0);
if(!_disableNonJSON)
{
_publisher->publishBool(mqtt_topic_battery_doorsensor_critical, offDoorsensorCritical, true);
}
publishBatteryJson = true;
}
else if(strcmp(topic, mqtt_topic_official_commandResponse) == 0)
{
offCommandResponse = atoi(value);
if(offCommandResponse == 0)
{
clearOffCommandExecutedTs();
}
char resultStr[15] = {0};
NukiLock::cmdResultToString((Nuki::CmdResult)offCommandResponse, resultStr);
_publisher->publishString(mqtt_topic_lock_action_command_result, resultStr, true);
}
else if(strcmp(topic, mqtt_topic_official_lockActionEvent) == 0)
{
clearAuthId();
clearOffCommandExecutedTs();
offLockActionEvent = (char*)value;
String LockActionEvent = offLockActionEvent;
const int ind1 = LockActionEvent.indexOf(',');
const int ind2 = LockActionEvent.indexOf(',', ind1+1);
const int ind3 = LockActionEvent.indexOf(',', ind2+1);
const int ind4 = LockActionEvent.indexOf(',', ind3+1);
const int ind5 = LockActionEvent.indexOf(',', ind4+1);
offLockAction = atoi(LockActionEvent.substring(0, ind1).c_str());
offTrigger = atoi(LockActionEvent.substring(ind1 + 1, ind2 + 1).c_str());
offAuthId = atoi(LockActionEvent.substring(ind2 + 1, ind3 + 1).c_str());
offCodeId = atoi(LockActionEvent.substring(ind3 + 1, ind4 + 1).c_str());
offContext = atoi(LockActionEvent.substring(ind4 + 1, ind5 + 1).c_str());
memset(&str, 0, sizeof(str));
lockactionToString((NukiLock::LockAction)offLockAction, str);
_publisher->publishString(mqtt_topic_lock_last_lock_action, str, true);
memset(&str, 0, sizeof(str));
triggerToString((NukiLock::Trigger)offTrigger, str);
_publisher->publishString(mqtt_topic_lock_trigger, str, true);
if(offAuthId > 0 || offCodeId > 0)
{
if(offCodeId > 0)
{
_authId = offCodeId;
switch(offContext)
{
case 0:
_publisher->publishString(mqtt_topic_lock_lock_action_context, "keypadBackKey", true);
break;
case 1:
_publisher->publishString(mqtt_topic_lock_lock_action_context, "keypadCode", true);
break;
case 2:
_publisher->publishString(mqtt_topic_lock_lock_action_context, "keypadFingerprint", true);
break;
default:
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
break;
}
}
else
{
_authId = offAuthId;
switch(offTrigger)
{
case 0:
if (offContext == 1)
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, "autoUnlock", true);
}
else
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
}
break;
case 2:
if (offContext > 0)
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, String("button") + String(offContext) + "press", true);
}
else
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
}
break;
case 3:
if (offContext > 0)
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, String("fob") + String(offContext) + "press", true);
}
else
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
}
break;
default:
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
break;
}
}
_hasAuthId = true;
}
else
{
_publisher->publishString(mqtt_topic_lock_lock_action_context, "", true);
}
}
if(publishBatteryJson)
{
JsonDocument jsonBattery;
char _resbuf[2048];
jsonBattery["critical"] = offCritical ? "1" : "0";
jsonBattery["charging"] = offCharging ? "1" : "0";
jsonBattery["level"] = offChargeState;
jsonBattery["keypadCritical"] = offKeypadCritical ? "1" : "0";
jsonBattery["doorSensorCritical"] = offDoorsensorCritical ? "1" : "0";
serializeJson(jsonBattery, _resbuf, sizeof(_resbuf));
_publisher->publishString(mqtt_topic_battery_basic_json, _resbuf, true);
}
}
const bool NukiOfficial::getStatusUpdated()
{
bool stu = _statusUpdated;
_statusUpdated = false;
return stu;
}
const bool NukiOfficial::hasOffStateToPublish()
{
bool hasOff = _hasOffStateToPublish;
_hasOffStateToPublish = false;
return hasOff;
}
const NukiLock::LockState NukiOfficial::getOffStateToPublish() const
{
return _offStateToPublish;
}
const uint32_t NukiOfficial::getAuthId() const
{
return _authId;
}
const bool NukiOfficial::hasAuthId() const
{
return _hasAuthId;
}
void NukiOfficial::clearAuthId()
{
_hasAuthId = false;
_authId = 0;
}
const bool NukiOfficial::getOffConnected() const
{
return offConnected;
}
const bool NukiOfficial::getOffEnabled() const
{
return offEnabled;
}
const uint8_t NukiOfficial::getOffDoorsensorState() const
{
return offDoorsensorState;
}
const uint8_t NukiOfficial::getOffState() const
{
return offState;
}
const uint8_t NukiOfficial::getOffLockAction() const
{
return offLockAction;
}
const uint8_t NukiOfficial::getOffTrigger() const
{
return offTrigger;
}
const uint8_t NukiOfficial::getOffContext() const
{
return offContext;
}
const int64_t NukiOfficial::getOffCommandExecutedTs() const
{
return offCommandExecutedTs;
}
void NukiOfficial::setOffCommandExecutedTs(const int64_t &value)
{
offCommandExecutedTs = value;
}
void NukiOfficial::clearOffCommandExecutedTs()
{
offCommandExecutedTs = 0;
}
const std::vector<char *> NukiOfficial::getOffTopics() const
{
return offTopics;
}