HASS Addon TTLock offline integration

So i’ve been reading this thread and as i’m currently debating with myself on what lock to get, i’ve narrowed it down to yale and this other (BG4000 ot BG3000) ttlock based one:

It seems like both can be operated with local bluetooth solutions witch is much more desireble that the cloud, also ttlock seems to be hosted in china?
Coud be a problem in the future, who knows really.

Anyway, my question is, as i run a few bt beacons (mainly because i run/track out phones with Bermuda Bluetooth/BLE Triangulation / Trilateration for HomeAssistant) i would guess using my “normal” beacon config via esphome would work, i usually run ESP32-WROOM (might be misspelled) devices.

Would be cool if anyone could try this.
Found some info here for yale, so it seems possible for this as well i’de guess.

this is the config i use:


esphome:
  name: beacon2
  friendly_name: beacon2

esp32:
  board: node32s
  # framework:
  #   type: arduino

  framework:
      type: esp-idf
      version: recommended
      # Custom sdkconfig options
      sdkconfig_options:
        CONFIG_COMPILER_OPTIMIZATION_PERF: y

#CONFIG_COMPILER_OPTIMIZATION_PERF
#COMPILER_OPTIMIZATION_SIZE

# Enable logging
logger:
  #level: VERBOSE

#web_server:
#  port: 80

#captive_portal:

wifi:
  fast_connect: true
  networks:
  - ssid: !secret wifi_ssid
    password: !secret wifi_password

time:
  - platform: homeassistant    

esp32_ble_tracker:
  scan_parameters:
    interval: 1000ms
    window: 900ms

bluetooth_proxy:
  active: true

switch:
  - platform: restart
    name: device_WiFi-restart
    id: restart_switch
sensor:
  - platform: uptime
    name: "device Uptime Sensor"
api:
  encryption:
    key: "MASKED_OUT"

ota:
  platform: esphome
  password: "MASKED_OUT"

Adapt and try? :slight_smile:

Hello, i’m intrested in this project.

I’m works with hotel automation.

We have some locks that use ttlock tecnology.

I would like develop a system that’s work out of TTLOCK SERVER to encode cards and lock or unlock remotaly the lock.

For what I read this project just can lock or unlock remotaly. Is possible integrate a module to encode cards?

My understanding is that this integration allows to manage the cards/biometrics from home assistant, but currently i am struggling to install the bluetooth proxy. I tried the ESP bluetooth proxy and after months trying i am trying to install GitHub - kind3r/ttlock-sdk-js: JavaScript port of the TTLock Android SDK instead on a pi.
i am running homeassistant on a PC with no bluetooth, so the proxy is the only option for me.

If i can get it to run, i’ll post with the step by step instructions for it

1 Like

Has anyone picked up this project?

I have been trying all kinds of things for months now.
I tried the ESP-32 based proxy but it wouldn’t work for me.
I tried to install [kind3r][ttlock-sdk-js] on an orangepi 3 i had lying around as i had no raspberry pis but it didn’t work. At that point i had some typescript errors that i thought i had sorted, but i guess it didn’t.

Finally bought a RPI zero w 2 and upon installing the above repo… it seems a lot more promising.

when scanning for locks they now show, but pressing pair isn’t working.

I assume the issue is on:
ttlock-sdk-js/src/device/TTDevice.ts
on line 63:

    Object.getOwnPropertyNames(temp).forEach((key) => {
      if (!excludedKeys.has(key)) {
        const val = Reflect.get(this, key);
        if (typeof val != 'undefined' && ((typeof val == "string" && val != "") || typeof val != "string")) {
          if ((typeof val) == "object") {
            if (val.length && val.length > 0) {
              Reflect.set(json, key, val.toString('hex'));
            }
          } else {
            Reflect.set(json, key, val);
          }
        }
      }
    });

the problem is the lines:
if (val.length && val.length > 0) {
Reflect.set(json, key, val.toString(‘hex’));

the error is: error TS2339: Property ‘length’ does not exist on type ‘string extends keyof this ? this[keyof this & string] : any’.

I dont know enough of typescript to fix this. I tried to cast the val to any (val as any) or (val) but although they compile, the function to pair does not work

If anybody knows how to fix the code would be greatly appreciated

Ermargerd!!! i got it to work. No idea what made it work… so here’s all the steps:

On a fresh rpi zero w 2

sudo apt install git
sudo apt install npm
git clone https://github.com/kind3r/ttlock-sdk-js.git
cd ttlock-sdk-js
sudo nano ./src/device/TTDevice.ts

**Edited to add missing closing } as pointed out by MuzzaM **
replace the file’s content with the following:

'use strict';

import { EventEmitter } from "events";
import { LockType } from "../constant/Lock";

