-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathapi.test-d.ts
More file actions
336 lines (304 loc) · 13 KB
/
Copy pathapi.test-d.ts
File metadata and controls
336 lines (304 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/**
* Compile-time check of the public typings (`npm run check-types`).
* Mirrors the example of contract C.8.
*/
import {
ActionFields,
ContainerPort,
ContainerPortProtocol,
createLogger,
Device,
DEVICE_FEATURE_CATEGORIES,
DEVICE_FEATURE_TYPES,
DEVICE_FEATURE_UNITS,
DEVICE_TRANSPORTS,
DeviceExternalIds,
DeviceFeature,
DeviceFeatureSupportedOption,
DeviceTransport,
DeviceTransportEntry,
GladysApiError,
GladysIntegration,
HardwareUpdatedContainer,
IntegrationConfig,
IntegrationContainer,
LinkedContact,
LinkedUser,
logger,
MessageContact,
Logger,
MdnsScanResult,
NetworkActiveScanOptions,
OutgoingMessage,
SsdpScanResult,
UdpBroadcastScanResult,
WakeOnLanOptions,
WEATHER_ALERT_SEVERITIES,
WEATHER_ALERT_TYPES,
WEATHER_CONDITIONS,
WeatherAlert,
WeatherAlertType,
WeatherCondition,
WeatherDayForecast,
WeatherGetOptions,
WeatherHourForecast,
WeatherImage,
WeatherPayload,
WebhookRequest,
WebhooksInfo,
WebhookSyncResponse,
WEBSOCKET_MESSAGE_TYPES,
} from '@gladysassistant/integration-sdk';
const main = async (): Promise<void> => {
const gladys = new GladysIntegration({
hostApiUrl: 'http://172.30.0.1:80',
token: 'jwt',
selector: 'ext-demo',
});
gladys.onScanRequest(async () => {
await gladys.publishDiscoveredDevices([
{
name: 'Virtual switch',
external_id: gladys.externalId('switch'),
features: [
{
name: 'On/Off',
external_id: gladys.externalId('switch:binary'),
category: 'switch',
type: 'binary',
min: 0,
max: 1,
read_only: false,
has_feedback: true,
keep_history: true,
},
],
},
]);
});
gladys.onSetValue(async (device: Device, feature: DeviceFeature, value: number | string) => {
if (feature.type === DEVICE_FEATURE_TYPES.TEXT.SELECT) {
// A select command carries the selected option's string value; its
// state is the string form (last_value_string, no history).
await gladys.publishState(feature.external_id, { text: String(value) });
return;
}
await gladys.publishState(feature.external_id, Number(value));
});
gladys.onPoll(async (device: Device) => {
await gladys.publishState(`${device.external_id}:temperature`, { state: 21.5, created_at: new Date() });
});
gladys.onGetImage(async (device: Device) => `image/jpg;base64,${device.external_id}`);
gladys.onConfigUpdated(async (config: IntegrationConfig) => {
await gladys.setConfig({ last_seen_config: JSON.stringify(config) });
});
gladys.onHardwareUpdated(async (containers: HardwareUpdatedContainer[]) => {
const granted: boolean = containers[0].devices[0].granted;
void granted;
});
gladys.onOAuthAuthorizeUrl(
// redirectUri is undefined for an `account_link` field (a provider that
// never redirects back to Gladys).
async (key: string, redirectUri: string | undefined) =>
`https://provider.example/authorize?key=${key}&redirect_uri=${redirectUri ?? ''}`,
);
gladys.onOAuthCallback(async (key: string, params: { code: string; state: string; redirectUri: string }) => {
await gladys.setConfig({ [`${key}_code`]: params.code });
await gladys.setConnectionStatus(true);
});
gladys.onAction('detect_protocol', async (fields: ActionFields) => `Detected on ${String(fields.ip)}`);
gladys.onAction('test_connection', async () => ({ en: 'Connected!', fr: 'Connecté !' }));
gladys.onSendMessage(async (contact: MessageContact, message: OutgoingMessage) => {
// Linked channel (receive: true) → contact.id; send-only channel
// (receive: false) → the target user's contact_schema values.
const line: string = `${contact.id ?? ''} ${String(contact.username ?? '')}: ${message.text} ${message.file ?? ''}`;
void line;
});
gladys.on('connected', () => {});
gladys.on('disconnected', () => {});
await gladys.connect();
const devices: Device[] = await gladys.getDevices();
const config: IntegrationConfig = await gladys.getConfig();
const status = await gladys.getStatus();
const version: string = status.gladys_version;
await gladys.publishState(gladys.externalId('sensor:text'), { text: 'hello' });
await gladys.publishStates([{ device_feature_external_id: gladys.externalId('sensor:temperature'), state: 20 }]);
await gladys.publishCameraImage(gladys.externalId('cam:abc'), 'image/jpg;base64,/9j/4AAQ');
const transport: DeviceTransport = DEVICE_TRANSPORTS.LOCAL;
const entries: DeviceTransportEntry[] = [{ external_id: gladys.externalId('plug:abc'), transport }];
await gladys.publishTransports(entries);
const degradedEntries: DeviceTransportEntry[] = [
{
external_id: gladys.externalId('plug:abc'),
transport: DEVICE_TRANSPORTS.CLOUD,
degraded: true,
message: { en: 'Local session refused, falling back to cloud', fr: 'Session locale refusée, bascule cloud' },
},
];
await gladys.publishTransports(degradedEntries);
const preferLocal: unknown = gladys.config.GLADYS_PREFER_LOCAL;
void preferLocal;
await gladys.setConnectionStatus(false, { en: 'Token expired, please reconnect.', fr: 'Token expiré.' });
const containers: IntegrationContainer[] = await gladys.getContainers();
const hostPort: number | null | undefined = containers[0]?.ports[0]?.host_port;
// direct assignment: the optional chaining above would type-check even without the null
const unassignedHostPort: ContainerPort['host_port'] = null;
const portProtocol: ContainerPortProtocol | undefined = containers[0]?.ports[0]?.protocol;
const browsable: boolean | undefined = containers[0]?.ports[0]?.browsable;
const portLabel: string | undefined = containers[0]?.ports[0]?.label.en;
// same direct assignment for the optional port name of the {{port:<name>}} placeholder
const unnamedPort: ContainerPort['name'] = null;
const portName: ContainerPort['name'] = 'ocpp';
await gladys.startContainer('mqtt', { env: { MQTT_PASSWORD: 's3cr3t' } });
await gladys.startContainer('mqtt');
await gladys.stopContainer('mqtt');
await gladys.restartContainer('frigate');
const oauthType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.OAUTH_GET_AUTHORIZE_URL;
void [hostPort, unassignedHostPort, portProtocol, browsable, portLabel, unnamedPort, portName, oauthType];
gladys.onWebhook('events', async (request: WebhookRequest) => {
const line: string = `${request.method} ${request.body ?? ''} ${request.contentType ?? ''}`;
void line;
});
gladys.onWebhook('callback', async (request: WebhookRequest): Promise<WebhookSyncResponse> => {
return { status: 200, contentType: 'application/json', body: JSON.stringify(request.query) };
});
gladys.onWebhookUpdated(async (info: WebhooksInfo) => {
const urls: string[] = info.webhooks.map((webhook) => webhook.url);
void urls;
});
const webhooksInfo: WebhooksInfo = await gladys.getWebhooks();
const webhookAvailable: boolean = webhooksInfo.available;
const webhookType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.WEBHOOK_REQUEST;
void [webhookAvailable, webhookType];
gladys.onWeatherGet(async (options: WeatherGetOptions): Promise<WeatherPayload> => {
const units: 'metric' | 'us' = options.units;
const hour: WeatherHourForecast = {
temperature: 20.1,
weather: WEATHER_CONDITIONS.DRIZZLE,
datetime: '2026-08-01T13:00:00.000Z',
precipitation_probability: 60,
is_day: false,
};
const day: WeatherDayForecast = {
temperature_min: 14,
temperature_max: 24,
datetime: new Date(),
weather: WEATHER_CONDITIONS.PARTLY_CLOUDY,
sunrise: new Date(),
sunset: new Date(),
};
const alertType: WeatherAlertType = WEATHER_ALERT_TYPES.THUNDERSTORM;
const alert: WeatherAlert = {
severity: WEATHER_ALERT_SEVERITIES.SEVERE,
event: 'Orages violents',
type: alertType,
description: 'Vigilance orange',
start: new Date(),
end: new Date(),
};
const condition: WeatherCondition = units === 'metric' ? WEATHER_CONDITIONS.POURING : 'hail';
const image: WeatherImage = { key: 'vigilance-map', label: { en: 'Vigilance map', fr: 'Carte de vigilance' } };
return {
temperature: 21.5,
weather: condition,
datetime: new Date(),
apparent_temperature: 20.9,
humidity: 80,
wind_speed: 4.2,
uv_index: 3,
sunrise: '2026-08-01T04:30:00.000Z',
is_day: true,
hours: [hour],
days: [day],
alerts: [alert],
images: [image],
};
});
gladys.onWeatherGetImage(async (key: string) => `iVBORw0KGgo${key}`);
gladys.requestWeatherRefresh();
const weatherType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.WEATHER_GET;
const weatherImageType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.WEATHER_GET_IMAGE;
const weatherRefreshType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.WEATHER_REFRESH;
void [weatherType, weatherImageType, weatherRefreshType];
await gladys.publishMessage('12345', 'Turn on the light');
await gladys.publishMessage('12345', 'Received offline', { createdAt: new Date() });
const linkedUser: LinkedUser = await gladys.linkContact('AB23CD45', '12345', 'John');
const contacts: LinkedContact[] = await gladys.getContacts();
const messageType: string = WEBSOCKET_MESSAGE_TYPES.EXTERNAL_INTEGRATION.MESSAGE_SEND;
void [linkedUser.first_name, contacts[0]?.contact_id, messageType];
const announcements: UdpBroadcastScanResult[] = await gladys.scanNetwork('udp-broadcast', { timeoutSeconds: 10 });
const payload: string = announcements[0].payload_base64;
const services: MdnsScanResult[] = await gladys.scanNetwork('mdns');
const txt: string[] = services[0].txt;
const responders: SsdpScanResult[] = await gladys.scanNetwork('ssdp', { timeoutSeconds: 5 });
const headers: string = responders[0].headers;
const mac: string | undefined = responders[0].source_mac;
const replies: UdpBroadcastScanResult[] = await gladys.scanNetwork('udp-active-broadcast', {
port: 9999,
payload: Buffer.from('kasa-discovery-request'),
timeoutSeconds: 5,
});
const activeScanOptions: NetworkActiveScanOptions = { port: 20002, payload: 'AAAB' };
void [payload, txt, headers, mac, replies[0].source_ip, activeScanOptions];
await gladys.wakeOnLan('64:e4:d5:b4:12:66');
const wakeOptions: WakeOnLanOptions = { address: '192.168.1.255', port: 9, sourcePort: 0 };
await gladys.wakeOnLan('64E4D5B41266', wakeOptions);
// PTZ camera (CAMERA.MOVE/PRESET) and dynamic select (TEXT.SELECT):
// enum-like features narrowed per device through supported_options.
const moveOptions: DeviceFeatureSupportedOption[] = [
{ value: 1, label: 'Pan left', sort_order: 0 },
{ value: 2, label: 'Pan right', sort_order: 1 },
];
const ptzFeature: DeviceFeature = {
name: 'Move',
external_id: gladys.externalId('cam:abc:move'),
category: DEVICE_FEATURE_CATEGORIES.CAMERA,
type: DEVICE_FEATURE_TYPES.CAMERA.MOVE,
min: 0,
max: 6,
read_only: false,
has_feedback: false,
keep_history: false,
supported_options: moveOptions,
};
// String option values are only valid on the text/select pair: a dynamic
// select is a `text` category feature, whatever device carries it (here the
// HDMI sources of a TV device).
const sourceFeature: DeviceFeature = {
name: 'Source',
external_id: gladys.externalId('tv:abc:source'),
category: DEVICE_FEATURE_CATEGORIES.TEXT,
type: DEVICE_FEATURE_TYPES.TEXT.SELECT,
read_only: false,
supported_options: [{ value: 'hdmi1', label: 'HDMI 1' }],
};
const steppedFeature: DeviceFeature = {
name: 'Target temperature',
external_id: gladys.externalId('ac:abc:target-temperature'),
category: DEVICE_FEATURE_CATEGORIES.AIR_CONDITIONING,
type: DEVICE_FEATURE_TYPES.AIR_CONDITIONING.TARGET_TEMPERATURE,
min: 16,
max: 30,
step: 0.5,
};
void [ptzFeature, sourceFeature, steppedFeature];
const error = new GladysApiError(401, 'UNAUTHORIZED', 'Invalid token');
const parts: [number, string, string] = [error.status, error.code, error.message];
const authType: string = WEBSOCKET_MESSAGE_TYPES.AUTHENTICATE.INTEGRATION_REQUEST;
logger.info('connected', status.gladys_version);
const namedLogger: Logger = createLogger({ name: 'weather-station', level: 'debug' });
namedLogger.child('poll').debug('polling');
const ids: DeviceExternalIds = gladys.externalIds('plug', '0x00158d0001a2b3c4');
const featureId: string = ids.feature('power');
const category: 'temperature-sensor' = DEVICE_FEATURE_CATEGORIES.TEMPERATURE_SENSOR;
const featureType: 'binary' = DEVICE_FEATURE_TYPES.SWITCH.BINARY;
const unit: 'celsius' = DEVICE_FEATURE_UNITS.CELSIUS;
gladys.handleShutdown(async (signal: 'SIGTERM' | 'SIGINT') => {
logger.info('stopping on', signal);
});
void [ids.device, featureId, category, featureType, unit];
await gladys.disconnect();
// Reference otherwise-unused values so noUnusedLocals-style checks stay quiet.
void [devices, config, version, parts, authType];
};
void main;