HASS Addon TTLock offline integration

I am beginning to feel why you abandoned the ESP32 option
I managed to upload and compile tot he board, but the page is blank when I try to connect to the ESP32GW access point…

did you follow my post of the 30th of September exactly?
I have installed following those steps several times and so have others done so successfully

MuzzaM has a few more details a few days later.
I also have a post saying how to set it to run on boot.

A detail i might have missed is that i am installing the Raspbery pi OS headless 32bit. Someone has mentioned they had tried the 64bit OS and it wouldn’t install.
.

Yes I remember having this problem too. the graphical interface only needs to enter the connection data, I solved it like this:

  • modify the parameters inside the gw_settings.cpp file located inside the src folder. The number above each setting indicates the length of what you enter.

obviously after setting the parameters it must be reflashed as I told you in the previous post. The interface is still not visible but everything has been working perfectly for 2 years.

example of file modification:

* bool GwSettings::ready = false;
* Preferences GwSettings::prefs;
* char *GwSettings::name = nullptr;
* size_t GwSettings::nameLen = 0;
* char *GwSettings::password = nullptr;
* size_t GwSettings::passwordLen = 0;
* char *GwSettings::ssid = "wifinome";
* size_t GwSettings::ssidLen = 8;
* char *GwSettings::pass = "wifipassword";
* size_t GwSettings::passLen = 12;
* char *GwSettings::aes = "stringa_aes";
* char *GwSettings::certName = nullptr;
* size_t GwSettings::certNameLen = 0;
* uint8_t *GwSettings::cert = nullptr;
* size_t GwSettings::certLen = 0;
* uint8_t *GwSettings::pk = nullptr;
* size_t GwSettings::pkLen = 0;

in the example 12 and 8 change based on the length of your network name and password.
The " symbols should be left and should not be considered in the character count.

I don’t know if I changed other things in the file, if anything send it to me so I can compare it with mine

