TTLOCK Integration with HA

i installed the docker and the app has been approved just now. where should I enter the promo code block “curl”?

any CMD window

ok i managed to send the instruction and it seems to have worked correctly. Now I have to create the docker. where should I enter the code below? there is no CMD inside the docker

docker run \
-e CLIENTID='your id' \
-e CLIENTSECRET='your secret' \
-e LOCKID='your lock id' \
-e USER='your prefixed application user' \
-e PASSWORD='your application users password md5 hashed' \
-p 5000:5000 stevendodd/ttlock

Run it in a CMD window on the machine you installed docker on

Rooot.999. I’d buy you a coffee for this integration!!! :slight_smile:

I have written it but I am using APIs and need to re-factor to use objects before they will accept it into the main branch core/homeassistant/components/ttlock at Add-TTLock-Platform · stevendodd/core · GitHub

# Example configuration.yaml entry
lock:
  - platform: ttlock
    name: "New Lock"
    client_id: xxx
    client_secret: xxx
    lockId: xxx
    username: xxx
    password: xxx

This lib already exists: https://github.com/tonyldo/ttlockio
And the author has also published a HA integration https://github.com/tonyldo/lock.ttlock

The library looks like I could use it but I can’t get it to authenticate atm

Here is what it looks like in my dev environment


3 Likes

way above my pay grade…can’t wait til it gets its “auto install” certification…thanks Steve

I approved your pull request! tks for colaborating!

3 Likes

any updated help for this newbie???

s

bump please?

Happy to find this topic, I hope the integration can be developed since many chinese affordable locks use ttlock an the other workwarounds didn’t work in my case.

1 Like

Totally agree with you Henry_Ceron. Fingers crossed that this gets some attention.

1 Like

This worked for me, but since I wanted to run the container inside my raspberrypi I rebuilt the docker file for ARM archs and pushed it to my docker hub, if anyone wants to use it: Docker Hub

Thank you @stevendodd and all folks sharing their templates and knowledge.

I’m a newbie and would appreciate a tutorial to integrate my TTLOCK with Home Assistant.

4 Likes

How can I help here - I’ve been playing with both the lib and direct APIs.

What do you mean by needing to re-factor the code - is there a link to what the hassio core wants?

2 Likes

The barrier to deliver a home assistant integration is quite high; I have set up a working version however they won’t accept an integration that has direct API calls in the home Assistant repository; GitHub - stevendodd/core at Add-TTLock-Platform

Have a working version using GitHub - tonyldo/ttlockio: Python wrapper for TTLock API however I’m not really happy with it; in order to initiate a pull request to the core home assistant repository it probably needs a bit more effort including documentation.

"""Support for TTLock."""
from __future__ import annotations

from ttlockwrapper import TTLock, TTlockAPIError
from datetime import datetime
import time
from typing import Any

import requests
import voluptuous as vol

from homeassistant.components.lock import PLATFORM_SCHEMA, LockEntity
from homeassistant.const import (
    ATTR_BATTERY_LEVEL,
    ATTR_HW_VERSION,
    ATTR_LOCKED,
    ATTR_MODEL,
    ATTR_SW_VERSION,
    CONF_CLIENT_ID,
    CONF_CLIENT_SECRET,
    CONF_DOMAIN,
    CONF_NAME,
    CONF_PASSWORD,
    CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

ATTR_AUTO_LOCK_TIME = "autoLockTime"
ATTR_PASSAGE_MODE = "passageMode"
ATTR_PASSAGE_MODE_AUTO_UNLOCK = "passageModeAutoUnlock"
ATTR_SOUND_VOLUME = "soundVolume"
ATTR_TAMPER_ALERT = "tamperAlert"
ATTR_ACCESS_TOKEN = "accessToken"
ATTR_ACCESS_TOKEN_EXPIRY_TIME = "accessTokenExpiryTime"
ATTR_LAST_USER = "lastUser"
ATTR_LAST_ENTRY_TIME = "lastEntryTime"
CONF_LOCK_ID = "lockId"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Optional(CONF_DOMAIN, default="euapi.ttlock.com"): cv.string,
        vol.Optional(CONF_NAME, default="TTLock"): cv.string,
        vol.Required(CONF_LOCK_ID): cv.string,
        vol.Required(CONF_CLIENT_ID): cv.string,
        vol.Required(CONF_CLIENT_SECRET): cv.string,
        vol.Required(CONF_USERNAME): cv.string,
        vol.Required(CONF_PASSWORD): cv.string,
    }
)