export class TTDevice extends EventEmitter {
  id: string = "";
  uuid: string = "";
  name: string = "";
  manufacturer: string = "unknown";
  model: string = "unknown";
  hardware: string = "unknown";
  firmware: string = "unknown";
  address: string = "";
  rssi: number = 0;
  protocolType: number = 0;
  protocolVersion: number = 0;
  scene: number = 0;
  groupId: number = 0;
  orgId: number = 0;
  lockType: LockType = LockType.UNKNOWN;
  isTouch: boolean = false;
  isUnlock: boolean = false;
  hasEvents: boolean = true;
  isSettingMode: boolean = false;
  txPowerLevel: number = 0;
  batteryCapacity: number = -1;
  date: number = 0;
  isWristband: boolean = false;
  isRoomLock: boolean = false;
  isSafeLock: boolean = false;
  isBicycleLock: boolean = false;
  isLockcar: boolean = false;
  isGlassLock: boolean = false;
  isPadLock: boolean = false;
  isCyLinder: boolean = false;
  isRemoteControlDevice: boolean = false;
  isDfuMode: boolean = false;
  isNoLockService: boolean = false;
  remoteUnlockSwitch: number = 0;
  disconnectStatus: number = 0;
  parkStatus: number = 0;

  toJSON(asObject: boolean = false): string | Object {
    const json: { [key: string]: any } = {};

    const excludedKeys = new Set([
      "_eventsCount"
    ]);

    Object.getOwnPropertyNames(this).forEach((key) => {
      if (!excludedKeys.has(key)) {
        const val = Reflect.get(this, key);

        if (val !== undefined && val !== '') {
          if (typeof val === "object" && val !== null) {
            if (Buffer.isBuffer(val)) {
              if (val.length > 0) {
                json[key] = val.toString('hex');
              }
            }
            else if(Array.isArray(val)) {
              if (val.length > 0) {
                json[key] = val.toString();
              }
            }
            else {
              json[key] = val;
            }
          } else {
            json[key] = val;
          }
        }
      }
    });

    return asObject ? json : JSON.stringify(json);
  }
}

sudo npm i
sudo npm run server-tool

when its running set the add on configuration to have the gateway to say noble
the ip to the pi IP
the gateway port to 2846
the dateway key to f8b55c272eb007f501560839be1f1e7e
and both user and pass to be admin

Finally on the webUI of the add on press the top right corner and activate the lock. it should show to be paired.
then try to pair. For me it didn’t go through first time. It took a good 20-30 attempts

lt only took the better part of a year… damn

Now… i need multiple proxies because the locks are too far to each other… good times

2 Likes

Nice work and thanks for the instructions, that’s a lot of attempts. Did you do any reset or is it just re-pair?

Just to clarify, is that esp32 bluetooth proxy or rpi w running the ttlock-sdk?

The locks will only pair to one device at a time. I had to remove them from the app before pairing to Home Assistant.

This was a RPI zero W 2 running the ttlock-sdk-js.
I never got the esp32 version to work unfortunately.

I now still need to do some extra work on this as i have 4 locks fairly far from each other beyond bluetooth range.

im looking at:

  1. Hacking the Pi to add an external antenna

  2. Alter the addon to allow multiple proxies

  3. use regular esp32 to extend the range of the pi.

I’m not sure what will pan out. Adding the antenna seems the simplest, and might work as my house isn’t that big and the locks are just barely out of range.
The other two options might be a bit more useful for the community.
We’ll see how it goes.

I am stubborn and keep at it for a while, but i do have other fights to fight. The cat flaps for example :slight_smile:

1 Like

Just to clarify, removing the lock from the app and doing a factory reset seems to amount to the same. In both cases all the stored access gets wiped and it all has to be done again.

A bit of a pain, but well worth it to get into Home Assistant with offline support

I was gonna ask about that if you need extra rpi zeros running nearby the locks. Would adding one of those long range bluetooth dongle to the pi work?

For now it’s limited to one proxy?

I do hope you could get that working

Really? I thought removing the lock from the app would still keep all the saved keycodes/fingerprint

That TTDevice.ts file content you listed is missing the last trailing ’ } ’
by my trial of your process steps.
Just so anybody else runs into the error it produces .

Thanks for the documenting the steps, have not yet completed pairing yet…

(Running a test on rpi4)

Completely correct, i must have missed it on the copy and paste.

I have edited my original post to correct the code

OK all done following the instructions above.
Thanks @TheTrueWanderer for documenting this.

A few notes from my testing this install.
I forgot to delete a TTLock HACS cloud integration, and this offline version had similar name which was a bit confusing before I realised.
Remember to turn off the original WIFI Bridge if you are coming from the cloud interface.
Once I set up the Pi4 running the Server I managed to PAIR the lock on the second attempt. I think the key is the order you activate the lock and hit the PAIR button.
From my reading and my own experience you need to activate the lock FIRST (mine ‘spoke’ a message to now enter bluetooth admin or some such words) … Then hit the PAIR button.

Very happy that I can run this OFFLINE.

