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