def current_milli_time():
    """Return time millis."""
    return round(time.time() * 1000)


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the TTLock platform."""
    domain = config.get(CONF_DOMAIN)
    client_id = config.get(CONF_CLIENT_ID)
    client_secret = config.get(CONF_CLIENT_SECRET)
    user = config.get(CONF_USERNAME)
    password = config.get(CONF_PASSWORD)
    lock_id = config.get(CONF_LOCK_ID)
    name = config.get(CONF_NAME)

    token = TTLock.get_token(client_id, client_secret, user, password, "", True)
    access_token = token["access_token"]
    refresh_token = token["refresh_token"]
    token_expiry_time = token["expires_in"] * 1000 + current_milli_time() - 25000

    add_entities(
        [
            TTLockDevice(
                access_token,
                refresh_token,
                token_expiry_time,
                domain,
                client_id,
                client_secret,
                lock_id,
                name,
            )
        ],
        update_before_add=True,
    )


class TTLockDevice(LockEntity):
    """Representation of a TTLock device."""

    def __init__(
        self,
        access_token,
        refresh_token,
        token_expiry_time,
        domain,
        client_id,
        client_secret,
        lock_id,
        name,
    ) -> None:
        """Initialize the TTLock device."""
        self._ttlock = TTLock(client_id, access_token)

        self._access_token = access_token
        self._refresh_token = refresh_token
        self._access_token_expiry_time = token_expiry_time
        self._domain = domain
        self._client_id = client_id
        self._client_secret = client_secret
        self._lock_id = lock_id
        self._nickname = name

        self._auto_lock_time = -1
        self._electric_quantity = -1
        self._firmware_revision: str | None = None
        self._hardware_revision: str | None = None
        self._lock_alias: str | None = None
        self._model_num: str | None = None
        self._passage_mode = -1
        self._passage_mode_auto_unlock = -1
        self._sound_volume = -1
        self._tamper_alert = -1
        self._last_user = ""
        self._last_entry_time: str | None = None
        self._is_locked = True
        self._responsive = False

    @property
    def name(self) -> str | None:
        """Return the name of the device."""
        return self._nickname

    @property
    def available(self) -> bool:
        """Return True if entity is available."""
        return self._responsive

    @property
    def is_locked(self) -> bool:
        """Return True if the device is currently locked, else False."""
        return self._is_locked

    def get_token(self) -> None:
        """Refresh access token."""
        if current_milli_time() > self._access_token_expiry_time:
            token = self._ttlock.refresh_token(
                self._client_id, self._client_secret, self._refresh_token, ""
            )
            self._access_token = token["access_token"]
            self._refresh_token = token["refresh_token"]
            self._access_token_expiry_time = (
                token["expires_in"] * 1000 + current_milli_time() - 25000
            )

    def unlock(self, **kwargs: Any) -> None:
        """Unlock the device."""
        self.get_token()
        self._ttlock.unlock(self._lock_id)
        self._is_locked = self._ttlock.lock_state(self._lock_id)

    def lock(self, **kwargs: Any) -> None:
        """Lock the device."""
        self.get_token()
        self._ttlock.lock(self._lock_id)
        self._is_locked = self._ttlock.lock_state(self._lock_id)

    def update(self) -> None:
        """Update the internal state of the device."""
        try:
            self._electric_quantity = self._ttlock.lock_electric_quantity(self._lock_id)

            if self._ttlock.lock_state(self._lock_id) == 0:
                self._is_locked = True
            else:
                self._is_locked = False

            for user_record in self._ttlock.get_lock_records_generator(
                self._lock_id, 1
            ):
                self._last_user = user_record["username"]
                self._last_entry_time = datetime.fromtimestamp(
                    int(user_record["lockDate"]) / 1000
                ).strftime("%a, %d %b %Y %H:%M")
                break

            self._responsive = True

        except TTlockAPIError:
            self._responsive = False
            raise

    @property
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return the state attributes."""
        return {
            ATTR_MODEL: self._model_num,
            ATTR_SW_VERSION: self._firmware_revision,
            ATTR_HW_VERSION: self._hardware_revision,
            ATTR_LOCKED: self._is_locked,
            ATTR_AUTO_LOCK_TIME: self._auto_lock_time,
            ATTR_PASSAGE_MODE: self._passage_mode,
            ATTR_PASSAGE_MODE_AUTO_UNLOCK: self._passage_mode_auto_unlock,
            ATTR_SOUND_VOLUME: self._sound_volume,
            ATTR_TAMPER_ALERT: self._tamper_alert,
            CONF_LOCK_ID: self._lock_id,
            ATTR_BATTERY_LEVEL: self._electric_quantity,
            ATTR_LAST_USER: self._last_user,
            ATTR_LAST_ENTRY_TIME: self._last_entry_time,
        }

1 Like

Is the TTLock2MQTT for this reason?
Or is it just me?
I left a GitHub issue last month.

Can you please confirm if this is an issue or just me?
I am running home assistant supervised.

Finally got everything working using TTlock2MQTT

My issue was I was using EMQX for MQTT rather than Mosquitto.

Even though that should not cause any issues but it does. For some reason, it only works with mosquito.

One crucial step missing after installing TTLOCK2MQTT is adding it to the dashboard.
It seems obvious, but some people might be struggling with that.

After installing the addon and proper connection, restart Home Assistant.
And then, you will see your TTLock gateway and lock in the MQTT as entities.

After that, we should now be able to add the lock to our UI. Go to Overview (or your choice of the dashboard) > click ‘+’ on the bottom right > click ‘By Entity’ up top > search “lock”. You should see your lock. Check all entities matching lock. Add to UI.

Finally:

image

2 Likes

How did you get this working? I’ve done everything in tonyldo-hassio-addons/DOCS.md at 183528083ba1e3d017ac97591a40a50ccf0e75cd · tonyldo/tonyldo-hassio-addons · GitHub and GitHub - tonyldo/ttlockio: Python wrapper for TTLock API but still can’t get it working.

In HASS the add-on logs show an invalid token error, but I’m absolutely certain it’s correct as I copied it directly from the https://euopen.ttlock.com/manager page

UPDATE: I got it all working at last. :smiley:

Tried TTlock2MQTT today, Quite complicated to set up but I got it working. However It doesn’t not as good as I wish.

  • Lock status takes a long time to update in HA starting from 1-60 sec. Even I configure publishstatdelay to 1 sec.
  • When TTlock2MQTT add-on is running there are some problems with the lock. Sometimes If I press a lock from a button or App. It will lock and follow by unlocking immediately after that.