From ea93aa7b27e2fbeb749c430ad94b85f2ca7818b8 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 16:07:27 +0100 Subject: [PATCH 01/10] Publish additional lock states --- Network.cpp | 15 ++++++++------- Network.h | 6 +++--- NetworkLock.cpp | 23 ++++++++++++++++++----- NetworkLock.h | 2 +- NetworkOpener.cpp | 10 +++++++--- NetworkOpener.h | 2 +- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Network.cpp b/Network.cpp index c643a0bd..92c1e5e1 100644 --- a/Network.cpp +++ b/Network.cpp @@ -706,7 +706,7 @@ bool Network::publishString(const char* prefix, const char *topic, const char *v return _device->mqttPublish(path, MQTT_QOS_LEVEL, true, value) > 0; } -void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState) +void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction) { String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); @@ -741,8 +741,11 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n json["pl_unlk"] = unlockAction; json["pl_open"] = openAction; json["stat_t"] = String("~") + mqtt_topic_lock_binary_state; - json["stat_locked"] = lockedState; - json["stat_unlocked"] = unlockedState; + json["stat_jammed"] = "jammed"; + json["stat_locked"] = "locked"; + json["stat_locking"] = "locking"; + json["stat_unlocked"] = "unlocked"; + json["stat_unlocking"] = "unlocking"; json["opt"] = "false"; serializeJson(json, _buffer, _bufferSize); @@ -994,7 +997,7 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n } -void Network::publishHASSConfigAdditionalButtons(char *deviceType, const char *baseTopic, char *name, char *uidString, const char *availabilityTopic, const bool &hasKeypad, char *lockAction, char *unlockAction, char *openAction, char *lockedState, char *unlockedState) +void Network::publishHASSConfigAdditionalButtons(char *deviceType, const char *baseTopic, char *name, char *uidString) { // Lock 'n' Go publishHassTopic("button", @@ -1056,9 +1059,7 @@ void Network::publishHASSConfigBatLevel(char *deviceType, const char *baseTopic, } } -void Network::publishHASSConfigDoorSensor(char *deviceType, const char *baseTopic, char *name, char *uidString, - char *lockAction, char *unlockAction, char *openAction, char *lockedState, - char *unlockedState) +void Network::publishHASSConfigDoorSensor(char *deviceType, const char *baseTopic, char *name, char *uidString) { String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); diff --git a/Network.h b/Network.h index c9aa133b..d736eefa 100644 --- a/Network.h +++ b/Network.h @@ -43,10 +43,10 @@ class Network void publishBool(const char* prefix, const char* topic, const bool value); bool publishString(const char* prefix, const char* topic, const char* value); - void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); - void publishHASSConfigAdditionalButtons(char* deviceType, const char* baseTopic, char* name, char* uidString, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); + void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction); + void publishHASSConfigAdditionalButtons(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigBatLevel(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSConfigDoorSensor(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); + void publishHASSConfigDoorSensor(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigRingDetect(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigLedBrightness(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigSoundLevel(char* deviceType, const char* baseTopic, char* name, char* uidString); diff --git a/NetworkLock.cpp b/NetworkLock.cpp index 549afc5e..d6fcb006 100644 --- a/NetworkLock.cpp +++ b/NetworkLock.cpp @@ -281,16 +281,29 @@ void NetworkLock::publishBinaryState(NukiLock::LockState lockState) switch(lockState) { case NukiLock::LockState::Locked: - case NukiLock::LockState::Locking: publishString(mqtt_topic_lock_binary_state, "locked"); break; + case NukiLock::LockState::Locking: + publishString(mqtt_topic_lock_binary_state, "locking"); + break; case NukiLock::LockState::Unlocked: + publishString(mqtt_topic_lock_binary_state, "unlocked"); + break; case NukiLock::LockState::Unlocking: + publishString(mqtt_topic_lock_binary_state, "unlocking"); + break; case NukiLock::LockState::Unlatched: + publishString(mqtt_topic_lock_binary_state, "unlatched"); + break; case NukiLock::LockState::Unlatching: + publishString(mqtt_topic_lock_binary_state, "unlatching"); + break; case NukiLock::LockState::UnlockedLnga: publishString(mqtt_topic_lock_binary_state, "unlocked"); break; + case NukiLock::LockState::MotorBlocked: + publishString(mqtt_topic_lock_binary_state, "jammed"); + break; default: break; } @@ -529,15 +542,15 @@ bool NetworkLock::comparePrefixedPath(const char *fullPath, const char *subPath) } void NetworkLock::publishHASSConfig(char *deviceType, const char *baseTopic, char *name, char *uidString, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char *lockAction, - char *unlockAction, char *openAction, char *lockedState, char *unlockedState) + char *unlockAction, char *openAction) { - _network->publishHASSConfig(deviceType, baseTopic, name, uidString, "~/maintenance/mqttConnectionState", hasKeypad, lockAction, unlockAction, openAction, lockedState, unlockedState); - _network->publishHASSConfigAdditionalButtons(deviceType, baseTopic, name, uidString, "~/maintenance/mqttConnectionState", hasKeypad, lockAction, unlockAction, openAction, lockedState, unlockedState); + _network->publishHASSConfig(deviceType, baseTopic, name, uidString, "~/maintenance/mqttConnectionState", hasKeypad, lockAction, unlockAction, openAction); + _network->publishHASSConfigAdditionalButtons(deviceType, baseTopic, name, uidString); _network->publishHASSConfigBatLevel(deviceType, baseTopic, name, uidString); _network->publishHASSConfigLedBrightness(deviceType, baseTopic, name, uidString); if(hasDoorSensor) { - _network->publishHASSConfigDoorSensor(deviceType, baseTopic, name, uidString, lockAction, unlockAction, openAction, lockedState, unlockedState); + _network->publishHASSConfigDoorSensor(deviceType, baseTopic, name, uidString); } else { diff --git a/NetworkLock.h b/NetworkLock.h index 95f0e881..ccd25385 100644 --- a/NetworkLock.h +++ b/NetworkLock.h @@ -34,7 +34,7 @@ class NetworkLock : public MqttReceiver void publishRssi(const int& rssi); void publishRetry(const std::string& message); void publishBleAddress(const std::string& address); - void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); + void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char* lockAction, char* unlockAction, char* openAction); void removeHASSConfig(char* uidString); void publishKeypad(const std::list& entries, uint maxKeypadCodeCount); void publishKeypadCommandResult(const char* result); diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 89e5a4ff..92873616 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -274,10 +274,14 @@ void NetworkOpener::publishBinaryState(NukiOpener::OpenerState lockState) publishString(mqtt_topic_lock_binary_state, "locked"); break; case NukiOpener::LockState::RTOactive: + publishString(mqtt_topic_lock_binary_state, "unlocked"); + break; case NukiOpener::LockState::Open: - case NukiOpener::LockState::Opening: publishString(mqtt_topic_lock_binary_state, "unlocked"); break; + case NukiOpener::LockState::Opening: + publishString(mqtt_topic_lock_binary_state, "unlocking"); + break; default: break; } @@ -494,12 +498,12 @@ void NetworkOpener::publishBleAddress(const std::string &address) publishString(mqtt_topic_lock_address, address); } -void NetworkOpener::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState) +void NetworkOpener::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction) { String availabilityTopic = _preferences->getString("mqttpath"); availabilityTopic.concat("/maintenance/mqttConnectionState"); - _network->publishHASSConfig(deviceType, baseTopic, name, uidString, availabilityTopic.c_str(), false, lockAction, unlockAction, openAction, lockedState, unlockedState); + _network->publishHASSConfig(deviceType, baseTopic, name, uidString, availabilityTopic.c_str(), false, lockAction, unlockAction, openAction); _network->publishHASSConfigRingDetect(deviceType, baseTopic, name, uidString); _network->publishHASSConfigSoundLevel(deviceType, baseTopic, name, uidString); _network->publishHASSBleRssiConfig(deviceType, baseTopic, name, uidString); diff --git a/NetworkOpener.h b/NetworkOpener.h index 5a2853ed..9b5c112e 100644 --- a/NetworkOpener.h +++ b/NetworkOpener.h @@ -31,7 +31,7 @@ class NetworkOpener : public MqttReceiver void publishRssi(const int& rssi); void publishRetry(const std::string& message); void publishBleAddress(const std::string& address); - void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); + void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction); void removeHASSConfig(char* uidString); void publishKeypad(const std::list& entries, uint maxKeypadCodeCount); void publishKeypadCommandResult(const char* result); From 157d4f9b8159d51fd5346060e43572a28898c550 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 16:14:56 +0100 Subject: [PATCH 02/10] Fixes --- Network.cpp | 2 +- NetworkLock.cpp | 15 +-------------- NetworkOpener.cpp | 6 +----- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Network.cpp b/Network.cpp index 92c1e5e1..528d861a 100644 --- a/Network.cpp +++ b/Network.cpp @@ -740,7 +740,7 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n json["pl_lock"] = lockAction; json["pl_unlk"] = unlockAction; json["pl_open"] = openAction; - json["stat_t"] = String("~") + mqtt_topic_lock_binary_state; + json["stat_t"] = String("~") + mqtt_topic_lock_state; json["stat_jammed"] = "jammed"; json["stat_locked"] = "locked"; json["stat_locking"] = "locking"; diff --git a/NetworkLock.cpp b/NetworkLock.cpp index d6fcb006..19803e7a 100644 --- a/NetworkLock.cpp +++ b/NetworkLock.cpp @@ -281,29 +281,16 @@ void NetworkLock::publishBinaryState(NukiLock::LockState lockState) switch(lockState) { case NukiLock::LockState::Locked: - publishString(mqtt_topic_lock_binary_state, "locked"); - break; case NukiLock::LockState::Locking: - publishString(mqtt_topic_lock_binary_state, "locking"); + publishString(mqtt_topic_lock_binary_state, "locked"); break; case NukiLock::LockState::Unlocked: - publishString(mqtt_topic_lock_binary_state, "unlocked"); - break; case NukiLock::LockState::Unlocking: - publishString(mqtt_topic_lock_binary_state, "unlocking"); - break; case NukiLock::LockState::Unlatched: - publishString(mqtt_topic_lock_binary_state, "unlatched"); - break; case NukiLock::LockState::Unlatching: - publishString(mqtt_topic_lock_binary_state, "unlatching"); - break; case NukiLock::LockState::UnlockedLnga: publishString(mqtt_topic_lock_binary_state, "unlocked"); break; - case NukiLock::LockState::MotorBlocked: - publishString(mqtt_topic_lock_binary_state, "jammed"); - break; default: break; } diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 92873616..132dab8a 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -274,13 +274,9 @@ void NetworkOpener::publishBinaryState(NukiOpener::OpenerState lockState) publishString(mqtt_topic_lock_binary_state, "locked"); break; case NukiOpener::LockState::RTOactive: - publishString(mqtt_topic_lock_binary_state, "unlocked"); - break; case NukiOpener::LockState::Open: - publishString(mqtt_topic_lock_binary_state, "unlocked"); - break; case NukiOpener::LockState::Opening: - publishString(mqtt_topic_lock_binary_state, "unlocking"); + publishString(mqtt_topic_lock_binary_state, "unlocked"); break; default: break; From ceacdcafb38932c1f6b2e651f0a77bdbbd4e0470 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 19:22:43 +0100 Subject: [PATCH 03/10] Fixes --- NukiOpenerWrapper.cpp | 2 +- NukiWrapper.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 9cf86073..dda36682 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -740,7 +740,7 @@ void NukiOpenerWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_opener_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation","locked","unlocked"); + _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation"); _hassSetupCompleted = true; Log->println("HASS setup for opener completed."); diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 50519eb6..72575aea 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -739,7 +739,7 @@ void NukiWrapper::setupHASS() char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData,"lock", "unlock", "unlatch", "locked", "unlocked"); + _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData,"lock", "unlock", "unlatch"); _hassSetupCompleted = true; Log->println("HASS setup for lock completed."); From cc252f6e8bb2755042353020675e824442f0e253 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 20:01:32 +0100 Subject: [PATCH 04/10] Make opener lock/unlock action configurable --- NukiOpenerWrapper.cpp | 11 ++++++++++- PreferencesKeys.h | 5 +++-- WebCfgServer.cpp | 6 ++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index dda36682..327ad49a 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -740,7 +740,16 @@ void NukiOpenerWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_opener_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation"); + + if (_preferences->getBool(preference_opener_continuous_mode)) + { + _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateCM","activateCM","electricStrikeActuation"); + } + else + { + _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation"); + } + _hassSetupCompleted = true; Log->println("HASS setup for opener completed."); diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 13757d53..b4ca6577 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -13,6 +13,7 @@ #define preference_lock_enabled "lockena" #define preference_mqtt_lock_path "mqttpath" #define preference_opener_enabled "openerena" +#define preference_opener_continuous_mode "openercont" #define preference_mqtt_opener_path "mqttoppath" #define preference_lock_max_keypad_code_count "maxkpad" #define preference_opener_max_keypad_code_count "opmaxkpad" @@ -63,7 +64,7 @@ class DebugPreferences { preference_started_before, preference_device_id_lock, preference_device_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_lock_enabled, - preference_mqtt_lock_path, preference_opener_enabled, preference_mqtt_opener_path, + preference_mqtt_lock_path, preference_opener_enabled, preference_opener_continuous_mode, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, @@ -85,7 +86,7 @@ class DebugPreferences }; std::vector _boolPrefs = { - preference_started_before, preference_mqtt_log_enabled, preference_lock_enabled, preference_opener_enabled, + preference_started_before, preference_mqtt_log_enabled, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_register_as_app, preference_ip_dhcp_enabled, preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled }; diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index d6a13729..f4286eb1 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -352,6 +352,11 @@ bool WebCfgServer::processArgs(String& message) configChanged = true; } } + else if(key == "OPENERCONT") + { + _preferences->putBool(preference_opener_continuous_mode, (value == "1")); + configChanged = true; + } else if(key == "HASSCUURL") { _preferences->putString(preference_mqtt_hass_cu_url, value); @@ -770,6 +775,7 @@ void WebCfgServer::buildMqttConfigHtml(String &response) response.concat(""); printInputField(response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30); printInputField(response, "HASSCUURL", "Home Assistant device configuration URL (empty to use http://LOCALIP; fill when using a reverse proxy for example)", _preferences->getString(preference_mqtt_hass_cu_url).c_str(), 261); + if(_nukiOpener != nullptr) printCheckBox(response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode)); printTextarea(response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported(), true); printTextarea(response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported(), true); printTextarea(response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, _network->encryptionSupported(), true); From db5e66196c531ec25cf308ce2a81b597c7a36011 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 20:38:54 +0100 Subject: [PATCH 05/10] Fixes --- MqttTopics.h | 1 + Network.cpp | 2 +- NetworkLock.cpp | 20 +++++++++++++++++--- NetworkLock.h | 2 +- NetworkOpener.cpp | 16 +++++++++++++--- NetworkOpener.h | 2 +- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/MqttTopics.h b/MqttTopics.h index 650a0545..c2d4310a 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -10,6 +10,7 @@ #define mqtt_topic_battery_keypad_critical "/battery/keypadCritical" #define mqtt_topic_lock_state "/lock/state" +#define mqtt_topic_lock_ha_state "/lock/hastate" #define mqtt_topic_query_config "/lock/query/config" #define mqtt_topic_query_lockstate "/lock/query/lockstate" #define mqtt_topic_query_keypad "/lock/query/keypad" diff --git a/Network.cpp b/Network.cpp index 528d861a..bc131314 100644 --- a/Network.cpp +++ b/Network.cpp @@ -740,7 +740,7 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n json["pl_lock"] = lockAction; json["pl_unlk"] = unlockAction; json["pl_open"] = openAction; - json["stat_t"] = String("~") + mqtt_topic_lock_state; + json["stat_t"] = String("~") + mqtt_topic_lock_ha_state; json["stat_jammed"] = "jammed"; json["stat_locked"] = "locked"; json["stat_locking"] = "locking"; diff --git a/NetworkLock.cpp b/NetworkLock.cpp index 19803e7a..7474e380 100644 --- a/NetworkLock.cpp +++ b/NetworkLock.cpp @@ -218,7 +218,7 @@ void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurne if(_haEnabled) { - publishBinaryState(keyTurnerState.lockState); + publishState(keyTurnerState.lockState); } } @@ -276,21 +276,35 @@ void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurne _firstTunerStatePublish = false; } -void NetworkLock::publishBinaryState(NukiLock::LockState lockState) +void NetworkLock::publishState(NukiLock::LockState lockState) { switch(lockState) { case NukiLock::LockState::Locked: + publishString(mqtt_topic_lock_ha_state, "locked"); + publishString(mqtt_topic_lock_binary_state, "locked"); + break; case NukiLock::LockState::Locking: + publishString(mqtt_topic_lock_ha_state, "locking"); publishString(mqtt_topic_lock_binary_state, "locked"); break; - case NukiLock::LockState::Unlocked: case NukiLock::LockState::Unlocking: + publishString(mqtt_topic_lock_ha_state, "unlocking"); + publishString(mqtt_topic_lock_binary_state, "unlocked"); + break; + case NukiLock::LockState::Unlocked: case NukiLock::LockState::Unlatched: case NukiLock::LockState::Unlatching: case NukiLock::LockState::UnlockedLnga: + publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); break; + case NukiLock::LockState::Uncalibrated: + case NukiLock::LockState::Calibration: + case NukiLock::LockState::BootRun: + case NukiLock::LockState::MotorBlocked: + publishString(mqtt_topic_lock_ha_state, "jammed"); + break; default: break; } diff --git a/NetworkLock.h b/NetworkLock.h index ccd25385..56326c48 100644 --- a/NetworkLock.h +++ b/NetworkLock.h @@ -23,7 +23,7 @@ class NetworkLock : public MqttReceiver void initialize(); void publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState); - void publishBinaryState(NukiLock::LockState lockState); + void publishState(NukiLock::LockState lockState); void publishAuthorizationInfo(const std::list& logEntries); void clearAuthorizationInfo(); void publishCommandResult(const char* resultStr); diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 132dab8a..a6ada96e 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -219,7 +219,7 @@ void NetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurn if(_haEnabled) { - publishBinaryState(keyTurnerState); + publishState(keyTurnerState); } } @@ -260,10 +260,11 @@ void NetworkOpener::publishRing() _resetLockStateTs = millis() + 2000; } -void NetworkOpener::publishBinaryState(NukiOpener::OpenerState lockState) +void NetworkOpener::publishState(NukiOpener::OpenerState lockState) { if(lockState.nukiState == NukiOpener::State::ContinuousMode) { + publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); } else @@ -271,12 +272,21 @@ void NetworkOpener::publishBinaryState(NukiOpener::OpenerState lockState) switch (lockState.lockState) { case NukiOpener::LockState::Locked: + publishString(mqtt_topic_lock_ha_state, "locked"); publishString(mqtt_topic_lock_binary_state, "locked"); break; case NukiOpener::LockState::RTOactive: case NukiOpener::LockState::Open: - case NukiOpener::LockState::Opening: + publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); + break; + case NukiOpener::LockState::Opening: + publishString(mqtt_topic_lock_ha_state, "unlocking"); + publishString(mqtt_topic_lock_binary_state, "unlocked"); + break; + case NukiOpener::LockState::Undefined: + case NukiOpener::LockState::Uncalibrated: + publishString(mqtt_topic_lock_ha_state, "jammed"); break; default: break; diff --git a/NetworkOpener.h b/NetworkOpener.h index 9b5c112e..1a2be6df 100644 --- a/NetworkOpener.h +++ b/NetworkOpener.h @@ -20,7 +20,7 @@ class NetworkOpener : public MqttReceiver void publishKeyTurnerState(const NukiOpener::OpenerState& keyTurnerState, const NukiOpener::OpenerState& lastKeyTurnerState); void publishRing(); - void publishBinaryState(NukiOpener::OpenerState lockState); + void publishState(NukiOpener::OpenerState lockState); void publishAuthorizationInfo(const std::list& logEntries); void clearAuthorizationInfo(); void publishCommandResult(const char* resultStr); From 2c5ae46b7823cad82240935a52c106b683573364 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 20:49:31 +0100 Subject: [PATCH 06/10] Fixes --- NetworkOpener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index a6ada96e..cde655f0 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -280,7 +280,7 @@ void NetworkOpener::publishState(NukiOpener::OpenerState lockState) publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); break; - case NukiOpener::LockState::Opening: + case NukiOpener::LockState::Opening: publishString(mqtt_topic_lock_ha_state, "unlocking"); publishString(mqtt_topic_lock_binary_state, "unlocked"); break; From a3b1ef7753337e0d9a3da2d875d209502c1d5f1a Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 9 Feb 2024 21:12:40 +0100 Subject: [PATCH 07/10] Add continuous mode binary sensor --- MqttTopics.h | 1 + Network.cpp | 25 ++++++++++++++++++++++++- Network.h | 1 + NetworkOpener.cpp | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/MqttTopics.h b/MqttTopics.h index c2d4310a..e79e95eb 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -17,6 +17,7 @@ #define mqtt_topic_query_battery "/lock/query/battery" #define mqtt_topic_query_lockstate_command_result "/lock/query/lockstateCommandResult" #define mqtt_topic_lock_binary_state "/lock/binaryState" +#define mqtt_topic_lock_continous_mode "/lock/continousMode" #define mqtt_topic_lock_trigger "/lock/trigger" #define mqtt_topic_lock_last_lock_action "/lock/lastLockAction" #define mqtt_topic_lock_log "/lock/log" diff --git a/Network.cpp b/Network.cpp index bc131314..469a5b57 100644 --- a/Network.cpp +++ b/Network.cpp @@ -1084,6 +1084,30 @@ void Network::publishHASSConfigDoorSensor(char *deviceType, const char *baseTopi } } +void Network::publishHASSConfigContinuousMode(char *deviceType, const char *baseTopic, char *name, char *uidString) +{ + String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); + + if (discoveryTopic != "") + { + publishHassTopic("binary_sensor", + "continuous_mode", + uidString, + "_continuous_mode", + "Continuous mode", + name, + baseTopic, + String("~") + mqtt_topic_lock_continous_mode, + deviceType, + "lock", + "", + "", + "", + {{"pl_on", "on"}, + {"pl_off", "off"}}); + } +} + void Network::publishHASSConfigRingDetect(char *deviceType, const char *baseTopic, char *name, char *uidString) { String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); @@ -1108,7 +1132,6 @@ void Network::publishHASSConfigRingDetect(char *deviceType, const char *baseTopi } } - void Network::publishHASSConfigLedBrightness(char *deviceType, const char *baseTopic, char *name, char *uidString) { publishHassTopic("number", diff --git a/Network.h b/Network.h index d736eefa..2df550bc 100644 --- a/Network.h +++ b/Network.h @@ -48,6 +48,7 @@ class Network void publishHASSConfigBatLevel(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigDoorSensor(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigRingDetect(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigContinuousMode(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigLedBrightness(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigSoundLevel(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigAccessLog(char* deviceType, const char* baseTopic, char* name, char* uidString); diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index cde655f0..d9170774 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -266,9 +266,12 @@ void NetworkOpener::publishState(NukiOpener::OpenerState lockState) { publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); + publishString(mqtt_topic_lock_continous_mode, "on"); } else { + publishString(mqtt_topic_lock_continous_mode, "off"); + switch (lockState.lockState) { case NukiOpener::LockState::Locked: @@ -511,6 +514,7 @@ void NetworkOpener::publishHASSConfig(char* deviceType, const char* baseTopic, c _network->publishHASSConfig(deviceType, baseTopic, name, uidString, availabilityTopic.c_str(), false, lockAction, unlockAction, openAction); _network->publishHASSConfigRingDetect(deviceType, baseTopic, name, uidString); + _network->publishHASSConfigContinuousMode(deviceType, baseTopic, name, uidString); _network->publishHASSConfigSoundLevel(deviceType, baseTopic, name, uidString); _network->publishHASSBleRssiConfig(deviceType, baseTopic, name, uidString); } From 533296878f698c0de4ccbc8698543b60b143d1f1 Mon Sep 17 00:00:00 2001 From: iranl Date: Sat, 17 Feb 2024 22:56:02 +0100 Subject: [PATCH 08/10] Updates --- Gpio.cpp | 26 ++++++++++++++++++++++++++ Gpio.h | 8 ++++++++ MqttTopics.h | 2 +- Network.cpp | 13 ++++++++----- NetworkOpener.cpp | 20 ++++++-------------- NukiOpenerWrapper.cpp | 34 ++++++++++++++++++++++++---------- NukiOpenerWrapper.h | 6 ++++-- README.md | 4 +++- 8 files changed, 80 insertions(+), 33 deletions(-) diff --git a/Gpio.cpp b/Gpio.cpp index 0ad2c4a0..c3b26017 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -74,6 +74,14 @@ void Gpio::init() pinMode(entry.pin, INPUT_PULLUP); attachInterrupt(entry.pin, isrDeactivateRtoCm, FALLING); break; + case PinRole::InputDeactivateRTO: + pinMode(entry.pin, INPUT_PULLUP); + attachInterrupt(entry.pin, isrDeactivateRTO, FALLING); + break; + case PinRole::InputDeactivateCM: + pinMode(entry.pin, INPUT_PULLUP); + attachInterrupt(entry.pin, isrDeactivateCM, FALLING); + break; case PinRole::OutputHighLocked: case PinRole::OutputHighUnlocked: case PinRole::OutputHighMotorBlocked: @@ -199,6 +207,10 @@ String Gpio::getRoleDescription(PinRole role) const return "Input: Activate CM"; case PinRole::InputDeactivateRtoCm: return "Input: Deactivate RTO/CM"; + case PinRole::InputDeactivateRTO: + return "Input: Deactivate RTO"; + case PinRole::InputDeactivateCM: + return "Input: Deactivate CM"; case PinRole::OutputHighLocked: return "Output: High when locked"; case PinRole::OutputHighUnlocked: @@ -327,6 +339,20 @@ void Gpio::isrDeactivateRtoCm() _debounceTs = millis() + _debounceTime; } +void Gpio::isrDeactivateRTO() +{ + if(millis() < _debounceTs) return; + _inst->notify(GpioAction::DeactivateRTO, -1); + _debounceTs = millis() + _debounceTime; +} + +void Gpio::isrDeactivateCM() +{ + if(millis() < _debounceTs) return; + _inst->notify(GpioAction::DeactivateCM, -1); + _debounceTs = millis() + _debounceTime; +} + void Gpio::setPinOutput(const uint8_t& pin, const uint8_t& state) { digitalWrite(pin, state); diff --git a/Gpio.h b/Gpio.h index 1203c660..a7823dab 100644 --- a/Gpio.h +++ b/Gpio.h @@ -16,6 +16,8 @@ enum class PinRole InputActivateRTO, InputActivateCM, InputDeactivateRtoCm, + InputDeactivateRTO, + InputDeactivateCM, OutputHighLocked, OutputHighUnlocked, OutputHighMotorBlocked, @@ -38,6 +40,8 @@ enum class GpioAction ActivateRTO, ActivateCM, DeactivateRtoCm, + DeactivateRTO, + DeactivateCM, GeneralInput }; @@ -88,6 +92,8 @@ class Gpio PinRole::InputActivateRTO, PinRole::InputActivateCM, PinRole::InputDeactivateRtoCm, + PinRole::InputDeactivateRTO, + PinRole::InputDeactivateCM, PinRole::OutputHighLocked, PinRole::OutputHighUnlocked, PinRole::OutputHighRtoActive, @@ -110,6 +116,8 @@ class Gpio static void IRAM_ATTR isrActivateRTO(); static void IRAM_ATTR isrActivateCM(); static void IRAM_ATTR isrDeactivateRtoCm(); + static void IRAM_ATTR isrDeactivateRTO(); + static void IRAM_ATTR isrDeactivateCM(); std::vector> _callbacks; diff --git a/MqttTopics.h b/MqttTopics.h index 48884a06..eb707a10 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -18,7 +18,7 @@ #define mqtt_topic_query_battery "/lock/query/battery" #define mqtt_topic_query_lockstate_command_result "/lock/query/lockstateCommandResult" #define mqtt_topic_lock_binary_state "/lock/binaryState" -#define mqtt_topic_lock_continous_mode "/lock/continousMode" +#define mqtt_topic_lock_continuous_mode "/lock/continuousMode" #define mqtt_topic_lock_trigger "/lock/trigger" #define mqtt_topic_lock_last_lock_action "/lock/lastLockAction" #define mqtt_topic_lock_log "/lock/log" diff --git a/Network.cpp b/Network.cpp index 631b8b5f..54520741 100644 --- a/Network.cpp +++ b/Network.cpp @@ -1166,21 +1166,24 @@ void Network::publishHASSConfigContinuousMode(char *deviceType, const char *base if (discoveryTopic != "") { - publishHassTopic("binary_sensor", + publishHassTopic("switch", "continuous_mode", uidString, "_continuous_mode", "Continuous mode", name, baseTopic, - String("~") + mqtt_topic_lock_continous_mode, + String("~") + mqtt_topic_lock_continuous_mode, deviceType, "lock", "", "", - "", - {{"pl_on", "on"}, - {"pl_off", "off"}}); + String("~") + mqtt_topic_lock_action, + {{ "enabled_by_default", "false" }, + {"state_on", "on"}, + {"state_on", "off"}, + {"pl_on", "activateCM"}, + {"pl_off", "deactivateCM"}}); } } diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 07059435..7d2bdc0a 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -210,14 +210,7 @@ void NetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurn if((_firstTunerStatePublish || keyTurnerState.lockState != lastKeyTurnerState.lockState || keyTurnerState.nukiState != lastKeyTurnerState.nukiState) && keyTurnerState.lockState != NukiOpener::LockState::Undefined) { - if(keyTurnerState.nukiState == NukiOpener::State::ContinuousMode) - { - publishString(mqtt_topic_lock_state, "ContinuousMode"); - } - else - { - publishString(mqtt_topic_lock_state, str); - } + publishString(mqtt_topic_lock_state, str); if(_haEnabled) { @@ -229,8 +222,10 @@ void NetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurn if(keyTurnerState.nukiState == NukiOpener::State::ContinuousMode) { + publishString(mqtt_topic_lock_continuous_mode, "on"); json["continuous_mode"] = 1; } else { + publishString(mqtt_topic_lock_continuous_mode, "off"); json["continuous_mode"] = 0; } @@ -291,12 +286,9 @@ void NetworkOpener::publishState(NukiOpener::OpenerState lockState) { publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); - publishString(mqtt_topic_lock_continous_mode, "on"); } else { - publishString(mqtt_topic_lock_continous_mode, "off"); - switch (lockState.lockState) { case NukiOpener::LockState::Locked: @@ -307,7 +299,7 @@ void NetworkOpener::publishState(NukiOpener::OpenerState lockState) case NukiOpener::LockState::Open: publishString(mqtt_topic_lock_ha_state, "unlocked"); publishString(mqtt_topic_lock_binary_state, "unlocked"); - break; + break; case NukiOpener::LockState::Opening: publishString(mqtt_topic_lock_ha_state, "unlocking"); publishString(mqtt_topic_lock_binary_state, "unlocked"); @@ -537,7 +529,7 @@ void NetworkOpener::publishHASSConfig(char* deviceType, const char* baseTopic, c _network->publishHASSConfig(deviceType, baseTopic, name, uidString, availabilityTopic.c_str(), false, lockAction, unlockAction, openAction); _network->publishHASSConfigRingDetect(deviceType, baseTopic, name, uidString); - _network->publishHASSConfigContinuousMode(deviceType, baseTopic, name, uidString); + _network->publishHASSConfigContinuousMode(deviceType, baseTopic, name, uidString); _network->publishHASSConfigSoundLevel(deviceType, baseTopic, name, uidString); _network->publishHASSBleRssiConfig(deviceType, baseTopic, name, uidString); } @@ -559,7 +551,7 @@ void NetworkOpener::publishKeypad(const std::list& entrie basePath.concat("/code_"); basePath.concat(std::to_string(index).c_str()); publishKeypadEntry(basePath, entry); - + auto jsonEntry = json.add(); jsonEntry["id"] = entry.codeId; diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 327ad49a..fab2f545 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -291,6 +291,16 @@ void NukiOpenerWrapper::deactivateRtoCm() } } +void NukiOpenerWrapper::deactivateRTO() +{ + _nextLockAction = NukiOpener::LockAction::DeactivateRTO; +} + +void NukiOpenerWrapper::deactivateCM() +{ + _nextLockAction = NukiOpener::LockAction::DeactivateCM; +} + bool NukiOpenerWrapper::isPinSet() { return _nukiOpener.getSecurityPincode() != 0; @@ -347,12 +357,10 @@ void NukiOpenerWrapper::updateKeyTurnerState() { Log->println(F("Continuous Mode")); } - else - { - char lockStateStr[20]; - lockstateToString(_keyTurnerState.lockState, lockStateStr); - Log->println(lockStateStr); - } + + char lockStateStr[20]; + lockstateToString(_keyTurnerState.lockState, lockStateStr); + Log->println(lockStateStr); } if(_publishAuthData) @@ -535,6 +543,12 @@ void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& case GpioAction::DeactivateRtoCm: nukiOpenerInst->deactivateRtoCm(); break; + case GpioAction::DeactivateRTO: + nukiOpenerInst->deactivateRTO(); + break; + case GpioAction::DeactivateCM: + nukiOpenerInst->deactivateCM(); + break; } } @@ -740,16 +754,16 @@ void NukiOpenerWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_opener_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - - if (_preferences->getBool(preference_opener_continuous_mode)) + + if (_preferences->getBool(preference_opener_continuous_mode)) { _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateCM","activateCM","electricStrikeActuation"); } - else + else { _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation"); } - + _hassSetupCompleted = true; Log->println("HASS setup for opener completed."); diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index b6169774..619ce04b 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -22,12 +22,14 @@ class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler void activateRTO(); void activateCM(); void deactivateRtoCm(); + void deactivateRTO(); + void deactivateCM(); bool isPinSet(); void setPin(const uint16_t pin); void unpair(); - + void disableHASS(); void disableWatchdog(); @@ -63,7 +65,7 @@ class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler void readConfig(); void readAdvancedConfig(); - + void setupHASS(); void printCommandResult(Nuki::CmdResult result); diff --git a/README.md b/README.md index 22aecdb8..b9386726 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,9 @@ can be configured for a specific role: - Input: Electric strike actuation: When connect to Ground, an electric strike actuation command is sent to the opener (open door for configured amount of time) - Input: Activate RTO: When connect to Ground, Ring-to-open is activated (opener) - Input: Activate CM: When connect to Ground, Continuous mode is activated (opener) -- Input: Deactivate RTO/CM: Disable RTO or CM, depending on which is active +- Input: Deactivate RTO/CM: Disable RTO or CM, depending on which is active (opener) +- Input: Dectivate RTO: When connect to Ground, Ring-to-open is deactivated (opener) +- Input: Dectivate CM: When connect to Ground, Continuous mode is deactivated (opener) - Output: High when locked: Outputs a high signal when the door is locked - Output: High when unlocked: Outputs a high signal when the door is unlocked - Output: High when motor blocked: Outputs a high signal when the motor is blocked (lock) From 937cce7956d6f737906173ad7099efb5b70fd08c Mon Sep 17 00:00:00 2001 From: iranl Date: Sat, 17 Feb 2024 23:02:34 +0100 Subject: [PATCH 09/10] Add HA Binary Sensor --- Network.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Network.cpp b/Network.cpp index 54520741..746bcd7f 100644 --- a/Network.cpp +++ b/Network.cpp @@ -1166,6 +1166,23 @@ void Network::publishHASSConfigContinuousMode(char *deviceType, const char *base if (discoveryTopic != "") { + + publishHassTopic("binary_sensor", + "continuous_mode", + uidString, + "_continuous_mode", + "Continuous mode", + name, + baseTopic, + String("~") + mqtt_topic_lock_continuous_mode, + deviceType, + "lock", + "", + "", + "", + {{"pl_on", "on"}, + {"pl_off", "off"}}); + publishHassTopic("switch", "continuous_mode", uidString, From bca8caf7a517943c54346309cba7ab5fa3f5acfa Mon Sep 17 00:00:00 2001 From: iranl Date: Sat, 17 Feb 2024 23:14:45 +0100 Subject: [PATCH 10/10] Remove HASS topic for new discovery topics --- Network.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Network.cpp b/Network.cpp index 746bcd7f..e002e335 100644 --- a/Network.cpp +++ b/Network.cpp @@ -1456,6 +1456,8 @@ void Network::removeHASSConfig(char* uidString) removeHassTopic("sensor", "keypad_status", uidString); removeHassTopic("sensor", "wifi_signal_strength", uidString); removeHassTopic("sensor", "bluetooth_signal_strength", uidString); + removeHassTopic("binary_sensor", "continuous_mode", uidString); + removeHassTopic("switch", "continuous_mode", uidString); } }