myio-js-library 0.1.133 → 0.1.135

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/dist/index.js CHANGED
@@ -6975,6 +6975,632 @@ if (typeof module !== "undefined" && module.exports) {
6975
6975
  module.exports = { MyIOToast };
6976
6976
  }
6977
6977
 
6978
+ // src/components/RealTimeTelemetryModal.ts
6979
+ var TELEMETRY_CONFIG = {
6980
+ voltage: { label: "Tens\xE3o", unit: "V", icon: "\u26A1", decimals: 1 },
6981
+ current: { label: "Corrente", unit: "A", icon: "\u{1F50C}", decimals: 2 },
6982
+ power: { label: "Pot\xEAncia", unit: "kW", icon: "\u2699\uFE0F", decimals: 2 },
6983
+ energy: { label: "Energia", unit: "kWh", icon: "\u{1F4CA}", decimals: 1 },
6984
+ temperature: { label: "Temperatura", unit: "\xB0C", icon: "\u{1F321}\uFE0F", decimals: 1 },
6985
+ activePower: { label: "Pot\xEAncia Ativa", unit: "kW", icon: "\u2699\uFE0F", decimals: 2 },
6986
+ reactivePower: { label: "Pot\xEAncia Reativa", unit: "kVAr", icon: "\u{1F504}", decimals: 2 },
6987
+ apparentPower: { label: "Pot\xEAncia Aparente", unit: "kVA", icon: "\u{1F4C8}", decimals: 2 },
6988
+ powerFactor: { label: "Fator de Pot\xEAncia", unit: "", icon: "\u{1F4D0}", decimals: 3 }
6989
+ };
6990
+ var STRINGS = {
6991
+ "pt-BR": {
6992
+ title: "Telemetrias em Tempo Real",
6993
+ close: "Fechar",
6994
+ pause: "Pausar",
6995
+ resume: "Retomar",
6996
+ export: "Exportar CSV",
6997
+ autoUpdate: "Atualiza\xE7\xE3o autom\xE1tica",
6998
+ lastUpdate: "\xDAltima atualiza\xE7\xE3o",
6999
+ noData: "Sem dados",
7000
+ loading: "Carregando...",
7001
+ error: "Erro ao carregar telemetrias",
7002
+ trend_up: "Aumentando",
7003
+ trend_down: "Diminuindo",
7004
+ trend_stable: "Est\xE1vel"
7005
+ },
7006
+ "en-US": {
7007
+ title: "Real-Time Telemetry",
7008
+ close: "Close",
7009
+ pause: "Pause",
7010
+ resume: "Resume",
7011
+ export: "Export CSV",
7012
+ autoUpdate: "Auto-update",
7013
+ lastUpdate: "Last update",
7014
+ noData: "No data",
7015
+ loading: "Loading...",
7016
+ error: "Error loading telemetry",
7017
+ trend_up: "Increasing",
7018
+ trend_down: "Decreasing",
7019
+ trend_stable: "Stable"
7020
+ }
7021
+ };
7022
+ async function openRealTimeTelemetryModal(params) {
7023
+ const {
7024
+ token,
7025
+ deviceId,
7026
+ deviceLabel = "Dispositivo",
7027
+ telemetryKeys = ["voltage", "current", "power", "energy"],
7028
+ refreshInterval = 8e3,
7029
+ historyPoints = 50,
7030
+ onClose,
7031
+ locale = "pt-BR"
7032
+ } = params;
7033
+ const strings = STRINGS[locale] || STRINGS["pt-BR"];
7034
+ let refreshIntervalId = null;
7035
+ let isPaused = false;
7036
+ let telemetryHistory = /* @__PURE__ */ new Map();
7037
+ let chart = null;
7038
+ const overlay = document.createElement("div");
7039
+ overlay.className = "myio-realtime-telemetry-overlay";
7040
+ overlay.innerHTML = `
7041
+ <style>
7042
+ .myio-realtime-telemetry-overlay {
7043
+ position: fixed;
7044
+ top: 0;
7045
+ left: 0;
7046
+ right: 0;
7047
+ bottom: 0;
7048
+ background: rgba(0, 0, 0, 0.5);
7049
+ display: flex;
7050
+ align-items: center;
7051
+ justify-content: center;
7052
+ z-index: 10000;
7053
+ padding: 20px;
7054
+ animation: fadeIn 0.2s ease;
7055
+ }
7056
+
7057
+ @keyframes fadeIn {
7058
+ from { opacity: 0; }
7059
+ to { opacity: 1; }
7060
+ }
7061
+
7062
+ .myio-realtime-telemetry-container {
7063
+ background: #ffffff;
7064
+ border-radius: 12px;
7065
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
7066
+ max-width: 1200px;
7067
+ width: 100%;
7068
+ max-height: 90vh;
7069
+ display: flex;
7070
+ flex-direction: column;
7071
+ overflow: hidden;
7072
+ animation: slideUp 0.3s ease;
7073
+ }
7074
+
7075
+ @keyframes slideUp {
7076
+ from { transform: translateY(20px); opacity: 0; }
7077
+ to { transform: translateY(0); opacity: 1; }
7078
+ }
7079
+
7080
+ .myio-realtime-telemetry-header {
7081
+ padding: 20px 24px;
7082
+ border-bottom: 1px solid #e0e0e0;
7083
+ display: flex;
7084
+ justify-content: space-between;
7085
+ align-items: center;
7086
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
7087
+ color: white;
7088
+ }
7089
+
7090
+ .myio-realtime-telemetry-title {
7091
+ font-size: 20px;
7092
+ font-weight: 600;
7093
+ margin: 0;
7094
+ display: flex;
7095
+ align-items: center;
7096
+ gap: 8px;
7097
+ }
7098
+
7099
+ .myio-realtime-telemetry-close {
7100
+ background: rgba(255, 255, 255, 0.2);
7101
+ border: none;
7102
+ color: white;
7103
+ font-size: 24px;
7104
+ width: 32px;
7105
+ height: 32px;
7106
+ border-radius: 6px;
7107
+ cursor: pointer;
7108
+ display: flex;
7109
+ align-items: center;
7110
+ justify-content: center;
7111
+ transition: all 0.2s ease;
7112
+ }
7113
+
7114
+ .myio-realtime-telemetry-close:hover {
7115
+ background: rgba(255, 255, 255, 0.3);
7116
+ transform: scale(1.1);
7117
+ }
7118
+
7119
+ .myio-realtime-telemetry-body {
7120
+ padding: 24px;
7121
+ overflow-y: auto;
7122
+ flex: 1;
7123
+ }
7124
+
7125
+ .myio-telemetry-cards-grid {
7126
+ display: grid;
7127
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
7128
+ gap: 16px;
7129
+ margin-bottom: 24px;
7130
+ }
7131
+
7132
+ .myio-telemetry-card {
7133
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
7134
+ border-radius: 12px;
7135
+ padding: 16px;
7136
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
7137
+ transition: all 0.3s ease;
7138
+ }
7139
+
7140
+ .myio-telemetry-card:hover {
7141
+ transform: translateY(-4px);
7142
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
7143
+ }
7144
+
7145
+ .myio-telemetry-card-header {
7146
+ display: flex;
7147
+ align-items: center;
7148
+ gap: 8px;
7149
+ margin-bottom: 12px;
7150
+ font-size: 14px;
7151
+ color: #555;
7152
+ font-weight: 500;
7153
+ }
7154
+
7155
+ .myio-telemetry-card-icon {
7156
+ font-size: 20px;
7157
+ }
7158
+
7159
+ .myio-telemetry-card-value {
7160
+ font-size: 28px;
7161
+ font-weight: 700;
7162
+ color: #2c3e50;
7163
+ margin-bottom: 4px;
7164
+ }
7165
+
7166
+ .myio-telemetry-card-trend {
7167
+ font-size: 12px;
7168
+ color: #777;
7169
+ display: flex;
7170
+ align-items: center;
7171
+ gap: 4px;
7172
+ }
7173
+
7174
+ .myio-telemetry-card-trend.up {
7175
+ color: #27ae60;
7176
+ }
7177
+
7178
+ .myio-telemetry-card-trend.down {
7179
+ color: #e74c3c;
7180
+ }
7181
+
7182
+ .myio-telemetry-chart-container {
7183
+ background: white;
7184
+ border-radius: 12px;
7185
+ padding: 20px;
7186
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
7187
+ margin-bottom: 20px;
7188
+ }
7189
+
7190
+ .myio-telemetry-chart-title {
7191
+ font-size: 16px;
7192
+ font-weight: 600;
7193
+ margin: 0 0 16px 0;
7194
+ color: #2c3e50;
7195
+ }
7196
+
7197
+ .myio-telemetry-chart {
7198
+ height: 200px;
7199
+ }
7200
+
7201
+ .myio-realtime-telemetry-footer {
7202
+ padding: 16px 24px;
7203
+ border-top: 1px solid #e0e0e0;
7204
+ display: flex;
7205
+ justify-content: space-between;
7206
+ align-items: center;
7207
+ background: #f8f9fa;
7208
+ }
7209
+
7210
+ .myio-telemetry-status {
7211
+ display: flex;
7212
+ align-items: center;
7213
+ gap: 12px;
7214
+ font-size: 14px;
7215
+ color: #555;
7216
+ }
7217
+
7218
+ .myio-telemetry-status-indicator {
7219
+ width: 8px;
7220
+ height: 8px;
7221
+ border-radius: 50%;
7222
+ background: #27ae60;
7223
+ animation: pulse 2s infinite;
7224
+ }
7225
+
7226
+ .myio-telemetry-status-indicator.paused {
7227
+ background: #e74c3c;
7228
+ animation: none;
7229
+ }
7230
+
7231
+ @keyframes pulse {
7232
+ 0%, 100% { opacity: 1; }
7233
+ 50% { opacity: 0.5; }
7234
+ }
7235
+
7236
+ .myio-telemetry-actions {
7237
+ display: flex;
7238
+ gap: 12px;
7239
+ }
7240
+
7241
+ .myio-telemetry-btn {
7242
+ padding: 8px 16px;
7243
+ border: none;
7244
+ border-radius: 6px;
7245
+ font-size: 14px;
7246
+ font-weight: 500;
7247
+ cursor: pointer;
7248
+ transition: all 0.2s ease;
7249
+ display: flex;
7250
+ align-items: center;
7251
+ gap: 6px;
7252
+ }
7253
+
7254
+ .myio-telemetry-btn-primary {
7255
+ background: #667eea;
7256
+ color: white;
7257
+ }
7258
+
7259
+ .myio-telemetry-btn-primary:hover {
7260
+ background: #5568d3;
7261
+ transform: translateY(-1px);
7262
+ }
7263
+
7264
+ .myio-telemetry-btn-secondary {
7265
+ background: #e0e0e0;
7266
+ color: #333;
7267
+ }
7268
+
7269
+ .myio-telemetry-btn-secondary:hover {
7270
+ background: #d0d0d0;
7271
+ transform: translateY(-1px);
7272
+ }
7273
+
7274
+ .myio-telemetry-loading {
7275
+ text-align: center;
7276
+ padding: 40px;
7277
+ color: #999;
7278
+ font-size: 16px;
7279
+ }
7280
+
7281
+ .myio-telemetry-error {
7282
+ text-align: center;
7283
+ padding: 40px;
7284
+ color: #e74c3c;
7285
+ font-size: 16px;
7286
+ }
7287
+ </style>
7288
+
7289
+ <div class="myio-realtime-telemetry-container">
7290
+ <div class="myio-realtime-telemetry-header">
7291
+ <h2 class="myio-realtime-telemetry-title">
7292
+ \u26A1 ${strings.title} - ${deviceLabel}
7293
+ </h2>
7294
+ <button class="myio-realtime-telemetry-close" id="close-btn" title="${strings.close}">
7295
+ \xD7
7296
+ </button>
7297
+ </div>
7298
+
7299
+ <div class="myio-realtime-telemetry-body">
7300
+ <div class="myio-telemetry-loading" id="loading-state">
7301
+ ${strings.loading}
7302
+ </div>
7303
+
7304
+ <div id="telemetry-content" style="display: none;">
7305
+ <div class="myio-telemetry-cards-grid" id="telemetry-cards"></div>
7306
+
7307
+ <div class="myio-telemetry-chart-container" id="chart-container" style="display: none;">
7308
+ <h3 class="myio-telemetry-chart-title" id="chart-title">Pot\xEAncia (\xFAltimos 5 min)</h3>
7309
+ <canvas class="myio-telemetry-chart" id="telemetry-chart"></canvas>
7310
+ </div>
7311
+ </div>
7312
+
7313
+ <div class="myio-telemetry-error" id="error-state" style="display: none;">
7314
+ ${strings.error}
7315
+ </div>
7316
+ </div>
7317
+
7318
+ <div class="myio-realtime-telemetry-footer">
7319
+ <div class="myio-telemetry-status">
7320
+ <span class="myio-telemetry-status-indicator" id="status-indicator"></span>
7321
+ <span id="status-text">${strings.autoUpdate}: ON</span>
7322
+ <span>\u2022</span>
7323
+ <span id="last-update-text">${strings.lastUpdate}: --:--:--</span>
7324
+ </div>
7325
+
7326
+ <div class="myio-telemetry-actions">
7327
+ <button class="myio-telemetry-btn myio-telemetry-btn-secondary" id="pause-btn">
7328
+ <span id="pause-btn-icon">\u23F8\uFE0F</span>
7329
+ <span id="pause-btn-text">${strings.pause}</span>
7330
+ </button>
7331
+ <button class="myio-telemetry-btn myio-telemetry-btn-primary" id="export-btn">
7332
+ \u2B07\uFE0F ${strings.export}
7333
+ </button>
7334
+ </div>
7335
+ </div>
7336
+ </div>
7337
+ `;
7338
+ document.body.appendChild(overlay);
7339
+ const closeBtn = document.getElementById("close-btn");
7340
+ const pauseBtn = document.getElementById("pause-btn");
7341
+ const pauseBtnIcon = document.getElementById("pause-btn-icon");
7342
+ const pauseBtnText = document.getElementById("pause-btn-text");
7343
+ const exportBtn = document.getElementById("export-btn");
7344
+ const loadingState = document.getElementById("loading-state");
7345
+ const telemetryContent = document.getElementById("telemetry-content");
7346
+ const errorState = document.getElementById("error-state");
7347
+ const telemetryCards = document.getElementById("telemetry-cards");
7348
+ const chartContainer = document.getElementById("chart-container");
7349
+ const chartCanvas = document.getElementById("telemetry-chart");
7350
+ const statusIndicator = document.getElementById("status-indicator");
7351
+ const statusText = document.getElementById("status-text");
7352
+ const lastUpdateText = document.getElementById("last-update-text");
7353
+ function closeModal() {
7354
+ if (refreshIntervalId) {
7355
+ clearInterval(refreshIntervalId);
7356
+ refreshIntervalId = null;
7357
+ }
7358
+ if (chart) {
7359
+ chart.destroy();
7360
+ chart = null;
7361
+ }
7362
+ overlay.remove();
7363
+ if (onClose) {
7364
+ onClose();
7365
+ }
7366
+ }
7367
+ async function fetchLatestTelemetry() {
7368
+ const keys = telemetryKeys.join(",");
7369
+ const url = `/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&limit=1&agg=NONE`;
7370
+ const response = await fetch(url, {
7371
+ headers: {
7372
+ "X-Authorization": `Bearer ${token}`
7373
+ }
7374
+ });
7375
+ if (!response.ok) {
7376
+ throw new Error(`Failed to fetch telemetry: ${response.statusText}`);
7377
+ }
7378
+ return await response.json();
7379
+ }
7380
+ function processTelemetryData(data) {
7381
+ const values = [];
7382
+ for (const key of telemetryKeys) {
7383
+ const telemetryData = data[key];
7384
+ if (!telemetryData || telemetryData.length === 0) continue;
7385
+ const latest = telemetryData[0];
7386
+ const config = TELEMETRY_CONFIG[key] || { label: key, unit: "", icon: "\u{1F4CA}", decimals: 2 };
7387
+ const numValue = Number(latest.value) || 0;
7388
+ const formatted = numValue.toFixed(config.decimals);
7389
+ values.push({
7390
+ key,
7391
+ value: numValue,
7392
+ timestamp: latest.ts,
7393
+ formatted: `${formatted} ${config.unit}`,
7394
+ unit: config.unit,
7395
+ icon: config.icon,
7396
+ label: config.label,
7397
+ trend: "stable"
7398
+ // Will be calculated based on history
7399
+ });
7400
+ }
7401
+ return values;
7402
+ }
7403
+ function calculateTrend(key, currentValue) {
7404
+ const history = telemetryHistory.get(key);
7405
+ if (!history || history.length < 2) return "stable";
7406
+ const previousValue = history[history.length - 2].y;
7407
+ const diff = currentValue - previousValue;
7408
+ const threshold = previousValue * 0.02;
7409
+ if (diff > threshold) return "up";
7410
+ if (diff < -threshold) return "down";
7411
+ return "stable";
7412
+ }
7413
+ function updateTelemetryCards(values) {
7414
+ telemetryCards.innerHTML = values.map((tel) => {
7415
+ const trend = calculateTrend(tel.key, tel.value);
7416
+ const trendIcon = trend === "up" ? "\u2197" : trend === "down" ? "\u2198" : "\u2192";
7417
+ const trendClass = trend;
7418
+ const trendLabel = strings[`trend_${trend}`] || "";
7419
+ return `
7420
+ <div class="myio-telemetry-card">
7421
+ <div class="myio-telemetry-card-header">
7422
+ <span class="myio-telemetry-card-icon">${tel.icon}</span>
7423
+ <span>${tel.label}</span>
7424
+ </div>
7425
+ <div class="myio-telemetry-card-value">${tel.formatted}</div>
7426
+ <div class="myio-telemetry-card-trend ${trendClass}">
7427
+ ${trendIcon} ${trendLabel}
7428
+ </div>
7429
+ </div>
7430
+ `;
7431
+ }).join("");
7432
+ }
7433
+ function updateHistory(values) {
7434
+ const now = Date.now();
7435
+ for (const tel of values) {
7436
+ if (!telemetryHistory.has(tel.key)) {
7437
+ telemetryHistory.set(tel.key, []);
7438
+ }
7439
+ const history = telemetryHistory.get(tel.key);
7440
+ history.push({ x: now, y: tel.value });
7441
+ if (history.length > historyPoints) {
7442
+ history.shift();
7443
+ }
7444
+ }
7445
+ if (telemetryHistory.has("power") && chart) {
7446
+ const powerHistory = telemetryHistory.get("power");
7447
+ chart.data.datasets[0].data = powerHistory;
7448
+ chart.update("none");
7449
+ }
7450
+ }
7451
+ function initializeChart() {
7452
+ const Chart = window.Chart;
7453
+ if (!Chart) {
7454
+ console.warn("[RealTimeTelemetry] Chart.js not loaded");
7455
+ return;
7456
+ }
7457
+ chartContainer.style.display = "block";
7458
+ chart = new Chart(chartCanvas, {
7459
+ type: "line",
7460
+ data: {
7461
+ datasets: [{
7462
+ label: "Pot\xEAncia",
7463
+ data: [],
7464
+ borderColor: "#667eea",
7465
+ backgroundColor: "rgba(102, 126, 234, 0.1)",
7466
+ borderWidth: 2,
7467
+ fill: true,
7468
+ tension: 0.4,
7469
+ pointRadius: 0
7470
+ }]
7471
+ },
7472
+ options: {
7473
+ responsive: true,
7474
+ maintainAspectRatio: false,
7475
+ plugins: {
7476
+ legend: { display: false },
7477
+ tooltip: {
7478
+ callbacks: {
7479
+ title: function(context) {
7480
+ const timestamp = context[0].parsed.x;
7481
+ const date = new Date(timestamp);
7482
+ return date.toLocaleTimeString(locale);
7483
+ },
7484
+ label: function(context) {
7485
+ return `${context.parsed.y.toFixed(2)} kW`;
7486
+ }
7487
+ }
7488
+ }
7489
+ },
7490
+ scales: {
7491
+ x: {
7492
+ type: "linear",
7493
+ ticks: {
7494
+ callback: function(value) {
7495
+ const date = new Date(value);
7496
+ return date.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" });
7497
+ }
7498
+ }
7499
+ },
7500
+ y: {
7501
+ beginAtZero: true,
7502
+ title: {
7503
+ display: true,
7504
+ text: "kW"
7505
+ }
7506
+ }
7507
+ }
7508
+ }
7509
+ });
7510
+ }
7511
+ async function refreshData() {
7512
+ try {
7513
+ const data = await fetchLatestTelemetry();
7514
+ const values = processTelemetryData(data);
7515
+ updateHistory(values);
7516
+ updateTelemetryCards(values);
7517
+ const now = /* @__PURE__ */ new Date();
7518
+ lastUpdateText.textContent = `${strings.lastUpdate}: ${now.toLocaleTimeString(locale)}`;
7519
+ if (loadingState.style.display !== "none") {
7520
+ loadingState.style.display = "none";
7521
+ telemetryContent.style.display = "block";
7522
+ if (telemetryKeys.includes("power")) {
7523
+ initializeChart();
7524
+ }
7525
+ }
7526
+ } catch (error) {
7527
+ console.error("[RealTimeTelemetry] Error fetching data:", error);
7528
+ errorState.style.display = "block";
7529
+ loadingState.style.display = "none";
7530
+ telemetryContent.style.display = "none";
7531
+ }
7532
+ }
7533
+ function togglePause() {
7534
+ isPaused = !isPaused;
7535
+ if (isPaused) {
7536
+ if (refreshIntervalId) {
7537
+ clearInterval(refreshIntervalId);
7538
+ refreshIntervalId = null;
7539
+ }
7540
+ pauseBtnIcon.textContent = "\u25B6\uFE0F";
7541
+ pauseBtnText.textContent = strings.resume;
7542
+ statusIndicator.classList.add("paused");
7543
+ statusText.textContent = `${strings.autoUpdate}: OFF`;
7544
+ } else {
7545
+ refreshIntervalId = window.setInterval(refreshData, refreshInterval);
7546
+ pauseBtnIcon.textContent = "\u23F8\uFE0F";
7547
+ pauseBtnText.textContent = strings.pause;
7548
+ statusIndicator.classList.remove("paused");
7549
+ statusText.textContent = `${strings.autoUpdate}: ON`;
7550
+ }
7551
+ }
7552
+ function exportToCSV2() {
7553
+ const rows = [];
7554
+ rows.push("Timestamp," + telemetryKeys.map((k) => TELEMETRY_CONFIG[k]?.label || k).join(","));
7555
+ let maxLength = 0;
7556
+ for (const key of telemetryKeys) {
7557
+ const history = telemetryHistory.get(key);
7558
+ if (history && history.length > maxLength) {
7559
+ maxLength = history.length;
7560
+ }
7561
+ }
7562
+ for (let i = 0; i < maxLength; i++) {
7563
+ const row = [];
7564
+ let timestamp = "";
7565
+ for (const key of telemetryKeys) {
7566
+ const history = telemetryHistory.get(key);
7567
+ if (history && history[i]) {
7568
+ if (!timestamp) {
7569
+ timestamp = new Date(history[i].x).toISOString();
7570
+ }
7571
+ row.push(history[i].y.toFixed(2));
7572
+ } else {
7573
+ row.push("");
7574
+ }
7575
+ }
7576
+ if (timestamp) {
7577
+ rows.push(timestamp + "," + row.join(","));
7578
+ }
7579
+ }
7580
+ const csv = rows.join("\n");
7581
+ const blob = new Blob([csv], { type: "text/csv" });
7582
+ const url = URL.createObjectURL(blob);
7583
+ const a = document.createElement("a");
7584
+ a.href = url;
7585
+ a.download = `telemetry_${deviceLabel}_${(/* @__PURE__ */ new Date()).toISOString()}.csv`;
7586
+ a.click();
7587
+ URL.revokeObjectURL(url);
7588
+ }
7589
+ closeBtn.addEventListener("click", closeModal);
7590
+ pauseBtn.addEventListener("click", togglePause);
7591
+ exportBtn.addEventListener("click", exportToCSV2);
7592
+ overlay.addEventListener("click", (e) => {
7593
+ if (e.target === overlay) {
7594
+ closeModal();
7595
+ }
7596
+ });
7597
+ await refreshData();
7598
+ refreshIntervalId = window.setInterval(refreshData, refreshInterval);
7599
+ return {
7600
+ destroy: closeModal
7601
+ };
7602
+ }
7603
+
6978
7604
  // src/components/premium-modals/internal/styles/tokens.ts
