myio-js-library 0.1.494 → 0.1.495

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.cjs CHANGED
@@ -1162,7 +1162,7 @@ module.exports = __toCommonJS(index_exports);
1162
1162
  // package.json
1163
1163
  var package_default = {
1164
1164
  name: "myio-js-library",
1165
- version: "0.1.494",
1165
+ version: "0.1.495",
1166
1166
  description: "A clean, standalone JS SDK for MYIO projects",
1167
1167
  license: "MIT",
1168
1168
  repository: "github:gh-myio/myio-js-library",
@@ -23680,13 +23680,13 @@ function createDateRangePicker($2, input, opts) {
23680
23680
  const now = moment();
23681
23681
  ranges = {
23682
23682
  "\xDAltima hora": [moment().subtract(1, "hours"), now.clone()],
23683
- "\xDAltimas 6 horas": [moment().subtract(6, "hours"), now.clone()],
23684
23683
  "\xDAltimas 12 horas": [moment().subtract(12, "hours"), now.clone()],
23685
23684
  "\xDAltimas 24 horas": [moment().subtract(24, "hours"), now.clone()],
23686
23685
  "Hoje": [moment().startOf("day"), now.clone()],
23687
23686
  "Ontem": [moment().subtract(1, "day").startOf("day"), moment().subtract(1, "day").endOf("day")],
23688
23687
  "\xDAltimos 7 dias": [moment().subtract(6, "days").startOf("day"), now.clone()],
23689
- "Este m\xEAs": [moment().startOf("month"), now.clone()]
23688
+ "Este m\xEAs": [moment().startOf("month"), now.clone()],
23689
+ "M\xEAs Anterior": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
23690
23690
  };
23691
23691
  } else {
23692
23692
  ranges = {
@@ -23932,11 +23932,14 @@ async function openRealTimeTelemetryModal(params) {
23932
23932
  const isMyioUser = !!userEmail?.toLowerCase().includes("@myio.com.br");
23933
23933
  let checkDeviceIntervalMs = 3e4;
23934
23934
  let checkDeviceWaitMs = 15e3;
23935
+ const SESSION_LIMIT_MS = 5 * 60 * 1e3;
23935
23936
  let refreshIntervalId = null;
23936
23937
  let countdownTimerId = null;
23937
23938
  let nextTickAt = 0;
23938
23939
  let isFirstTick = true;
23939
23940
  let isPaused = false;
23941
+ let sessionCountdownTimerId = null;
23942
+ let sessionExpiresAt = 0;
23940
23943
  let cardTooltipEl = null;
23941
23944
  let cardTooltipKey = null;
23942
23945
  let cardTooltipPinned = false;
@@ -24889,7 +24892,7 @@ async function openRealTimeTelemetryModal(params) {
24889
24892
  <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:16px;">
24890
24893
  <div class="myio-rtt-mode-tabs" id="rtt-mode-tabs">
24891
24894
  <button class="myio-rtt-tab active" data-mode="realtime">Realtime</button>
24892
- <button class="myio-rtt-tab" data-mode="period">Per\xEDodo</button>
24895
+ <button class="myio-rtt-tab" data-mode="period">Pro Per\xEDodo</button>
24893
24896
  </div>
24894
24897
  <div class="myio-rtt-period-input" id="rtt-period-row">
24895
24898
  <div class="myio-rtt-period-controls">
@@ -24955,6 +24958,7 @@ async function openRealTimeTelemetryModal(params) {
24955
24958
  </div>
24956
24959
 
24957
24960
  <div class="myio-telemetry-actions">
24961
+ <span id="rtt-session-countdown" style="display:none;font-size:12px;font-weight:600;color:#667eea;background:rgba(102,126,234,0.1);padding:2px 10px;border-radius:10px;white-space:nowrap;"></span>
24958
24962
  <button class="myio-telemetry-btn myio-telemetry-btn-secondary" id="pause-btn">
24959
24963
  <span id="pause-btn-icon">\u23F8\uFE0F</span>
24960
24964
  <span id="pause-btn-text">${strings.pause}</span>
@@ -24979,6 +24983,7 @@ async function openRealTimeTelemetryModal(params) {
24979
24983
  const pauseBtn = overlay.querySelector("#pause-btn");
24980
24984
  const pauseBtnIcon = overlay.querySelector("#pause-btn-icon");
24981
24985
  const pauseBtnText = overlay.querySelector("#pause-btn-text");
24986
+ const sessionCountdownEl = overlay.querySelector("#rtt-session-countdown");
24982
24987
  const exportBtn = overlay.querySelector("#export-btn");
24983
24988
  const loadingState = overlay.querySelector("#loading-state");
24984
24989
  const telemetryContent = overlay.querySelector("#telemetry-content");
@@ -25000,6 +25005,26 @@ async function openRealTimeTelemetryModal(params) {
25000
25005
  const countdownText = overlay.querySelector("#rtt-countdown-text");
25001
25006
  const centralBadge = overlay.querySelector("#rtt-central-badge");
25002
25007
  const deviceBadge = overlay.querySelector("#rtt-device-badge");
25008
+ const chartTitleEl = overlay.querySelector("#chart-title");
25009
+ function updateChartTitle() {
25010
+ if (!chartTitleEl) return;
25011
+ if (currentMode === "period") {
25012
+ chartTitleEl.innerHTML = "Hist\xF3rico de Telemetria";
25013
+ return;
25014
+ }
25015
+ const deviceOk = lastTelemetryUpdateMs > 0 && Date.now() - lastTelemetryUpdateMs <= DEVICE_OK_DELTA_MS;
25016
+ let iconHtml = "";
25017
+ if (centralStatus === "unknown") {
25018
+ iconHtml = "";
25019
+ } else if (centralStatus === "ok" && deviceOk) {
25020
+ iconHtml = `<span title="Central online e dispositivo online" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u2705</span>`;
25021
+ } else if (centralStatus === "ok" && !deviceOk) {
25022
+ iconHtml = `<span title="Central online e dispositivo offline / conex\xE3o fraca" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u26A0\uFE0F</span>`;
25023
+ } else {
25024
+ iconHtml = `<span title="Central offline" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u{1F534}</span>`;
25025
+ }
25026
+ chartTitleEl.innerHTML = `Telemetria em Tempo Real${iconHtml}`;
25027
+ }
25003
25028
  function updateStatusBadges() {
25004
25029
  if (!centralBadge || !deviceBadge) return;
25005
25030
  if (centralStatus === "unknown") {
@@ -25024,7 +25049,9 @@ async function openRealTimeTelemetryModal(params) {
25024
25049
  deviceBadge.textContent = "Device OFFLINE";
25025
25050
  deviceBadge.style.cssText = "display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:10px;letter-spacing:0.3px;color:#fff;background:#e74c3c;";
25026
25051
  }
25052
+ updateChartTitle();
25027
25053
  }
25054
+ updateChartTitle();
25028
25055
  function startCountdown(targetMs, label = "pr\xF3xima em") {
25029
25056
  nextTickAt = Date.now() + targetMs;
25030
25057
  if (countdownTimerId !== null) {
@@ -25045,6 +25072,36 @@ async function openRealTimeTelemetryModal(params) {
25045
25072
  }
25046
25073
  if (countdownText) countdownText.textContent = "";
25047
25074
  }
25075
+ function startSession() {
25076
+ stopSession();
25077
+ sessionExpiresAt = Date.now() + SESSION_LIMIT_MS;
25078
+ sessionCountdownTimerId = window.setInterval(() => {
25079
+ const remaining = Math.max(0, Math.ceil((sessionExpiresAt - Date.now()) / 1e3));
25080
+ if (sessionCountdownEl) {
25081
+ if (remaining > 0) {
25082
+ const mins = Math.floor(remaining / 60);
25083
+ const secs = remaining % 60;
25084
+ sessionCountdownEl.textContent = `\u23F1 ${mins}:${secs.toString().padStart(2, "0")}`;
25085
+ sessionCountdownEl.style.display = "inline-block";
25086
+ }
25087
+ }
25088
+ if (remaining <= 0) {
25089
+ stopSession();
25090
+ if (!isPaused) {
25091
+ togglePause();
25092
+ clearCountdown();
25093
+ showRTTToast("Sess\xE3o de 5 min encerrada. Clique em Retomar para continuar.", "warn");
25094
+ }
25095
+ }
25096
+ }, 1e3);
25097
+ }
25098
+ function stopSession() {
25099
+ if (sessionCountdownTimerId !== null) {
25100
+ clearInterval(sessionCountdownTimerId);
25101
+ sessionCountdownTimerId = null;
25102
+ }
25103
+ if (sessionCountdownEl) sessionCountdownEl.style.display = "none";
25104
+ }
25048
25105
  function showRTTToast(message, type = "info") {
25049
25106
  const bg = { warn: "#e67e22", error: "#e74c3c", info: "#3498db" }[type];
25050
25107
  overlay.querySelector(".myio-rtt-toast")?.remove();
@@ -25450,6 +25507,7 @@ async function openRealTimeTelemetryModal(params) {
25450
25507
  refreshIntervalId = null;
25451
25508
  }
25452
25509
  clearCountdown();
25510
+ stopSession();
25453
25511
  if (chart) {
25454
25512
  chart.destroy();
25455
25513
  chart = null;
@@ -25713,6 +25771,7 @@ async function openRealTimeTelemetryModal(params) {
25713
25771
  const points = series.map((pt) => {
25714
25772
  let y = pt.value ?? 0;
25715
25773
  if (key === "total_current" || key === "current") y = y / 1e3;
25774
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") y = y / 255;
25716
25775
  return { x: pt.ts, y };
25717
25776
  });
25718
25777
  telemetryHistory.set(key, points);
@@ -25749,6 +25808,7 @@ async function openRealTimeTelemetryModal(params) {
25749
25808
  const points = series.map((pt) => {
25750
25809
  let y = pt.value ?? 0;
25751
25810
  if (key === "total_current" || key === "current") y = y / 1e3;
25811
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") y = y / 255;
25752
25812
  return { x: pt.ts, y };
25753
25813
  });
25754
25814
  telemetryHistory.set(key, points);
@@ -25795,6 +25855,9 @@ async function openRealTimeTelemetryModal(params) {
25795
25855
  if (key === "total_current" || key === "current" || key === "current_a" || key === "current_b" || key === "current_c") {
25796
25856
  numValue = numValue / 1e3;
25797
25857
  }
25858
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") {
25859
+ numValue = numValue / 255;
25860
+ }
25798
25861
  const formattedNum = numValue.toFixed(config.decimals);
25799
25862
  const displayUnit = config.unit === "fp" ? "" : config.unit;
25800
25863
  const formatted = displayUnit ? `${formattedNum} ${displayUnit}` : formattedNum;
@@ -26164,12 +26227,14 @@ async function openRealTimeTelemetryModal(params) {
26164
26227
  clearTimeout(refreshIntervalId);
26165
26228
  refreshIntervalId = null;
26166
26229
  }
26230
+ stopSession();
26167
26231
  pauseBtnIcon.textContent = "\u25B6\uFE0F";
26168
26232
  pauseBtnText.textContent = strings.resume;
26169
26233
  statusIndicator.classList.add("paused");
26170
26234
  statusText.textContent = `${strings.autoUpdate}: OFF`;
26171
26235
  } else {
26172
26236
  scheduleCheckDeviceTick();
26237
+ startSession();
26173
26238
  pauseBtnIcon.textContent = "\u23F8\uFE0F";
26174
26239
  pauseBtnText.textContent = strings.pause;
26175
26240
  statusIndicator.classList.remove("paused");
@@ -26245,6 +26310,7 @@ async function openRealTimeTelemetryModal(params) {
26245
26310
  async function switchMode(mode) {
26246
26311
  if (mode === currentMode) return;
26247
26312
  currentMode = mode;
26313
+ updateChartTitle();
26248
26314
  modeTabs.forEach((btn) => {
26249
26315
  btn.classList.toggle("active", btn.dataset["mode"] === mode);
26250
26316
  });
@@ -26285,6 +26351,7 @@ async function openRealTimeTelemetryModal(params) {
26285
26351
  isFirstTick = true;
26286
26352
  await refreshData();
26287
26353
  scheduleCheckDeviceTick();
26354
+ startSession();
26288
26355
  }
26289
26356
  }
26290
26357
  }
@@ -26391,6 +26458,7 @@ async function openRealTimeTelemetryModal(params) {
26391
26458
  await refreshData();
26392
26459
  isFirstTick = false;
26393
26460
  scheduleCheckDeviceTick();
26461
+ startSession();
26394
26462
  return {
26395
26463
  destroy: closeModal2
26396
26464
  };
@@ -30396,7 +30464,7 @@ var EnergyModalView = class {
30396
30464
  token: jwtToken,
30397
30465
  deviceId: this.config.params.deviceId,
30398
30466
  tbBaseUrl: this.config.params.tbBaseUrl || "",
30399
- deviceLabel: this.config.params.deviceLabel || "Dispositivo",
30467
+ deviceLabel: this.config.context.device.label || this.config.params.deviceLabel || "Dispositivo",
30400
30468
  deviceName: this.config.context.device.name,
30401
30469
  customerName: this.config.params.customerName,
30402
30470
  centralId: this.config.context.resolved.centralId,
@@ -30466,7 +30534,7 @@ var EnergyModalView = class {
30466
30534
  presetEnd: this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString().split("T")[0] : this.config.params.endDate,
30467
30535
  maxRangeDays: 90,
30468
30536
  includeTime: true,
30469
- timePrecision: "hour",
30537
+ timePrecision: "minute",
30470
30538
  parentEl: this.modal.element,
30471
30539
  onApply: ({ startISO, endISO }) => {
30472
30540
  this.hideError();
@@ -30866,7 +30934,7 @@ var EnergyModal = class {
30866
30934
  * Normalizes and validates parameters
30867
30935
  */
30868
30936
  normalizeParams(params) {
30869
- if (!validateJwtToken(params.tbJwtToken)) {
30937
+ if (!validateJwtToken(params.tbJwtToken || "")) {
30870
30938
  throw new Error("Invalid JWT token format");
30871
30939
  }
30872
30940
  return {
@@ -30898,10 +30966,10 @@ var EnergyModal = class {
30898
30966
  attributes: {}
30899
30967
  },
30900
30968
  resolved: {
30901
- ingestionId: null,
30902
- centralId: null,
30903
- slaveId: null,
30904
- customerId: null
30969
+ ingestionId: void 0,
30970
+ centralId: void 0,
30971
+ slaveId: void 0,
30972
+ customerId: void 0
30905
30973
  }
30906
30974
  };
30907
30975
  }
@@ -31037,10 +31105,10 @@ var EnergyModal = class {
31037
31105
  };
31038
31106
  const mainTitle = domainTitles[readingType] ?? "\u26A1 Gr\xE1fico de Energia";
31039
31107
  const label = this.context?.device.label || "";
31040
- const identifier = this.context?.device?.attributes?.identifier || "";
31108
+ const deviceName = this.context?.device.name || this.context?.device?.attributes?.identifier || "";
31041
31109
  const customer = this.params.customerName || "";
31042
31110
  const version2 = window.MyIOLibrary?.version;
31043
- const deviceBadge = label ? `<span class="myio-modal-header-device-label">${label}${identifier && identifier !== label ? `<span class="myio-modal-header-device-name">(${identifier})</span>` : ""}</span>` : "";
31111
+ const deviceBadge = label ? `<span class="myio-modal-header-device-label">${label}${deviceName && deviceName !== label ? `<span class="myio-modal-header-device-name">(${deviceName})</span>` : ""}</span>` : "";
31044
31112
  const customerBadge = customer ? `<span class="myio-modal-header-customer-badge">${customer}</span>` : "";
31045
31113
  const versionBadge = version2 ? `<span class="myio-modal-header-version-badge">v${version2}</span>` : "";
31046
31114
  return `${mainTitle}${deviceBadge}${customerBadge}${versionBadge}`;
@@ -31169,10 +31237,15 @@ var EnergyModal = class {
31169
31237
  title: "Dispositivo n\xE3o encontrado",
31170
31238
  detail: "Este dispositivo ainda n\xE3o possui dados de telemetria cadastrados. Verifique a integra\xE7\xE3o ou contate o suporte."
31171
31239
  };
31240
+ if (msg.includes("token_expired") || msg.includes("token has expired") || msg.includes("authentication token") || msg.includes("token expirou"))
31241
+ return {
31242
+ title: "Sess\xE3o expirada",
31243
+ detail: "Seu token de acesso expirou. Recarregue a p\xE1gina para continuar."
31244
+ };
31172
31245
  if (msg.includes("insufficient permissions") || msg.includes("401") || msg.includes("403") || msg.includes("unauthorized") || msg.includes("forbidden"))
31173
31246
  return {
31174
31247
  title: "Sem permiss\xE3o de acesso",
31175
- detail: "Sua sess\xE3o pode ter expirado ou voc\xEA n\xE3o tem permiss\xE3o para visualizar estes dados. Tente reabrir o modal."
31248
+ detail: "Voc\xEA n\xE3o tem permiss\xE3o para visualizar estes dados. Tente reabrir o modal ou contate o suporte."
31176
31249
  };
31177
31250
  if (msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network error") || msg.includes("err_network"))
31178
31251
  return {
package/dist/index.js CHANGED
@@ -546,7 +546,7 @@ var init_template_card = __esm({
546
546
  // package.json
547
547
  var package_default = {
548
548
  name: "myio-js-library",
549
- version: "0.1.494",
549
+ version: "0.1.495",
550
550
  description: "A clean, standalone JS SDK for MYIO projects",
551
551
  license: "MIT",
552
552
  repository: "github:gh-myio/myio-js-library",
@@ -23064,13 +23064,13 @@ function createDateRangePicker($2, input, opts) {
23064
23064
  const now = moment();
23065
23065
  ranges = {
23066
23066
  "\xDAltima hora": [moment().subtract(1, "hours"), now.clone()],
23067
- "\xDAltimas 6 horas": [moment().subtract(6, "hours"), now.clone()],
23068
23067
  "\xDAltimas 12 horas": [moment().subtract(12, "hours"), now.clone()],
23069
23068
  "\xDAltimas 24 horas": [moment().subtract(24, "hours"), now.clone()],
23070
23069
  "Hoje": [moment().startOf("day"), now.clone()],
23071
23070
  "Ontem": [moment().subtract(1, "day").startOf("day"), moment().subtract(1, "day").endOf("day")],
23072
23071
  "\xDAltimos 7 dias": [moment().subtract(6, "days").startOf("day"), now.clone()],
23073
- "Este m\xEAs": [moment().startOf("month"), now.clone()]
23072
+ "Este m\xEAs": [moment().startOf("month"), now.clone()],
23073
+ "M\xEAs Anterior": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
23074
23074
  };
23075
23075
  } else {
23076
23076
  ranges = {
@@ -23316,11 +23316,14 @@ async function openRealTimeTelemetryModal(params) {
23316
23316
  const isMyioUser = !!userEmail?.toLowerCase().includes("@myio.com.br");
23317
23317
  let checkDeviceIntervalMs = 3e4;
23318
23318
  let checkDeviceWaitMs = 15e3;
23319
+ const SESSION_LIMIT_MS = 5 * 60 * 1e3;
23319
23320
  let refreshIntervalId = null;
23320
23321
  let countdownTimerId = null;
23321
23322
  let nextTickAt = 0;
23322
23323
  let isFirstTick = true;
23323
23324
  let isPaused = false;
23325
+ let sessionCountdownTimerId = null;
23326
+ let sessionExpiresAt = 0;
23324
23327
  let cardTooltipEl = null;
23325
23328
  let cardTooltipKey = null;
23326
23329
  let cardTooltipPinned = false;
@@ -24273,7 +24276,7 @@ async function openRealTimeTelemetryModal(params) {
24273
24276
  <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:16px;">
24274
24277
  <div class="myio-rtt-mode-tabs" id="rtt-mode-tabs">
24275
24278
  <button class="myio-rtt-tab active" data-mode="realtime">Realtime</button>
24276
- <button class="myio-rtt-tab" data-mode="period">Per\xEDodo</button>
24279
+ <button class="myio-rtt-tab" data-mode="period">Pro Per\xEDodo</button>
24277
24280
  </div>
24278
24281
  <div class="myio-rtt-period-input" id="rtt-period-row">
24279
24282
  <div class="myio-rtt-period-controls">
@@ -24339,6 +24342,7 @@ async function openRealTimeTelemetryModal(params) {
24339
24342
  </div>
24340
24343
 
24341
24344
  <div class="myio-telemetry-actions">
24345
+ <span id="rtt-session-countdown" style="display:none;font-size:12px;font-weight:600;color:#667eea;background:rgba(102,126,234,0.1);padding:2px 10px;border-radius:10px;white-space:nowrap;"></span>
24342
24346
  <button class="myio-telemetry-btn myio-telemetry-btn-secondary" id="pause-btn">
24343
24347
  <span id="pause-btn-icon">\u23F8\uFE0F</span>
24344
24348
  <span id="pause-btn-text">${strings.pause}</span>
@@ -24363,6 +24367,7 @@ async function openRealTimeTelemetryModal(params) {
24363
24367
  const pauseBtn = overlay.querySelector("#pause-btn");
24364
24368
  const pauseBtnIcon = overlay.querySelector("#pause-btn-icon");
24365
24369
  const pauseBtnText = overlay.querySelector("#pause-btn-text");
24370
+ const sessionCountdownEl = overlay.querySelector("#rtt-session-countdown");
24366
24371
  const exportBtn = overlay.querySelector("#export-btn");
24367
24372
  const loadingState = overlay.querySelector("#loading-state");
24368
24373
  const telemetryContent = overlay.querySelector("#telemetry-content");
@@ -24384,6 +24389,26 @@ async function openRealTimeTelemetryModal(params) {
24384
24389
  const countdownText = overlay.querySelector("#rtt-countdown-text");
24385
24390
  const centralBadge = overlay.querySelector("#rtt-central-badge");
24386
24391
  const deviceBadge = overlay.querySelector("#rtt-device-badge");
24392
+ const chartTitleEl = overlay.querySelector("#chart-title");
24393
+ function updateChartTitle() {
24394
+ if (!chartTitleEl) return;
24395
+ if (currentMode === "period") {
24396
+ chartTitleEl.innerHTML = "Hist\xF3rico de Telemetria";
24397
+ return;
24398
+ }
24399
+ const deviceOk = lastTelemetryUpdateMs > 0 && Date.now() - lastTelemetryUpdateMs <= DEVICE_OK_DELTA_MS;
24400
+ let iconHtml = "";
24401
+ if (centralStatus === "unknown") {
24402
+ iconHtml = "";
24403
+ } else if (centralStatus === "ok" && deviceOk) {
24404
+ iconHtml = `<span title="Central online e dispositivo online" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u2705</span>`;
24405
+ } else if (centralStatus === "ok" && !deviceOk) {
24406
+ iconHtml = `<span title="Central online e dispositivo offline / conex\xE3o fraca" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u26A0\uFE0F</span>`;
24407
+ } else {
24408
+ iconHtml = `<span title="Central offline" style="margin-left:8px;cursor:default;font-size:16px;vertical-align:middle;">\u{1F534}</span>`;
24409
+ }
24410
+ chartTitleEl.innerHTML = `Telemetria em Tempo Real${iconHtml}`;
24411
+ }
24387
24412
  function updateStatusBadges() {
24388
24413
  if (!centralBadge || !deviceBadge) return;
24389
24414
  if (centralStatus === "unknown") {
@@ -24408,7 +24433,9 @@ async function openRealTimeTelemetryModal(params) {
24408
24433
  deviceBadge.textContent = "Device OFFLINE";
24409
24434
  deviceBadge.style.cssText = "display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:10px;letter-spacing:0.3px;color:#fff;background:#e74c3c;";
24410
24435
  }
24436
+ updateChartTitle();
24411
24437
  }
24438
+ updateChartTitle();
24412
24439
  function startCountdown(targetMs, label = "pr\xF3xima em") {
24413
24440
  nextTickAt = Date.now() + targetMs;
24414
24441
  if (countdownTimerId !== null) {
@@ -24429,6 +24456,36 @@ async function openRealTimeTelemetryModal(params) {
24429
24456
  }
24430
24457
  if (countdownText) countdownText.textContent = "";
24431
24458
  }
24459
+ function startSession() {
24460
+ stopSession();
24461
+ sessionExpiresAt = Date.now() + SESSION_LIMIT_MS;
24462
+ sessionCountdownTimerId = window.setInterval(() => {
24463
+ const remaining = Math.max(0, Math.ceil((sessionExpiresAt - Date.now()) / 1e3));
24464
+ if (sessionCountdownEl) {
24465
+ if (remaining > 0) {
24466
+ const mins = Math.floor(remaining / 60);
24467
+ const secs = remaining % 60;
24468
+ sessionCountdownEl.textContent = `\u23F1 ${mins}:${secs.toString().padStart(2, "0")}`;
24469
+ sessionCountdownEl.style.display = "inline-block";
24470
+ }
24471
+ }
24472
+ if (remaining <= 0) {
24473
+ stopSession();
24474
+ if (!isPaused) {
24475
+ togglePause();
24476
+ clearCountdown();
24477
+ showRTTToast("Sess\xE3o de 5 min encerrada. Clique em Retomar para continuar.", "warn");
24478
+ }
24479
+ }
24480
+ }, 1e3);
24481
+ }
24482
+ function stopSession() {
24483
+ if (sessionCountdownTimerId !== null) {
24484
+ clearInterval(sessionCountdownTimerId);
24485
+ sessionCountdownTimerId = null;
24486
+ }
24487
+ if (sessionCountdownEl) sessionCountdownEl.style.display = "none";
24488
+ }
24432
24489
  function showRTTToast(message, type = "info") {
24433
24490
  const bg = { warn: "#e67e22", error: "#e74c3c", info: "#3498db" }[type];
24434
24491
  overlay.querySelector(".myio-rtt-toast")?.remove();
@@ -24834,6 +24891,7 @@ async function openRealTimeTelemetryModal(params) {
24834
24891
  refreshIntervalId = null;
24835
24892
  }
24836
24893
  clearCountdown();
24894
+ stopSession();
24837
24895
  if (chart) {
24838
24896
  chart.destroy();
24839
24897
  chart = null;
@@ -25097,6 +25155,7 @@ async function openRealTimeTelemetryModal(params) {
25097
25155
  const points = series.map((pt) => {
25098
25156
  let y = pt.value ?? 0;
25099
25157
  if (key === "total_current" || key === "current") y = y / 1e3;
25158
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") y = y / 255;
25100
25159
  return { x: pt.ts, y };
25101
25160
  });
25102
25161
  telemetryHistory.set(key, points);
@@ -25133,6 +25192,7 @@ async function openRealTimeTelemetryModal(params) {
25133
25192
  const points = series.map((pt) => {
25134
25193
  let y = pt.value ?? 0;
25135
25194
  if (key === "total_current" || key === "current") y = y / 1e3;
25195
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") y = y / 255;
25136
25196
  return { x: pt.ts, y };
25137
25197
  });
25138
25198
  telemetryHistory.set(key, points);
@@ -25179,6 +25239,9 @@ async function openRealTimeTelemetryModal(params) {
25179
25239
  if (key === "total_current" || key === "current" || key === "current_a" || key === "current_b" || key === "current_c") {
25180
25240
  numValue = numValue / 1e3;
25181
25241
  }
25242
+ if (key === "fp_a" || key === "fp_b" || key === "fp_c" || key === "powerFactor") {
25243
+ numValue = numValue / 255;
25244
+ }
25182
25245
  const formattedNum = numValue.toFixed(config.decimals);
25183
25246
  const displayUnit = config.unit === "fp" ? "" : config.unit;
25184
25247
  const formatted = displayUnit ? `${formattedNum} ${displayUnit}` : formattedNum;
@@ -25548,12 +25611,14 @@ async function openRealTimeTelemetryModal(params) {
25548
25611
  clearTimeout(refreshIntervalId);
25549
25612
  refreshIntervalId = null;
25550
25613
  }
25614
+ stopSession();
25551
25615
  pauseBtnIcon.textContent = "\u25B6\uFE0F";
25552
25616
  pauseBtnText.textContent = strings.resume;
25553
25617
  statusIndicator.classList.add("paused");
25554
25618
  statusText.textContent = `${strings.autoUpdate}: OFF`;
25555
25619
  } else {
25556
25620
  scheduleCheckDeviceTick();
25621
+ startSession();
25557
25622
  pauseBtnIcon.textContent = "\u23F8\uFE0F";
25558
25623
  pauseBtnText.textContent = strings.pause;
25559
25624
  statusIndicator.classList.remove("paused");
@@ -25629,6 +25694,7 @@ async function openRealTimeTelemetryModal(params) {
25629
25694
  async function switchMode(mode) {
25630
25695
  if (mode === currentMode) return;
25631
25696
  currentMode = mode;
25697
+ updateChartTitle();
25632
25698
  modeTabs.forEach((btn) => {
25633
25699
  btn.classList.toggle("active", btn.dataset["mode"] === mode);
25634
25700
  });
@@ -25669,6 +25735,7 @@ async function openRealTimeTelemetryModal(params) {
25669
25735
  isFirstTick = true;
25670
25736
  await refreshData();
25671
25737
  scheduleCheckDeviceTick();
25738
+ startSession();
25672
25739
  }
25673
25740
  }
25674
25741
  }
@@ -25775,6 +25842,7 @@ async function openRealTimeTelemetryModal(params) {
25775
25842
  await refreshData();
25776
25843
  isFirstTick = false;
25777
25844
  scheduleCheckDeviceTick();
25845
+ startSession();
25778
25846
  return {
25779
25847
  destroy: closeModal2
25780
25848
  };
@@ -29780,7 +29848,7 @@ var EnergyModalView = class {
29780
29848
  token: jwtToken,
29781
29849
  deviceId: this.config.params.deviceId,
29782
29850
  tbBaseUrl: this.config.params.tbBaseUrl || "",
29783
- deviceLabel: this.config.params.deviceLabel || "Dispositivo",
29851
+ deviceLabel: this.config.context.device.label || this.config.params.deviceLabel || "Dispositivo",
29784
29852
  deviceName: this.config.context.device.name,
29785
29853
  customerName: this.config.params.customerName,
29786
29854
  centralId: this.config.context.resolved.centralId,
@@ -29850,7 +29918,7 @@ var EnergyModalView = class {
29850
29918
  presetEnd: this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString().split("T")[0] : this.config.params.endDate,
29851
29919
  maxRangeDays: 90,
29852
29920
  includeTime: true,
29853
- timePrecision: "hour",
29921
+ timePrecision: "minute",
29854
29922
  parentEl: this.modal.element,
29855
29923
  onApply: ({ startISO, endISO }) => {
29856
29924
  this.hideError();
@@ -30250,7 +30318,7 @@ var EnergyModal = class {
30250
30318
  * Normalizes and validates parameters
30251
30319
  */
30252
30320
  normalizeParams(params) {
30253
- if (!validateJwtToken(params.tbJwtToken)) {
30321
+ if (!validateJwtToken(params.tbJwtToken || "")) {
30254
30322
  throw new Error("Invalid JWT token format");
30255
30323
  }
30256
30324
  return {
@@ -30282,10 +30350,10 @@ var EnergyModal = class {
30282
30350
  attributes: {}
30283
30351
  },
30284
30352
  resolved: {
30285
- ingestionId: null,
30286
- centralId: null,
30287
- slaveId: null,
30288
- customerId: null
30353
+ ingestionId: void 0,
30354
+ centralId: void 0,
30355
+ slaveId: void 0,
30356
+ customerId: void 0
30289
30357
  }
30290
30358
  };
30291
30359
  }
@@ -30421,10 +30489,10 @@ var EnergyModal = class {
30421
30489
  };
30422
30490
  const mainTitle = domainTitles[readingType] ?? "\u26A1 Gr\xE1fico de Energia";
30423
30491
  const label = this.context?.device.label || "";
30424
- const identifier = this.context?.device?.attributes?.identifier || "";
30492
+ const deviceName = this.context?.device.name || this.context?.device?.attributes?.identifier || "";
30425
30493
  const customer = this.params.customerName || "";
30426
30494
  const version2 = window.MyIOLibrary?.version;
30427
- const deviceBadge = label ? `<span class="myio-modal-header-device-label">${label}${identifier && identifier !== label ? `<span class="myio-modal-header-device-name">(${identifier})</span>` : ""}</span>` : "";
30495
+ const deviceBadge = label ? `<span class="myio-modal-header-device-label">${label}${deviceName && deviceName !== label ? `<span class="myio-modal-header-device-name">(${deviceName})</span>` : ""}</span>` : "";
30428
30496
  const customerBadge = customer ? `<span class="myio-modal-header-customer-badge">${customer}</span>` : "";
30429
30497
  const versionBadge = version2 ? `<span class="myio-modal-header-version-badge">v${version2}</span>` : "";
30430
30498
  return `${mainTitle}${deviceBadge}${customerBadge}${versionBadge}`;
@@ -30553,10 +30621,15 @@ var EnergyModal = class {
30553
30621
  title: "Dispositivo n\xE3o encontrado",
30554
30622
  detail: "Este dispositivo ainda n\xE3o possui dados de telemetria cadastrados. Verifique a integra\xE7\xE3o ou contate o suporte."
30555
30623
  };
30624
+ if (msg.includes("token_expired") || msg.includes("token has expired") || msg.includes("authentication token") || msg.includes("token expirou"))
30625
+ return {
30626
+ title: "Sess\xE3o expirada",
30627
+ detail: "Seu token de acesso expirou. Recarregue a p\xE1gina para continuar."
30628
+ };
30556
30629
  if (msg.includes("insufficient permissions") || msg.includes("401") || msg.includes("403") || msg.includes("unauthorized") || msg.includes("forbidden"))
30557
30630
  return {
30558
30631
  title: "Sem permiss\xE3o de acesso",
30559
- detail: "Sua sess\xE3o pode ter expirado ou voc\xEA n\xE3o tem permiss\xE3o para visualizar estes dados. Tente reabrir o modal."
30632
+ detail: "Voc\xEA n\xE3o tem permiss\xE3o para visualizar estes dados. Tente reabrir o modal ou contate o suporte."
30560
30633
  };
30561
30634
  if (msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network error") || msg.includes("err_network"))
30562
30635
  return {