@dodi-smart/ttlock-graphql-api v1.3.0
This package creates a TTLock GraphQL API, allowing for interaction with data at TTLock. It is unofficial TTLock GraphQL API generated based on documentation, mainly configured to be used as Hasura Remote Schema.
Here's an example of how to use the TTLock GraphQL API to get a list of smart locks of the configured TTLock user:
query MyLocks {
ttlock {
smartlocks {
lockName
lockId
details {
state {
state
}
electricQuantity
firmwareRevision
hardwareRevision
}
}
}
}It's recommended to add the TTLock GraphQL API as a Remote Schema in Hasura and connect data from your database with data in TTLock. By doing so, it's possible to request data from your database and TTLock in a single GraphQL query.
Here's an example of how to use the TTLock GraphQL API to get a list of smart locks for a specific TTLock user. Note that the user data is fetched from your database and the TTLock user data is fetched trough the TTLock API:
query UsersLocks {
users {
id
displayName
ttlocks {
# Remote schema relation in users table
lockName
lockId
details {
state {
state
}
electricQuantity
firmwareRevision
hardwareRevision
}
}
}
}Supported APIs
APIs can be added upon request. PRs are welcome of course!
https://euopen.ttlock.com/document/doc?urlName=userGuide%2FekeyEn.html
| Api | Supported | Comment |
|---|---|---|
| User | 🔴 | |
| Lock | 🟠| |
| Ekey | 🔴 | |
| Passcode | 🟢 | |
| Gateway | 🟠| |
| AddressToken | 🔴 | |
| IC Card | 🔴 | |
| Fingerprint | 🔴 | |
| Group | 🔴 | |
| Unlock record | 🟠| |
| Lock upgrade | 🔴 | |
| Wireless Keyboard | 🟢 | |
| Remote | 🔴 | |
| Door Sensor | 🔴 | |
| NB-IoT | 🔴 | |
| QR Code | 🔴 | |
| WiFi Lock | 🔴 |
🟢 - Supported 🟠- Partial 🔴 - Not supported
Getting Started
Install the package:
pnpm add @dodi-smart/ttlock-graphql-apiSetup serverless function
Create a new Serverless Function: functions/graphql/ttlock.ts:
import { createTTlockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTlockGraphQLServer({
graphiql: true,
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
return {
// Should provide authentication
}
},
onUpdateSession(context, auth) {
// Handle session update
}
})
export default serverSetup TTLock Management
Go to TTLock Management and obtain OAuth2 API key and secret by creating an application and wait for approval.
- Add
TTLOCK_CLIENT_IDas an environment variable. - Add
TTLOCK_CLIENT_SECRETas an environment variable.
If you're using Nhost, add TTLOCK_CLIENT_ID, TTLOCK_CLIENT_SECRET to .env.development like this:
TTLOCK_CLIENT_ID=6EYY••••
TTLOCK_CLIENT_SECRET=BPum••••And add the production key values to environment variables in the Nhost dashboard.
Start Nhost
nhost upLearn more about the Nhost CLI.
Test
Test the TTLock GraphQL API in the browser:
https://local.functions.nhost.run/v1/graphql/ttlock
Remote Schema
Add the TTLock GraphQL API as a Remote Schema in Hasura.
URL
{{NHOST_FUNCTIONS_URL}}/graphql/ttlockHeaders
x-nhost-webhook-secret: NHOST_WEBHOOK_SECRET (From env var)The
NHOST_WEBHOOK_SECRETis used to verify that the request is coming from Nhost. The environment variable is a system environment variable and is always available.
Authentication
You have several options to authentication your users agains TTLock using this library. All of them depends on the usecase:
Single user usecase (easy to setup)
When the system should work with the locks of a single user (administrator). Permissions and access to the locks are granted to others users via the administrator or using Hasura user roles.
Multi user usecase (advanced)
When system work with locks of multiple users. Each user can authenticate against TTLock using their own credentials via OAuth 2.0. This usecase can be used for multi tenant apps for example.
How to choose
| Use case | Using JWT Token (easy) | OAuth 2.0 (advanced) |
|---|---|---|
| Single user | 🟠| 🟢 |
| Multi user | 🔴 | 🟢 |
🟢 - Preferable way 🟠- Supported 🔴 - Not supported
Using JWT Token
Obtain a JWT token by logging the user via curl, paw or postman. The Refresh token is valid for 10 years.
- Add
TTLOCK_ACCESS_TOKENas an environment variable. - Add
TTLOCK_REFRESH_TOKENas an environment variable.
If you're using Nhost, add TTLOCK_ACCESS_TOKEN and TTLOCK_REFRESH_TOKEN to .env.development like this:
TTLOCK_ACCESS_TOKEN=59f6d••••
TTLOCK_REFRESH_TOKEN=BPum••••And add the production key values to environment variables in the Nhost dashboard.
Configure the TTLock GraphQL API
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
const { request } = context
return {
accessToken: process.env["TTLOCK_ACCESS_TOKEN"]
refreshToken: process.env["TTLOCK_REFRESH_TOKEN"]
}
},
onUpdateSession(context, auth) {
// Can be empty as TTLOCK_REFRESH_TOKEN is valid for 10 years
}
})
export default serverOAuth 2.0
From the TTLock Management page get client id and secret.
- Add
TTLOCK_CLIENT_IDas an environment variable. - Add
TTLOCK_CLIENT_SECRETas an environment variable.
If you're using Nhost, add TTLOCK_CLIENT_ID and TTLOCK_CLIENT_SECRET to .env.development like this:
TTLOCK_CLIENT_ID=59f6d••••
TTLOCK_CLIENT_SECRET=BPum••••Configure the TTLock GraphQL API
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
const { userClaims } = context
const { user } = await gqlSDK.getUser({
id: userId
})
return {
// Example if ttlock creds are saved in user's metadata, can differ
accessToken: user.metadata.ttlockAccessToken,
refreshToken: user.metadata.ttlockRefreshToken,
}
},
onUpdateSession(context, auth) {
const { userClaims } = context
const { accessToken, refreshToken } = auth
await gqlSDK.updateUserTTLockAuth({
id: userId,
accessToken,
refreshToken
})
}
})
export default serverPermissions
Here's a minimal example without any custom permissions. Only requests using the x-hasura-admin-secret header will work:
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api'
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
provideAuth(context) {
/* ... */
},
onUpdateSession(context, auth) {
/* ... */
}
})
export default serverFor more granular permissions, you can pass an isAllowed function to the createTTLockGraphQLServer. The isAllowed function takes a context as parameter and runs every time the GraphQL server makes a request to TTLock to get or modify data for a specific TTLock user.
Here is an example of an isAllowed function:
import { createTTLockGraphQLServer } from '@dodi-smart/ttlock-graphql-api';
const isAllowed = (context: Context) => {
const { isAdmin, userClaims } = context
// allow all requests if they have a valid `x-hasura-admin-secret`
if (isAdmin) {
return true
}
// get user id
const userId = userClaims['x-hasura-user-id']
// check if the user is signed in
if (!userId) {
return false
}
// get more user information from the database
const { user } = await gqlSDK.getUser({
id: userId
})
if (!user) {
return false
}
// other checks
}
const server = createTTLockGraphQLServer({
graphqlEndpoint: '/graphql/ttlock',
healthCheckEndpoint: '/graphql/ttlock/health',
isAllowed,
provideAuth(context) {
/* ... */
},
onUpdateSession(context, auth) {
/* ... */
}
});
export default server;Context
The context object contains:
userClaims- verified JWT claims from the user's access token.isAdmin-trueif the request was made using a validx-hasura-admin-secretheader.request- Fetch API Request object that represents the incoming HTTP request in platform-independent way. It can be useful for accessing headers to authenticate a userquery- the DocumentNode that was parsed from the GraphQL query stringoperationName- the operation name selected from the incoming queryvariables- the variables that were defined in the queryextensions- the extensions that were received from the client
Read more about the default context from GraphQL Yoga.