> #include "gw_settings.h"
> 
> bool GwSettings::ready = false;
> Preferences GwSettings::prefs;
> char *GwSettings::name = nullptr;
> size_t GwSettings::nameLen = 0;
> char *GwSettings::password = nullptr;
> size_t GwSettings::passwordLen = 0;
> char *GwSettings::ssid = nullptr;
> size_t GwSettings::ssidLen = 0;
> char *GwSettings::pass = nullptr;
> size_t GwSettings::passLen = 0;
> char *GwSettings::aes = nullptr;
> char *GwSettings::certName = nullptr;
> size_t GwSettings::certNameLen = 0;
> uint8_t *GwSettings::cert = nullptr;
> size_t GwSettings::certLen = 0;
> uint8_t *GwSettings::pk = nullptr;
> size_t GwSettings::pkLen = 0;
> 
> bool GwSettings::init()
> {
>   prefs.begin("ESP32GW");
> 
>   if (!prefs.isKey("name"))
>   { // default name
>     nameLen = 7;
>     name = "esp32gw";
>     prefs.putBytes("name", name, nameLen);
>   }
>   else
>   {
>     nameLen = prefs.getBytesLength("name") + 1;
>     name = new char[nameLen];
>     prefs.getBytes("name", name, nameLen - 1);
>     name[nameLen - 1] = '\0';
>   }
> 
>   if (!prefs.isKey("password"))
>   { // default admin password
>     passwordLen = 5;
>     password = "admin";
>     prefs.putBytes("password", password, passwordLen);
>   }
>   else
>   {
>     passwordLen = prefs.getBytesLength("password") + 1;
>     password = new char[passwordLen];
>     prefs.getBytes("password", password, passwordLen - 1);
>     password[passwordLen - 1] = '\0';
>   }
> 
>   if (prefs.isKey("ssid"))
>   {
>     ssidLen = prefs.getBytesLength("ssid") + 1;
>     ssid = new char[ssidLen];
>     prefs.getBytes("ssid", ssid, ssidLen - 1);
>     ssid[ssidLen - 1] = '\0';
>   }
> 
>   if (prefs.isKey("pass"))
>   {
>     passLen = prefs.getBytesLength("pass") + 1;
>     pass = new char[passLen];
>     prefs.getBytes("pass", pass, passLen - 1);
>     pass[passLen - 1] = '\0';
>   }
> 
>   aes = new char[BLOCK_SIZE * 2 + 1];
>   if (!prefs.isKey("aes"))
>   { // new AES key
>     Security::generateKey(aes);
>     prefs.putBytes("aes", (uint8_t *)aes, BLOCK_SIZE * 2);
>   }
>   else
>   {
>     prefs.getBytes("aes", aes, BLOCK_SIZE * 2);
>     aes[BLOCK_SIZE * 2] = '\0';
>   }
> 
>   if (prefs.isKey("cert_name")) {
>     certNameLen = prefs.getBytesLength("cert_name") + 1;
>     certName = new char[certNameLen];
>     prefs.getBytes("cert_name", certName, certNameLen - 1);
>     certName[certNameLen - 1] = '\0';
>   }
> 
>   if (prefs.isKey("cert")) {
>     certLen = prefs.getBytesLength("cert");
>     cert = new uint8_t[certLen];
>     prefs.getBytes("cert", cert, certLen);
>   }
> 
>   if (prefs.isKey("pk")) {
>     pkLen = prefs.getBytesLength("pk");
>     pk = new uint8_t[pkLen];
>     prefs.getBytes("pk", pk, pkLen);
>   }
> 
>   ready = true;
>   return true;
> }
> 
> bool GwSettings::isConfigured()
> {
>   return (ssidLen > 0 && passLen > 0);
> }
> 
> void GwSettings::clear() {
>   prefs.clear();
> }
> 
> char *GwSettings::getName()
> {
>   return name;
> }
> 
> size_t GwSettings::getNameLen()
> {
>   return nameLen;
> }
> 
> void GwSettings::setName(const char *val, size_t len) {
>   prefs.putBytes("name", val, len - 1);
>   delete[] name;
>   nameLen = len;
>   name = new char[nameLen];
>   memcpy(name, val, nameLen);
> }
> 
> char *GwSettings::getPassword()
> {
>   return password;
> }
> 
> size_t GwSettings::getPasswordLen()
> {
>   return passwordLen;
> }
> 
> void GwSettings::setPassword(const char *val, size_t len) {
>   prefs.putBytes("password", val, len - 1);
>   delete[] password;
>   passwordLen = len;
>   password = new char[passwordLen];
>   memcpy(password, val, passwordLen);
> }
> 
> char *GwSettings::getSsid()
> {
>   return ssid;
> }
> 
> size_t GwSettings::getSsidLen()
> {
>   return ssidLen;
> }
> 
> void GwSettings::setSsid(const char *val, size_t len) {
>   prefs.putBytes("ssid", val, len - 1);
>   delete[] ssid;
>   ssidLen = len;
>   ssid = new char[ssidLen];
>   memcpy(ssid, val, ssidLen);
> }
> 
> char *GwSettings::getPass()
> {
>   return pass;
> }
> 
> size_t GwSettings::getPassLen()
> {
>   return passLen;
> }
> 
> void GwSettings::setPass(const char *val, size_t len) {
>   prefs.putBytes("pass", val, len - 1);
>   delete[] pass;
>   passLen = len;
>   pass = new char[passLen];
>   memcpy(pass, val, passLen);
> }
> 
> char *GwSettings::getAes()
> {
>   return aes;
> }
> 
> bool GwSettings::hasCert() {
>   return (certNameLen > 0 && certLen > 0 && pkLen > 0);
> }
> 
> char *GwSettings::getCertName() {
>   return certName;
> }
> 
> size_t GwSettings::getCertNameLen() {
>   return certNameLen;
> }
> 
> void GwSettings::setCertName(const char *val, size_t len) {
>   prefs.putBytes("cert_name", val, len - 1);
>   delete[] certName;
>   certNameLen = len;
>   certName = new char[certNameLen];
>   memcpy(certName, val, certNameLen);
> }
> 
> uint8_t *GwSettings::getCert() {
>   return cert;
> }
> 
> size_t GwSettings::getCertLen() {
>   return certLen;
> }
> 
> void GwSettings::setCert(const uint8_t *val, size_t len) {
>   prefs.putBytes("cert", val, len);
>   delete[] cert;
>   certLen = len;
>   cert = new uint8_t[certLen];
>   memcpy(cert, val, certLen);
> }
> 
> uint8_t *GwSettings::getPk() {
>   return pk;
> }
> 
> size_t GwSettings::getPkLen() {
>   return pkLen;
> }
> 
> void GwSettings::setPk(const uint8_t *val, size_t len) {
>   prefs.putBytes("pk", val, len);
>   delete[] pk;
>   pkLen = len;
>   pk = new uint8_t[pkLen];
>   memcpy(pk, val, pkLen);
> }

This is the content in the
\esp32-ble-gateway-main\esp32-ble-gateway-main\src\gw_settings.cpp

