myio-js-library 0.1.181 → 0.1.182

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
@@ -583,6 +583,7 @@ __export(index_exports, {
583
583
  EXPORT_DOMAIN_ICONS: () => EXPORT_DOMAIN_ICONS,
584
584
  EXPORT_DOMAIN_LABELS: () => EXPORT_DOMAIN_LABELS,
585
585
  EXPORT_DOMAIN_UNITS: () => EXPORT_DOMAIN_UNITS,
586
+ EnergyRangeTooltip: () => EnergyRangeTooltip,
586
587
  MyIOChartModal: () => MyIOChartModal,
587
588
  MyIODraggableCard: () => MyIODraggableCard,
588
589
  MyIOSelectionStore: () => MyIOSelectionStore,
@@ -591,6 +592,7 @@ __export(index_exports, {
591
592
  POWER_LIMITS_DEVICE_TYPES: () => DEVICE_TYPES,
592
593
  POWER_LIMITS_STATUS_CONFIG: () => STATUS_CONFIG,
593
594
  POWER_LIMITS_TELEMETRY_TYPES: () => TELEMETRY_TYPES2,
595
+ TempRangeTooltip: () => TempRangeTooltip,
594
596
  addDetectionContext: () => addDetectionContext,
595
597
  addNamespace: () => addNamespace,
596
598
  aggregateByDay: () => aggregateByDay,
@@ -6064,214 +6066,283 @@ function createLogHelper(debugActive = false) {
6064
6066
  }
6065
6067
  var LogHelper = createLogHelper(false);
6066
6068
 
6067
- // src/thingsboard/main-dashboard-shopping/v-4.0.0/card/head-office/card-head-office.js
6068
- var LABEL_CHAR_LIMIT = 18;
6069
- var DEFAUL_DELAY_TIME_CONNECTION_IN_MINS = 1440;
6070
- var CSS_TAG = "head-office-card-v1";
6071
- function ensureCss() {
6072
- if (!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)) {
6073
- const style = document.createElement("style");
6074
- style.setAttribute("data-myio-css", CSS_TAG);
6075
- style.textContent = CSS_STRING;
6076
- document.head.appendChild(style);
6077
- }
6069
+ // src/utils/TempRangeTooltip.ts
6070
+ var TOOLTIP_STYLES = `
6071
+ .temp-range-tooltip {
6072
+ position: fixed;
6073
+ z-index: 99999;
6074
+ pointer-events: none;
6075
+ opacity: 0;
6076
+ transition: opacity 0.2s ease, transform 0.2s ease;
6077
+ transform: translateY(5px);
6078
6078
  }
6079
- function normalizeParams(params) {
6080
- if (!params || !params.entityObject) {
6081
- throw new Error("renderCardCompenteHeadOffice: entityObject is required");
6082
- }
6083
- const LogHelper2 = createLogHelper(params.debugActive ?? false);
6084
- const entityObject = params.entityObject;
6085
- if (!entityObject.entityId) {
6086
- LogHelper2.warn("[CardHeadOffice] entityId is missing, generating temporary ID");
6087
- entityObject.entityId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
6088
- }
6089
- if (!params.delayTimeConnectionInMins) {
6090
- LogHelper2.warn(
6091
- `[CardHeadOffice] delayTimeConnectionInMins is missing, defaulting to ${DEFAUL_DELAY_TIME_CONNECTION_IN_MINS} mins`
6092
- );
6093
- }
6094
- return {
6095
- entityObject,
6096
- i18n: { ...DEFAULT_I18N, ...params.i18n || {} },
6097
- enableSelection: Boolean(params.enableSelection),
6098
- enableDragDrop: Boolean(params.enableDragDrop),
6099
- useNewComponents: Boolean(params.useNewComponents),
6100
- // RFC-0091: Configurable delay time for connection status check (default 15 minutes)
6101
- delayTimeConnectionInMins: params.delayTimeConnectionInMins ?? DEFAUL_DELAY_TIME_CONNECTION_IN_MINS,
6102
- // Debug options
6103
- debugActive: params.debugActive ?? false,
6104
- activeTooltipDebug: params.activeTooltipDebug ?? false,
6105
- // LogHelper instance for this card
6106
- LogHelper: LogHelper2,
6107
- callbacks: {
6108
- handleActionDashboard: params.handleActionDashboard,
6109
- handleActionReport: params.handleActionReport,
6110
- handleActionSettings: params.handleActionSettings,
6111
- handleSelect: params.handleSelect,
6112
- handInfo: params.handInfo,
6113
- handleClickCard: params.handleClickCard
6114
- }
6115
- };
6079
+
6080
+ .temp-range-tooltip.visible {
6081
+ opacity: 1;
6082
+ pointer-events: auto;
6083
+ transform: translateY(0);
6116
6084
  }
6117
- function getIconSvg(deviceType, domain) {
6118
- if (domain === "water") {
6119
- return Icons.waterDrop;
6120
- }
6121
- if (domain === "temperature") {
6122
- return Icons.thermometer;
6123
- }
6124
- return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
6085
+
6086
+ .temp-range-tooltip__content {
6087
+ background: #ffffff;
6088
+ border: 1px solid #e2e8f0;
6089
+ border-radius: 12px;
6090
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
6091
+ min-width: 280px;
6092
+ max-width: 320px;
6093
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6094
+ font-size: 12px;
6095
+ color: #1e293b;
6096
+ overflow: hidden;
6125
6097
  }
6126
- function formatPower(valueInWatts) {
6127
- if (valueInWatts === null || valueInWatts === void 0 || isNaN(valueInWatts)) {
6128
- return { num: "-", unit: "" };
6129
- }
6130
- const val = Number(valueInWatts);
6131
- if (val >= 1e3) {
6132
- const kw = Math.ceil(val / 1e3 * 100) / 100;
6133
- return { num: kw.toFixed(2), unit: "kW" };
6134
- } else {
6135
- const w = Math.ceil(val);
6136
- return { num: w.toString(), unit: "W" };
6137
- }
6098
+
6099
+ .temp-range-tooltip__header {
6100
+ display: flex;
6101
+ align-items: center;
6102
+ gap: 8px;
6103
+ padding: 12px 16px;
6104
+ background: linear-gradient(90deg, #fff7ed 0%, #fed7aa 100%);
6105
+ border-bottom: 1px solid #fdba74;
6138
6106
  }
6139
- function formatValueByDomain(value, domain) {
6140
- if (domain === "water") {
6141
- return formatWaterVolumeM3(value);
6142
- }
6143
- if (domain === "temperature") {
6144
- return formatTemperature(value, 0);
6145
- }
6146
- return formatEnergy(value);
6107
+
6108
+ .temp-range-tooltip__icon {
6109
+ font-size: 18px;
6147
6110
  }
6148
- function formatRelativeTime2(timestamp) {
6149
- if (!timestamp || isNaN(timestamp)) return "\u2014";
6150
- const date = new Date(timestamp);
6151
- const hours = String(date.getHours()).padStart(2, "0");
6152
- const minutes = String(date.getMinutes()).padStart(2, "0");
6153
- const day = String(date.getDate()).padStart(2, "0");
6154
- const month = String(date.getMonth() + 1).padStart(2, "0");
6155
- const year = date.getFullYear();
6156
- return `${hours}:${minutes} ${day}/${month}/${year}`;
6111
+
6112
+ .temp-range-tooltip__title {
6113
+ font-weight: 700;
6114
+ font-size: 13px;
6115
+ color: #c2410c;
6157
6116
  }
6158
- function calculateConsumptionPercentage(target, consumption) {
6159
- const numericTarget = Number(target);
6160
- const numericConsumption = Number(consumption);
6161
- if (isNaN(numericTarget) || isNaN(numericConsumption) || numericTarget <= 0) {
6162
- return 0;
6163
- }
6164
- const percentage = numericConsumption / numericTarget * 100;
6165
- return percentage;
6117
+
6118
+ .temp-range-tooltip__body {
6119
+ padding: 16px;
6166
6120
  }
6167
- function getStatusInfo(deviceStatus, i18n, domain) {
6168
- switch (deviceStatus) {
6169
- // --- Novos Status de Temperatura ---
6170
- case "normal":
6171
- return { chipClass: "chip--ok", label: "Normal" };
6172
- case "cold":
6173
- return { chipClass: "chip--standby", label: "Frio" };
6174
- case "hot":
6175
- return { chipClass: "chip--alert", label: "Quente" };
6176
- // --- Status Existentes (aligned with getCardStateClass) ---
6177
- case DeviceStatusType.POWER_ON:
6178
- if (domain === "water") {
6179
- return { chipClass: "chip--power-on", label: i18n.in_operation_water };
6180
- }
6181
- return { chipClass: "chip--power-on", label: i18n.in_operation };
6182
- case DeviceStatusType.STANDBY:
6183
- return { chipClass: "chip--standby", label: i18n.standby };
6184
- case DeviceStatusType.WARNING:
6185
- return { chipClass: "chip--warning", label: i18n.alert };
6186
- case DeviceStatusType.MAINTENANCE:
6187
- return { chipClass: "chip--maintenance", label: i18n.maintenance };
6188
- case DeviceStatusType.FAILURE:
6189
- return { chipClass: "chip--failure", label: i18n.failure };
6190
- case DeviceStatusType.POWER_OFF:
6191
- return { chipClass: "chip--power-off", label: i18n.power_off || i18n.failure };
6192
- case DeviceStatusType.OFFLINE:
6193
- return { chipClass: "chip--offline", label: i18n.offline };
6194
- case DeviceStatusType.NO_INFO:
6195
- return { chipClass: "chip--no-info", label: i18n.no_info || i18n.offline };
6196
- case DeviceStatusType.NOT_INSTALLED:
6197
- return { chipClass: "chip--not-installed", label: i18n.not_installed };
6198
- default:
6199
- return { chipClass: "chip--offline", label: i18n.offline };
6200
- }
6121
+
6122
+ .temp-range-tooltip__value-row {
6123
+ display: flex;
6124
+ justify-content: space-between;
6125
+ align-items: center;
6126
+ margin-bottom: 16px;
6201
6127
  }
6202
- function getCardStateClass(deviceStatus) {
6203
- switch (deviceStatus) {
6204
- case DeviceStatusType.POWER_ON:
6205
- return "is-power-on";
6206
- // Blue border
6207
- case DeviceStatusType.STANDBY:
6208
- return "is-standby";
6209
- // Green border
6210
- case DeviceStatusType.WARNING:
6211
- return "is-warning";
6212
- // Yellow border
6213
- case DeviceStatusType.MAINTENANCE:
6214
- return "is-maintenance";
6215
- // Yellow border
6216
- case DeviceStatusType.FAILURE:
6217
- return "is-failure";
6218
- // Dark Red border
6219
- case DeviceStatusType.POWER_OFF:
6220
- return "is-power-off";
6221
- // Light Red border
6222
- case DeviceStatusType.OFFLINE:
6223
- return "is-offline";
6224
- // Dark Gray border
6225
- case DeviceStatusType.NO_INFO:
6226
- return "is-no-info";
6227
- // Dark Orange border
6228
- case DeviceStatusType.NOT_INSTALLED:
6229
- return "is-not-installed";
6230
- // Purple border
6231
- default:
6232
- return "";
6233
- }
6128
+
6129
+ .temp-range-tooltip__current {
6130
+ font-size: 28px;
6131
+ font-weight: 700;
6132
+ color: #1e293b;
6234
6133
  }
6235
- function getTempRangeClass(entityObject) {
6236
- if (entityObject.domain !== "temperature") return "";
6237
- const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
6238
- const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
6239
- const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
6240
- if (tempMin === void 0 || tempMax === void 0 || tempMin === null || tempMax === null) {
6241
- return "";
6242
- }
6243
- if (currentTemp > tempMax) return "is-temp-hot";
6244
- if (currentTemp < tempMin) return "is-temp-cold";
6245
- return "is-temp-ok";
6134
+
6135
+ .temp-range-tooltip__current sup {
6136
+ font-size: 14px;
6137
+ color: #64748b;
6246
6138
  }
6247
- var TempRangeTooltip = {
6248
- containerId: "myio-temp-range-tooltip",
6249
- /**
6250
- * Create or get the tooltip container
6251
- */
6252
- getContainer() {
6253
- let container = document.getElementById(this.containerId);
6254
- if (!container) {
6255
- container = document.createElement("div");
6256
- container.id = this.containerId;
6257
- container.className = "temp-range-tooltip";
6258
- document.body.appendChild(container);
6259
- }
6260
- return container;
6261
- },
6262
- /**
6263
- * Calculate temperature status and deviation
6264
- */
6265
- calculateStatus(currentTemp, tempMin, tempMax) {
6266
- if (tempMin == null || tempMax == null) {
6267
- return { status: "unknown", deviation: null, deviationPercent: null };
6268
- }
6269
- const rangeSize = tempMax - tempMin;
6270
- const midPoint = (tempMin + tempMax) / 2;
6271
- if (currentTemp < tempMin) {
6272
- const deviation = tempMin - currentTemp;
6273
- const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
6274
- return { status: "cold", deviation: -deviation, deviationPercent: -deviationPercent };
6139
+
6140
+ .temp-range-tooltip__deviation {
6141
+ text-align: right;
6142
+ }
6143
+
6144
+ .temp-range-tooltip__deviation-value {
6145
+ font-size: 16px;
6146
+ font-weight: 700;
6147
+ }
6148
+
6149
+ .temp-range-tooltip__deviation-value.cold {
6150
+ color: #2563eb;
6151
+ }
6152
+
6153
+ .temp-range-tooltip__deviation-value.ok {
6154
+ color: #16a34a;
6155
+ }
6156
+
6157
+ .temp-range-tooltip__deviation-value.hot {
6158
+ color: #dc2626;
6159
+ }
6160
+
6161
+ .temp-range-tooltip__deviation-label {
6162
+ font-size: 10px;
6163
+ color: #64748b;
6164
+ text-transform: uppercase;
6165
+ letter-spacing: 0.5px;
6166
+ }
6167
+
6168
+ .temp-range-tooltip__ruler {
6169
+ position: relative;
6170
+ height: 32px;
6171
+ margin: 12px 0;
6172
+ border-radius: 8px;
6173
+ overflow: visible;
6174
+ }
6175
+
6176
+ .temp-range-tooltip__ruler-track {
6177
+ position: absolute;
6178
+ top: 12px;
6179
+ left: 0;
6180
+ right: 0;
6181
+ height: 8px;
6182
+ background: linear-gradient(90deg, #dbeafe 0%, #dcfce7 50%, #fee2e2 100%);
6183
+ border-radius: 4px;
6184
+ border: 1px solid #e2e8f0;
6185
+ }
6186
+
6187
+ .temp-range-tooltip__ruler-range {
6188
+ position: absolute;
6189
+ top: 12px;
6190
+ height: 8px;
6191
+ background: #22c55e;
6192
+ border-radius: 4px;
6193
+ opacity: 0.6;
6194
+ }
6195
+
6196
+ .temp-range-tooltip__ruler-marker {
6197
+ position: absolute;
6198
+ top: 4px;
6199
+ width: 4px;
6200
+ height: 24px;
6201
+ background: #1e293b;
6202
+ border-radius: 2px;
6203
+ transform: translateX(-50%);
6204
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6205
+ }
6206
+
6207
+ .temp-range-tooltip__ruler-marker::after {
6208
+ content: '';
6209
+ position: absolute;
6210
+ top: -4px;
6211
+ left: 50%;
6212
+ transform: translateX(-50%);
6213
+ width: 12px;
6214
+ height: 12px;
6215
+ background: #1e293b;
6216
+ border-radius: 50%;
6217
+ border: 2px solid #fff;
6218
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6219
+ }
6220
+
6221
+ .temp-range-tooltip__ruler-labels {
6222
+ display: flex;
6223
+ justify-content: space-between;
6224
+ margin-top: 8px;
6225
+ font-size: 10px;
6226
+ color: #64748b;
6227
+ }
6228
+
6229
+ .temp-range-tooltip__ruler-min,
6230
+ .temp-range-tooltip__ruler-max {
6231
+ font-weight: 600;
6232
+ }
6233
+
6234
+ .temp-range-tooltip__range-info {
6235
+ display: flex;
6236
+ justify-content: space-between;
6237
+ padding: 10px 12px;
6238
+ background: #f8fafc;
6239
+ border-radius: 8px;
6240
+ margin-top: 12px;
6241
+ }
6242
+
6243
+ .temp-range-tooltip__range-item {
6244
+ text-align: center;
6245
+ }
6246
+
6247
+ .temp-range-tooltip__range-label {
6248
+ font-size: 10px;
6249
+ color: #64748b;
6250
+ text-transform: uppercase;
6251
+ letter-spacing: 0.3px;
6252
+ margin-bottom: 2px;
6253
+ }
6254
+
6255
+ .temp-range-tooltip__range-value {
6256
+ font-size: 14px;
6257
+ font-weight: 600;
6258
+ color: #334155;
6259
+ }
6260
+
6261
+ .temp-range-tooltip__status {
6262
+ display: flex;
6263
+ align-items: center;
6264
+ justify-content: center;
6265
+ gap: 6px;
6266
+ margin-top: 12px;
6267
+ padding: 8px 12px;
6268
+ border-radius: 6px;
6269
+ font-size: 11px;
6270
+ font-weight: 600;
6271
+ }
6272
+
6273
+ .temp-range-tooltip__status.cold {
6274
+ background: #dbeafe;
6275
+ color: #1d4ed8;
6276
+ border: 1px solid #93c5fd;
6277
+ }
6278
+
6279
+ .temp-range-tooltip__status.ok {
6280
+ background: #dcfce7;
6281
+ color: #15803d;
6282
+ border: 1px solid #86efac;
6283
+ }
6284
+
6285
+ .temp-range-tooltip__status.hot {
6286
+ background: #fee2e2;
6287
+ color: #b91c1c;
6288
+ border: 1px solid #fca5a5;
6289
+ }
6290
+
6291
+ .temp-range-tooltip__status.unknown {
6292
+ background: #f3f4f6;
6293
+ color: #6b7280;
6294
+ border: 1px solid #d1d5db;
6295
+ }
6296
+ `;
6297
+ function injectStyles() {
6298
+ if (typeof document === "undefined") return;
6299
+ const styleId = "myio-temp-range-tooltip-styles";
6300
+ if (document.getElementById(styleId)) return;
6301
+ const style = document.createElement("style");
6302
+ style.id = styleId;
6303
+ style.textContent = TOOLTIP_STYLES;
6304
+ document.head.appendChild(style);
6305
+ }
6306
+ function extractTemperature(entityData) {
6307
+ return Number(entityData.val ?? entityData.currentTemperature ?? entityData.temperature) || 0;
6308
+ }
6309
+ function extractRange(entityData) {
6310
+ const tempMin = entityData.temperatureMin ?? entityData.minTemperature ?? null;
6311
+ const tempMax = entityData.temperatureMax ?? entityData.maxTemperature ?? null;
6312
+ return { tempMin, tempMax };
6313
+ }
6314
+ function extractLabel(entityData) {
6315
+ return entityData.labelOrName || entityData.name || entityData.label || "Sensor";
6316
+ }
6317
+ var TempRangeTooltip = {
6318
+ containerId: "myio-temp-range-tooltip",
6319
+ /**
6320
+ * Create or get the tooltip container
6321
+ */
6322
+ getContainer() {
6323
+ injectStyles();
6324
+ let container = document.getElementById(this.containerId);
6325
+ if (!container) {
6326
+ container = document.createElement("div");
6327
+ container.id = this.containerId;
6328
+ container.className = "temp-range-tooltip";
6329
+ document.body.appendChild(container);
6330
+ }
6331
+ return container;
6332
+ },
6333
+ /**
6334
+ * Calculate temperature status and deviation
6335
+ */
6336
+ calculateStatus(currentTemp, tempMin, tempMax) {
6337
+ if (tempMin == null || tempMax == null) {
6338
+ return { status: "unknown", deviation: null, deviationPercent: null };
6339
+ }
6340
+ const rangeSize = tempMax - tempMin;
6341
+ const midPoint = (tempMin + tempMax) / 2;
6342
+ if (currentTemp < tempMin) {
6343
+ const deviation = tempMin - currentTemp;
6344
+ const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
6345
+ return { status: "cold", deviation: -deviation, deviationPercent: -deviationPercent };
6275
6346
  } else if (currentTemp > tempMax) {
6276
6347
  const deviation = currentTemp - tempMax;
6277
6348
  const deviationPercent = rangeSize > 0 ? deviation / rangeSize * 100 : 0;
@@ -6299,20 +6370,20 @@ var TempRangeTooltip = {
6299
6370
  },
6300
6371
  /**
6301
6372
  * Show tooltip for a temperature card
6302
- * @param {HTMLElement} triggerElement - The card element
6303
- * @param {Object} entityObject - Entity data
6304
- * @param {MouseEvent} event - Mouse event for cursor position
6373
+ * @param triggerElement - The card element
6374
+ * @param entityData - Entity data with temperature info
6375
+ * @param event - Mouse event for cursor position
6305
6376
  */
6306
- show(triggerElement, entityObject, event) {
6377
+ show(triggerElement, entityData, event) {
6307
6378
  const container = this.getContainer();
6308
- const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
6309
- const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
6310
- const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
6379
+ const currentTemp = extractTemperature(entityData);
6380
+ const { tempMin, tempMax } = extractRange(entityData);
6381
+ const label = extractLabel(entityData);
6311
6382
  const hasRange = tempMin != null && tempMax != null;
6312
- const { status, deviation, deviationPercent } = this.calculateStatus(currentTemp, tempMin, tempMax);
6383
+ const { status, deviationPercent } = this.calculateStatus(currentTemp, tempMin, tempMax);
6313
6384
  const markerPos = this.calculateMarkerPosition(currentTemp, tempMin, tempMax);
6314
6385
  let rangeLeft = 0, rangeWidth = 100;
6315
- if (hasRange) {
6386
+ if (hasRange && tempMin != null && tempMax != null) {
6316
6387
  const rangeSize = tempMax - tempMin;
6317
6388
  const extension = rangeSize * 0.3;
6318
6389
  const visibleMin = tempMin - extension;
@@ -6321,28 +6392,23 @@ var TempRangeTooltip = {
6321
6392
  rangeLeft = (tempMin - visibleMin) / visibleRange * 100;
6322
6393
  rangeWidth = rangeSize / visibleRange * 100;
6323
6394
  }
6324
- let deviationText = "";
6325
- let deviationClass = status;
6326
- if (status === "cold") {
6327
- deviationText = `${Math.abs(deviation).toFixed(1)}\xB0C abaixo`;
6328
- } else if (status === "hot") {
6329
- deviationText = `+${deviation.toFixed(1)}\xB0C acima`;
6330
- } else if (status === "ok") {
6331
- deviationText = "Na faixa ideal";
6332
- } else {
6333
- deviationText = "Faixa n\xE3o configurada";
6334
- }
6335
6395
  const statusLabels = {
6336
6396
  cold: "\u2744\uFE0F Abaixo da Faixa Ideal",
6337
6397
  ok: "\u2714\uFE0F Dentro da Faixa Ideal",
6338
6398
  hot: "\u{1F525} Acima da Faixa Ideal",
6339
6399
  unknown: "\u2753 Faixa N\xE3o Configurada"
6340
6400
  };
6401
+ const statusColors = {
6402
+ cold: "#2563eb",
6403
+ ok: "#16a34a",
6404
+ hot: "#dc2626",
6405
+ unknown: "#64748b"
6406
+ };
6341
6407
  container.innerHTML = `
6342
6408
  <div class="temp-range-tooltip__content">
6343
6409
  <div class="temp-range-tooltip__header">
6344
6410
  <span class="temp-range-tooltip__icon">\u{1F321}\uFE0F</span>
6345
- <span class="temp-range-tooltip__title">${entityObject.labelOrName || entityObject.name || "Sensor"}</span>
6411
+ <span class="temp-range-tooltip__title">${label}</span>
6346
6412
  </div>
6347
6413
  <div class="temp-range-tooltip__body">
6348
6414
  <div class="temp-range-tooltip__value-row">
@@ -6350,7 +6416,7 @@ var TempRangeTooltip = {
6350
6416
  ${currentTemp.toFixed(1)}<sup>\xB0C</sup>
6351
6417
  </div>
6352
6418
  <div class="temp-range-tooltip__deviation">
6353
- <div class="temp-range-tooltip__deviation-value ${deviationClass}">
6419
+ <div class="temp-range-tooltip__deviation-value ${status}">
6354
6420
  ${status === "ok" ? "\u2713" : status === "cold" ? "\u2193" : status === "hot" ? "\u2191" : "?"} ${Math.abs(deviationPercent || 0).toFixed(0)}%
6355
6421
  </div>
6356
6422
  <div class="temp-range-tooltip__deviation-label">Desvio</div>
@@ -6377,7 +6443,7 @@ var TempRangeTooltip = {
6377
6443
  </div>
6378
6444
  <div class="temp-range-tooltip__range-item">
6379
6445
  <div class="temp-range-tooltip__range-label">Atual</div>
6380
- <div class="temp-range-tooltip__range-value" style="color: ${status === "ok" ? "#16a34a" : status === "cold" ? "#2563eb" : "#dc2626"}">${currentTemp.toFixed(1)}\xB0C</div>
6446
+ <div class="temp-range-tooltip__range-value" style="color: ${statusColors[status]}">${currentTemp.toFixed(1)}\xB0C</div>
6381
6447
  </div>
6382
6448
  <div class="temp-range-tooltip__range-item">
6383
6449
  <div class="temp-range-tooltip__range-label">M\xE1ximo</div>
@@ -6422,7 +6488,320 @@ var TempRangeTooltip = {
6422
6488
  if (container) {
6423
6489
  container.classList.remove("visible");
6424
6490
  }
6491
+ },
6492
+ /**
6493
+ * Attach tooltip to an element
6494
+ * Returns cleanup function
6495
+ */
6496
+ attach(element, entityData) {
6497
+ const showHandler = (e) => this.show(element, entityData, e);
6498
+ const hideHandler = () => this.hide();
6499
+ element.style.cursor = "help";
6500
+ element.addEventListener("mouseenter", showHandler);
6501
+ element.addEventListener("mouseleave", hideHandler);
6502
+ return () => {
6503
+ element.removeEventListener("mouseenter", showHandler);
6504
+ element.removeEventListener("mouseleave", hideHandler);
6505
+ element.style.cursor = "";
6506
+ this.hide();
6507
+ };
6508
+ }
6509
+ };
6510
+
6511
+ // src/utils/EnergyRangeTooltip.ts
6512
+ var ENERGY_RANGE_TOOLTIP_CSS = `
6513
+ /* ============================================
6514
+ Energy Range Tooltip (for domain=energy)
6515
+ Shows power ruler with current position and status ranges
6516
+ ============================================ */
6517
+ .energy-range-tooltip {
6518
+ position: fixed;
6519
+ z-index: 99999;
6520
+ pointer-events: none;
6521
+ opacity: 0;
6522
+ transition: opacity 0.2s ease, transform 0.2s ease;
6523
+ transform: translateY(5px);
6524
+ }
6525
+
6526
+ .energy-range-tooltip.visible {
6527
+ opacity: 1;
6528
+ pointer-events: auto;
6529
+ transform: translateY(0);
6530
+ }
6531
+
6532
+ .energy-range-tooltip__content {
6533
+ background: #ffffff;
6534
+ border: 1px solid #e2e8f0;
6535
+ border-radius: 12px;
6536
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
6537
+ min-width: 300px;
6538
+ max-width: 360px;
6539
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6540
+ font-size: 12px;
6541
+ color: #1e293b;
6542
+ overflow: hidden;
6543
+ }
6544
+
6545
+ .energy-range-tooltip__header {
6546
+ display: flex;
6547
+ align-items: center;
6548
+ gap: 8px;
6549
+ padding: 12px 16px;
6550
+ background: linear-gradient(90deg, #ecfdf5 0%, #d1fae5 100%);
6551
+ border-bottom: 1px solid #6ee7b7;
6552
+ }
6553
+
6554
+ .energy-range-tooltip__icon {
6555
+ font-size: 18px;
6556
+ }
6557
+
6558
+ .energy-range-tooltip__title {
6559
+ font-weight: 700;
6560
+ font-size: 13px;
6561
+ color: #047857;
6562
+ }
6563
+
6564
+ .energy-range-tooltip__body {
6565
+ padding: 16px;
6566
+ }
6567
+
6568
+ /* Power value display */
6569
+ .energy-range-tooltip__value-row {
6570
+ display: flex;
6571
+ justify-content: space-between;
6572
+ align-items: center;
6573
+ margin-bottom: 16px;
6574
+ }
6575
+
6576
+ .energy-range-tooltip__current {
6577
+ font-size: 28px;
6578
+ font-weight: 700;
6579
+ color: #1e293b;
6580
+ }
6581
+
6582
+ .energy-range-tooltip__current sup {
6583
+ font-size: 14px;
6584
+ color: #64748b;
6585
+ }
6586
+
6587
+ .energy-range-tooltip__status-badge {
6588
+ text-align: right;
6589
+ }
6590
+
6591
+ .energy-range-tooltip__status-value {
6592
+ font-size: 14px;
6593
+ font-weight: 700;
6594
+ padding: 4px 10px;
6595
+ border-radius: 6px;
6596
+ }
6597
+
6598
+ .energy-range-tooltip__status-value.standby {
6599
+ background: #dbeafe;
6600
+ color: #1d4ed8;
6601
+ }
6602
+
6603
+ .energy-range-tooltip__status-value.normal {
6604
+ background: #dcfce7;
6605
+ color: #15803d;
6606
+ }
6607
+
6608
+ .energy-range-tooltip__status-value.alert {
6609
+ background: #fef3c7;
6610
+ color: #b45309;
6611
+ }
6612
+
6613
+ .energy-range-tooltip__status-value.failure {
6614
+ background: #fee2e2;
6615
+ color: #b91c1c;
6616
+ }
6617
+
6618
+ .energy-range-tooltip__status-value.offline {
6619
+ background: #f3f4f6;
6620
+ color: #6b7280;
6621
+ }
6622
+
6623
+ /* Power ruler/gauge */
6624
+ .energy-range-tooltip__ruler {
6625
+ position: relative;
6626
+ height: 40px;
6627
+ margin: 12px 0;
6628
+ border-radius: 8px;
6629
+ overflow: visible;
6630
+ }
6631
+
6632
+ .energy-range-tooltip__ruler-track {
6633
+ position: absolute;
6634
+ top: 16px;
6635
+ left: 0;
6636
+ right: 0;
6637
+ height: 8px;
6638
+ display: flex;
6639
+ border-radius: 4px;
6640
+ overflow: hidden;
6641
+ border: 1px solid #e2e8f0;
6642
+ }
6643
+
6644
+ .energy-range-tooltip__ruler-segment {
6645
+ height: 100%;
6646
+ }
6647
+
6648
+ .energy-range-tooltip__ruler-segment.standby {
6649
+ background: #dbeafe;
6650
+ }
6651
+
6652
+ .energy-range-tooltip__ruler-segment.normal {
6653
+ background: #dcfce7;
6654
+ }
6655
+
6656
+ .energy-range-tooltip__ruler-segment.alert {
6657
+ background: #fef3c7;
6658
+ }
6659
+
6660
+ .energy-range-tooltip__ruler-segment.failure {
6661
+ background: #fee2e2;
6662
+ }
6663
+
6664
+ .energy-range-tooltip__ruler-marker {
6665
+ position: absolute;
6666
+ top: 8px;
6667
+ width: 4px;
6668
+ height: 24px;
6669
+ background: #1e293b;
6670
+ border-radius: 2px;
6671
+ transform: translateX(-50%);
6672
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6673
+ }
6674
+
6675
+ .energy-range-tooltip__ruler-marker::after {
6676
+ content: '';
6677
+ position: absolute;
6678
+ top: -4px;
6679
+ left: 50%;
6680
+ transform: translateX(-50%);
6681
+ width: 12px;
6682
+ height: 12px;
6683
+ background: #1e293b;
6684
+ border-radius: 50%;
6685
+ border: 2px solid #fff;
6686
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
6687
+ }
6688
+
6689
+ /* Range info grid */
6690
+ .energy-range-tooltip__ranges {
6691
+ display: grid;
6692
+ grid-template-columns: repeat(4, 1fr);
6693
+ gap: 8px;
6694
+ margin-top: 16px;
6695
+ }
6696
+
6697
+ .energy-range-tooltip__range-item {
6698
+ text-align: center;
6699
+ padding: 8px 4px;
6700
+ border-radius: 6px;
6701
+ background: #f8fafc;
6702
+ }
6703
+
6704
+ .energy-range-tooltip__range-item.standby {
6705
+ border-left: 3px solid #3b82f6;
6706
+ }
6707
+
6708
+ .energy-range-tooltip__range-item.normal {
6709
+ border-left: 3px solid #22c55e;
6710
+ }
6711
+
6712
+ .energy-range-tooltip__range-item.alert {
6713
+ border-left: 3px solid #f59e0b;
6714
+ }
6715
+
6716
+ .energy-range-tooltip__range-item.failure {
6717
+ border-left: 3px solid #ef4444;
6718
+ }
6719
+
6720
+ .energy-range-tooltip__range-label {
6721
+ font-size: 9px;
6722
+ color: #64748b;
6723
+ text-transform: uppercase;
6724
+ letter-spacing: 0.3px;
6725
+ margin-bottom: 2px;
6726
+ }
6727
+
6728
+ .energy-range-tooltip__range-value {
6729
+ font-size: 11px;
6730
+ font-weight: 600;
6731
+ color: #334155;
6732
+ }
6733
+
6734
+ /* Status info */
6735
+ .energy-range-tooltip__status-info {
6736
+ display: flex;
6737
+ align-items: center;
6738
+ justify-content: center;
6739
+ gap: 6px;
6740
+ margin-top: 12px;
6741
+ padding: 8px 12px;
6742
+ border-radius: 6px;
6743
+ font-size: 11px;
6744
+ font-weight: 600;
6745
+ }
6746
+
6747
+ .energy-range-tooltip__status-info.standby {
6748
+ background: #dbeafe;
6749
+ color: #1d4ed8;
6750
+ border: 1px solid #93c5fd;
6751
+ }
6752
+
6753
+ .energy-range-tooltip__status-info.normal {
6754
+ background: #dcfce7;
6755
+ color: #15803d;
6756
+ border: 1px solid #86efac;
6757
+ }
6758
+
6759
+ .energy-range-tooltip__status-info.alert {
6760
+ background: #fef3c7;
6761
+ color: #b45309;
6762
+ border: 1px solid #fcd34d;
6763
+ }
6764
+
6765
+ .energy-range-tooltip__status-info.failure {
6766
+ background: #fee2e2;
6767
+ color: #b91c1c;
6768
+ border: 1px solid #fca5a5;
6769
+ }
6770
+
6771
+ .energy-range-tooltip__status-info.offline {
6772
+ background: #f3f4f6;
6773
+ color: #6b7280;
6774
+ border: 1px solid #d1d5db;
6775
+ }
6776
+ `;
6777
+ var cssInjected = false;
6778
+ function injectCSS() {
6779
+ if (cssInjected) return;
6780
+ if (typeof document === "undefined") return;
6781
+ const styleId = "myio-energy-range-tooltip-styles";
6782
+ if (document.getElementById(styleId)) {
6783
+ cssInjected = true;
6784
+ return;
6425
6785
  }
6786
+ const style = document.createElement("style");
6787
+ style.id = styleId;
6788
+ style.textContent = ENERGY_RANGE_TOOLTIP_CSS;
6789
+ document.head.appendChild(style);
6790
+ cssInjected = true;
6791
+ }
6792
+ var STATUS_LABELS = {
6793
+ standby: "Standby",
6794
+ normal: "Operacao Normal",
6795
+ alert: "Alerta",
6796
+ failure: "Falha",
6797
+ offline: "Fora da faixa"
6798
+ };
6799
+ var STATUS_INFO_LABELS = {
6800
+ standby: "\u{1F535} Standby",
6801
+ normal: "\u2705 Operacao Normal",
6802
+ alert: "\u26A0\uFE0F Alerta",
6803
+ failure: "\u{1F534} Falha",
6804
+ offline: "\u26AB Offline / Sem dados"
6426
6805
  };
6427
6806
  var EnergyRangeTooltip = {
6428
6807
  containerId: "myio-energy-range-tooltip",
@@ -6430,6 +6809,7 @@ var EnergyRangeTooltip = {
6430
6809
  * Create or get the tooltip container
6431
6810
  */
6432
6811
  getContainer() {
6812
+ injectCSS();
6433
6813
  let container = document.getElementById(this.containerId);
6434
6814
  if (!container) {
6435
6815
  container = document.createElement("div");
@@ -6449,18 +6829,18 @@ var EnergyRangeTooltip = {
6449
6829
  const power = Number(powerValue) || 0;
6450
6830
  const { standbyRange, normalRange, alertRange, failureRange } = ranges;
6451
6831
  if (standbyRange && power >= standbyRange.down && power <= standbyRange.up) {
6452
- return { status: "standby", label: "Standby" };
6832
+ return { status: "standby", label: STATUS_LABELS.standby };
6453
6833
  }
6454
6834
  if (normalRange && power >= normalRange.down && power <= normalRange.up) {
6455
- return { status: "normal", label: "Normal" };
6835
+ return { status: "normal", label: STATUS_LABELS.normal };
6456
6836
  }
6457
6837
  if (alertRange && power >= alertRange.down && power <= alertRange.up) {
6458
- return { status: "alert", label: "Alerta" };
6838
+ return { status: "alert", label: STATUS_LABELS.alert };
6459
6839
  }
6460
6840
  if (failureRange && power >= failureRange.down && power <= failureRange.up) {
6461
- return { status: "failure", label: "Falha" };
6841
+ return { status: "failure", label: STATUS_LABELS.failure };
6462
6842
  }
6463
- return { status: "offline", label: "Fora da faixa" };
6843
+ return { status: "offline", label: STATUS_LABELS.offline };
6464
6844
  },
6465
6845
  /**
6466
6846
  * Calculate marker position on ruler (0-100%)
@@ -6491,7 +6871,7 @@ var EnergyRangeTooltip = {
6491
6871
  * Format power value for display
6492
6872
  */
6493
6873
  formatPower(value) {
6494
- if (value == null || isNaN(value)) return "-";
6874
+ if (value == null || isNaN(Number(value))) return "-";
6495
6875
  const num = Number(value);
6496
6876
  if (num >= 1e3) {
6497
6877
  return `${(num / 1e3).toFixed(2)} kW`;
@@ -6509,13 +6889,40 @@ var EnergyRangeTooltip = {
6509
6889
  const { status, label } = this.calculateStatus(powerValue, ranges);
6510
6890
  const markerPos = this.calculateMarkerPosition(powerValue, ranges);
6511
6891
  const segmentWidths = this.calculateSegmentWidths(ranges);
6512
- const statusLabels = {
6513
- standby: "\u{1F535} Standby",
6514
- normal: "\u2705 Opera\xE7\xE3o Normal",
6515
- alert: "\u26A0\uFE0F Alerta",
6516
- failure: "\u{1F534} Falha",
6517
- offline: "\u26AB Offline / Sem dados"
6518
- };
6892
+ const rangesHtml = hasRanges && ranges ? `
6893
+ <div class="energy-range-tooltip__ruler">
6894
+ <div class="energy-range-tooltip__ruler-track">
6895
+ <div class="energy-range-tooltip__ruler-segment standby" style="width: ${segmentWidths.standby}%"></div>
6896
+ <div class="energy-range-tooltip__ruler-segment normal" style="width: ${segmentWidths.normal}%"></div>
6897
+ <div class="energy-range-tooltip__ruler-segment alert" style="width: ${segmentWidths.alert}%"></div>
6898
+ <div class="energy-range-tooltip__ruler-segment failure" style="width: ${segmentWidths.failure}%"></div>
6899
+ </div>
6900
+ <div class="energy-range-tooltip__ruler-marker" style="left: ${markerPos}%;"></div>
6901
+ </div>
6902
+
6903
+ <div class="energy-range-tooltip__ranges">
6904
+ <div class="energy-range-tooltip__range-item standby">
6905
+ <div class="energy-range-tooltip__range-label">Standby</div>
6906
+ <div class="energy-range-tooltip__range-value">${ranges.standbyRange?.down || 0}-${ranges.standbyRange?.up || 0}W</div>
6907
+ </div>
6908
+ <div class="energy-range-tooltip__range-item normal">
6909
+ <div class="energy-range-tooltip__range-label">Normal</div>
6910
+ <div class="energy-range-tooltip__range-value">${ranges.normalRange?.down || 0}-${ranges.normalRange?.up || 0}W</div>
6911
+ </div>
6912
+ <div class="energy-range-tooltip__range-item alert">
6913
+ <div class="energy-range-tooltip__range-label">Alerta</div>
6914
+ <div class="energy-range-tooltip__range-value">${ranges.alertRange?.down || 0}-${ranges.alertRange?.up || 0}W</div>
6915
+ </div>
6916
+ <div class="energy-range-tooltip__range-item failure">
6917
+ <div class="energy-range-tooltip__range-label">Falha</div>
6918
+ <div class="energy-range-tooltip__range-value">&gt;${ranges.failureRange?.down || 0}W</div>
6919
+ </div>
6920
+ </div>
6921
+ ` : `
6922
+ <div style="text-align: center; padding: 16px; color: #64748b; font-size: 12px;">
6923
+ Ranges de potencia nao configurados
6924
+ </div>
6925
+ `;
6519
6926
  container.innerHTML = `
6520
6927
  <div class="energy-range-tooltip__content">
6521
6928
  <div class="energy-range-tooltip__header">
@@ -6532,51 +6939,18 @@ var EnergyRangeTooltip = {
6532
6939
  </div>
6533
6940
  </div>
6534
6941
 
6535
- ${hasRanges ? `
6536
- <div class="energy-range-tooltip__ruler">
6537
- <div class="energy-range-tooltip__ruler-track">
6538
- <div class="energy-range-tooltip__ruler-segment standby" style="width: ${segmentWidths.standby}%"></div>
6539
- <div class="energy-range-tooltip__ruler-segment normal" style="width: ${segmentWidths.normal}%"></div>
6540
- <div class="energy-range-tooltip__ruler-segment alert" style="width: ${segmentWidths.alert}%"></div>
6541
- <div class="energy-range-tooltip__ruler-segment failure" style="width: ${segmentWidths.failure}%"></div>
6542
- </div>
6543
- <div class="energy-range-tooltip__ruler-marker" style="left: ${markerPos}%;"></div>
6544
- </div>
6545
-
6546
- <div class="energy-range-tooltip__ranges">
6547
- <div class="energy-range-tooltip__range-item standby">
6548
- <div class="energy-range-tooltip__range-label">Standby</div>
6549
- <div class="energy-range-tooltip__range-value">${ranges.standbyRange?.down || 0}-${ranges.standbyRange?.up || 0}W</div>
6550
- </div>
6551
- <div class="energy-range-tooltip__range-item normal">
6552
- <div class="energy-range-tooltip__range-label">Normal</div>
6553
- <div class="energy-range-tooltip__range-value">${ranges.normalRange?.down || 0}-${ranges.normalRange?.up || 0}W</div>
6554
- </div>
6555
- <div class="energy-range-tooltip__range-item alert">
6556
- <div class="energy-range-tooltip__range-label">Alerta</div>
6557
- <div class="energy-range-tooltip__range-value">${ranges.alertRange?.down || 0}-${ranges.alertRange?.up || 0}W</div>
6558
- </div>
6559
- <div class="energy-range-tooltip__range-item failure">
6560
- <div class="energy-range-tooltip__range-label">Falha</div>
6561
- <div class="energy-range-tooltip__range-value">&gt;${ranges.failureRange?.down || 0}W</div>
6562
- </div>
6563
- </div>
6564
- ` : `
6565
- <div style="text-align: center; padding: 16px; color: #64748b; font-size: 12px;">
6566
- Ranges de pot\xEAncia n\xE3o configurados
6567
- </div>
6568
- `}
6942
+ ${rangesHtml}
6569
6943
 
6570
6944
  <div class="energy-range-tooltip__status-info ${status}">
6571
- ${statusLabels[status] || statusLabels.offline}
6945
+ ${STATUS_INFO_LABELS[status] || STATUS_INFO_LABELS.offline}
6572
6946
  </div>
6573
6947
  </div>
6574
6948
  </div>
6575
6949
  `;
6576
6950
  let left, top;
6577
6951
  if (event && event.clientX && event.clientY) {
6578
- left = event.clientX + 15;
6579
- top = event.clientY + 15;
6952
+ left = event.clientX + 8;
6953
+ top = event.clientY + 8;
6580
6954
  } else {
6581
6955
  const rect = triggerElement.getBoundingClientRect();
6582
6956
  left = rect.left + rect.width / 2 - 150;
@@ -6585,11 +6959,11 @@ var EnergyRangeTooltip = {
6585
6959
  const tooltipWidth = 320;
6586
6960
  const tooltipHeight = 380;
6587
6961
  if (left + tooltipWidth > window.innerWidth - 10) {
6588
- left = (event?.clientX || left) - tooltipWidth - 15;
6962
+ left = (event?.clientX || left) - tooltipWidth - 8;
6589
6963
  }
6590
6964
  if (left < 10) left = 10;
6591
6965
  if (top + tooltipHeight > window.innerHeight - 10) {
6592
- top = (event?.clientY || top) - tooltipHeight - 15;
6966
+ top = (event?.clientY || top) - tooltipHeight - 8;
6593
6967
  }
6594
6968
  if (top < 10) top = 10;
6595
6969
  container.style.left = left + "px";
@@ -6604,8 +6978,229 @@ var EnergyRangeTooltip = {
6604
6978
  if (container) {
6605
6979
  container.classList.remove("visible");
6606
6980
  }
6981
+ },
6982
+ /**
6983
+ * Attach tooltip to an element with automatic show/hide on hover
6984
+ * Returns cleanup function to remove event listeners
6985
+ */
6986
+ attach(element, entityData) {
6987
+ const handleMouseEnter = (e) => {
6988
+ this.show(element, entityData, e);
6989
+ };
6990
+ const handleMouseLeave = () => {
6991
+ this.hide();
6992
+ };
6993
+ const handleMouseMove = (e) => {
6994
+ const container = document.getElementById(this.containerId);
6995
+ if (container && container.classList.contains("visible")) {
6996
+ let left = e.clientX + 8;
6997
+ let top = e.clientY + 8;
6998
+ const tooltipWidth = 320;
6999
+ const tooltipHeight = 380;
7000
+ if (left + tooltipWidth > window.innerWidth - 10) {
7001
+ left = e.clientX - tooltipWidth - 8;
7002
+ }
7003
+ if (left < 10) left = 10;
7004
+ if (top + tooltipHeight > window.innerHeight - 10) {
7005
+ top = e.clientY - tooltipHeight - 8;
7006
+ }
7007
+ if (top < 10) top = 10;
7008
+ container.style.left = left + "px";
7009
+ container.style.top = top + "px";
7010
+ }
7011
+ };
7012
+ element.addEventListener("mouseenter", handleMouseEnter);
7013
+ element.addEventListener("mouseleave", handleMouseLeave);
7014
+ element.addEventListener("mousemove", handleMouseMove);
7015
+ return () => {
7016
+ element.removeEventListener("mouseenter", handleMouseEnter);
7017
+ element.removeEventListener("mouseleave", handleMouseLeave);
7018
+ element.removeEventListener("mousemove", handleMouseMove);
7019
+ this.hide();
7020
+ };
6607
7021
  }
6608
7022
  };
7023
+
7024
+ // src/thingsboard/main-dashboard-shopping/v-4.0.0/card/head-office/card-head-office.js
7025
+ var LABEL_CHAR_LIMIT = 18;
7026
+ var DEFAUL_DELAY_TIME_CONNECTION_IN_MINS = 1440;
7027
+ var CSS_TAG = "head-office-card-v1";
7028
+ function ensureCss() {
7029
+ if (!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)) {
7030
+ const style = document.createElement("style");
7031
+ style.setAttribute("data-myio-css", CSS_TAG);
7032
+ style.textContent = CSS_STRING;
7033
+ document.head.appendChild(style);
7034
+ }
7035
+ }
7036
+ function normalizeParams(params) {
7037
+ if (!params || !params.entityObject) {
7038
+ throw new Error("renderCardCompenteHeadOffice: entityObject is required");
7039
+ }
7040
+ const LogHelper2 = createLogHelper(params.debugActive ?? false);
7041
+ const entityObject = params.entityObject;
7042
+ if (!entityObject.entityId) {
7043
+ LogHelper2.warn("[CardHeadOffice] entityId is missing, generating temporary ID");
7044
+ entityObject.entityId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
7045
+ }
7046
+ if (!params.delayTimeConnectionInMins) {
7047
+ LogHelper2.warn(
7048
+ `[CardHeadOffice] delayTimeConnectionInMins is missing, defaulting to ${DEFAUL_DELAY_TIME_CONNECTION_IN_MINS} mins`
7049
+ );
7050
+ }
7051
+ return {
7052
+ entityObject,
7053
+ i18n: { ...DEFAULT_I18N, ...params.i18n || {} },
7054
+ enableSelection: Boolean(params.enableSelection),
7055
+ enableDragDrop: Boolean(params.enableDragDrop),
7056
+ useNewComponents: Boolean(params.useNewComponents),
7057
+ // RFC-0091: Configurable delay time for connection status check (default 15 minutes)
7058
+ delayTimeConnectionInMins: params.delayTimeConnectionInMins ?? DEFAUL_DELAY_TIME_CONNECTION_IN_MINS,
7059
+ // Debug options
7060
+ debugActive: params.debugActive ?? false,
7061
+ activeTooltipDebug: params.activeTooltipDebug ?? false,
7062
+ // LogHelper instance for this card
7063
+ LogHelper: LogHelper2,
7064
+ callbacks: {
7065
+ handleActionDashboard: params.handleActionDashboard,
7066
+ handleActionReport: params.handleActionReport,
7067
+ handleActionSettings: params.handleActionSettings,
7068
+ handleSelect: params.handleSelect,
7069
+ handInfo: params.handInfo,
7070
+ handleClickCard: params.handleClickCard
7071
+ }
7072
+ };
7073
+ }
7074
+ function getIconSvg(deviceType, domain) {
7075
+ if (domain === "water") {
7076
+ return Icons.waterDrop;
7077
+ }
7078
+ if (domain === "temperature") {
7079
+ return Icons.thermometer;
7080
+ }
7081
+ return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
7082
+ }
7083
+ function formatPower(valueInWatts) {
7084
+ if (valueInWatts === null || valueInWatts === void 0 || isNaN(valueInWatts)) {
7085
+ return { num: "-", unit: "" };
7086
+ }
7087
+ const val = Number(valueInWatts);
7088
+ if (val >= 1e3) {
7089
+ const kw = Math.ceil(val / 1e3 * 100) / 100;
7090
+ return { num: kw.toFixed(2), unit: "kW" };
7091
+ } else {
7092
+ const w = Math.ceil(val);
7093
+ return { num: w.toString(), unit: "W" };
7094
+ }
7095
+ }
7096
+ function formatValueByDomain(value, domain) {
7097
+ if (domain === "water") {
7098
+ return formatWaterVolumeM3(value);
7099
+ }
7100
+ if (domain === "temperature") {
7101
+ return formatTemperature(value, 0);
7102
+ }
7103
+ return formatEnergy(value);
7104
+ }
7105
+ function formatRelativeTime2(timestamp) {
7106
+ if (!timestamp || isNaN(timestamp)) return "\u2014";
7107
+ const date = new Date(timestamp);
7108
+ const hours = String(date.getHours()).padStart(2, "0");
7109
+ const minutes = String(date.getMinutes()).padStart(2, "0");
7110
+ const day = String(date.getDate()).padStart(2, "0");
7111
+ const month = String(date.getMonth() + 1).padStart(2, "0");
7112
+ const year = date.getFullYear();
7113
+ return `${hours}:${minutes} ${day}/${month}/${year}`;
7114
+ }
7115
+ function calculateConsumptionPercentage(target, consumption) {
7116
+ const numericTarget = Number(target);
7117
+ const numericConsumption = Number(consumption);
7118
+ if (isNaN(numericTarget) || isNaN(numericConsumption) || numericTarget <= 0) {
7119
+ return 0;
7120
+ }
7121
+ const percentage = numericConsumption / numericTarget * 100;
7122
+ return percentage;
7123
+ }
7124
+ function getStatusInfo(deviceStatus, i18n, domain) {
7125
+ switch (deviceStatus) {
7126
+ // --- Novos Status de Temperatura ---
7127
+ case "normal":
7128
+ return { chipClass: "chip--ok", label: "Normal" };
7129
+ case "cold":
7130
+ return { chipClass: "chip--standby", label: "Frio" };
7131
+ case "hot":
7132
+ return { chipClass: "chip--alert", label: "Quente" };
7133
+ // --- Status Existentes (aligned with getCardStateClass) ---
7134
+ case DeviceStatusType.POWER_ON:
7135
+ if (domain === "water") {
7136
+ return { chipClass: "chip--power-on", label: i18n.in_operation_water };
7137
+ }
7138
+ return { chipClass: "chip--power-on", label: i18n.in_operation };
7139
+ case DeviceStatusType.STANDBY:
7140
+ return { chipClass: "chip--standby", label: i18n.standby };
7141
+ case DeviceStatusType.WARNING:
7142
+ return { chipClass: "chip--warning", label: i18n.alert };
7143
+ case DeviceStatusType.MAINTENANCE:
7144
+ return { chipClass: "chip--maintenance", label: i18n.maintenance };
7145
+ case DeviceStatusType.FAILURE:
7146
+ return { chipClass: "chip--failure", label: i18n.failure };
7147
+ case DeviceStatusType.POWER_OFF:
7148
+ return { chipClass: "chip--power-off", label: i18n.power_off || i18n.failure };
7149
+ case DeviceStatusType.OFFLINE:
7150
+ return { chipClass: "chip--offline", label: i18n.offline };
7151
+ case DeviceStatusType.NO_INFO:
7152
+ return { chipClass: "chip--no-info", label: i18n.no_info || i18n.offline };
7153
+ case DeviceStatusType.NOT_INSTALLED:
7154
+ return { chipClass: "chip--not-installed", label: i18n.not_installed };
7155
+ default:
7156
+ return { chipClass: "chip--offline", label: i18n.offline };
7157
+ }
7158
+ }
7159
+ function getCardStateClass(deviceStatus) {
7160
+ switch (deviceStatus) {
7161
+ case DeviceStatusType.POWER_ON:
7162
+ return "is-power-on";
7163
+ // Blue border
7164
+ case DeviceStatusType.STANDBY:
7165
+ return "is-standby";
7166
+ // Green border
7167
+ case DeviceStatusType.WARNING:
7168
+ return "is-warning";
7169
+ // Yellow border
7170
+ case DeviceStatusType.MAINTENANCE:
7171
+ return "is-maintenance";
7172
+ // Yellow border
7173
+ case DeviceStatusType.FAILURE:
7174
+ return "is-failure";
7175
+ // Dark Red border
7176
+ case DeviceStatusType.POWER_OFF:
7177
+ return "is-power-off";
7178
+ // Light Red border
7179
+ case DeviceStatusType.OFFLINE:
7180
+ return "is-offline";
7181
+ // Dark Gray border
7182
+ case DeviceStatusType.NO_INFO:
7183
+ return "is-no-info";
7184
+ // Dark Orange border
7185
+ case DeviceStatusType.NOT_INSTALLED:
7186
+ return "is-not-installed";
7187
+ // Purple border
7188
+ default:
7189
+ return "";
7190
+ }
7191
+ }
7192
+ function getTempRangeClass(entityObject) {
7193
+ if (entityObject.domain !== "temperature") return "";
7194
+ const currentTemp = Number(entityObject.val ?? entityObject.currentTemperature ?? entityObject.temperature) || 0;
7195
+ const tempMin = entityObject.temperatureMin ?? entityObject.minTemperature;
7196
+ const tempMax = entityObject.temperatureMax ?? entityObject.maxTemperature;
7197
+ if (tempMin === void 0 || tempMax === void 0 || tempMin === null || tempMax === null) {
7198
+ return "";
7199
+ }
7200
+ if (currentTemp > tempMax) return "is-temp-hot";
7201
+ if (currentTemp < tempMin) return "is-temp-cold";
7202
+ return "is-temp-ok";
7203
+ }
6609
7204
  function getStatusDotClass(deviceStatus) {
6610
7205
  switch (deviceStatus) {
6611
7206
  // --- Novos Status de Temperatura ---
@@ -7726,6 +8321,7 @@ function renderCardComponentV5({
7726
8321
  };
7727
8322
  const isTankDevice = deviceType === "TANK" || deviceType === "CAIXA_DAGUA";
7728
8323
  const isTermostatoDevice = deviceType?.toUpperCase() === "TERMOSTATO";
8324
+ const isEnergyDeviceFlag = isEnergyDevice(deviceType);
7729
8325
  const percentageForDisplay = isTankDevice ? (waterPercentage || 0) * 100 : perc;
7730
8326
  const calculateTempStatus = () => {
7731
8327
  if (temperatureStatus) return temperatureStatus;
@@ -7742,33 +8338,6 @@ function renderCardComponentV5({
7742
8338
  tempStatus,
7743
8339
  isOffline
7744
8340
  });
7745
- const getTemperatureTooltip = () => {
7746
- if (!isTermostatoDevice) return "";
7747
- const currentTemp = Number(val) || 0;
7748
- const hasRange = temperatureMin !== void 0 && temperatureMax !== void 0 && temperatureMin !== null && temperatureMax !== null;
7749
- if (isOffline) {
7750
- return "Dispositivo offline";
7751
- }
7752
- if (!hasRange) {
7753
- return `Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7754
- Faixa n\xE3o configurada`;
7755
- }
7756
- const rangeText = `Faixa ideal: ${temperatureMin}\xB0C a ${temperatureMax}\xB0C`;
7757
- if (tempStatus === "above") {
7758
- return `ACIMA da faixa ideal
7759
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7760
- ${rangeText}`;
7761
- } else if (tempStatus === "below") {
7762
- return `ABAIXO da faixa ideal
7763
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7764
- ${rangeText}`;
7765
- } else {
7766
- return `DENTRO da faixa ideal
7767
- Temperatura atual: ${currentTemp.toFixed(1)}\xB0C
7768
- ${rangeText}`;
7769
- }
7770
- };
7771
- const temperatureTooltip = getTemperatureTooltip();
7772
8341
  const cardHTML = `
7773
8342
  <div class="device-card-centered clickable ${cardEntity.status === "offline" ? "offline" : ""}"
7774
8343
  data-entity-id="${entityId}"
@@ -7794,7 +8363,7 @@ ${rangeText}`;
7794
8363
  ` : ""}
7795
8364
  </div>
7796
8365
 
7797
- <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" ${isTermostatoDevice && temperatureTooltip ? `title="${temperatureTooltip}"` : ""} style="${isTermostatoDevice ? "cursor: help;" : ""}" />
8366
+ <img class="device-image ${isTermostatoDevice ? "temp-tooltip-trigger" : ""}${isEnergyDeviceFlag ? " energy-tooltip-trigger" : ""}" src="${deviceImageUrl}" alt="${deviceType}" style="${isTermostatoDevice || isEnergyDeviceFlag ? "cursor: help;" : ""}" />
7798
8367
 
7799
8368
 
7800
8369
  ${customerName && String(customerName).trim() !== "" ? `
@@ -8358,6 +8927,33 @@ ${rangeText}`;
8358
8927
  }
8359
8928
  });
8360
8929
  }
8930
+ const tempTooltipTrigger = enhancedCardElement.querySelector(".temp-tooltip-trigger");
8931
+ let tempTooltipCleanup = null;
8932
+ if (tempTooltipTrigger && isTermostatoDevice) {
8933
+ const tooltipEntityData = {
8934
+ val,
8935
+ temperatureMin,
8936
+ temperatureMax,
8937
+ labelOrName: cardEntity.name,
8938
+ name: cardEntity.name
8939
+ };
8940
+ tempTooltipCleanup = TempRangeTooltip.attach(tempTooltipTrigger, tooltipEntityData);
8941
+ }
8942
+ const energyTooltipTrigger = enhancedCardElement.querySelector(".energy-tooltip-trigger");
8943
+ let energyTooltipCleanup = null;
8944
+ if (energyTooltipTrigger && isEnergyDeviceFlag) {
8945
+ const energyTooltipData = {
8946
+ labelOrName: cardEntity.name,
8947
+ name: cardEntity.name,
8948
+ instantaneousPower: entityObject.instantaneousPower ?? entityObject.consumption_power ?? val,
8949
+ powerRanges: entityObject.powerRanges || entityObject.ranges
8950
+ };
8951
+ energyTooltipCleanup = EnergyRangeTooltip.attach(energyTooltipTrigger, energyTooltipData);
8952
+ }
8953
+ container._cleanup = () => {
8954
+ if (tempTooltipCleanup) tempTooltipCleanup();
8955
+ if (energyTooltipCleanup) energyTooltipCleanup();
8956
+ };
8361
8957
  const jQueryLikeObject = {
8362
8958
  get: (index) => index === 0 ? container : void 0,
8363
8959
  0: container,
@@ -11342,7 +11938,7 @@ var chartJsLoaded = false;
11342
11938
  var zoomPluginLoaded = false;
11343
11939
  var jsPdfLoaded = false;
11344
11940
  var _jspdfPromise = null;
11345
- var cssInjected = false;
11941
+ var cssInjected2 = false;
11346
11942
  var STRINGS2 = {
11347
11943
  "pt-BR": {
11348
11944
  title: "Demanda",
@@ -11518,8 +12114,8 @@ function ensureRoom(doc, nextY, minRoom = 40) {
11518
12114
  }
11519
12115
  return nextY;
11520
12116
  }
11521
- function injectCSS(styles) {
11522
- if (cssInjected) return;
12117
+ function injectCSS2(styles) {
12118
+ if (cssInjected2) return;
11523
12119
  const css = `
11524
12120
  .myio-demand-modal-overlay {
11525
12121
  position: fixed;
@@ -11914,7 +12510,7 @@ function injectCSS(styles) {
11914
12510
  const style = document.createElement("style");
11915
12511
  style.textContent = css;
11916
12512
  document.head.appendChild(style);
11917
- cssInjected = true;
12513
+ cssInjected2 = true;
11918
12514
  }
11919
12515
  function formatDate2(date, locale) {
11920
12516
  return date.toLocaleDateString(locale, {
@@ -12096,7 +12692,7 @@ async function openDemandModal(params) {
12096
12692
  const locale = params.locale || "pt-BR";
12097
12693
  const strings = STRINGS2[locale] || STRINGS2["pt-BR"];
12098
12694
  await loadExternalLibraries();
12099
- injectCSS(styles);
12695
+ injectCSS2(styles);
12100
12696
  const container = typeof params.container === "string" ? document.querySelector(params.container) : params.container || document.body;
12101
12697
  if (!container) {
12102
12698
  throw new Error("Container element not found");
@@ -21745,7 +22341,7 @@ async function createInputDateRangePickerInsideDIV(params) {
21745
22341
  placeholder = "Clique para selecionar per\xEDodo",
21746
22342
  pickerOptions = {},
21747
22343
  classNames = {},
21748
- injectStyles = true,
22344
+ injectStyles: injectStyles2 = true,
21749
22345
  showHelper = true
21750
22346
  } = params;
21751
22347
  validateId(containerId, "containerId");
@@ -21754,7 +22350,7 @@ async function createInputDateRangePickerInsideDIV(params) {
21754
22350
  if (!container) {
21755
22351
  throw new Error(`[createInputDateRangePickerInsideDIV] Container '#${containerId}' not found`);
21756
22352
  }
21757
- if (injectStyles) {
22353
+ if (injectStyles2) {
21758
22354
  injectPremiumStyles();
21759
22355
  }
21760
22356
  let inputEl = document.getElementById(inputId);
@@ -21949,7 +22545,7 @@ function openGoalsPanel(params) {
21949
22545
  modal.setAttribute("aria-labelledby", "goals-modal-title");
21950
22546
  modal.innerHTML = generateModalHTML();
21951
22547
  document.body.appendChild(modal);
21952
- injectStyles();
22548
+ injectStyles2();
21953
22549
  attachEventListeners();
21954
22550
  trapFocus(modal);
21955
22551
  renderTabContent();
@@ -22702,7 +23298,7 @@ function openGoalsPanel(params) {
22702
23298
  maximumFractionDigits: 2
22703
23299
  }).format(value);
22704
23300
  }
22705
- function injectStyles() {
23301
+ function injectStyles2() {
22706
23302
  const styleId = "myio-goals-panel-styles";
22707
23303
  if (document.getElementById(styleId)) return;
22708
23304
  const style = document.createElement("style");
@@ -24959,7 +25555,7 @@ var LIGHT_THEME2 = {
24959
25555
  function getColors(theme) {
24960
25556
  return theme === "dark" ? DARK_THEME2 : LIGHT_THEME2;
24961
25557
  }
24962
- async function fetchCustomerAttributes(customerId, token) {
25558
+ async function fetchCustomerAttributes(customerId, token, onError) {
24963
25559
  const url = `/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;
24964
25560
  const response = await fetch(url, {
24965
25561
  method: "GET",
@@ -24972,6 +25568,10 @@ async function fetchCustomerAttributes(customerId, token) {
24972
25568
  if (response.status === 404 || response.status === 400) {
24973
25569
  return { minTemperature: null, maxTemperature: null };
24974
25570
  }
25571
+ if (onError) {
25572
+ onError({ status: response.status, message: `Failed to fetch attributes: ${response.status}` });
25573
+ return { minTemperature: null, maxTemperature: null };
25574
+ }
24975
25575
  throw new Error(`Failed to fetch attributes: ${response.status}`);
24976
25576
  }
24977
25577
  const attributes = await response.json();
@@ -24988,7 +25588,7 @@ async function fetchCustomerAttributes(customerId, token) {
24988
25588
  }
24989
25589
  return { minTemperature, maxTemperature };
24990
25590
  }
24991
- async function saveCustomerAttributes(customerId, token, minTemperature, maxTemperature) {
25591
+ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemperature, onError) {
24992
25592
  const url = `/api/plugins/telemetry/CUSTOMER/${customerId}/SERVER_SCOPE`;
24993
25593
  const attributes = {
24994
25594
  minTemperature,
@@ -25003,6 +25603,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
25003
25603
  body: JSON.stringify(attributes)
25004
25604
  });
25005
25605
  if (!response.ok) {
25606
+ if (onError) {
25607
+ onError({ status: response.status, message: `Failed to save attributes: ${response.status}` });
25608
+ return;
25609
+ }
25006
25610
  throw new Error(`Failed to save attributes: ${response.status}`);
25007
25611
  }
25008
25612
  }
@@ -25419,7 +26023,7 @@ function openTemperatureSettingsModal(params) {
25419
26023
  state.successMessage = null;
25420
26024
  renderModal3(container, state, modalId, destroy, handleSave);
25421
26025
  try {
25422
- await saveCustomerAttributes(state.customerId, state.token, min, max);
26026
+ await saveCustomerAttributes(state.customerId, state.token, min, max, params.onError);
25423
26027
  state.minTemperature = min;
25424
26028
  state.maxTemperature = max;
25425
26029
  state.isSaving = false;
@@ -25436,7 +26040,7 @@ function openTemperatureSettingsModal(params) {
25436
26040
  }
25437
26041
  };
25438
26042
  renderModal3(container, state, modalId, destroy, handleSave);
25439
- fetchCustomerAttributes(state.customerId, state.token).then(({ minTemperature, maxTemperature }) => {
26043
+ fetchCustomerAttributes(state.customerId, state.token, params.onError).then(({ minTemperature, maxTemperature }) => {
25440
26044
  state.minTemperature = minTemperature;
25441
26045
  state.maxTemperature = maxTemperature;
25442
26046
  state.isLoading = false;
@@ -27597,7 +28201,7 @@ function createConsumptionChartWidget(config) {
27597
28201
  </div>
27598
28202
  `;
27599
28203
  }
27600
- function injectStyles() {
28204
+ function injectStyles2() {
27601
28205
  if (styleElement) return;
27602
28206
  styleElement = document.createElement("style");
27603
28207
  styleElement.id = `${widgetId}-styles`;
@@ -28079,7 +28683,7 @@ function createConsumptionChartWidget(config) {
28079
28683
  console.error(`[ConsumptionWidget] Container #${config.containerId} not found`);
28080
28684
  return;
28081
28685
  }
28082
- injectStyles();
28686
+ injectStyles2();
28083
28687
  containerElement.innerHTML = renderHTML();
28084
28688
  setupListeners();
28085
28689
  setLoading(true);
@@ -29219,6 +29823,7 @@ function createDistributionChartWidget(config) {
29219
29823
  EXPORT_DOMAIN_ICONS,
29220
29824
  EXPORT_DOMAIN_LABELS,
29221
29825
  EXPORT_DOMAIN_UNITS,
29826
+ EnergyRangeTooltip,
29222
29827
  MyIOChartModal,
29223
29828
  MyIODraggableCard,
29224
29829
  MyIOSelectionStore,
@@ -29227,6 +29832,7 @@ function createDistributionChartWidget(config) {
29227
29832
  POWER_LIMITS_DEVICE_TYPES,
29228
29833
  POWER_LIMITS_STATUS_CONFIG,
29229
29834
  POWER_LIMITS_TELEMETRY_TYPES,
29835
+ TempRangeTooltip,
29230
29836
  addDetectionContext,
29231
29837
  addNamespace,
29232
29838
  aggregateByDay,