6979
7605
  var CSS_TOKENS = `
6980
7606
  :root {
@@ -8416,7 +9042,7 @@ var zoomPluginLoaded = false;
8416
9042
  var jsPdfLoaded = false;
8417
9043
  var _jspdfPromise = null;
8418
9044
  var cssInjected = false;
8419
- var STRINGS = {
9045
+ var STRINGS2 = {
8420
9046
  "pt-BR": {
8421
9047
  title: "Demanda",
8422
9048
  period: "Per\xEDodo",
@@ -9003,6 +9629,32 @@ function formatDateTime(date, locale) {
9003
9629
  year: "numeric"
9004
9630
  });
9005
9631
  }
9632
+ function interpolateTemperatureData(rawPoints) {
9633
+ if (rawPoints.length === 0) return [];
9634
+ const interpolated = [];
9635
+ const interval = 30 * 60 * 1e3;
9636
+ const sorted = [...rawPoints].sort((a, b) => a.x - b.x);
9637
+ const startTime = sorted[0].x;
9638
+ const endTime = sorted[sorted.length - 1].x;
9639
+ let lastKnownValue = sorted[0].y;
9640
+ let dataIndex = 0;
9641
+ for (let time = startTime; time <= endTime; time += interval) {
9642
+ const actualPoint = sorted.find((p, idx) => {
9643
+ if (idx >= dataIndex && Math.abs(p.x - time) < 5 * 60 * 1e3) {
9644
+ dataIndex = idx + 1;
9645
+ return true;
9646
+ }
9647
+ return false;
9648
+ });
9649
+ if (actualPoint) {
9650
+ lastKnownValue = actualPoint.y;
9651
+ interpolated.push({ x: time, y: lastKnownValue });
9652
+ } else {
9653
+ interpolated.push({ x: time, y: lastKnownValue });
9654
+ }
9655
+ }
9656
+ return interpolated;
9657
+ }
9006
9658
  async function fetchTelemetryData(token, deviceId, startDate, endDate, queryParams) {
9007
9659
  const startTs = new Date(startDate).getTime();
9008
9660
  const endTs = new Date(endDate).getTime();
@@ -9141,7 +9793,7 @@ async function openDemandModal(params) {
9141
9793
  }
9142
9794
  const styles = { ...DEFAULT_STYLES, ...params.styles };
9143
9795
  const locale = params.locale || "pt-BR";
9144
- const strings = STRINGS[locale] || STRINGS["pt-BR"];
9796
+ const strings = STRINGS2[locale] || STRINGS2["pt-BR"];
9145
9797
  await loadExternalLibraries();
9146
9798
  injectCSS(styles);
9147
9799
  const container = typeof params.container === "string" ? document.querySelector(params.container) : params.container || document.body;
@@ -9715,6 +10367,12 @@ async function openDemandModal(params) {
9715
10367
  params.timezoneOffset
9716
10368
  // Pass timezone offset (default: -3)
9717
10369
  );
10370
+ if (params.readingType === "temperature" && !chartData.isEmpty) {
10371
+ chartData.series = chartData.series.map((series) => ({
10372
+ ...series,
10373
+ points: interpolateTemperatureData(series.points)
10374
+ }));
10375
+ }
9718
10376
  if (chartData.isEmpty) {
9719
10377
  errorEl.style.display = "flex";
9720
10378
  errorText.textContent = strings.noData;
@@ -9746,11 +10404,21 @@ async function openDemandModal(params) {
9746
10404
  title: function(context) {
9747
10405
  const timestamp = context[0].parsed.x;
9748
10406
  const date = new Date(timestamp);
9749
- return date.toLocaleDateString(locale, {
9750
- day: "2-digit",
9751
- month: "2-digit",
9752
- year: "numeric"
9753
- });
10407
+ if (params.readingType === "temperature") {
10408
+ return date.toLocaleString(locale, {
10409
+ day: "2-digit",
10410
+ month: "2-digit",
10411
+ year: "numeric",
10412
+ hour: "2-digit",
10413
+ minute: "2-digit"
10414
+ });
10415
+ } else {
10416
+ return date.toLocaleDateString(locale, {
10417
+ day: "2-digit",
10418
+ month: "2-digit",
10419
+ year: "numeric"
10420
+ });
10421
+ }
9754
10422
  },
9755
10423
  label: function(context) {
9756
10424
  const seriesLabel = context.dataset.label || "";
@@ -9758,6 +10426,20 @@ async function openDemandModal(params) {
9758
10426
  return `${seriesLabel}: ${value.toFixed(2)} kW`;
9759
10427
  }
9760
10428
  };
10429
+ chart.options.scales.x.ticks.callback = function(value) {
10430
+ const date = new Date(value);
10431
+ if (params.readingType === "temperature") {
10432
+ return date.toLocaleTimeString(locale, {
10433
+ hour: "2-digit",
10434
+ minute: "2-digit"
10435
+ });
10436
+ } else {
10437
+ return date.toLocaleDateString(locale, {
10438
+ day: "2-digit",
10439
+ month: "2-digit"
10440
+ });
10441
+ }
10442
+ };
9761
10443
  chart.update();
9762
10444
  } else {
9763
10445
  chart = new Chart(chartCanvas, {
@@ -9789,11 +10471,21 @@ async function openDemandModal(params) {
9789
10471
  title: function(context) {
9790
10472
  const timestamp = context[0].parsed.x;
9791
10473
  const date = new Date(timestamp);
9792
- return date.toLocaleDateString(locale, {
9793
- day: "2-digit",
9794
- month: "2-digit",
9795
- year: "numeric"
9796
- });
10474
+ if (params.readingType === "temperature") {
10475
+ return date.toLocaleString(locale, {
10476
+ day: "2-digit",
10477
+ month: "2-digit",
10478
+ year: "numeric",
10479
+ hour: "2-digit",
10480
+ minute: "2-digit"
10481
+ });
10482
+ } else {
10483
+ return date.toLocaleDateString(locale, {
10484
+ day: "2-digit",
10485
+ month: "2-digit",
10486
+ year: "numeric"
10487
+ });
10488
+ }
9797
10489
  },
9798
10490
  label: function(context) {
9799
10491
  const seriesLabel = context.dataset.label || "";
@@ -9827,10 +10519,17 @@ async function openDemandModal(params) {
9827
10519
  ticks: {
9828
10520
  callback: function(value) {
9829
10521
  const date = new Date(value);
9830
- return date.toLocaleDateString(locale, {
9831
- day: "2-digit",
9832
- month: "2-digit"
9833
- });
10522
+ if (params.readingType === "temperature") {
10523
+ return date.toLocaleTimeString(locale, {
10524
+ hour: "2-digit",
10525
+ minute: "2-digit"
10526
+ });
10527
+ } else {
10528
+ return date.toLocaleDateString(locale, {
10529
+ day: "2-digit",
10530
+ month: "2-digit"
10531
+ });
10532
+ }
9834
10533
  }
9835
10534
  }
9836
10535
  },
@@ -10095,6 +10794,16 @@ var EnergyModalView = class {
10095
10794
  Exportar CSV
10096
10795
  </button>
10097
10796
  ${this.config.params.readingType === "energy" && this.config.params.mode !== "comparison" ? `
