homebridge-ttlock-accesscode 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/config.schema.json +83 -0
- package/dist/api/ttlockApi.d.ts +29 -0
- package/dist/api/ttlockApi.js +255 -0
- package/dist/api/ttlockApi.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +79 -0
- package/dist/config.js.map +1 -0
- package/dist/homekitDevice.d.ts +44 -0
- package/dist/homekitDevice.js +506 -0
- package/dist/homekitDevice.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +42 -0
- package/dist/platform.js +336 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessoryInformation.d.ts +3 -0
- package/dist/platformAccessoryInformation.js +35 -0
- package/dist/platformAccessoryInformation.js.map +1 -0
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +3 -0
- package/dist/settings.js.map +1 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +57 -0
- package/dist/utils.js.map +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Robert Louis Morren, Jr.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<p align="center" style="text-align:center;">
|
|
2
|
+
|
|
3
|
+
<img src="https://github.com/homebridge/branding/raw/latest/logos/homebridge-wordmark-logo-vertical.png" width="150" style="display:block; margin:auto;">
|
|
4
|
+
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<span align="center">
|
|
8
|
+
|
|
9
|
+
# Homebridge TTLock Access Code Plug-In
|
|
10
|
+
|
|
11
|
+
</span>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://github.com/ZeliardM/homebridge-ttlock-accesscode/blob/latest/LICENSE"><img src="https://badgen.net/npm/license/homebridge-ttlock-accesscode" alt="mit license"></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/homebridge-ttlock-accesscode/v/latest"><img src="https://badgen.net/npm/v/homebridge-ttlock-accesscode/latest?label=npm@latest" alt="latest npm version"></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/homebridge-ttlock-accesscode/v/latest"><img src="https://badgen.net/npm/dt/homebridge-ttlock-accesscode" alt="npm downloads total"></a>
|
|
17
|
+
<a href="https://www.paypal.me/ZeliardM/USD/"><img src="https://badgen.net/badge/donate/paypal?color=orange" alt="donate"></a>
|
|
18
|
+
<a href="https://github.com/sponsors/ZeliardM"><img src="https://badgen.net/badge/donate/github?color=orange" alt="donate"></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
|
|
23
|
+
>## PLEASE READ!!!
|
|
24
|
+
>HomeKey Support is not physically possible with TTLock Readers, but Access Code Features are working in HomeKit.
|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
This is a [Homebridge](https://github.com/homebridge/homebridge) plug-in based for integrating TTLock smart locks with the TTLock Cloud API.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Get the status of your TTLock devices.
|
|
33
|
+
- Lock and unlock your TTLock devices.
|
|
34
|
+
- Manage passcodes for your TTLock devices.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
1. TTLock Smart Lock
|
|
39
|
+
2. A Gateway (If your lock does not have built-in Wi-Fi, I purchased a TTLock G2 Gateway off Amazon.)
|
|
40
|
+
3. Remote Unlock must be enabled through the TTLock App locally within bluetooth or Wi-Fi rnage of your lock.
|
|
41
|
+
4. You must create a TTLock Open API Account. This is not the username and password that will be used in the setting in the plug-in, this is for creating an OAUTH2.0 App with the TTLock Cloud API for the plug-in to get access to your TTLock Devices.
|
|
42
|
+
|
|
43
|
+
## Setup
|
|
44
|
+
|
|
45
|
+
1. Create an account in the [TTLock Cloud API](https://euopen.ttlock.com/register).
|
|
46
|
+
2. Create your OAUTH2.0 App, give it a name, icon, select the 'App' option, and a short description.</br>
|
|
47
|
+
*This may take a few days to be approved by their Development Team.*
|
|
48
|
+
3. Once approved, you will need the *client_id* and *client_secret* from the app.
|
|
49
|
+
4. Setup the TTLock Mobile App, create an account in the Mobile App, setup and install your lock(s).</br>
|
|
50
|
+
*If your Lock is not Wi-Fi Capable, make sure you setup your Gateway as well and have it close enough to your Lock for the Gateway to pick up the Lock in the Mobile App.*
|
|
51
|
+
5. Once you have your Mobile App Username and Password, and the client_id and client_secret, you can setup the plug-in in Homebridge.
|
|
52
|
+
|
|
53
|
+
## IMPORTANT
|
|
54
|
+
I have only tested this with a G2 Gateway.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "TTLockAccessCode",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"headerDisplay": "TTLock Access Code Plugin.<p>Follow the [README](https://github.com/ZeliardM/homebridge-ttlock-accesscode/blob/latest/README.md) for configuration instructions and then click \"Save\" to get started.</p>",
|
|
6
|
+
"footerDisplay": "",
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"title": "Name",
|
|
12
|
+
"type": "string",
|
|
13
|
+
"required": true,
|
|
14
|
+
"default": "TTLockAccessCode",
|
|
15
|
+
"minLength": 1
|
|
16
|
+
},
|
|
17
|
+
"clientId": {
|
|
18
|
+
"title": "Client ID",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Client ID for TTLock Cloud API.",
|
|
21
|
+
"placeholder": "Client ID",
|
|
22
|
+
"minLength": 1
|
|
23
|
+
},
|
|
24
|
+
"clientSecret": {
|
|
25
|
+
"title": "Client Secret",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Client Secret for TTLock Cloud API.",
|
|
28
|
+
"placeholder": "Client Secret",
|
|
29
|
+
"minLength": 1
|
|
30
|
+
},
|
|
31
|
+
"username": {
|
|
32
|
+
"title": "Username",
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Username for TTLock App Account.",
|
|
35
|
+
"placeholder": "Username",
|
|
36
|
+
"minLength": 1
|
|
37
|
+
},
|
|
38
|
+
"password": {
|
|
39
|
+
"title": "Password",
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Password for TTLock App Account.",
|
|
42
|
+
"placeholder": "Password",
|
|
43
|
+
"minLength": 1
|
|
44
|
+
},
|
|
45
|
+
"pollingInterval": {
|
|
46
|
+
"title": "Polling Interval (seconds)",
|
|
47
|
+
"type": "integer",
|
|
48
|
+
"description": "How often to check device status in the background (seconds)",
|
|
49
|
+
"default": 5
|
|
50
|
+
},
|
|
51
|
+
"discoveryPollingInterval": {
|
|
52
|
+
"title": "Discovery Polling Interval (seconds)",
|
|
53
|
+
"type": "integer",
|
|
54
|
+
"description": "How often to discover new devices in the background (seconds)",
|
|
55
|
+
"default": 300
|
|
56
|
+
},
|
|
57
|
+
"offlineInterval": {
|
|
58
|
+
"title": "Offline Interval (days)",
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"description": "How often to remove offline devices in the background (days)",
|
|
61
|
+
"default": 7
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"layout": [
|
|
66
|
+
"name",
|
|
67
|
+
"clientId",
|
|
68
|
+
"clientSecret",
|
|
69
|
+
"username",
|
|
70
|
+
"password",
|
|
71
|
+
{
|
|
72
|
+
"type": "fieldset",
|
|
73
|
+
"title": "Device Discovery (Optional)",
|
|
74
|
+
"description": "Customize device discovery",
|
|
75
|
+
"expandable": true,
|
|
76
|
+
"items": [
|
|
77
|
+
"pollingInterval",
|
|
78
|
+
"discoveryPollingInterval",
|
|
79
|
+
"offlineInterval"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Logging } from 'homebridge';
|
|
2
|
+
import { BatteryLevel, Lock, LockDetails, LockIdList, LockState, PasscodeList } from '../types/index.js';
|
|
3
|
+
export declare class TTLockApi {
|
|
4
|
+
private log;
|
|
5
|
+
private clientId;
|
|
6
|
+
private clientSecret;
|
|
7
|
+
private apiClient;
|
|
8
|
+
accessToken: string | null;
|
|
9
|
+
refreshToken: string | null;
|
|
10
|
+
private tokenMutex;
|
|
11
|
+
constructor(log: Logging, clientId: string, clientSecret: string);
|
|
12
|
+
private encryptPassword;
|
|
13
|
+
authenticate(username: string, password: string): Promise<void>;
|
|
14
|
+
private refreshTokenIfNeeded;
|
|
15
|
+
private makeAuthenticatedRequest;
|
|
16
|
+
private handleError;
|
|
17
|
+
getLocks(): Promise<LockIdList>;
|
|
18
|
+
getLockDetails(lockId: string): Promise<LockDetails>;
|
|
19
|
+
getLockState(lockId: string): Promise<LockState>;
|
|
20
|
+
mapToLock(lockDetails: LockDetails, lockState: LockState, lockBattery: BatteryLevel, lockPasscodes: PasscodeList): Promise<Lock>;
|
|
21
|
+
lock(lockId: string): Promise<void>;
|
|
22
|
+
unlock(lockId: string): Promise<void>;
|
|
23
|
+
getBatteryLevel(lockId: string): Promise<BatteryLevel>;
|
|
24
|
+
getPasscodes(lockId: string): Promise<PasscodeList>;
|
|
25
|
+
addPasscode(lockId: string, passcode: string): Promise<{
|
|
26
|
+
keyboardPwdId: string;
|
|
27
|
+
}>;
|
|
28
|
+
deletePasscode(lockId: string, passcodeId: string): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { Mutex } from 'async-mutex';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import qs from 'qs';
|
|
5
|
+
class RequestFailed extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'RequestFailed';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class TTLockApi {
|
|
12
|
+
log;
|
|
13
|
+
clientId;
|
|
14
|
+
clientSecret;
|
|
15
|
+
apiClient;
|
|
16
|
+
accessToken = null;
|
|
17
|
+
refreshToken = null;
|
|
18
|
+
tokenMutex = new Mutex();
|
|
19
|
+
constructor(log, clientId, clientSecret) {
|
|
20
|
+
this.log = log;
|
|
21
|
+
this.clientId = clientId;
|
|
22
|
+
this.clientSecret = clientSecret;
|
|
23
|
+
this.apiClient = axios.create({
|
|
24
|
+
baseURL: 'https://euapi.ttlock.com/',
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
this.log.debug('TTLockApi instance created');
|
|
30
|
+
}
|
|
31
|
+
encryptPassword(password) {
|
|
32
|
+
return crypto.createHash('md5').update(password).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
async authenticate(username, password) {
|
|
35
|
+
this.log.debug('Authenticating with TTLock API');
|
|
36
|
+
try {
|
|
37
|
+
const encryptedPassword = this.encryptPassword(password);
|
|
38
|
+
const response = await this.apiClient.post('oauth2/token', qs.stringify({
|
|
39
|
+
client_id: this.clientId,
|
|
40
|
+
client_secret: this.clientSecret,
|
|
41
|
+
grant_type: 'password',
|
|
42
|
+
username,
|
|
43
|
+
password: encryptedPassword,
|
|
44
|
+
}));
|
|
45
|
+
if (response.data.access_token && response.data.refresh_token) {
|
|
46
|
+
this.accessToken = response.data.access_token;
|
|
47
|
+
this.refreshToken = response.data.refresh_token;
|
|
48
|
+
this.log.info('Authenticated with TTLock API');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.log.error('Authentication response did not contain tokens:', response.data);
|
|
52
|
+
throw new Error('Authentication failed: No tokens received');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
this.handleError('Failed to authenticate with TTLock API', error);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async refreshTokenIfNeeded() {
|
|
61
|
+
if (!this.refreshToken) {
|
|
62
|
+
throw new Error('No refresh token available. Please call authenticate() first.');
|
|
63
|
+
}
|
|
64
|
+
const release = await this.tokenMutex.acquire();
|
|
65
|
+
try {
|
|
66
|
+
this.log.debug('Refreshing access token');
|
|
67
|
+
const response = await this.apiClient.post('oauth2/token', qs.stringify({
|
|
68
|
+
client_id: this.clientId,
|
|
69
|
+
client_secret: this.clientSecret,
|
|
70
|
+
grant_type: 'refresh_token',
|
|
71
|
+
refresh_token: this.refreshToken,
|
|
72
|
+
}));
|
|
73
|
+
if (response.data.access_token && response.data.refresh_token) {
|
|
74
|
+
this.accessToken = response.data.access_token;
|
|
75
|
+
this.refreshToken = response.data.refresh_token;
|
|
76
|
+
this.log.debug('Access token refreshed');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new Error('Failed to refresh token: Invalid response');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.handleError('Failed to refresh access token', error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
release();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async makeAuthenticatedRequest(endpoint, method = 'GET', data) {
|
|
91
|
+
const maxRetries = 3;
|
|
92
|
+
if (!this.accessToken) {
|
|
93
|
+
throw new Error('Not authenticated. Please call authenticate() first.');
|
|
94
|
+
}
|
|
95
|
+
const requestData = {
|
|
96
|
+
...data,
|
|
97
|
+
clientId: this.clientId,
|
|
98
|
+
accessToken: this.accessToken,
|
|
99
|
+
date: Date.now(),
|
|
100
|
+
};
|
|
101
|
+
const fullEndpoint = `v3/${endpoint}`;
|
|
102
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
103
|
+
try {
|
|
104
|
+
const response = await this.apiClient.request({
|
|
105
|
+
url: fullEndpoint,
|
|
106
|
+
method,
|
|
107
|
+
headers: {
|
|
108
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
109
|
+
},
|
|
110
|
+
params: method === 'GET' ? requestData : undefined,
|
|
111
|
+
data: method === 'POST' ? qs.stringify(requestData) : undefined,
|
|
112
|
+
});
|
|
113
|
+
if (response.data.errcode && response.data.errcode !== 0) {
|
|
114
|
+
throw new RequestFailed(`API returned error: ${response.data.errmsg || 'Unknown error'}`);
|
|
115
|
+
}
|
|
116
|
+
return response.data;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error instanceof RequestFailed) {
|
|
120
|
+
const errorMsg = error.message;
|
|
121
|
+
this.log.debug(`Attempt ${attempt + 1} failed: ${errorMsg}`);
|
|
122
|
+
if (attempt < maxRetries) {
|
|
123
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
124
|
+
this.log.debug(`Retrying in ${delay / 1000} seconds...`);
|
|
125
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (axios.isAxiosError(error) && error.response) {
|
|
130
|
+
if (error.response.status === 401) {
|
|
131
|
+
this.log.debug('Access token expired, refreshing token...');
|
|
132
|
+
await this.refreshTokenIfNeeded();
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
this.log.error(`Request failed: status=${error.response.status}, body=${JSON.stringify(error.response.data)}`);
|
|
136
|
+
throw new RequestFailed(`Request failed: status=${error.response.status}, body=${JSON.stringify(error.response.data)}`);
|
|
137
|
+
}
|
|
138
|
+
this.handleError('Request failed', error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw new Error('Max retries reached');
|
|
143
|
+
}
|
|
144
|
+
handleError(message, error) {
|
|
145
|
+
if (axios.isAxiosError(error)) {
|
|
146
|
+
this.log.error(`${message}: ${error.response?.data || error.message}`);
|
|
147
|
+
}
|
|
148
|
+
else if (error instanceof Error) {
|
|
149
|
+
this.log.error(`${message}: ${error.message}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.log.error(`${message}: ${JSON.stringify(error)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async getLocks() {
|
|
156
|
+
this.log.debug('Fetching list of locks');
|
|
157
|
+
const response = await this.makeAuthenticatedRequest('lock/list', 'GET', { pageNo: 1, pageSize: 1000 });
|
|
158
|
+
if (!response.list || !Array.isArray(response.list)) {
|
|
159
|
+
this.log.error('Invalid response format: expected list of locks');
|
|
160
|
+
throw new Error('Invalid response format: expected list of locks');
|
|
161
|
+
}
|
|
162
|
+
this.log.info(`Found ${response.list.length} locks`);
|
|
163
|
+
return response.list.map((item) => ({
|
|
164
|
+
lockId: item.lockId.toString(),
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
async getLockDetails(lockId) {
|
|
168
|
+
this.log.debug(`Fetching details for lock: ${lockId}`);
|
|
169
|
+
const data = await this.makeAuthenticatedRequest('lock/detail', 'GET', { lockId });
|
|
170
|
+
return {
|
|
171
|
+
lockId: data.lockId.toString(),
|
|
172
|
+
lockAlias: data.lockAlias,
|
|
173
|
+
lockMac: data.lockMac,
|
|
174
|
+
modelNum: data.modelNum,
|
|
175
|
+
hardwareRevision: data.hardwareRevision,
|
|
176
|
+
firmwareRevision: data.firmwareRevision,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async getLockState(lockId) {
|
|
180
|
+
this.log.debug(`Fetching state for lock: ${lockId}`);
|
|
181
|
+
const data = await this.makeAuthenticatedRequest('lock/queryOpenState', 'GET', { lockId });
|
|
182
|
+
return {
|
|
183
|
+
state: data.state === 0 ? 1 : data.state === 1 ? 0 : data.state,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async mapToLock(lockDetails, lockState, lockBattery, lockPasscodes) {
|
|
187
|
+
this.log.debug(`Mapping lock details to Lock object for lock: ${lockDetails.lockId}`);
|
|
188
|
+
return {
|
|
189
|
+
id: lockDetails.lockId.toString(),
|
|
190
|
+
alias: lockDetails.lockAlias,
|
|
191
|
+
mac: lockDetails.lockMac,
|
|
192
|
+
model: lockDetails.modelNum,
|
|
193
|
+
hardwareRevision: lockDetails.hardwareRevision,
|
|
194
|
+
firmwareRevision: lockDetails.firmwareRevision,
|
|
195
|
+
state: lockState.state,
|
|
196
|
+
battery: lockBattery.battery,
|
|
197
|
+
passCodes: lockPasscodes,
|
|
198
|
+
offline: false,
|
|
199
|
+
lastSeen: new Date(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
async lock(lockId) {
|
|
203
|
+
this.log.debug(`Locking lock: ${lockId}`);
|
|
204
|
+
await this.makeAuthenticatedRequest('lock/lock', 'POST', { lockId });
|
|
205
|
+
this.log.debug(`Lock ${lockId} locked`);
|
|
206
|
+
}
|
|
207
|
+
async unlock(lockId) {
|
|
208
|
+
this.log.debug(`Unlocking lock: ${lockId}`);
|
|
209
|
+
await this.makeAuthenticatedRequest('lock/unlock', 'POST', { lockId });
|
|
210
|
+
this.log.debug(`Lock ${lockId} unlocked`);
|
|
211
|
+
}
|
|
212
|
+
async getBatteryLevel(lockId) {
|
|
213
|
+
this.log.debug(`Fetching battery level for lock: ${lockId}`);
|
|
214
|
+
const data = await this.makeAuthenticatedRequest('lock/queryElectricQuantity', 'GET', { lockId });
|
|
215
|
+
return {
|
|
216
|
+
battery: data.electricQuantity,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async getPasscodes(lockId) {
|
|
220
|
+
this.log.debug(`Fetching passcodes for lock: ${lockId}`);
|
|
221
|
+
const response = await this.makeAuthenticatedRequest('lock/listKeyboardPwd', 'GET', { lockId, pageNo: 1, pageSize: 1000, orderBy: 0 });
|
|
222
|
+
if (!response.list || !Array.isArray(response.list)) {
|
|
223
|
+
this.log.error('Invalid response format: expected list of passcodes');
|
|
224
|
+
throw new Error('Invalid response format: expected list of passcodes');
|
|
225
|
+
}
|
|
226
|
+
this.log.debug(`Found ${response.list.length} passcodes for lock: ${lockId}`);
|
|
227
|
+
return response.list.map((item, index) => ({
|
|
228
|
+
id: item.keyboardPwdId.toString(),
|
|
229
|
+
index: (index).toString(),
|
|
230
|
+
lockId: item.lockId.toString(),
|
|
231
|
+
passcode: item.keyboardPwd,
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
async addPasscode(lockId, passcode) {
|
|
235
|
+
this.log.debug(`Adding passcode to lock: ${lockId}`);
|
|
236
|
+
const response = await this.makeAuthenticatedRequest('keyboardPwd/add', 'POST', {
|
|
237
|
+
lockId,
|
|
238
|
+
keyboardPwd: passcode,
|
|
239
|
+
keyboardPwdType: 2,
|
|
240
|
+
addType: 2,
|
|
241
|
+
});
|
|
242
|
+
this.log.debug(`Passcode added to lock: ${lockId}`);
|
|
243
|
+
return response;
|
|
244
|
+
}
|
|
245
|
+
async deletePasscode(lockId, passcodeId) {
|
|
246
|
+
this.log.debug(`Deleting passcode for lock: ${lockId}`);
|
|
247
|
+
await this.makeAuthenticatedRequest('keyboardPwd/delete', 'POST', {
|
|
248
|
+
lockId,
|
|
249
|
+
keyboardPwdId: passcodeId,
|
|
250
|
+
deleteType: 2,
|
|
251
|
+
});
|
|
252
|
+
this.log.debug(`Passcode deleted for lock: ${lockId}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=ttlockApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttlockApi.js","sourceRoot":"","sources":["../../src/api/ttlockApi.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare class ConfigParseError extends Error {
|
|
2
|
+
errors?: string[] | null | undefined;
|
|
3
|
+
unknownError?: unknown | undefined;
|
|
4
|
+
constructor(message: string, errors?: string[] | null | undefined, unknownError?: unknown | undefined);
|
|
5
|
+
private formatMessage;
|
|
6
|
+
}
|
|
7
|
+
export interface TTLockAccessCodeConfigInput {
|
|
8
|
+
name?: string;
|
|
9
|
+
clientId?: string;
|
|
10
|
+
clientSecret?: string;
|
|
11
|
+
username?: string;
|
|
12
|
+
password?: string;
|
|
13
|
+
pollingInterval?: number;
|
|
14
|
+
discoveryPollingInterval?: number;
|
|
15
|
+
offlineInterval?: number;
|
|
16
|
+
}
|
|
17
|
+
export type TTLockAccessCodeConfig = {
|
|
18
|
+
name: string;
|
|
19
|
+
clientId: string;
|
|
20
|
+
clientSecret: string;
|
|
21
|
+
username: string;
|
|
22
|
+
password: string;
|
|
23
|
+
discoveryOptions: {
|
|
24
|
+
pollingInterval: number;
|
|
25
|
+
discoveryPollingInterval: number;
|
|
26
|
+
offlineInterval: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export declare const defaultConfig: TTLockAccessCodeConfig;
|
|
30
|
+
export declare function parseConfig(config: Record<string, unknown>): TTLockAccessCodeConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isObjectLike } from './utils.js';
|
|
2
|
+
export class ConfigParseError extends Error {
|
|
3
|
+
errors;
|
|
4
|
+
unknownError;
|
|
5
|
+
constructor(message, errors, unknownError) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.errors = errors;
|
|
8
|
+
this.unknownError = unknownError;
|
|
9
|
+
this.name = 'ConfigParseError';
|
|
10
|
+
this.message = this.formatMessage(message, errors, unknownError);
|
|
11
|
+
Error.captureStackTrace(this, this.constructor);
|
|
12
|
+
}
|
|
13
|
+
formatMessage(message, errors, unknownError) {
|
|
14
|
+
let formattedMessage = message;
|
|
15
|
+
if (errors && errors.length > 0) {
|
|
16
|
+
const errorsAsString = errors.join('\n');
|
|
17
|
+
formattedMessage += `:\n${errorsAsString}`;
|
|
18
|
+
}
|
|
19
|
+
if (unknownError instanceof Error) {
|
|
20
|
+
formattedMessage += `\nAdditional Error: ${unknownError.message}`;
|
|
21
|
+
}
|
|
22
|
+
else if (unknownError) {
|
|
23
|
+
formattedMessage += `\nAdditional Error: [Error details not available: ${unknownError}]`;
|
|
24
|
+
}
|
|
25
|
+
return formattedMessage;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const defaultConfig = {
|
|
29
|
+
name: 'TTLockAccessCode',
|
|
30
|
+
clientId: '',
|
|
31
|
+
clientSecret: '',
|
|
32
|
+
username: '',
|
|
33
|
+
password: '',
|
|
34
|
+
discoveryOptions: {
|
|
35
|
+
pollingInterval: 5,
|
|
36
|
+
discoveryPollingInterval: 300,
|
|
37
|
+
offlineInterval: 7,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
function validateConfig(config) {
|
|
41
|
+
const errors = [];
|
|
42
|
+
validateType(config, 'name', 'string', errors);
|
|
43
|
+
validateType(config, 'clientId', 'string', errors);
|
|
44
|
+
validateType(config, 'clientSecret', 'string', errors);
|
|
45
|
+
validateType(config, 'username', 'string', errors);
|
|
46
|
+
validateType(config, 'password', 'string', errors);
|
|
47
|
+
validateType(config, 'pollingInterval', 'number', errors);
|
|
48
|
+
validateType(config, 'discoveryPollingInterval', 'number', errors);
|
|
49
|
+
validateType(config, 'offlineInterval', 'number', errors);
|
|
50
|
+
return errors;
|
|
51
|
+
}
|
|
52
|
+
function validateType(config, key, expectedType, errors) {
|
|
53
|
+
if (config[key] !== undefined && typeof config[key] !== expectedType) {
|
|
54
|
+
errors.push(`\`${key}\` should be a ${expectedType}.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function parseConfig(config) {
|
|
58
|
+
const errors = validateConfig(config);
|
|
59
|
+
if (errors.length > 0) {
|
|
60
|
+
throw new ConfigParseError('Error parsing config', errors);
|
|
61
|
+
}
|
|
62
|
+
if (!isObjectLike(config)) {
|
|
63
|
+
throw new ConfigParseError('Error parsing config');
|
|
64
|
+
}
|
|
65
|
+
const c = { ...defaultConfig, ...config };
|
|
66
|
+
return {
|
|
67
|
+
name: c.name ?? defaultConfig.name,
|
|
68
|
+
clientId: c.clientId ?? defaultConfig.clientId,
|
|
69
|
+
clientSecret: c.clientSecret ?? defaultConfig.clientSecret,
|
|
70
|
+
username: c.username ?? defaultConfig.username,
|
|
71
|
+
password: c.password ?? defaultConfig.password,
|
|
72
|
+
discoveryOptions: {
|
|
73
|
+
pollingInterval: (c.pollingInterval ?? defaultConfig.discoveryOptions.pollingInterval) * 1000,
|
|
74
|
+
discoveryPollingInterval: (c.discoveryPollingInterval ?? defaultConfig.discoveryOptions.discoveryPollingInterval) * 1000,
|
|
75
|
+
offlineInterval: (c.offlineInterval ?? defaultConfig.discoveryOptions.offlineInterval) * 24 * 60 * 60 * 1000,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PlatformAccessory } from 'homebridge';
|
|
2
|
+
import { TTLockAccessoryContext, TTLockAccessCodePlatform } from './platform.js';
|
|
3
|
+
import { Lock } from './types/index.js';
|
|
4
|
+
export declare class TTLockHomeKitDevice {
|
|
5
|
+
private readonly platform;
|
|
6
|
+
lock: Lock;
|
|
7
|
+
private readonly log;
|
|
8
|
+
private readonly ttLockApi?;
|
|
9
|
+
private debounceTimeout;
|
|
10
|
+
private lastStateChangeTime;
|
|
11
|
+
private lockBusy;
|
|
12
|
+
private pollingInterval?;
|
|
13
|
+
private previousLock?;
|
|
14
|
+
private updateEmitter;
|
|
15
|
+
isUpdating: boolean;
|
|
16
|
+
platformAccessory: PlatformAccessory<TTLockAccessoryContext>;
|
|
17
|
+
constructor(platform: TTLockAccessCodePlatform, lock: Lock);
|
|
18
|
+
private initalizeAccessory;
|
|
19
|
+
private updatePlatformAccessory;
|
|
20
|
+
private correctPlatformAccessory;
|
|
21
|
+
get id(): string;
|
|
22
|
+
get name(): string;
|
|
23
|
+
get manufacturer(): string;
|
|
24
|
+
get model(): string;
|
|
25
|
+
get serialNumber(): string;
|
|
26
|
+
get firmwareRevision(): string;
|
|
27
|
+
private checkServices;
|
|
28
|
+
private checkCharacteristics;
|
|
29
|
+
private getOrAddCharacteristic;
|
|
30
|
+
private handleOnGet;
|
|
31
|
+
private getInitialValue;
|
|
32
|
+
private getDefaultValue;
|
|
33
|
+
private handleAccessCodeSupportedConfigurationOnGet;
|
|
34
|
+
private handleConfigurationStateOnGet;
|
|
35
|
+
private handleLockTargetStateOnSet;
|
|
36
|
+
private handleAccessCodeControlPointOnSet;
|
|
37
|
+
protected updateState(): Promise<void>;
|
|
38
|
+
startPolling(): void;
|
|
39
|
+
stopPolling(): void;
|
|
40
|
+
identify(): void;
|
|
41
|
+
private waitForPendingUpdates;
|
|
42
|
+
private updateBatteryService;
|
|
43
|
+
private updateLockService;
|
|
44
|
+
}
|