With the old cloud connection I did use an option to SET Passage Mode between certain hours which was very handy, but this is not provided as far as I can see.
The sensors displayed via MQTT are LOCK/UNLOCK, Battery Level, RSSI.

I did change the Auto Lock from 5s to 59s, so this works.
I also tested the Sound On/OFF feature which worked.

I’ll probably be running this on a PiZero next once I tidy things up and do some more testing.

Just an update to my experience. It seems to work but after restarting the addon it cant read the access controls. I’m not sure if this is a common problem or just me.
Edit: so… this was true, but all of the sudden it started to work. not sure why. Its either a bit flaky, or behaves differently from a cron job and when run manually with a debugger attached. Very odd indeed.
Edit again: even being run as a service it works… but required a restart on the add on and a few tries. Must be what OP referred to bluetooth being unstable

I had my pi zero close to the lock and worked fine but as soon as i put the lock and the pi about 4 to 5 meters away, it doesn’t work anymore. I guess the only real way is to get a pi per lock. I don’t mind that but that will require changes to allow for that.

Looking at ttlock-sdk-js/tree/main/src/scanner/noble/NobleWebsocketBinding.ts
It seems to me that it would be possible to alter the webserver instance with an array and pass a comma separated list of IPs as a quick and dirty way to get this working with multiple proxies. Unfortunately i dont know how to make changes to the dependency an have the addon use it.

But will keep at looking at this

As a matter of interest, which Pi OS did you use for this?

The standard Raspberry Pi OS. Its pretty much debian and is very stable on the pi.

the pi zero w 2 might be overkill but i had no other PIs at hand and the zero 2 wasn’t that expensive

Thanks for the instructions! I’ve just moved in to a place with one of these locks and have been struggling to manage it without the very limited app. I saw your post, and grabbed a Pi zero W 2, fresh install, update/upgrade, followed your instructions but get “Error: Cannot find module ‘…/build/Release/bluetooth_hci_socket.node’” when ruinning ‘sudo npm run server-tool’. Am I missing something?

Hard to tell without checking all the previous steps :joy:

I took some notes when I did my setup if it helps …

Fresh install to SD Card using RaspImager
Using Bookworm 32 No Desktop to 16Gb

Power up with Card
Wait for connection to WIFI and get IP address

ssh [email protected]

might need to remove fingerprint from known hosts by using
ssh-keygen -R 192.168.1.208

Enter password to get to cmd line:

[email protected]’s password:
Linux rp32z2w 6.6.31+rpt-rpi-v7 #1 SMP Raspbian 1:6.6.31-1+rpt1 (2024-05-29) armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@rp32z2w:~ $

sudo apt update :heavy_check_mark:

sudo apt full-upgrade ✓

sudo reboot ✓

sudo apt autoremove ✓

sudo apt clean ✓

sudo apt install git ✓

sudo apt install npm ✓

git clone GitHub - kind3r/ttlock-sdk-js: JavaScript port of the TTLock Android SDK

cd ttlock-sdk-js

Now need to edit the TTDevice.ts file to resolve some errors in the code .
refer HASS Addon TTLock offline integration - #136 by TheTrueWanderer

sudo nano ./src/device/TTDevice.ts

cut and paste updated TTDevice.ts code into here and SAVE/EXIT

Check file looks OK
cat ./src/device/TTDevice.ts

sudo npm I

pi@rp32z2w:~/ttlock-sdk-js $ sudo npm I
npm WARN deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated [email protected]: This package is no longer supported.
npm WARN deprecated [email protected]: This package is no longer supported.
npm WARN deprecated [email protected]: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated [email protected]: This package is no longer supported.
npm WARN deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm WARN deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm WARN deprecated [email protected]: TSLint has been deprecated in favor of ESLint. Please see Roadmap: TSLint -> ESLint · Issue #4534 · palantir/tslint · GitHub for more information.

[email protected] prepare
npm run build

[email protected] build
rm -rf ./dist && tsc

added 212 packages, and audited 213 packages in 2m

24 packages are looking for funding
run npm fund for details

found 0 vulnerabilities

sudo npm run server-tool

pi@rp32z2w:~/ttlock-sdk-js $ sudo npm run server-tool

[email protected] server-tool
NOBLE_REPORT_ALL_HCI_EVENTS=1 node ./tools/server.js

20241005 21:35:36.674 noble - ws slave - server mode
20241005 21:35:36.806 → {“type”:“stateChange”,“state”:“poweredOn”}

Regards

That did it.

The only difference of note was the OS. I was using the default 64bit Raspbian from the flash tool. Switching to 32bit seems to have solved the issue.

Kia ora bro, thank you so much!

I must admit i didn’t pay any attention to what bitness OS i installed.
I have another pi on the way to try to connect multiple gateways to the addon and i’ll try to pay a bit more attention on the next install

I think a small change on ttlock-sdk-js might allow for that although i have absolutely no idea how to deploy it. I don’t really know how docker works :frowning:
but we’ll cross that bridge when we reach it