10797
+ <button id="view-demand-btn" class="myio-btn myio-btn-secondary" style="
10798
+ background: linear-gradient(135deg, #1976D2 0%, #2196F3 100%);
10799
+ color: white;
10800
+ border: none;
10801
+ transition: all 0.3s ease;
10802
+ box-shadow: 0 2px 8px rgba(25, 118, 210, 0.3);
10803
+ ">
10804
+ <span style="font-size: 16px; margin-right: 4px;">\u{1F4CA}</span>
10805
+ Pico de Demanda
10806
+ </button>
10098
10807
  <button id="view-telemetry-btn" class="myio-btn myio-btn-secondary" style="
10099
10808
  background: linear-gradient(135deg, #4A148C 0%, #6A1B9A 100%);
10100
10809
  color: white;
@@ -10720,42 +11429,64 @@ var EnergyModalView = class {
10720
11429
  if (loadBtn) {
10721
11430
  loadBtn.addEventListener("click", () => this.loadData());
10722
11431
  }
10723
- const viewTelemetryBtn = document.getElementById("view-telemetry-btn");
10724
- if (viewTelemetryBtn) {
10725
- viewTelemetryBtn.addEventListener("click", async () => {
11432
+ const viewDemandBtn = document.getElementById("view-demand-btn");
11433
+ if (viewDemandBtn) {
11434
+ viewDemandBtn.addEventListener("click", async () => {
10726
11435
  try {
10727
- console.log("[EnergyModalView] Opening demand modal");
11436
+ console.log("[EnergyModalView] Opening demand modal (Pico de Demanda)");
10728
11437
  const jwtToken = localStorage.getItem("jwt_token");
10729
11438
  if (!jwtToken) {
10730
11439
  throw new Error("Token de autentica\xE7\xE3o n\xE3o encontrado");
10731
11440
  }
10732
- let startDate = this.config.params.startDate;
10733
- let endDate = this.config.params.endDate;
11441
+ let startDate;
11442
+ let endDate;
10734
11443
  if (this.dateRangePicker) {
10735
- const dates = this.dateRangePicker.getDates();
10736
- if (dates.startISO && dates.endISO) {
10737
- startDate = dates.startISO.split("T")[0];
10738
- endDate = dates.endISO.split("T")[0];
10739
- }
11444
+ const dates = this.dateRangePicker.getRange();
11445
+ startDate = dates.startISO;
11446
+ endDate = dates.endISO;
11447
+ } else {
11448
+ startDate = this.config.params.startDate instanceof Date ? this.config.params.startDate.toISOString() : this.config.params.startDate;
11449
+ endDate = this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString() : this.config.params.endDate;
10740
11450
  }
10741
11451
  await openDemandModal({
10742
11452
  token: jwtToken,
10743
11453
  deviceId: this.config.params.deviceId,
10744
- startDate: startDate instanceof Date ? startDate.toISOString().split("T")[0] : startDate,
10745
- endDate: endDate instanceof Date ? endDate.toISOString().split("T")[0] : endDate,
10746
- label: this.config.params.label || "Dispositivo",
10747
- telemetryQuery: {
10748
- keys: "consumption",
10749
- intervalType: "MILLISECONDS",
10750
- interval: 864e5,
10751
- // 24 hours - default, can be changed inside modal
10752
- agg: "MAX",
10753
- // default, can be changed inside modal
10754
- limit: 1e4
10755
- }
11454
+ startDate,
11455
+ endDate,
11456
+ label: this.config.params.deviceLabel || "Dispositivo",
11457
+ locale: "pt-BR",
11458
+ readingType: this.config.params.readingType || "energy",
11459
+ enableRealTimeMode: true,
11460
+ realTimeInterval: 8e3,
11461
+ realTimeAutoScroll: true
10756
11462
  });
10757
11463
  } catch (error) {
10758
11464
  console.error("[EnergyModalView] Error opening demand modal:", error);
11465
+ this.showError("Erro ao abrir pico de demanda: " + error.message);
11466
+ }
11467
+ });
11468
+ }
11469
+ const viewTelemetryBtn = document.getElementById("view-telemetry-btn");
11470
+ if (viewTelemetryBtn) {
11471
+ viewTelemetryBtn.addEventListener("click", async () => {
11472
+ try {
11473
+ console.log("[EnergyModalView] Opening real-time telemetry modal");
11474
+ const jwtToken = localStorage.getItem("jwt_token");
11475
+ if (!jwtToken) {
11476
+ throw new Error("Token de autentica\xE7\xE3o n\xE3o encontrado");
11477
+ }
11478
+ await openRealTimeTelemetryModal({
11479
+ token: jwtToken,
11480
+ deviceId: this.config.params.deviceId,
11481
+ deviceLabel: this.config.params.deviceLabel || "Dispositivo",
11482
+ telemetryKeys: ["voltage", "current", "power", "energy"],
11483
+ refreshInterval: 8e3,
11484
+ // 8 seconds
11485
+ historyPoints: 50,
11486
+ locale: "pt-BR"
11487
+ });
11488
+ } catch (error) {
11489
+ console.error("[EnergyModalView] Error opening real-time telemetry modal:", error);
10759
11490
  this.showError("Erro ao abrir telemetrias: " + error.message);
10760
11491
  }
10761
11492
  });
@@ -18749,6 +19480,7 @@ export {
18749
19480
  openDashboardPopupWaterTank,
18750
19481
  openDemandModal,
18751
19482
  openGoalsPanel,
19483
+ openRealTimeTelemetryModal,
18752
19484
  parseInputDateToDate,
18753
19485
  renderCardComponent,
18754
19486
  renderCardComponent2 as renderCardComponentEnhanced,