iobroker.fairland 0.2.1
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 +23 -0
- package/README.md +219 -0
- package/THIRD_PARTY_NOTICES.md +37 -0
- package/admin/fairland.png +0 -0
- package/admin/i18n/de/translations.json +9 -0
- package/admin/i18n/en/translations.json +9 -0
- package/admin/i18n/es/translations.json +9 -0
- package/admin/i18n/fr/translations.json +9 -0
- package/admin/i18n/it/translations.json +9 -0
- package/admin/i18n/nl/translations.json +9 -0
- package/admin/i18n/pl/translations.json +9 -0
- package/admin/i18n/pt/translations.json +9 -0
- package/admin/i18n/ru/translations.json +9 -0
- package/admin/i18n/uk/translations.json +9 -0
- package/admin/i18n/zh-cn/translations.json +9 -0
- package/admin/jsonConfig.json +59 -0
- package/build/dpUtils.js +186 -0
- package/build/fairlandApi.js +190 -0
- package/build/main.js +757 -0
- package/build/mappings.js +427 -0
- package/build/types.js +2 -0
- package/io-package.json +228 -0
- package/package.json +63 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FairlandApiClient = exports.FairlandApiClientAuthenticationError = exports.FairlandApiClientCommunicationError = exports.FairlandApiClientError = exports.API_REGIONS = void 0;
|
|
4
|
+
exports.API_REGIONS = {
|
|
5
|
+
eu: 'https://api-eu.fairlandiot.com',
|
|
6
|
+
us: 'https://api-us.fairlandiot.com',
|
|
7
|
+
cn: 'https://api-cn.fairlandiot.com',
|
|
8
|
+
hk: 'https://api-hk.fairlandiot.com',
|
|
9
|
+
};
|
|
10
|
+
const DEFAULT_REGION = 'eu';
|
|
11
|
+
const SUCCESS_CODE = 200000;
|
|
12
|
+
class FairlandApiClientError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'FairlandApiClientError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.FairlandApiClientError = FairlandApiClientError;
|
|
19
|
+
class FairlandApiClientCommunicationError extends FairlandApiClientError {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'FairlandApiClientCommunicationError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.FairlandApiClientCommunicationError = FairlandApiClientCommunicationError;
|
|
26
|
+
class FairlandApiClientAuthenticationError extends FairlandApiClientError {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'FairlandApiClientAuthenticationError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.FairlandApiClientAuthenticationError = FairlandApiClientAuthenticationError;
|
|
33
|
+
class FairlandApiClient {
|
|
34
|
+
username;
|
|
35
|
+
password;
|
|
36
|
+
countryCode;
|
|
37
|
+
phoneCode;
|
|
38
|
+
timeoutMs;
|
|
39
|
+
token;
|
|
40
|
+
userId;
|
|
41
|
+
region;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.username = options.username;
|
|
44
|
+
this.password = options.password;
|
|
45
|
+
this.countryCode = options.countryCode ?? 'DE';
|
|
46
|
+
this.phoneCode = options.phoneCode ?? '49';
|
|
47
|
+
this.region = options.region ?? DEFAULT_REGION;
|
|
48
|
+
this.timeoutMs = options.timeoutMs ?? 10_000;
|
|
49
|
+
}
|
|
50
|
+
get baseUrl() {
|
|
51
|
+
return exports.API_REGIONS[this.region] ?? exports.API_REGIONS[DEFAULT_REGION];
|
|
52
|
+
}
|
|
53
|
+
get currentUserId() {
|
|
54
|
+
return this.userId;
|
|
55
|
+
}
|
|
56
|
+
async detectRegion() {
|
|
57
|
+
let lastAuthError;
|
|
58
|
+
let lastError;
|
|
59
|
+
for (const region of Object.keys(exports.API_REGIONS)) {
|
|
60
|
+
this.region = region;
|
|
61
|
+
this.token = undefined;
|
|
62
|
+
try {
|
|
63
|
+
await this.login();
|
|
64
|
+
return region;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof FairlandApiClientAuthenticationError) {
|
|
68
|
+
lastAuthError = error;
|
|
69
|
+
}
|
|
70
|
+
else if (error instanceof FairlandApiClientError) {
|
|
71
|
+
lastError = error;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
lastError = new FairlandApiClientError(String(error));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw lastError ?? lastAuthError ?? new FairlandApiClientAuthenticationError('Login failed');
|
|
79
|
+
}
|
|
80
|
+
async login() {
|
|
81
|
+
const response = await this.fetchJson(`${this.baseUrl}/fyld-user-api/user/loginByPassword`, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: this.publicHeaders(),
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
phoneCode: this.phoneCode,
|
|
86
|
+
accountName: this.username,
|
|
87
|
+
password: this.password,
|
|
88
|
+
countryCode: this.countryCode,
|
|
89
|
+
randStr: '',
|
|
90
|
+
ticket: '',
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
if (response.code !== SUCCESS_CODE) {
|
|
94
|
+
throw new FairlandApiClientAuthenticationError(`Login failed: ${response.code} ${response.msg ?? ''}`.trim());
|
|
95
|
+
}
|
|
96
|
+
this.token = response.data.authorization;
|
|
97
|
+
this.userId = response.data.userId;
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
async getCourtyards() {
|
|
101
|
+
return this.apiRequest('/fyld-device-api/deviceGroupApi/allGroupInfo', {
|
|
102
|
+
needDeviceCount: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async getAllDevicesInCourtyard(courtyardId) {
|
|
106
|
+
const data = await this.apiRequest('/fyld-device-api/deviceApi/deviceAllGroupInfo', {
|
|
107
|
+
deviceGroupId: courtyardId,
|
|
108
|
+
shareId: null,
|
|
109
|
+
});
|
|
110
|
+
return data.bindDeviceInfos ?? [];
|
|
111
|
+
}
|
|
112
|
+
async getDeviceStatus(deviceId) {
|
|
113
|
+
return this.apiRequest('/fyld-device-api/deviceDataPointApi/deviceDataPointInfo', {
|
|
114
|
+
deviceId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async setDeviceStatus(deviceId, dpId, value) {
|
|
118
|
+
return this.apiRequest('/fyld-device-api/devicePropertySetApi/set', {
|
|
119
|
+
deviceId,
|
|
120
|
+
dpIdValues: [{ type: '', dpId, value }],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async apiRequest(path, payload, retryOnAuth = true) {
|
|
124
|
+
if (!this.token) {
|
|
125
|
+
throw new FairlandApiClientAuthenticationError('Not logged in');
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const response = await this.fetchJson(`${this.baseUrl}${path}`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: this.authHeaders(),
|
|
131
|
+
body: JSON.stringify(payload),
|
|
132
|
+
});
|
|
133
|
+
if (response.code !== SUCCESS_CODE) {
|
|
134
|
+
throw new FairlandApiClientError(`API failed: ${response.code} ${response.msg ?? ''}`.trim());
|
|
135
|
+
}
|
|
136
|
+
return response.data;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (retryOnAuth && error instanceof FairlandApiClientAuthenticationError) {
|
|
140
|
+
await this.login();
|
|
141
|
+
return this.apiRequest(path, payload, false);
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async fetchJson(url, init) {
|
|
147
|
+
const signal = AbortSignal.timeout(this.timeoutMs);
|
|
148
|
+
try {
|
|
149
|
+
const response = await fetch(url, {
|
|
150
|
+
...init,
|
|
151
|
+
signal,
|
|
152
|
+
});
|
|
153
|
+
if (response.status === 401 || response.status === 403) {
|
|
154
|
+
throw new FairlandApiClientAuthenticationError('Invalid credentials');
|
|
155
|
+
}
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
const body = await response.text();
|
|
158
|
+
throw new FairlandApiClientCommunicationError(`HTTP ${response.status}: ${body}`);
|
|
159
|
+
}
|
|
160
|
+
return (await response.json());
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof FairlandApiClientError) {
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {
|
|
167
|
+
throw new FairlandApiClientCommunicationError(`Timeout fetching ${url}`);
|
|
168
|
+
}
|
|
169
|
+
throw new FairlandApiClientCommunicationError(`Error fetching ${url}: ${String(error)}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
publicHeaders() {
|
|
173
|
+
return {
|
|
174
|
+
'Content-Type': 'application/json',
|
|
175
|
+
terminal: '2',
|
|
176
|
+
'User-Agent': 'Dart/3.5 (dart:io)',
|
|
177
|
+
Accept: 'application/json;charset=UTF-8',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
authHeaders() {
|
|
181
|
+
if (!this.token) {
|
|
182
|
+
throw new FairlandApiClientAuthenticationError('Not logged in');
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
...this.publicHeaders(),
|
|
186
|
+
Authorization: this.token,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.FairlandApiClient = FairlandApiClient;
|