@attraccess/plugin-shelly 0.1.0-nightly.1003.1 → 0.1.0-nightly.1012.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/frontend/{__federation_expose_Plugin-Dd1Vs4iR.js → __federation_expose_Plugin-C8kWx_i3.js} +563 -221
- package/frontend/host-shared-placeholder-BvRk9kiK.js +0 -0
- package/frontend/remoteEntry.js +68 -2
- package/package.json +4 -3
- package/frontend/jsx-runtime-B9Qmn8k7.js +0 -42
- package/frontend/preload-helper-DxWiKyOd.js +0 -68
- package/frontend/react-D5kF5LxZ.js +0 -383
- package/frontend/rolldown-runtime-EAsCxrDo.js +0 -14
package/frontend/{__federation_expose_Plugin-Dd1Vs4iR.js → __federation_expose_Plugin-C8kWx_i3.js}
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { r as importShared } from "./_virtual___federation_fn_import-BXODRNto.js";
|
|
2
|
-
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
|
+
//#endregion
|
|
3
5
|
//#region ../../../libs/plugins-frontend-sdk/src/lib/frontend.api-client.ts
|
|
4
6
|
var BASE_URL_GLOBAL = "__ATTRACCESS_API_BASE_URL__";
|
|
5
7
|
/** The API origin the host is talking to. Falls back to the current origin. */
|
|
@@ -60,6 +62,48 @@ function createPluginApiClient(basePath = "") {
|
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
//#endregion
|
|
65
|
+
//#region ../../../node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
66
|
+
/**
|
|
67
|
+
* @license React
|
|
68
|
+
* react-jsx-runtime.production.js
|
|
69
|
+
*
|
|
70
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
71
|
+
*
|
|
72
|
+
* This source code is licensed under the MIT license found in the
|
|
73
|
+
* LICENSE file in the root directory of this source tree.
|
|
74
|
+
*/
|
|
75
|
+
var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
76
|
+
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
77
|
+
function jsxProd(type, config, maybeKey) {
|
|
78
|
+
var key = null;
|
|
79
|
+
void 0 !== maybeKey && (key = "" + maybeKey);
|
|
80
|
+
void 0 !== config.key && (key = "" + config.key);
|
|
81
|
+
if ("key" in config) {
|
|
82
|
+
maybeKey = {};
|
|
83
|
+
for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]);
|
|
84
|
+
} else maybeKey = config;
|
|
85
|
+
config = maybeKey.ref;
|
|
86
|
+
return {
|
|
87
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
88
|
+
type,
|
|
89
|
+
key,
|
|
90
|
+
ref: void 0 !== config ? config : null,
|
|
91
|
+
props: maybeKey
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
exports.jsx = jsxProd;
|
|
95
|
+
exports.jsxs = jsxProd;
|
|
96
|
+
}));
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region ../../../libs/plugins-frontend-sdk/src/lib/frontend.standard-drawer.tsx
|
|
99
|
+
var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
100
|
+
module.exports = require_react_jsx_runtime_production();
|
|
101
|
+
})))();
|
|
102
|
+
var { DrawerBackdrop: DrawerBackdrop$1, DrawerContent: DrawerContent$1, DrawerDialog: DrawerDialog$1 } = await importShared("@heroui/react");
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region ../../../libs/plugins-frontend-sdk/src/lib/frontend.standard-modal.tsx
|
|
105
|
+
var { Modal: Modal$1, ModalBackdrop: ModalBackdrop$1, ModalContainer: ModalContainer$1, ModalDialog: ModalDialog$1 } = await importShared("@heroui/react");
|
|
106
|
+
//#endregion
|
|
63
107
|
//#region frontend/src/api.ts
|
|
64
108
|
var api = createPluginApiClient("/api/shelly");
|
|
65
109
|
function listDevices() {
|
|
@@ -117,9 +161,279 @@ function discoverDevices(input = {}) {
|
|
|
117
161
|
body: input
|
|
118
162
|
});
|
|
119
163
|
}
|
|
164
|
+
var en_default = {
|
|
165
|
+
devices: {
|
|
166
|
+
"title": "Shelly Devices",
|
|
167
|
+
"table": "Shelly devices",
|
|
168
|
+
"description": "Discovered and manually added Shelly devices.",
|
|
169
|
+
"discover": "Discover",
|
|
170
|
+
"add": "Add device",
|
|
171
|
+
"loadError": "Failed to load devices",
|
|
172
|
+
"device": "Device",
|
|
173
|
+
"address": "Address",
|
|
174
|
+
"model": "Model",
|
|
175
|
+
"auth": "Auth",
|
|
176
|
+
"firmware": "Firmware",
|
|
177
|
+
"actions": "Actions",
|
|
178
|
+
"unknown": "Unknown",
|
|
179
|
+
"generation": "Gen {{generation}}",
|
|
180
|
+
"generationPlus": "Gen {{generation}}+",
|
|
181
|
+
"empty": "No devices yet. Run discovery, or add your first Shelly by its IP.",
|
|
182
|
+
"probeError": "Probe failed: {{message}}",
|
|
183
|
+
"info": "View device info",
|
|
184
|
+
"more": "More actions",
|
|
185
|
+
"deviceActions": "Device actions",
|
|
186
|
+
"manageFirmware": "Manage firmware",
|
|
187
|
+
"password": "Set admin password",
|
|
188
|
+
"reprobe": "Re-probe device",
|
|
189
|
+
"delete": "Delete device",
|
|
190
|
+
"deleteDescription": "Remove {{name}} ({{address}}) from the registry? The device itself is not changed."
|
|
191
|
+
},
|
|
192
|
+
auth: {
|
|
193
|
+
"none": "No auth",
|
|
194
|
+
"required": "Auth required",
|
|
195
|
+
"unknown": "Unknown"
|
|
196
|
+
},
|
|
197
|
+
add: {
|
|
198
|
+
"title": "Add a device",
|
|
199
|
+
"description": "Enter the device's IP address. We probe GET /shelly to detect its generation and model.",
|
|
200
|
+
"ip": "IP address",
|
|
201
|
+
"name": "Name (optional)",
|
|
202
|
+
"placeholder": "Workshop light",
|
|
203
|
+
"error": "Could not add the device",
|
|
204
|
+
"required": "IP address is required."
|
|
205
|
+
},
|
|
206
|
+
password: {
|
|
207
|
+
"title": "Admin password",
|
|
208
|
+
"description": "Set or change the admin password of {{name}} ({{address}}).",
|
|
209
|
+
"current": "Current password",
|
|
210
|
+
"currentDescription": "Required because this device already has authentication enabled.",
|
|
211
|
+
"new": "New admin password",
|
|
212
|
+
"newDescription": "Protects the device's local web interface and API.",
|
|
213
|
+
"error": "Could not set password",
|
|
214
|
+
"required": "New password is required.",
|
|
215
|
+
"save": "Save password",
|
|
216
|
+
"show": "Show password",
|
|
217
|
+
"hide": "Hide password"
|
|
218
|
+
},
|
|
219
|
+
discovery: {
|
|
220
|
+
"title": "Discover devices",
|
|
221
|
+
"description": "Listens for Shelly devices announcing over mDNS, then probes every address of a subnet. Everything found is added to the registry.",
|
|
222
|
+
"subnet": "Subnet to scan (optional)",
|
|
223
|
+
"subnetDescription": "Leave empty to scan the server's own networks. Private ranges only, /22 at most. Required when Attraccess runs in a container, since its network is not your LAN.",
|
|
224
|
+
"error": "Discovery failed",
|
|
225
|
+
"running": "Probing addresses — a /24 takes a few seconds.",
|
|
226
|
+
"start": "Start discovery",
|
|
227
|
+
"found": "Found {{count}}, added {{added}}",
|
|
228
|
+
"empty": "No devices found",
|
|
229
|
+
"mdnsOnly": "no subnet (mDNS only)",
|
|
230
|
+
"probed": {
|
|
231
|
+
"one": "Probed {{count}} address in {{subnets}}.",
|
|
232
|
+
"many": "Probed {{count}} addresses in {{subnets}}."
|
|
233
|
+
},
|
|
234
|
+
"hint": "Nothing answered GET /shelly. If Attraccess runs in a container, enter the subnet your devices are on — the container's own network is not your LAN.",
|
|
235
|
+
"via": "via {{source}}",
|
|
236
|
+
"scan": "subnet scan",
|
|
237
|
+
"added": "Added",
|
|
238
|
+
"known": "Already known"
|
|
239
|
+
},
|
|
240
|
+
info: {
|
|
241
|
+
"title": "Device info",
|
|
242
|
+
"loadError": "Could not load device info",
|
|
243
|
+
"auth": "This device requires authentication. Enter its admin password to read protected info.",
|
|
244
|
+
"passwordPlaceholder": "device admin password",
|
|
245
|
+
"load": "Load info",
|
|
246
|
+
"notReported": "Not reported",
|
|
247
|
+
"on": "On",
|
|
248
|
+
"off": "Off",
|
|
249
|
+
"name": "Name",
|
|
250
|
+
"generation": "Generation",
|
|
251
|
+
"timezone": "Timezone",
|
|
252
|
+
"uptime": "Uptime",
|
|
253
|
+
"network": "Network",
|
|
254
|
+
"wifi": "Wi-Fi network",
|
|
255
|
+
"signal": "Signal",
|
|
256
|
+
"output": "Output",
|
|
257
|
+
"state": "State",
|
|
258
|
+
"power": "Power",
|
|
259
|
+
"voltage": "Voltage",
|
|
260
|
+
"current": "Current",
|
|
261
|
+
"updated": "Updated {{time}}"
|
|
262
|
+
},
|
|
263
|
+
firmware: {
|
|
264
|
+
"checking": "Checking…",
|
|
265
|
+
"unavailable": "Unavailable",
|
|
266
|
+
"available": "{{version}} available",
|
|
267
|
+
"updateAvailable": "Firmware update available: {{version}}",
|
|
268
|
+
"empty": "No firmware info loaded yet.",
|
|
269
|
+
"installed": "Installed",
|
|
270
|
+
"stable": "Stable channel",
|
|
271
|
+
"beta": "Beta channel",
|
|
272
|
+
"upToDate": "Up to date",
|
|
273
|
+
"nothingNewer": "Nothing newer",
|
|
274
|
+
"checked": "Checked",
|
|
275
|
+
"description": "Check for and install firmware updates on {{name}} ({{address}}).",
|
|
276
|
+
"error": "Firmware check failed",
|
|
277
|
+
"running": "Update running",
|
|
278
|
+
"installing": "Installing the {{stage}} firmware{{version}}. The device reboots during the update and is offline for a moment — this page keeps checking.",
|
|
279
|
+
"finished": "Update finished",
|
|
280
|
+
"success": "The device now runs {{version}}.",
|
|
281
|
+
"recheck": "Check again",
|
|
282
|
+
"installLabel": "Install {{stage}} firmware {{version}}",
|
|
283
|
+
"install": "Install {{stage}} {{version}}",
|
|
284
|
+
"stableStage": "stable",
|
|
285
|
+
"betaStage": "beta",
|
|
286
|
+
"timeout": "The device did not report the expected firmware version within 5 minutes. Check it and re-check the firmware manually."
|
|
287
|
+
},
|
|
288
|
+
common: {
|
|
289
|
+
"cancel": "Cancel",
|
|
290
|
+
"close": "Close",
|
|
291
|
+
"delete": "Delete",
|
|
292
|
+
"done": "Done",
|
|
293
|
+
"refresh": "Refresh"
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var de_default = {
|
|
297
|
+
devices: {
|
|
298
|
+
"title": "Shelly-Geräte",
|
|
299
|
+
"table": "Shelly-Geräte",
|
|
300
|
+
"description": "Automatisch erkannte und manuell hinzugefügte Shelly-Geräte.",
|
|
301
|
+
"discover": "Suchen",
|
|
302
|
+
"add": "Gerät hinzufügen",
|
|
303
|
+
"loadError": "Geräte konnten nicht geladen werden",
|
|
304
|
+
"device": "Gerät",
|
|
305
|
+
"address": "Adresse",
|
|
306
|
+
"model": "Modell",
|
|
307
|
+
"auth": "Authentifizierung",
|
|
308
|
+
"firmware": "Firmware",
|
|
309
|
+
"actions": "Aktionen",
|
|
310
|
+
"unknown": "Unbekannt",
|
|
311
|
+
"generation": "Gen {{generation}}",
|
|
312
|
+
"generationPlus": "Gen {{generation}}+",
|
|
313
|
+
"empty": "Noch keine Geräte vorhanden. Starte die Suche oder füge dein erstes Shelly-Gerät über seine IP-Adresse hinzu.",
|
|
314
|
+
"probeError": "Geräteprüfung fehlgeschlagen: {{message}}",
|
|
315
|
+
"info": "Geräteinformationen anzeigen",
|
|
316
|
+
"more": "Weitere Aktionen",
|
|
317
|
+
"deviceActions": "Geräteaktionen",
|
|
318
|
+
"manageFirmware": "Firmware verwalten",
|
|
319
|
+
"password": "Admin-Passwort festlegen",
|
|
320
|
+
"reprobe": "Gerät erneut prüfen",
|
|
321
|
+
"delete": "Gerät löschen",
|
|
322
|
+
"deleteDescription": "{{name}} ({{address}}) aus der Registrierung entfernen? Das Gerät selbst wird nicht verändert."
|
|
323
|
+
},
|
|
324
|
+
auth: {
|
|
325
|
+
"none": "Keine Authentifizierung",
|
|
326
|
+
"required": "Authentifizierung erforderlich",
|
|
327
|
+
"unknown": "Unbekannt"
|
|
328
|
+
},
|
|
329
|
+
add: {
|
|
330
|
+
"title": "Gerät hinzufügen",
|
|
331
|
+
"description": "Gib die IP-Adresse des Geräts ein. Wir prüfen GET /shelly, um Generation und Modell zu erkennen.",
|
|
332
|
+
"ip": "IP-Adresse",
|
|
333
|
+
"name": "Name (optional)",
|
|
334
|
+
"placeholder": "Werkstattbeleuchtung",
|
|
335
|
+
"error": "Gerät konnte nicht hinzugefügt werden",
|
|
336
|
+
"required": "Eine IP-Adresse ist erforderlich."
|
|
337
|
+
},
|
|
338
|
+
password: {
|
|
339
|
+
"title": "Admin-Passwort",
|
|
340
|
+
"description": "Admin-Passwort von {{name}} ({{address}}) festlegen oder ändern.",
|
|
341
|
+
"current": "Aktuelles Passwort",
|
|
342
|
+
"currentDescription": "Erforderlich, da für dieses Gerät bereits die Authentifizierung aktiviert ist.",
|
|
343
|
+
"new": "Neues Admin-Passwort",
|
|
344
|
+
"newDescription": "Schützt die lokale Weboberfläche und API des Geräts.",
|
|
345
|
+
"error": "Passwort konnte nicht festgelegt werden",
|
|
346
|
+
"required": "Ein neues Passwort ist erforderlich.",
|
|
347
|
+
"save": "Passwort speichern",
|
|
348
|
+
"show": "Passwort anzeigen",
|
|
349
|
+
"hide": "Passwort verbergen"
|
|
350
|
+
},
|
|
351
|
+
discovery: {
|
|
352
|
+
"title": "Geräte suchen",
|
|
353
|
+
"description": "Sucht nach Shelly-Geräten, die sich über mDNS melden, und prüft anschließend jede Adresse eines Subnetzes. Gefundene Geräte werden registriert.",
|
|
354
|
+
"subnet": "Zu durchsuchendes Subnetz (optional)",
|
|
355
|
+
"subnetDescription": "Leer lassen, um die Netzwerke des Servers zu durchsuchen. Nur private Bereiche, maximal /22. Bei Attraccess in einem Container erforderlich, da dessen Netzwerk nicht dein LAN ist.",
|
|
356
|
+
"error": "Gerätesuche fehlgeschlagen",
|
|
357
|
+
"running": "Adressen werden geprüft – ein /24-Netz dauert einige Sekunden.",
|
|
358
|
+
"start": "Suche starten",
|
|
359
|
+
"found": "{{count}} gefunden, {{added}} hinzugefügt",
|
|
360
|
+
"empty": "Keine Geräte gefunden",
|
|
361
|
+
"mdnsOnly": "kein Subnetz (nur mDNS)",
|
|
362
|
+
"probed": {
|
|
363
|
+
"one": "{{count}} Adresse in {{subnets}} geprüft.",
|
|
364
|
+
"many": "{{count}} Adressen in {{subnets}} geprüft."
|
|
365
|
+
},
|
|
366
|
+
"hint": "Kein Gerät hat auf GET /shelly geantwortet. Wenn Attraccess in einem Container läuft, gib das Subnetz deiner Geräte an. Das Netzwerk des Containers ist nicht dein LAN.",
|
|
367
|
+
"via": "über {{source}}",
|
|
368
|
+
"scan": "Subnetzsuche",
|
|
369
|
+
"added": "Hinzugefügt",
|
|
370
|
+
"known": "Bereits bekannt"
|
|
371
|
+
},
|
|
372
|
+
info: {
|
|
373
|
+
"title": "Geräteinformationen",
|
|
374
|
+
"loadError": "Geräteinformationen konnten nicht geladen werden",
|
|
375
|
+
"auth": "Dieses Gerät erfordert eine Authentifizierung. Gib sein Admin-Passwort ein, um geschützte Informationen zu lesen.",
|
|
376
|
+
"passwordPlaceholder": "Admin-Passwort des Geräts",
|
|
377
|
+
"load": "Informationen laden",
|
|
378
|
+
"notReported": "Nicht gemeldet",
|
|
379
|
+
"on": "Ein",
|
|
380
|
+
"off": "Aus",
|
|
381
|
+
"name": "Name",
|
|
382
|
+
"generation": "Generation",
|
|
383
|
+
"timezone": "Zeitzone",
|
|
384
|
+
"uptime": "Betriebszeit",
|
|
385
|
+
"network": "Netzwerk",
|
|
386
|
+
"wifi": "WLAN-Netzwerk",
|
|
387
|
+
"signal": "Signal",
|
|
388
|
+
"output": "Ausgang",
|
|
389
|
+
"state": "Zustand",
|
|
390
|
+
"power": "Leistung",
|
|
391
|
+
"voltage": "Spannung",
|
|
392
|
+
"current": "Stromstärke",
|
|
393
|
+
"updated": "Aktualisiert {{time}}"
|
|
394
|
+
},
|
|
395
|
+
firmware: {
|
|
396
|
+
"checking": "Wird geprüft…",
|
|
397
|
+
"unavailable": "Nicht verfügbar",
|
|
398
|
+
"available": "{{version}} verfügbar",
|
|
399
|
+
"updateAvailable": "Firmware-Aktualisierung verfügbar: {{version}}",
|
|
400
|
+
"empty": "Noch keine Firmware-Informationen geladen.",
|
|
401
|
+
"installed": "Installiert",
|
|
402
|
+
"stable": "Stabiler Kanal",
|
|
403
|
+
"beta": "Beta-Kanal",
|
|
404
|
+
"upToDate": "Aktuell",
|
|
405
|
+
"nothingNewer": "Keine neuere Version",
|
|
406
|
+
"checked": "Geprüft",
|
|
407
|
+
"description": "Firmware-Aktualisierungen für {{name}} ({{address}}) prüfen und installieren.",
|
|
408
|
+
"error": "Firmware-Prüfung fehlgeschlagen",
|
|
409
|
+
"running": "Aktualisierung läuft",
|
|
410
|
+
"installing": "Firmware des Kanals {{stage}}{{version}} wird installiert. Das Gerät startet während der Aktualisierung neu und ist kurzzeitig offline. Diese Seite prüft weiterhin den Status.",
|
|
411
|
+
"finished": "Aktualisierung abgeschlossen",
|
|
412
|
+
"success": "Auf dem Gerät läuft jetzt {{version}}.",
|
|
413
|
+
"recheck": "Erneut prüfen",
|
|
414
|
+
"installLabel": "Firmware {{stage}} {{version}} installieren",
|
|
415
|
+
"install": "{{stage}} {{version}} installieren",
|
|
416
|
+
"stableStage": "stabil",
|
|
417
|
+
"betaStage": "Beta",
|
|
418
|
+
"timeout": "Das Gerät hat die erwartete Firmware-Version nicht innerhalb von 5 Minuten gemeldet. Prüfe das Gerät und die Firmware manuell erneut."
|
|
419
|
+
},
|
|
420
|
+
common: {
|
|
421
|
+
"cancel": "Abbrechen",
|
|
422
|
+
"close": "Schließen",
|
|
423
|
+
"delete": "Löschen",
|
|
424
|
+
"done": "Fertig",
|
|
425
|
+
"refresh": "Aktualisieren"
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region frontend/src/i18n.ts
|
|
430
|
+
var { useTranslations } = await importShared("@attraccess/plugins-frontend-ui");
|
|
431
|
+
var useShellyTranslations = () => useTranslations({
|
|
432
|
+
en: en_default,
|
|
433
|
+
de: de_default
|
|
434
|
+
}, { escapeValues: false });
|
|
120
435
|
//#endregion
|
|
121
436
|
//#region frontend/src/drawer.tsx
|
|
122
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
123
437
|
var { Button: Button$6, Description, DrawerBackdrop, DrawerContent, DrawerDialog, Input, InputGroup, Label, TextField, Tooltip: Tooltip$2 } = await importShared("@heroui/react");
|
|
124
438
|
var { EyeIcon: EyeIcon$1, EyeOffIcon: EyeOffIcon$1 } = await importShared("lucide-react");
|
|
125
439
|
var { useState: useState$6 } = await importShared("react");
|
|
@@ -159,8 +473,9 @@ function TextFieldRow({ label, value, onChange, placeholder, required, descripti
|
|
|
159
473
|
});
|
|
160
474
|
}
|
|
161
475
|
function PasswordFieldRow({ label, value, onChange, description, required, dataCy, autoComplete }) {
|
|
476
|
+
const { t } = useShellyTranslations();
|
|
162
477
|
const [visible, setVisible] = useState$6(false);
|
|
163
|
-
const toggleLabel = visible ? "
|
|
478
|
+
const toggleLabel = t(visible ? "password.hide" : "password.show");
|
|
164
479
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TextField, {
|
|
165
480
|
value,
|
|
166
481
|
onChange,
|
|
@@ -209,6 +524,7 @@ var { Button: Button$5, DrawerBody: DrawerBody$4, DrawerHeader: DrawerHeader$4,
|
|
|
209
524
|
var { PlusIcon: PlusIcon$1, XIcon: XIcon$4 } = await importShared("lucide-react");
|
|
210
525
|
var { useCallback: useCallback$5, useRef: useRef$2, useState: useState$5 } = await importShared("react");
|
|
211
526
|
function AddDeviceDrawer({ isOpen, onOpenChange, onAdded }) {
|
|
527
|
+
const { t, tMessage } = useShellyTranslations();
|
|
212
528
|
const [ipAddress, setIpAddress] = useState$5("");
|
|
213
529
|
const [name, setName] = useState$5("");
|
|
214
530
|
const [submitting, setSubmitting] = useState$5(false);
|
|
@@ -219,7 +535,7 @@ function AddDeviceDrawer({ isOpen, onOpenChange, onAdded }) {
|
|
|
219
535
|
if (inFlight.current) return;
|
|
220
536
|
const ip = ipAddress.trim();
|
|
221
537
|
if (!ip) {
|
|
222
|
-
setError(
|
|
538
|
+
setError({ key: "add.required" });
|
|
223
539
|
return;
|
|
224
540
|
}
|
|
225
541
|
inFlight.current = true;
|
|
@@ -255,19 +571,15 @@ function AddDeviceDrawer({ isOpen, onOpenChange, onAdded }) {
|
|
|
255
571
|
className: "sh:flex sh:flex-col sh:gap-1",
|
|
256
572
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
257
573
|
className: "sh:text-lg sh:font-semibold",
|
|
258
|
-
children: "
|
|
259
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.
|
|
574
|
+
children: t("add.title")
|
|
575
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
260
576
|
className: "sh:text-sm sh:text-muted",
|
|
261
|
-
children:
|
|
262
|
-
"Enter the device's IP address. We probe ",
|
|
263
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "GET /shelly" }),
|
|
264
|
-
" to detect its generation and model."
|
|
265
|
-
]
|
|
577
|
+
children: t("add.description")
|
|
266
578
|
})]
|
|
267
579
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$5, {
|
|
268
580
|
isIconOnly: true,
|
|
269
581
|
variant: "ghost",
|
|
270
|
-
"aria-label": "
|
|
582
|
+
"aria-label": t("common.close"),
|
|
271
583
|
onPress: close,
|
|
272
584
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon$4, { size: 16 })
|
|
273
585
|
})]
|
|
@@ -276,7 +588,7 @@ function AddDeviceDrawer({ isOpen, onOpenChange, onAdded }) {
|
|
|
276
588
|
className: "sh:flex sh:flex-col sh:gap-4",
|
|
277
589
|
children: [
|
|
278
590
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextFieldRow, {
|
|
279
|
-
label: "
|
|
591
|
+
label: t("add.ip"),
|
|
280
592
|
value: ipAddress,
|
|
281
593
|
onChange: setIpAddress,
|
|
282
594
|
placeholder: "192.168.1.42",
|
|
@@ -284,31 +596,35 @@ function AddDeviceDrawer({ isOpen, onOpenChange, onAdded }) {
|
|
|
284
596
|
dataCy: "shelly-add-ip"
|
|
285
597
|
}),
|
|
286
598
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextFieldRow, {
|
|
287
|
-
label: "
|
|
599
|
+
label: t("add.name"),
|
|
288
600
|
value: name,
|
|
289
601
|
onChange: setName,
|
|
290
|
-
placeholder: "
|
|
602
|
+
placeholder: t("add.placeholder"),
|
|
291
603
|
dataCy: "shelly-add-name"
|
|
292
604
|
}),
|
|
293
605
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
294
606
|
status: "danger",
|
|
295
|
-
title: "
|
|
607
|
+
title: t("add.error"),
|
|
296
608
|
dataCy: "shelly-add-error",
|
|
297
|
-
children: error
|
|
609
|
+
children: tMessage(error)
|
|
298
610
|
}),
|
|
299
611
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
300
612
|
className: "sh:flex sh:justify-end sh:gap-2 sh:pt-2",
|
|
301
613
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$5, {
|
|
302
614
|
variant: "secondary",
|
|
303
615
|
onPress: close,
|
|
304
|
-
children: "
|
|
616
|
+
children: t("common.cancel")
|
|
305
617
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$5, {
|
|
306
618
|
variant: "primary",
|
|
307
619
|
type: "submit",
|
|
308
620
|
isPending: submitting,
|
|
309
621
|
onPress: submit,
|
|
310
622
|
"data-cy": "shelly-add-submit",
|
|
311
|
-
children: [
|
|
623
|
+
children: [
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon$1, { className: "sh:h-4 sh:w-4" }),
|
|
625
|
+
" ",
|
|
626
|
+
t("devices.add")
|
|
627
|
+
]
|
|
312
628
|
})]
|
|
313
629
|
}),
|
|
314
630
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
@@ -325,6 +641,7 @@ var { Button: Button$4, DrawerBody: DrawerBody$3, DrawerFooter: DrawerFooter$2,
|
|
|
325
641
|
var { KeyRoundIcon: KeyRoundIcon$1, XIcon: XIcon$3 } = await importShared("lucide-react");
|
|
326
642
|
var { useCallback: useCallback$4, useEffect: useEffect$3, useState: useState$4 } = await importShared("react");
|
|
327
643
|
function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
644
|
+
const { t, tMessage } = useShellyTranslations();
|
|
328
645
|
const [currentPassword, setCurrentPassword] = useState$4("");
|
|
329
646
|
const [password, setPassword] = useState$4("");
|
|
330
647
|
const [submitting, setSubmitting] = useState$4(false);
|
|
@@ -341,7 +658,7 @@ function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
|
341
658
|
if (!device) return;
|
|
342
659
|
const nextPassword = password.trim();
|
|
343
660
|
if (!nextPassword) {
|
|
344
|
-
setError("
|
|
661
|
+
setError({ key: "password.required" });
|
|
345
662
|
return;
|
|
346
663
|
}
|
|
347
664
|
setSubmitting(true);
|
|
@@ -381,22 +698,19 @@ function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
|
381
698
|
className: "sh:flex sh:items-center sh:gap-2",
|
|
382
699
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeyRoundIcon$1, { className: "sh:h-5 sh:w-5 sh:shrink-0 sh:text-accent-soft-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
383
700
|
className: "sh:text-lg sh:font-semibold",
|
|
384
|
-
children: "
|
|
701
|
+
children: t("password.title")
|
|
385
702
|
})]
|
|
386
|
-
}), device && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
703
|
+
}), device && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
387
704
|
className: "sh:text-sm sh:text-muted",
|
|
388
|
-
children:
|
|
389
|
-
|
|
390
|
-
device.
|
|
391
|
-
|
|
392
|
-
device.ipAddress,
|
|
393
|
-
")."
|
|
394
|
-
]
|
|
705
|
+
children: t("password.description", {
|
|
706
|
+
name: device.name,
|
|
707
|
+
address: device.ipAddress
|
|
708
|
+
})
|
|
395
709
|
})]
|
|
396
710
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$4, {
|
|
397
711
|
isIconOnly: true,
|
|
398
712
|
variant: "ghost",
|
|
399
|
-
"aria-label": "
|
|
713
|
+
"aria-label": t("common.close"),
|
|
400
714
|
onPress: close,
|
|
401
715
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon$3, { size: 16 })
|
|
402
716
|
})]
|
|
@@ -406,27 +720,27 @@ function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
|
406
720
|
className: "sh:flex sh:flex-col sh:gap-4",
|
|
407
721
|
children: [
|
|
408
722
|
device?.authState === "required" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordFieldRow, {
|
|
409
|
-
label: "
|
|
723
|
+
label: t("password.current"),
|
|
410
724
|
value: currentPassword,
|
|
411
725
|
onChange: setCurrentPassword,
|
|
412
|
-
description: "
|
|
726
|
+
description: t("password.currentDescription"),
|
|
413
727
|
autoComplete: "current-password",
|
|
414
728
|
dataCy: "shelly-auth-current-password"
|
|
415
729
|
}),
|
|
416
730
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordFieldRow, {
|
|
417
|
-
label: "
|
|
731
|
+
label: t("password.new"),
|
|
418
732
|
value: password,
|
|
419
733
|
onChange: setPassword,
|
|
420
|
-
description: "
|
|
734
|
+
description: t("password.newDescription"),
|
|
421
735
|
required: true,
|
|
422
736
|
autoComplete: "new-password",
|
|
423
737
|
dataCy: "shelly-auth-password"
|
|
424
738
|
}),
|
|
425
739
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
426
740
|
status: "danger",
|
|
427
|
-
title: "
|
|
741
|
+
title: t("password.error"),
|
|
428
742
|
dataCy: "shelly-auth-error",
|
|
429
|
-
children: error
|
|
743
|
+
children: tMessage(error)
|
|
430
744
|
}),
|
|
431
745
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
432
746
|
type: "submit",
|
|
@@ -437,13 +751,17 @@ function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
|
437
751
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter$2, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$4, {
|
|
438
752
|
variant: "secondary",
|
|
439
753
|
onPress: close,
|
|
440
|
-
children: "
|
|
754
|
+
children: t("common.cancel")
|
|
441
755
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$4, {
|
|
442
756
|
variant: "primary",
|
|
443
757
|
isPending: submitting,
|
|
444
758
|
onPress: () => void submit(),
|
|
445
759
|
"data-cy": "shelly-auth-submit",
|
|
446
|
-
children: [
|
|
760
|
+
children: [
|
|
761
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeyRoundIcon$1, { className: "sh:h-4 sh:w-4" }),
|
|
762
|
+
" ",
|
|
763
|
+
t("password.save")
|
|
764
|
+
]
|
|
447
765
|
})] })
|
|
448
766
|
]
|
|
449
767
|
});
|
|
@@ -453,10 +771,6 @@ function AdminPasswordDrawer({ device, onOpenChange, onSaved }) {
|
|
|
453
771
|
var { Button: Button$3, Card, DrawerBody: DrawerBody$2, DrawerFooter: DrawerFooter$1, DrawerHeader: DrawerHeader$2, Form: Form$1, Skeleton } = await importShared("@heroui/react");
|
|
454
772
|
var { EyeIcon, EyeOffIcon, InfoIcon: InfoIcon$1, RefreshCwIcon: RefreshCwIcon$2, XIcon: XIcon$2 } = await importShared("lucide-react");
|
|
455
773
|
var { useCallback: useCallback$3, useEffect: useEffect$2, useState: useState$3 } = await importShared("react");
|
|
456
|
-
function generationLabel$1(generation) {
|
|
457
|
-
if (generation === null) return "Unknown";
|
|
458
|
-
return generation === 1 ? "Gen 1" : `Gen ${generation}+`;
|
|
459
|
-
}
|
|
460
774
|
function isRecord(value) {
|
|
461
775
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
462
776
|
}
|
|
@@ -472,14 +786,14 @@ function firstValue(source, paths) {
|
|
|
472
786
|
if (value !== void 0 && value !== null && value !== "") return value;
|
|
473
787
|
}
|
|
474
788
|
}
|
|
475
|
-
function
|
|
476
|
-
if (value === void 0 || value === null || value === "") return "
|
|
477
|
-
if (typeof value === "boolean") return value ? "
|
|
478
|
-
if (typeof value === "number") return `${
|
|
789
|
+
function formatInfoValue(value, t, language, suffix = "") {
|
|
790
|
+
if (value === void 0 || value === null || value === "") return t("info.notReported");
|
|
791
|
+
if (typeof value === "boolean") return t(value ? "info.on" : "info.off");
|
|
792
|
+
if (typeof value === "number") return `${value.toLocaleString(language, { maximumFractionDigits: 1 })}${suffix}`;
|
|
479
793
|
return String(value);
|
|
480
794
|
}
|
|
481
|
-
function formatUptime(seconds) {
|
|
482
|
-
if (typeof seconds !== "number") return
|
|
795
|
+
function formatUptime(seconds, t, language) {
|
|
796
|
+
if (typeof seconds !== "number") return formatInfoValue(seconds, t, language);
|
|
483
797
|
const days = Math.floor(seconds / 86400);
|
|
484
798
|
const hours = Math.floor(seconds % 86400 / 3600);
|
|
485
799
|
const minutes = Math.floor(seconds % 3600 / 60);
|
|
@@ -488,6 +802,7 @@ function formatUptime(seconds) {
|
|
|
488
802
|
return `${minutes}m`;
|
|
489
803
|
}
|
|
490
804
|
function AuthProtectedForm({ authState, currentPassword, onChange, loading, onLoad }) {
|
|
805
|
+
const { t } = useShellyTranslations();
|
|
491
806
|
const [visible, setVisible] = useState$3(false);
|
|
492
807
|
if (authState !== "required") return null;
|
|
493
808
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -501,21 +816,21 @@ function AuthProtectedForm({ authState, currentPassword, onChange, loading, onLo
|
|
|
501
816
|
children: [
|
|
502
817
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
503
818
|
className: "sh:text-sm",
|
|
504
|
-
children: "
|
|
819
|
+
children: t("info.auth")
|
|
505
820
|
}),
|
|
506
821
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
507
822
|
className: "sh:relative",
|
|
508
823
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextFieldRow, {
|
|
509
|
-
label: "
|
|
824
|
+
label: t("password.title"),
|
|
510
825
|
value: currentPassword,
|
|
511
826
|
onChange,
|
|
512
|
-
placeholder: "
|
|
827
|
+
placeholder: t("info.passwordPlaceholder"),
|
|
513
828
|
dataCy: "shelly-info-current-password"
|
|
514
829
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$3, {
|
|
515
830
|
isIconOnly: true,
|
|
516
831
|
variant: "ghost",
|
|
517
832
|
size: "sm",
|
|
518
|
-
"aria-label": visible ? "
|
|
833
|
+
"aria-label": t(visible ? "password.hide" : "password.show"),
|
|
519
834
|
className: "sh:absolute sh:right-1 sh:top-6",
|
|
520
835
|
onPress: () => setVisible((v) => !v),
|
|
521
836
|
children: visible ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EyeOffIcon, { className: "sh:h-4 sh:w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EyeIcon, { className: "sh:h-4 sh:w-4" })
|
|
@@ -529,7 +844,7 @@ function AuthProtectedForm({ authState, currentPassword, onChange, loading, onLo
|
|
|
529
844
|
isPending: loading,
|
|
530
845
|
onPress: onLoad,
|
|
531
846
|
"data-cy": "shelly-info-unlock",
|
|
532
|
-
children: "
|
|
847
|
+
children: t("info.load")
|
|
533
848
|
})
|
|
534
849
|
}),
|
|
535
850
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
@@ -541,6 +856,8 @@ function AuthProtectedForm({ authState, currentPassword, onChange, loading, onLo
|
|
|
541
856
|
});
|
|
542
857
|
}
|
|
543
858
|
function DeviceInfoCards({ info }) {
|
|
859
|
+
const { t, language } = useShellyTranslations();
|
|
860
|
+
const formatValue = (value, suffix = "") => formatInfoValue(value, t, language, suffix);
|
|
544
861
|
const status = info.status;
|
|
545
862
|
const config = info.config;
|
|
546
863
|
const output = firstValue(status, [
|
|
@@ -557,10 +874,10 @@ function DeviceInfoCards({ info }) {
|
|
|
557
874
|
const current = firstValue(status, ["switch:0.current", "meters.0.current"]);
|
|
558
875
|
const cards = [
|
|
559
876
|
{
|
|
560
|
-
title: "
|
|
877
|
+
title: t("devices.device"),
|
|
561
878
|
rows: [
|
|
562
879
|
{
|
|
563
|
-
label: "
|
|
880
|
+
label: t("info.name"),
|
|
564
881
|
value: formatValue(firstValue(config, [
|
|
565
882
|
"sys.device.name",
|
|
566
883
|
"name",
|
|
@@ -568,24 +885,24 @@ function DeviceInfoCards({ info }) {
|
|
|
568
885
|
]))
|
|
569
886
|
},
|
|
570
887
|
{
|
|
571
|
-
label: "
|
|
572
|
-
value:
|
|
888
|
+
label: t("info.generation"),
|
|
889
|
+
value: info.generation === null ? t("devices.unknown") : t(info.generation === 1 ? "devices.generation" : "devices.generationPlus", { generation: info.generation })
|
|
573
890
|
},
|
|
574
891
|
{
|
|
575
|
-
label: "
|
|
892
|
+
label: t("info.timezone"),
|
|
576
893
|
value: formatValue(firstValue(config, ["sys.location.tz", "timezone"]))
|
|
577
894
|
},
|
|
578
895
|
{
|
|
579
|
-
label: "
|
|
580
|
-
value: formatUptime(firstValue(status, ["sys.uptime", "uptime"]))
|
|
896
|
+
label: t("info.uptime"),
|
|
897
|
+
value: formatUptime(firstValue(status, ["sys.uptime", "uptime"]), t, language)
|
|
581
898
|
}
|
|
582
899
|
]
|
|
583
900
|
},
|
|
584
901
|
{
|
|
585
|
-
title: "
|
|
902
|
+
title: t("info.network"),
|
|
586
903
|
rows: [
|
|
587
904
|
{
|
|
588
|
-
label: "
|
|
905
|
+
label: t("add.ip"),
|
|
589
906
|
value: formatValue(firstValue(status, [
|
|
590
907
|
"wifi.sta_ip",
|
|
591
908
|
"wifi_sta.ip",
|
|
@@ -593,7 +910,7 @@ function DeviceInfoCards({ info }) {
|
|
|
593
910
|
]))
|
|
594
911
|
},
|
|
595
912
|
{
|
|
596
|
-
label: "
|
|
913
|
+
label: t("info.wifi"),
|
|
597
914
|
value: formatValue(firstValue(status, [
|
|
598
915
|
"wifi.ssid",
|
|
599
916
|
"wifi_sta.ssid",
|
|
@@ -601,28 +918,28 @@ function DeviceInfoCards({ info }) {
|
|
|
601
918
|
]))
|
|
602
919
|
},
|
|
603
920
|
{
|
|
604
|
-
label: "
|
|
921
|
+
label: t("info.signal"),
|
|
605
922
|
value: formatValue(firstValue(status, ["wifi.rssi", "wifi_sta.rssi"]), " dBm")
|
|
606
923
|
}
|
|
607
924
|
]
|
|
608
925
|
},
|
|
609
926
|
{
|
|
610
|
-
title: "
|
|
927
|
+
title: t("info.output"),
|
|
611
928
|
rows: [
|
|
612
929
|
{
|
|
613
|
-
label: "
|
|
930
|
+
label: t("info.state"),
|
|
614
931
|
value: formatValue(output)
|
|
615
932
|
},
|
|
616
933
|
{
|
|
617
|
-
label: "
|
|
934
|
+
label: t("info.power"),
|
|
618
935
|
value: formatValue(power, " W")
|
|
619
936
|
},
|
|
620
937
|
{
|
|
621
|
-
label: "
|
|
938
|
+
label: t("info.voltage"),
|
|
622
939
|
value: formatValue(voltage, " V")
|
|
623
940
|
},
|
|
624
941
|
{
|
|
625
|
-
label: "
|
|
942
|
+
label: t("info.current"),
|
|
626
943
|
value: formatValue(current, " A")
|
|
627
944
|
}
|
|
628
945
|
]
|
|
@@ -653,6 +970,7 @@ function DeviceInfoCards({ info }) {
|
|
|
653
970
|
});
|
|
654
971
|
}
|
|
655
972
|
function DeviceInfoDrawer({ device, onOpenChange }) {
|
|
973
|
+
const { t, language } = useShellyTranslations();
|
|
656
974
|
const [info, setInfo] = useState$3(null);
|
|
657
975
|
const [currentPassword, setCurrentPassword] = useState$3("");
|
|
658
976
|
const [loading, setLoading] = useState$3(false);
|
|
@@ -692,7 +1010,7 @@ function DeviceInfoDrawer({ device, onOpenChange }) {
|
|
|
692
1010
|
className: "sh:flex sh:items-center sh:gap-2",
|
|
693
1011
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(InfoIcon$1, { className: "sh:h-5 sh:w-5 sh:shrink-0 sh:text-accent-soft-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
694
1012
|
className: "sh:text-lg sh:font-semibold",
|
|
695
|
-
children: device?.name ?? "
|
|
1013
|
+
children: device?.name ?? t("info.title")
|
|
696
1014
|
})]
|
|
697
1015
|
}), device && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
698
1016
|
className: "sh:text-sm sh:text-muted",
|
|
@@ -701,7 +1019,7 @@ function DeviceInfoDrawer({ device, onOpenChange }) {
|
|
|
701
1019
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$3, {
|
|
702
1020
|
isIconOnly: true,
|
|
703
1021
|
variant: "ghost",
|
|
704
|
-
"aria-label": "
|
|
1022
|
+
"aria-label": t("common.close"),
|
|
705
1023
|
onPress: close,
|
|
706
1024
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon$2, { size: 16 })
|
|
707
1025
|
})]
|
|
@@ -718,7 +1036,7 @@ function DeviceInfoDrawer({ device, onOpenChange }) {
|
|
|
718
1036
|
}),
|
|
719
1037
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
720
1038
|
status: "danger",
|
|
721
|
-
title: "
|
|
1039
|
+
title: t("info.loadError"),
|
|
722
1040
|
children: error
|
|
723
1041
|
}),
|
|
724
1042
|
loading && !info ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -736,13 +1054,17 @@ function DeviceInfoDrawer({ device, onOpenChange }) {
|
|
|
736
1054
|
className: "sh:flex sh:w-full sh:items-center sh:justify-between sh:gap-3",
|
|
737
1055
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
738
1056
|
className: "sh:text-xs sh:text-default-500",
|
|
739
|
-
children: info ?
|
|
1057
|
+
children: info ? t("info.updated", { time: new Date(info.fetchedAt).toLocaleTimeString(language) }) : ""
|
|
740
1058
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$3, {
|
|
741
1059
|
variant: "secondary",
|
|
742
1060
|
onPress: () => void load(),
|
|
743
1061
|
isPending: loading,
|
|
744
1062
|
"data-cy": "shelly-info-refresh",
|
|
745
|
-
children: [
|
|
1063
|
+
children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCwIcon$2, { className: "sh:h-4 sh:w-4" }),
|
|
1065
|
+
" ",
|
|
1066
|
+
t("common.refresh")
|
|
1067
|
+
]
|
|
746
1068
|
})]
|
|
747
1069
|
}) })
|
|
748
1070
|
]
|
|
@@ -754,32 +1076,27 @@ var { Button: Button$2, Chip: Chip$1, DrawerBody: DrawerBody$1, DrawerHeader: Dr
|
|
|
754
1076
|
var { SearchIcon: SearchIcon$1, XIcon: XIcon$1 } = await importShared("lucide-react");
|
|
755
1077
|
var { useCallback: useCallback$2, useRef: useRef$1, useState: useState$2 } = await importShared("react");
|
|
756
1078
|
function ResultSummary({ result }) {
|
|
1079
|
+
const { t } = useShellyTranslations();
|
|
757
1080
|
const added = result.devices.filter((device) => device.isNew).length;
|
|
758
|
-
const scanned = result.subnets.length > 0 ? result.subnets.join(", ") : "
|
|
1081
|
+
const scanned = result.subnets.length > 0 ? result.subnets.join(", ") : t("discovery.mdnsOnly");
|
|
759
1082
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
760
1083
|
className: "sh:flex sh:flex-col sh:gap-3",
|
|
761
1084
|
"data-cy": "shelly-discover-result",
|
|
762
1085
|
children: [
|
|
763
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
764
1087
|
status: result.devices.length > 0 ? "success" : "warning",
|
|
765
|
-
title: result.devices.length > 0 ?
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
result.probed
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
"."
|
|
774
|
-
]
|
|
1088
|
+
title: result.devices.length > 0 ? t("discovery.found", {
|
|
1089
|
+
count: result.devices.length,
|
|
1090
|
+
added
|
|
1091
|
+
}) : t("discovery.empty"),
|
|
1092
|
+
children: t("discovery.probed", {
|
|
1093
|
+
count: result.probed,
|
|
1094
|
+
subnets: scanned
|
|
1095
|
+
})
|
|
775
1096
|
}),
|
|
776
|
-
result.devices.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1097
|
+
result.devices.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
777
1098
|
className: "sh:text-sm sh:text-muted",
|
|
778
|
-
children:
|
|
779
|
-
"Nothing answered ",
|
|
780
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "GET /shelly" }),
|
|
781
|
-
". If Attraccess runs in a container, enter the subnet your devices are on — the container's own network is not your LAN."
|
|
782
|
-
]
|
|
1099
|
+
children: t("discovery.hint")
|
|
783
1100
|
}),
|
|
784
1101
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
|
|
785
1102
|
className: "sh:flex sh:flex-col sh:gap-2",
|
|
@@ -795,16 +1112,17 @@ function ResultSummary({ result }) {
|
|
|
795
1112
|
className: "sh:text-xs sh:text-muted",
|
|
796
1113
|
children: [
|
|
797
1114
|
device.ipAddress,
|
|
798
|
-
" ·
|
|
799
|
-
device.generation,
|
|
800
|
-
" ·
|
|
801
|
-
|
|
1115
|
+
" · ",
|
|
1116
|
+
t("devices.generation", { generation: device.generation }),
|
|
1117
|
+
" ·",
|
|
1118
|
+
" ",
|
|
1119
|
+
t("discovery.via", { source: device.source === "mdns" ? "mDNS" : t("discovery.scan") })
|
|
802
1120
|
]
|
|
803
1121
|
})]
|
|
804
1122
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Chip$1, {
|
|
805
1123
|
variant: "soft",
|
|
806
1124
|
color: device.isNew ? "success" : "default",
|
|
807
|
-
children: device.isNew ? "
|
|
1125
|
+
children: t(device.isNew ? "discovery.added" : "discovery.known")
|
|
808
1126
|
})]
|
|
809
1127
|
}, device.deviceId))
|
|
810
1128
|
})
|
|
@@ -812,6 +1130,7 @@ function ResultSummary({ result }) {
|
|
|
812
1130
|
});
|
|
813
1131
|
}
|
|
814
1132
|
function DiscoverDrawer({ isOpen, onOpenChange, onDiscovered }) {
|
|
1133
|
+
const { t } = useShellyTranslations();
|
|
815
1134
|
const [cidr, setCidr] = useState$2("");
|
|
816
1135
|
const [running, setRunning] = useState$2(false);
|
|
817
1136
|
const [error, setError] = useState$2(null);
|
|
@@ -844,15 +1163,15 @@ function DiscoverDrawer({ isOpen, onOpenChange, onDiscovered }) {
|
|
|
844
1163
|
className: "sh:flex sh:flex-col sh:gap-1",
|
|
845
1164
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
846
1165
|
className: "sh:text-lg sh:font-semibold",
|
|
847
|
-
children: "
|
|
1166
|
+
children: t("discovery.title")
|
|
848
1167
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
849
1168
|
className: "sh:text-sm sh:text-muted",
|
|
850
|
-
children: "
|
|
1169
|
+
children: t("discovery.description")
|
|
851
1170
|
})]
|
|
852
1171
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$2, {
|
|
853
1172
|
isIconOnly: true,
|
|
854
1173
|
variant: "ghost",
|
|
855
|
-
"aria-label": "
|
|
1174
|
+
"aria-label": t("common.close"),
|
|
856
1175
|
onPress: close,
|
|
857
1176
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon$1, { size: 16 })
|
|
858
1177
|
})]
|
|
@@ -861,16 +1180,16 @@ function DiscoverDrawer({ isOpen, onOpenChange, onDiscovered }) {
|
|
|
861
1180
|
className: "sh:flex sh:flex-col sh:gap-4",
|
|
862
1181
|
children: [
|
|
863
1182
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextFieldRow, {
|
|
864
|
-
label: "
|
|
1183
|
+
label: t("discovery.subnet"),
|
|
865
1184
|
value: cidr,
|
|
866
1185
|
onChange: setCidr,
|
|
867
1186
|
placeholder: "192.168.1.0/24",
|
|
868
|
-
description: "
|
|
1187
|
+
description: t("discovery.subnetDescription"),
|
|
869
1188
|
dataCy: "shelly-discover-cidr"
|
|
870
1189
|
}),
|
|
871
1190
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
872
1191
|
status: "danger",
|
|
873
|
-
title: "
|
|
1192
|
+
title: t("discovery.error"),
|
|
874
1193
|
dataCy: "shelly-discover-error",
|
|
875
1194
|
children: error
|
|
876
1195
|
}),
|
|
@@ -879,7 +1198,7 @@ function DiscoverDrawer({ isOpen, onOpenChange, onDiscovered }) {
|
|
|
879
1198
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner$2, {
|
|
880
1199
|
color: "accent",
|
|
881
1200
|
size: "sm"
|
|
882
|
-
}), "
|
|
1201
|
+
}), t("discovery.running")]
|
|
883
1202
|
}),
|
|
884
1203
|
result && !running && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResultSummary, { result }),
|
|
885
1204
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -887,14 +1206,18 @@ function DiscoverDrawer({ isOpen, onOpenChange, onDiscovered }) {
|
|
|
887
1206
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$2, {
|
|
888
1207
|
variant: "secondary",
|
|
889
1208
|
onPress: close,
|
|
890
|
-
children: result ? "
|
|
1209
|
+
children: t(result ? "common.done" : "common.cancel")
|
|
891
1210
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$2, {
|
|
892
1211
|
variant: "primary",
|
|
893
1212
|
type: "submit",
|
|
894
1213
|
isPending: running,
|
|
895
1214
|
onPress: submit,
|
|
896
1215
|
"data-cy": "shelly-discover-submit",
|
|
897
|
-
children: [
|
|
1216
|
+
children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon$1, { className: "sh:h-4 sh:w-4" }),
|
|
1218
|
+
" ",
|
|
1219
|
+
t("discovery.start")
|
|
1220
|
+
]
|
|
898
1221
|
})]
|
|
899
1222
|
}),
|
|
900
1223
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
@@ -912,10 +1235,6 @@ var { ArrowUpCircleIcon, CpuIcon: CpuIcon$1, DownloadIcon, RefreshCwIcon: Refres
|
|
|
912
1235
|
var { useCallback: useCallback$1, useEffect: useEffect$1, useRef, useState: useState$1 } = await importShared("react");
|
|
913
1236
|
var POLL_INTERVAL_MS = 5e3;
|
|
914
1237
|
var UPDATE_TIMEOUT_MS = 3e5;
|
|
915
|
-
var STAGE_LABEL = {
|
|
916
|
-
stable: "stable",
|
|
917
|
-
beta: "beta"
|
|
918
|
-
};
|
|
919
1238
|
/**
|
|
920
1239
|
* Gen1 reports versions as `20230913-114150/v1.14.0` — only the tail is useful
|
|
921
1240
|
* at a glance, the full string stays in the tooltip.
|
|
@@ -926,25 +1245,26 @@ function shortVersion(version) {
|
|
|
926
1245
|
}
|
|
927
1246
|
/** Table cell summarising a device's firmware state from the bulk overview. */
|
|
928
1247
|
function FirmwareCell({ entry }) {
|
|
1248
|
+
const { t } = useShellyTranslations();
|
|
929
1249
|
if (!entry) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
930
1250
|
className: "sh:text-sm sh:text-default-400",
|
|
931
|
-
children: "
|
|
1251
|
+
children: t("firmware.checking")
|
|
932
1252
|
});
|
|
933
1253
|
if (entry.error || !entry.status) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
934
1254
|
className: "sh:block sh:max-w-40 sh:truncate sh:text-sm sh:text-default-400",
|
|
935
1255
|
title: entry.error ?? void 0,
|
|
936
|
-
children: "
|
|
1256
|
+
children: t("firmware.unavailable")
|
|
937
1257
|
});
|
|
938
1258
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
939
1259
|
className: "sh:flex sh:flex-col sh:gap-1",
|
|
940
1260
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
941
1261
|
className: "sh:block sh:max-w-40 sh:truncate sh:text-sm sh:text-default-700",
|
|
942
1262
|
title: entry.status.currentVersion ?? void 0,
|
|
943
|
-
children: entry.status.currentVersion ? shortVersion(entry.status.currentVersion) : "
|
|
944
|
-
}), entry.status.hasUpdate && entry.status.available.stable && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1263
|
+
children: entry.status.currentVersion ? shortVersion(entry.status.currentVersion) : t("devices.unknown")
|
|
1264
|
+
}), entry.status.hasUpdate && entry.status.available.stable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
945
1265
|
className: "sh:max-w-40 sh:truncate sh:text-xs sh:text-warning-600",
|
|
946
1266
|
title: entry.status.available.stable,
|
|
947
|
-
children:
|
|
1267
|
+
children: t("firmware.available", { version: shortVersion(entry.status.available.stable) })
|
|
948
1268
|
})]
|
|
949
1269
|
});
|
|
950
1270
|
}
|
|
@@ -955,9 +1275,10 @@ function FirmwareCell({ entry }) {
|
|
|
955
1275
|
* table's visible width on tablets.
|
|
956
1276
|
*/
|
|
957
1277
|
function UpdateAvailableIndicator({ entry }) {
|
|
1278
|
+
const { t } = useShellyTranslations();
|
|
958
1279
|
const version = entry?.status?.hasUpdate ? entry.status.available.stable : null;
|
|
959
1280
|
if (!version) return null;
|
|
960
|
-
const label =
|
|
1281
|
+
const label = t("firmware.updateAvailable", { version: shortVersion(version) });
|
|
961
1282
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip$1.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
962
1283
|
variant: "ghost",
|
|
963
1284
|
size: "sm",
|
|
@@ -982,34 +1303,36 @@ function VersionRow({ label, value }) {
|
|
|
982
1303
|
});
|
|
983
1304
|
}
|
|
984
1305
|
function FirmwareDetails({ status }) {
|
|
1306
|
+
const { t, language } = useShellyTranslations();
|
|
985
1307
|
if (!status) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
986
1308
|
className: "sh:rounded-xl sh:border sh:border-dashed sh:border-default-300 sh:p-4 sh:text-sm sh:text-default-500",
|
|
987
|
-
children: "
|
|
1309
|
+
children: t("firmware.empty")
|
|
988
1310
|
});
|
|
989
1311
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
|
|
990
1312
|
className: "sh:rounded-xl sh:border sh:border-default-200 sh:bg-surface sh:p-4",
|
|
991
1313
|
"data-cy": "shelly-firmware-details",
|
|
992
1314
|
children: [
|
|
993
1315
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(VersionRow, {
|
|
994
|
-
label: "
|
|
995
|
-
value: status.currentVersion ?? "
|
|
1316
|
+
label: t("firmware.installed"),
|
|
1317
|
+
value: status.currentVersion ?? t("devices.unknown")
|
|
996
1318
|
}),
|
|
997
1319
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(VersionRow, {
|
|
998
|
-
label: "
|
|
999
|
-
value: status.available.stable ?? "
|
|
1320
|
+
label: t("firmware.stable"),
|
|
1321
|
+
value: status.available.stable ?? t("firmware.upToDate")
|
|
1000
1322
|
}),
|
|
1001
1323
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(VersionRow, {
|
|
1002
|
-
label: "
|
|
1003
|
-
value: status.available.beta ?? "
|
|
1324
|
+
label: t("firmware.beta"),
|
|
1325
|
+
value: status.available.beta ?? t("firmware.nothingNewer")
|
|
1004
1326
|
}),
|
|
1005
1327
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(VersionRow, {
|
|
1006
|
-
label: "
|
|
1007
|
-
value: new Date(status.fetchedAt).toLocaleString()
|
|
1328
|
+
label: t("firmware.checked"),
|
|
1329
|
+
value: new Date(status.fetchedAt).toLocaleString(language)
|
|
1008
1330
|
})
|
|
1009
1331
|
]
|
|
1010
1332
|
});
|
|
1011
1333
|
}
|
|
1012
1334
|
function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
1335
|
+
const { t, tMessage } = useShellyTranslations();
|
|
1013
1336
|
const [status, setStatus] = useState$1(null);
|
|
1014
1337
|
const [currentPassword, setCurrentPassword] = useState$1("");
|
|
1015
1338
|
const [loading, setLoading] = useState$1(false);
|
|
@@ -1070,7 +1393,7 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1070
1393
|
if (cancelled) return;
|
|
1071
1394
|
if (Date.now() > deadline.current) {
|
|
1072
1395
|
setInstalling(null);
|
|
1073
|
-
setError(
|
|
1396
|
+
setError({ key: "firmware.timeout" });
|
|
1074
1397
|
return;
|
|
1075
1398
|
}
|
|
1076
1399
|
try {
|
|
@@ -1107,22 +1430,19 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1107
1430
|
className: "sh:flex sh:items-center sh:gap-2",
|
|
1108
1431
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CpuIcon$1, { className: "sh:h-5 sh:w-5 sh:shrink-0 sh:text-accent-soft-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
1109
1432
|
className: "sh:text-lg sh:font-semibold",
|
|
1110
|
-
children: "
|
|
1433
|
+
children: t("devices.firmware")
|
|
1111
1434
|
})]
|
|
1112
|
-
}), device && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1435
|
+
}), device && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
1113
1436
|
className: "sh:text-sm sh:text-muted",
|
|
1114
|
-
children:
|
|
1115
|
-
|
|
1116
|
-
device.
|
|
1117
|
-
|
|
1118
|
-
device.ipAddress,
|
|
1119
|
-
")."
|
|
1120
|
-
]
|
|
1437
|
+
children: t("firmware.description", {
|
|
1438
|
+
name: device.name,
|
|
1439
|
+
address: device.ipAddress
|
|
1440
|
+
})
|
|
1121
1441
|
})]
|
|
1122
1442
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
1123
1443
|
isIconOnly: true,
|
|
1124
1444
|
variant: "ghost",
|
|
1125
|
-
"aria-label": "
|
|
1445
|
+
"aria-label": t("common.close"),
|
|
1126
1446
|
onPress: close,
|
|
1127
1447
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon, { size: 16 })
|
|
1128
1448
|
})]
|
|
@@ -1131,47 +1451,39 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1131
1451
|
className: "sh:flex sh:flex-col sh:gap-4",
|
|
1132
1452
|
children: [
|
|
1133
1453
|
device?.authState === "required" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordFieldRow, {
|
|
1134
|
-
label: "
|
|
1454
|
+
label: t("password.current"),
|
|
1135
1455
|
value: currentPassword,
|
|
1136
1456
|
onChange: setCurrentPassword,
|
|
1137
|
-
description: "
|
|
1457
|
+
description: t("password.currentDescription"),
|
|
1138
1458
|
autoComplete: "current-password",
|
|
1139
1459
|
dataCy: "shelly-firmware-current-password"
|
|
1140
1460
|
}),
|
|
1141
1461
|
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
1142
1462
|
status: "danger",
|
|
1143
|
-
title: "
|
|
1463
|
+
title: t("firmware.error"),
|
|
1144
1464
|
dataCy: "shelly-firmware-error",
|
|
1145
|
-
children: error
|
|
1465
|
+
children: tMessage(error)
|
|
1146
1466
|
}),
|
|
1147
1467
|
installing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
1148
1468
|
status: "accent",
|
|
1149
|
-
title: "
|
|
1469
|
+
title: t("firmware.running"),
|
|
1150
1470
|
dataCy: "shelly-firmware-progress",
|
|
1151
1471
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
1152
1472
|
className: "sh:flex sh:items-center sh:gap-2",
|
|
1153
|
-
children: [
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}),
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
" firmware",
|
|
1161
|
-
targetVersion.current ? ` (${targetVersion.current})` : "",
|
|
1162
|
-
". The device reboots during the update and is offline for a moment — this page keeps checking."
|
|
1163
|
-
]
|
|
1473
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner$1, {
|
|
1474
|
+
size: "sm",
|
|
1475
|
+
color: "accent"
|
|
1476
|
+
}), t("firmware.installing", {
|
|
1477
|
+
stage: t(`firmware.${installing}Stage`),
|
|
1478
|
+
version: targetVersion.current ? ` (${targetVersion.current})` : ""
|
|
1479
|
+
})]
|
|
1164
1480
|
})
|
|
1165
1481
|
}),
|
|
1166
|
-
installedVersion && !installing && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1482
|
+
installedVersion && !installing && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
1167
1483
|
status: "success",
|
|
1168
|
-
title: "
|
|
1484
|
+
title: t("firmware.finished"),
|
|
1169
1485
|
dataCy: "shelly-firmware-success",
|
|
1170
|
-
children:
|
|
1171
|
-
"The device now runs ",
|
|
1172
|
-
installedVersion,
|
|
1173
|
-
"."
|
|
1174
|
-
]
|
|
1486
|
+
children: t("firmware.success", { version: installedVersion })
|
|
1175
1487
|
}),
|
|
1176
1488
|
loading && !status ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
1177
1489
|
className: "sh:flex sh:items-center sh:justify-center sh:p-6",
|
|
@@ -1187,7 +1499,11 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1187
1499
|
isPending: loading,
|
|
1188
1500
|
isDisabled: !!installing,
|
|
1189
1501
|
"data-cy": "shelly-firmware-refresh",
|
|
1190
|
-
children: [
|
|
1502
|
+
children: [
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCwIcon$1, { className: "sh:h-4 sh:w-4" }),
|
|
1504
|
+
" ",
|
|
1505
|
+
t("firmware.recheck")
|
|
1506
|
+
]
|
|
1191
1507
|
}), ["stable", "beta"].map((stage) => {
|
|
1192
1508
|
const version = status?.available[stage];
|
|
1193
1509
|
if (!version) return null;
|
|
@@ -1197,13 +1513,17 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1197
1513
|
isPending: installing === stage,
|
|
1198
1514
|
isDisabled: !!installing,
|
|
1199
1515
|
"data-cy": `shelly-firmware-install-${stage}`,
|
|
1200
|
-
"aria-label":
|
|
1516
|
+
"aria-label": t("firmware.installLabel", {
|
|
1517
|
+
stage: t(`firmware.${stage}Stage`),
|
|
1518
|
+
version
|
|
1519
|
+
}),
|
|
1201
1520
|
children: [
|
|
1202
1521
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DownloadIcon, { className: "sh:h-4 sh:w-4" }),
|
|
1203
|
-
" Install ",
|
|
1204
|
-
STAGE_LABEL[stage],
|
|
1205
1522
|
" ",
|
|
1206
|
-
|
|
1523
|
+
t("firmware.install", {
|
|
1524
|
+
stage: t(`firmware.${stage}Stage`),
|
|
1525
|
+
version: shortVersion(version)
|
|
1526
|
+
})
|
|
1207
1527
|
]
|
|
1208
1528
|
}, stage);
|
|
1209
1529
|
})]
|
|
@@ -1216,97 +1536,104 @@ function FirmwareDrawer({ device, onOpenChange, onUpdated }) {
|
|
|
1216
1536
|
var { Button, Chip, Dropdown, DropdownItem, DropdownMenu, DropdownPopover, DropdownTrigger, Modal, ModalBackdrop, ModalBody, ModalContainer, ModalDialog, ModalFooter, ModalHeader, ModalHeading, Spinner, Table, TableBody, TableCell, TableColumn, TableContent, TableHeader, TableRow, TableScrollContainer, Tooltip, useOverlayState } = await importShared("@heroui/react");
|
|
1217
1537
|
var { CpuIcon, InfoIcon, KeyRoundIcon, MehIcon, MoreVerticalIcon, PlusIcon, RefreshCwIcon, SearchIcon, Trash2Icon, TriangleAlertIcon, WifiIcon: WifiIcon$1 } = await importShared("lucide-react");
|
|
1218
1538
|
var { useCallback, useEffect, useState } = await importShared("react");
|
|
1219
|
-
function generationLabel(generation) {
|
|
1220
|
-
if (generation === null) return "Unknown";
|
|
1221
|
-
return generation === 1 ? "Gen 1" : `Gen ${generation}+`;
|
|
1222
|
-
}
|
|
1223
1539
|
function AuthChip({ state }) {
|
|
1224
|
-
const {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}
|
|
1229
|
-
required: {
|
|
1230
|
-
color: "warning",
|
|
1231
|
-
label: "Auth required"
|
|
1232
|
-
},
|
|
1233
|
-
unknown: {
|
|
1234
|
-
color: "default",
|
|
1235
|
-
label: "Unknown"
|
|
1236
|
-
}
|
|
1540
|
+
const { t } = useShellyTranslations();
|
|
1541
|
+
const { color } = {
|
|
1542
|
+
none: { color: "success" },
|
|
1543
|
+
required: { color: "warning" },
|
|
1544
|
+
unknown: { color: "default" }
|
|
1237
1545
|
}[state];
|
|
1238
1546
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Chip, {
|
|
1239
1547
|
variant: "soft",
|
|
1240
1548
|
color,
|
|
1241
1549
|
size: "sm",
|
|
1242
1550
|
className: "sh:whitespace-nowrap",
|
|
1243
|
-
children:
|
|
1551
|
+
children: t(`auth.${state}`)
|
|
1244
1552
|
});
|
|
1245
1553
|
}
|
|
1246
1554
|
function ProbeErrorIndicator({ message }) {
|
|
1555
|
+
const { t } = useShellyTranslations();
|
|
1247
1556
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
1248
1557
|
variant: "ghost",
|
|
1249
1558
|
size: "sm",
|
|
1250
1559
|
isIconOnly: true,
|
|
1251
|
-
"aria-label":
|
|
1560
|
+
"aria-label": t("devices.probeError", { message }),
|
|
1252
1561
|
className: "sh:h-6 sh:w-6 sh:min-w-6 sh:text-warning",
|
|
1253
1562
|
"data-cy": "shelly-device-probe-error",
|
|
1254
1563
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriangleAlertIcon, { className: "sh:h-4 sh:w-4" })
|
|
1255
|
-
}) }), /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1564
|
+
}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip.Content, { children: t("devices.probeError", { message }) })] });
|
|
1256
1565
|
}
|
|
1257
1566
|
function RowActions({ deviceId, isBusy, onInfo, onFirmware, onAuth, onReprobe, onDelete }) {
|
|
1567
|
+
const { t } = useShellyTranslations();
|
|
1258
1568
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
1259
1569
|
className: "sh:flex sh:flex-row sh:items-center sh:justify-end sh:gap-1 sh:whitespace-nowrap",
|
|
1260
1570
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
1261
1571
|
variant: "ghost",
|
|
1262
1572
|
size: "sm",
|
|
1263
1573
|
isIconOnly: true,
|
|
1264
|
-
"aria-label": "
|
|
1574
|
+
"aria-label": t("devices.info"),
|
|
1265
1575
|
isDisabled: isBusy,
|
|
1266
1576
|
onPress: onInfo,
|
|
1267
1577
|
"data-cy": `shelly-device-info-${deviceId}`,
|
|
1268
1578
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InfoIcon, { className: "sh:h-4 sh:w-4" })
|
|
1269
|
-
}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip.Content, { children: "
|
|
1579
|
+
}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip.Content, { children: t("devices.info") })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dropdown, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
1270
1580
|
variant: "ghost",
|
|
1271
1581
|
size: "sm",
|
|
1272
1582
|
isIconOnly: true,
|
|
1273
|
-
"aria-label": "
|
|
1583
|
+
"aria-label": t("devices.more"),
|
|
1274
1584
|
isPending: isBusy,
|
|
1275
1585
|
"data-cy": `shelly-device-menu-${deviceId}`,
|
|
1276
1586
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MoreVerticalIcon, { className: "sh:h-4 sh:w-4" })
|
|
1277
1587
|
}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownPopover, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu, {
|
|
1278
|
-
"aria-label": "
|
|
1588
|
+
"aria-label": t("devices.deviceActions"),
|
|
1279
1589
|
children: [
|
|
1280
1590
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownItem, {
|
|
1281
1591
|
id: "firmware",
|
|
1282
1592
|
onPress: onFirmware,
|
|
1283
1593
|
"data-cy": `shelly-device-firmware-${deviceId}`,
|
|
1284
|
-
children: [
|
|
1594
|
+
children: [
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CpuIcon, { className: "sh:mr-2 sh:inline sh:h-4 sh:w-4" }),
|
|
1596
|
+
" ",
|
|
1597
|
+
t("devices.manageFirmware")
|
|
1598
|
+
]
|
|
1285
1599
|
}),
|
|
1286
1600
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownItem, {
|
|
1287
1601
|
id: "auth",
|
|
1288
1602
|
onPress: onAuth,
|
|
1289
1603
|
"data-cy": `shelly-device-auth-${deviceId}`,
|
|
1290
|
-
children: [
|
|
1604
|
+
children: [
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeyRoundIcon, { className: "sh:mr-2 sh:inline sh:h-4 sh:w-4" }),
|
|
1606
|
+
" ",
|
|
1607
|
+
t("devices.password")
|
|
1608
|
+
]
|
|
1291
1609
|
}),
|
|
1292
1610
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownItem, {
|
|
1293
1611
|
id: "reprobe",
|
|
1294
1612
|
onPress: onReprobe,
|
|
1295
1613
|
"data-cy": `shelly-device-reprobe-${deviceId}`,
|
|
1296
|
-
children: [
|
|
1614
|
+
children: [
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCwIcon, { className: "sh:mr-2 sh:inline sh:h-4 sh:w-4" }),
|
|
1616
|
+
" ",
|
|
1617
|
+
t("devices.reprobe")
|
|
1618
|
+
]
|
|
1297
1619
|
}),
|
|
1298
1620
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownItem, {
|
|
1299
1621
|
id: "delete",
|
|
1300
1622
|
onPress: onDelete,
|
|
1301
1623
|
className: "sh:text-danger",
|
|
1302
1624
|
"data-cy": `shelly-device-delete-${deviceId}`,
|
|
1303
|
-
children: [
|
|
1625
|
+
children: [
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2Icon, { className: "sh:mr-2 sh:inline sh:h-4 sh:w-4" }),
|
|
1627
|
+
" ",
|
|
1628
|
+
t("devices.delete")
|
|
1629
|
+
]
|
|
1304
1630
|
})
|
|
1305
1631
|
]
|
|
1306
1632
|
}) })] })]
|
|
1307
1633
|
});
|
|
1308
1634
|
}
|
|
1309
1635
|
function EmptyDevices({ onAdd }) {
|
|
1636
|
+
const { t } = useShellyTranslations();
|
|
1310
1637
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
1311
1638
|
className: "sh:flex sh:flex-col sh:items-center sh:justify-center sh:gap-3 sh:px-4 sh:py-12",
|
|
1312
1639
|
children: [
|
|
@@ -1316,19 +1643,24 @@ function EmptyDevices({ onAdd }) {
|
|
|
1316
1643
|
}),
|
|
1317
1644
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
1318
1645
|
className: "sh:text-sm sh:text-default-500",
|
|
1319
|
-
children: "
|
|
1646
|
+
children: t("devices.empty")
|
|
1320
1647
|
}),
|
|
1321
1648
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
|
|
1322
1649
|
variant: "secondary",
|
|
1323
1650
|
size: "sm",
|
|
1324
1651
|
onPress: onAdd,
|
|
1325
1652
|
"data-cy": "shelly-add-open-empty",
|
|
1326
|
-
children: [
|
|
1653
|
+
children: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, { className: "sh:h-4 sh:w-4" }),
|
|
1655
|
+
" ",
|
|
1656
|
+
t("devices.add")
|
|
1657
|
+
]
|
|
1327
1658
|
})
|
|
1328
1659
|
]
|
|
1329
1660
|
});
|
|
1330
1661
|
}
|
|
1331
1662
|
function DevicesPage() {
|
|
1663
|
+
const { t, language } = useShellyTranslations();
|
|
1332
1664
|
const [devices, setDevices] = useState([]);
|
|
1333
1665
|
const [loading, setLoading] = useState(true);
|
|
1334
1666
|
const [pageError, setPageError] = useState(null);
|
|
@@ -1403,10 +1735,10 @@ function DevicesPage() {
|
|
|
1403
1735
|
className: "sh:flex sh:items-center sh:gap-3",
|
|
1404
1736
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(WifiIcon$1, { className: "sh:h-6 sh:w-6 sh:text-accent-soft-foreground" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
1405
1737
|
className: "sh:text-2xl sh:font-bold",
|
|
1406
|
-
children: "
|
|
1738
|
+
children: t("devices.title")
|
|
1407
1739
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
1408
1740
|
className: "sh:mt-1 sh:text-sm sh:text-muted",
|
|
1409
|
-
children: "
|
|
1741
|
+
children: t("devices.description")
|
|
1410
1742
|
})] })]
|
|
1411
1743
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
1412
1744
|
className: "sh:flex sh:flex-wrap sh:gap-2",
|
|
@@ -1414,18 +1746,26 @@ function DevicesPage() {
|
|
|
1414
1746
|
variant: "secondary",
|
|
1415
1747
|
onPress: discoverDrawer.open,
|
|
1416
1748
|
"data-cy": "shelly-discover-open",
|
|
1417
|
-
children: [
|
|
1749
|
+
children: [
|
|
1750
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, { className: "sh:h-4 sh:w-4" }),
|
|
1751
|
+
" ",
|
|
1752
|
+
t("devices.discover")
|
|
1753
|
+
]
|
|
1418
1754
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
|
|
1419
1755
|
variant: "primary",
|
|
1420
1756
|
onPress: addDrawer.open,
|
|
1421
1757
|
"data-cy": "shelly-add-open",
|
|
1422
|
-
children: [
|
|
1758
|
+
children: [
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, { className: "sh:h-4 sh:w-4" }),
|
|
1760
|
+
" ",
|
|
1761
|
+
t("devices.add")
|
|
1762
|
+
]
|
|
1423
1763
|
})]
|
|
1424
1764
|
})]
|
|
1425
1765
|
}),
|
|
1426
1766
|
pageError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusAlert, {
|
|
1427
1767
|
status: "danger",
|
|
1428
|
-
title: "
|
|
1768
|
+
title: t("devices.loadError"),
|
|
1429
1769
|
children: pageError
|
|
1430
1770
|
}),
|
|
1431
1771
|
loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -1434,35 +1774,39 @@ function DevicesPage() {
|
|
|
1434
1774
|
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Table, {
|
|
1435
1775
|
"data-cy": "shelly-device-table",
|
|
1436
1776
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableScrollContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableContent, {
|
|
1437
|
-
"aria-label": "
|
|
1777
|
+
"aria-label": t("devices.table"),
|
|
1438
1778
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableHeader, { children: [
|
|
1439
1779
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1440
1780
|
isRowHeader: true,
|
|
1441
|
-
children: "
|
|
1781
|
+
children: t("devices.device")
|
|
1442
1782
|
}),
|
|
1443
1783
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1444
1784
|
className: "sh:hidden sh:sm:table-cell sh:md:hidden sh:lg:table-cell",
|
|
1445
|
-
children: "
|
|
1785
|
+
children: t("devices.address")
|
|
1446
1786
|
}),
|
|
1447
1787
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1448
1788
|
className: "sh:hidden sh:lg:table-cell",
|
|
1449
|
-
children: "
|
|
1789
|
+
children: t("devices.model")
|
|
1450
1790
|
}),
|
|
1451
1791
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1452
1792
|
className: "sh:hidden sh:sm:table-cell",
|
|
1453
|
-
children: "
|
|
1793
|
+
children: t("devices.auth")
|
|
1454
1794
|
}),
|
|
1455
1795
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1456
1796
|
className: "sh:hidden sh:xl:table-cell",
|
|
1457
|
-
children: "
|
|
1797
|
+
children: t("devices.firmware")
|
|
1458
1798
|
}),
|
|
1459
1799
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableColumn, {
|
|
1460
1800
|
className: "sh:text-end",
|
|
1461
|
-
children: "
|
|
1801
|
+
children: t("devices.actions")
|
|
1462
1802
|
})
|
|
1463
1803
|
] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, {
|
|
1464
1804
|
items: devices,
|
|
1465
|
-
dependencies: [
|
|
1805
|
+
dependencies: [
|
|
1806
|
+
firmware,
|
|
1807
|
+
rowBusyId,
|
|
1808
|
+
language
|
|
1809
|
+
],
|
|
1466
1810
|
renderEmptyState: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDevices, { onAdd: addDrawer.open }),
|
|
1467
1811
|
children: (device) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
|
|
1468
1812
|
id: device.id,
|
|
@@ -1502,7 +1846,7 @@ function DevicesPage() {
|
|
|
1502
1846
|
variant: "soft",
|
|
1503
1847
|
size: "sm",
|
|
1504
1848
|
className: "sh:whitespace-nowrap",
|
|
1505
|
-
children:
|
|
1849
|
+
children: device.generation === null ? t("devices.unknown") : t(device.generation === 1 ? "devices.generation" : "devices.generationPlus", { generation: device.generation })
|
|
1506
1850
|
})]
|
|
1507
1851
|
})
|
|
1508
1852
|
}),
|
|
@@ -1564,28 +1908,26 @@ function DevicesPage() {
|
|
|
1564
1908
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalBackdrop, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalContainer, {
|
|
1565
1909
|
size: "sm",
|
|
1566
1910
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ModalDialog, { children: [
|
|
1567
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHeading, { children: "
|
|
1568
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalBody, { children: /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
children: deleteTarget?.name
|
|
1573
|
-
}),
|
|
1574
|
-
" (",
|
|
1575
|
-
deleteTarget?.ipAddress,
|
|
1576
|
-
") from the registry? The device itself is not changed."
|
|
1577
|
-
] }) }),
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHeading, { children: t("devices.delete") }) }),
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalBody, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("devices.deleteDescription", {
|
|
1913
|
+
name: deleteTarget?.name,
|
|
1914
|
+
address: deleteTarget?.ipAddress
|
|
1915
|
+
}) }) }),
|
|
1578
1916
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ModalFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
1579
1917
|
variant: "secondary",
|
|
1580
1918
|
onPress: () => setDeleteTarget(null),
|
|
1581
1919
|
"data-cy": "shelly-delete-cancel",
|
|
1582
|
-
children: "
|
|
1920
|
+
children: t("common.cancel")
|
|
1583
1921
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
|
|
1584
1922
|
variant: "danger",
|
|
1585
1923
|
onPress: confirmDelete,
|
|
1586
1924
|
isPending: deleting,
|
|
1587
1925
|
"data-cy": "shelly-delete-confirm",
|
|
1588
|
-
children: [
|
|
1926
|
+
children: [
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2Icon, { className: "sh:h-4 sh:w-4" }),
|
|
1928
|
+
" ",
|
|
1929
|
+
t("common.delete")
|
|
1930
|
+
]
|
|
1589
1931
|
})] })
|
|
1590
1932
|
] })
|
|
1591
1933
|
}) })
|