If I understand you correctly:
1-You never were able to see the interface on the AP webpage?
2-You changed these parameter in this file to reflect the length of your SSID name and password? How does the GW know to connect to the network if it does not know the actual credentials?
3-Even after rhe changes made, you were unable to see the AP interface of the AP webpage, but you were able to connect it to the HA addon?
I cannot thank you enough for helping me, I appreciate it.

I changed the following Lines
ssid —name of my wifi network
ssidLen —length of charachters
pass ---- wifi password
passLen— length of characters
aes---- I generated an AES encryption key

I reflashed
The ESP32 now has an ip address and I think it is connected to my networ

HOWEVER
when I go to the add-on in HA, I add everything as instructed

gateway: noble
gateway_host: IP_ADDRESS_OF_esp
gateway_port: 8080
gateway_key: AES_KEY_FROM_ESP_CONFIG (the AES key I generated)
gateway_user: admin
gateway_pass: admin

in VScode terminal
I get authentification failed

Please advise
thank you

and this is the log from HA add-on

Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
Received: {"type":"auth","challenge":"45313435383635424342343432424244"}
Sent:    {"action":"auth","response":"c1f2b343f054b28f9ce613aad5efedf6"}
[08:38:36] ERROR: e[35mUnable to access the API, forbiddene[0m
[08:38:36] ERROR: e[35mUnable to access the API, forbiddene[0m

> [email protected] start
> node ./index.js

Error: ENOENT: no such file or directory, access '/data/lockData.json'
    at async Object.access (node:internal/fs/promises:606:10)
    at async Store.loadData (/app/src/store.js:86:7)
    at async module.exports (/app/src/init.js:35:3) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'access',
  path: '/data/lockData.json'
}
Error: ENOENT: no such file or directory, access '/data/aliasData.json'
    at async Object.access (node:internal/fs/promises:606:10)
    at async Store.loadData (/app/src/store.js:94:7)
    at async module.exports (/app/src/init.js:35:3) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'access',
  path: '/data/aliasData.json'
}
noble: Initialization of USB device failed: ENODEV, No such device
Server started

after it connects to me it is gone so from here I don’t know how to help you anymore. I can only post my platformio.ini (in the root directory) and security.h (inside the src folder) files. These are the files I remember editing in 2023. Check if maybe there is something different that could fix the problem after reflashing it.

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env]
platform = espressif32
framework = arduino
monitor_speed = 921600
upload_speed = 921600
lib_deps = 
	bblanchon/ArduinoJson@^6.17.2
	links2004/WebSockets@^2.3.3
	h2zero/NimBLE-Arduino@^1.1.0
	esp32_https_server@^1.0.0
monitor_filters = esp32_exception_decoder
#platform_packages =
 #   platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
board_build.partitions = min_spiffs.csv

[env:esp-wrover-debug]
board = esp-wrover-kit
build_type = debug
build_flags =
		-DCORE_DEBUG_LEVEL=5
    ; -DBOARD_HAS_PSRAM
		; -mfix-esp32-psram-cache-issue
		-DLOG_LOCAL_LEVEL=ESP_LOG_VERBOSE

[env:esp-wrover]
board = esp-wrover-kit
build_flags =
    ; -DBOARD_HAS_PSRAM
		; -mfix-esp32-psram-cache-issue
		; -DCORE_DEBUG_LEVEL=5
#ifndef ESP_GW_SECURITY_H
#define ESP_GW_SECURITY_H

#include <esp32/aes.h>

#define BLOCK_SIZE 16

class Security {
  public:
    Security();
    Security(const char *aesKey);
    ~Security();
    void setKey(const char *aesKey);
    void getKey(uint8_t *aesKey);
    void generateIV(uint8_t IV[BLOCK_SIZE]);
    size_t getPadedSize(size_t dataLength);
    size_t encrypt(const uint8_t IV[BLOCK_SIZE], const uint8_t *data, size_t dataLength, uint8_t *encrypted);
    size_t decrypt(const uint8_t IV[BLOCK_SIZE], const uint8_t *data, size_t dataLength, uint8_t *decrypted);
    
    static void generateKey(char *newKey);
    static uint8_t fromHex(const char *data, const size_t dataLength, uint8_t *out);
    static uint8_t toHex(const uint8_t *data, const size_t dataLength, char *out);
  private:
    uint8_t key[BLOCK_SIZE];
    size_t keyLength;
    esp_aes_context aesContext;
};

#endif

check if they are the same, otherwise try using mine and see if it solves the problem.

thank you for sharing
In the previous post you changed gw_settings.cpp
my questio: the line that

char *GwSettings::aes = "stringa_aes"

what value did you use for “stringa_aes”
and is this the same value you add to HA addon

gateway_key: AES_KEY_FROM_ESP_CONFIG

this seems to be the issue, I think the HA addon is sneding the wrong encryption to the GW and thst is causing the authentication failure

The other files you sent seem identical to mine
Thank you for taking the time to respond and help

yes, obviously that string must be the same

which parameters i need to change here?
thank you

only these, and on aes string I don’t remember where it can be generated (maybe online on google you can search). And the aes key must be the same on both esp32 and homeassistant

* char *GwSettings::ssid = "wifinome";
* size_t GwSettings::ssidLen = 8;
* char *GwSettings::pass = "wifipassword";
* size_t GwSettings::passLen = 12;
* char *GwSettings::aes = "stringa_aes";

try here (put the 256bit one which is the last one at the bottom):

I want to thank you for your patience and help, I greatly appreciate it
I changed these parameters and unfortunately I keep getting authentication failed at the terminal everytime I start the HA add-on after adding thes valuesso:
I know the GW is connected to my network
I know the HTTPS has generated an SSL certificate
I know the add on has the correct parameters
I know the addon is attempting to connect with GW
but somewhere there is an authetication failure
well frustrating to say the least as I could get this far but not able to make it work
Hope you could have any other solution I can try
Thank you, sir

I hope anybody else here in the community who got this to work can help
I appreciate it

do you remove the [ ] and insert user or insert the user between the [ ]

Remove. It was just to get attention and be obvious it needs to be replaced

thank you
is there a command I can use ti make sure the SDK is running on the pi?

anyway you can help me build the gateway?
I am able to build and compile but the web UI og the AP is blank
someone suggested to edit

  • modify the parameters inside the gw_settings.cpp file located inside the src folder. The number above each setting indicates the length of what you enter.

I did but when I use the AES I manually generated , th add-on keeps having authentication failure

i suggest that you disable the service and run it manually initially, so you can make sure all is working correctly.

sudo systemctl stop myapp.service

and then from the folder ttlock-sdk-js run:

sudo npm run server-tool

that way you can compare what the addon is showing in the logs with what the gateway is saying.

It is possible to see the logs of the service in the logs somewhere but i find it hard to use

I guess I just should give up on this…

when i activate the lock keypad here is what I see in the log (even without clicking the scan button on the addon)

{"type":"discover","peripheralUuid":"e325319e9ad5","address":"e3:25:31:9e:9a:d5","addressType":"public","connectable":true,"advertisement":{"localName":"F2_d59a9e","txPowerLevel":-70,"serviceUuids":["1910"],"manufacturerData":"0503021c64b000f4f95365d59a9e3125e3","serviceData":""},"rssi":-39}

Received: {"type":"discover","peripheralUuid":"e325319e9ad5","address":"e3:25:31:9e:9a:d5","addressType":"public","connectable":true,"advertisement":{"localName":"F2_d59a9e","txPowerLevel":-70,"serviceUuids":["1910"],"manufacturerData":"0503021c64b000f4f95365d59a9e3125e3","serviceData":""},"rssi":-34}

Received: {"type":"discover","peripheralUuid":"e325319e9ad5","address":"e3:25:31:9e:9a:d5","addressType":"public","connectable":true,"advertisement":{"localName":"F2_d59a9e","txPowerLevel":-70,"serviceUuids":["1910"],"manufacturerData":"0503021c64b000f4f95365d59a9e3125e3","serviceData":""},"rssi":-34}

Received: {"type":"discover","peripheralUuid":"e325319e9ad5","address":"e3:25:31:9e:9a:d5","addressType":"public","connectable":true,"advertisement":{"localName":"F2_d59a9e","txPowerLevel":-70,"serviceUuids":["1910"],"manufacturerData":"0503021c64b000f4f95365d59a9e3125e3","serviceData":""},"rssi":-34}

Received: {"type":"discover","peripheralUuid":"e325319e9ad5","address":"e3:25:31:9e:9a:d5","addressType":"public","connectable":true,"advertisement":{"localName":"F2_d59a9e","txPowerLevel":-70,"serviceUuids":["1910"],"manufacturerData":"0503021c64b000f4f95365d59a9e3125e3","serviceData":""},"rssi":-34}

I do not see anything else
the addon does not show lock discovered
no pair button appears
door and PI gateway are in very close proximity

any suggestion?