myio-js-library 0.1.237 → 0.1.239

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
@@ -587,8 +587,10 @@ __export(index_exports, {
587
587
  DEFAULT_WATER_GROUP_COLORS: () => DEFAULT_WATER_GROUP_COLORS,
588
588
  DEVICE_COUNT_KEYS: () => DEVICE_COUNT_KEYS,
589
589
  DEVICE_TYPE_DOMAIN: () => DEVICE_TYPE_DOMAIN,
590
+ DeviceCategory: () => DeviceCategory,
590
591
  DeviceComparisonTooltip: () => DeviceComparisonTooltip,
591
592
  DeviceStatusType: () => DeviceStatusType,
593
+ DomainType: () => DomainType,
592
594
  ENERGY_UNITS: () => ENERGY_UNITS,
593
595
  EXPORT_DEFAULT_COLORS: () => EXPORT_DEFAULT_COLORS,
594
596
  EXPORT_DOMAIN_ICONS: () => EXPORT_DOMAIN_ICONS,
@@ -645,6 +647,8 @@ __export(index_exports, {
645
647
  createConsumptionChartWidget: () => createConsumptionChartWidget,
646
648
  createConsumptionModal: () => createConsumptionModal,
647
649
  createDateRangePicker: () => createDateRangePicker2,
650
+ createDeviceItem: () => createDeviceItem,
651
+ createDeviceItemsFromMap: () => createDeviceItemsFromMap,
648
652
  createDistributionChartWidget: () => createDistributionChartWidget,
649
653
  createInputDateRangePickerInsideDIV: () => createInputDateRangePickerInsideDIV,
650
654
  createModalHeader: () => createModalHeader,
@@ -659,6 +663,7 @@ __export(index_exports, {
659
663
  exportToCSV: () => exportToCSV,
660
664
  exportToCSVAll: () => exportToCSVAll,
661
665
  extractMyIOCredentials: () => extractMyIOCredentials,
666
+ extractPowerLimitsForDevice: () => extractPowerLimitsForDevice,
662
667
  fetchCurrentUserInfo: () => fetchCurrentUserInfo,
663
668
  fetchTemperatureData: () => fetchTemperatureData,
664
669
  fetchThingsboardCustomerAttrsFromStorage: () => fetchThingsboardCustomerAttrsFromStorage,
@@ -692,6 +697,7 @@ __export(index_exports, {
692
697
  getDeviceStatusIcon: () => getDeviceStatusIcon,
693
698
  getDeviceStatusInfo: () => getDeviceStatusInfo,
694
699
  getDistributionThemeColors: () => getThemeColors2,
700
+ getDomainFromDeviceType: () => getDomainFromDeviceType,
695
701
  getGroupColor: () => getGroupColor,
696
702
  getHashColor: () => getHashColor,
697
703
  getModalHeaderStyles: () => getModalHeaderStyles,
@@ -707,6 +713,10 @@ __export(index_exports, {
707
713
  handleDeviceType: () => handleDeviceType,
708
714
  interpolateTemperature: () => interpolateTemperature,
709
715
  isDeviceOffline: () => isDeviceOffline,
716
+ isEnergyDevice: () => isEnergyDevice,
717
+ isHydrometerDevice: () => isHydrometerDevice,
718
+ isTankDevice: () => isTankDevice,
719
+ isTemperatureDevice: () => isTemperatureDevice,
710
720
  isValidConnectionStatus: () => isValidConnectionStatus,
711
721
  isValidDeviceStatus: () => isValidDeviceStatus,
712
722
  isWaterCategory: () => isWaterCategory,
@@ -714,6 +724,7 @@ __export(index_exports, {
714
724
  mapDeviceStatusToCardStatus: () => mapDeviceStatusToCardStatus,
715
725
  mapDeviceToConnectionStatus: () => mapDeviceToConnectionStatus,
716
726
  myioExportData: () => myioExportData,
727
+ normalizeConnectionStatus: () => normalizeConnectionStatus,
717
728
  normalizeRecipients: () => normalizeRecipients,
718
729
  numbers: () => numbers_exports,
719
730
  openContractDevicesModal: () => openContractDevicesModal,
@@ -733,6 +744,7 @@ __export(index_exports, {
733
744
  openTemperatureSettingsModal: () => openTemperatureSettingsModal,
734
745
  openUpsellModal: () => openUpsellModal,
735
746
  parseInputDateToDate: () => parseInputDateToDate,
747
+ recalculateDeviceStatus: () => recalculateDeviceStatus,
736
748
  renderCardComponent: () => renderCardComponent,
737
749
  renderCardComponentEnhanced: () => renderCardComponent2,
738
750
  renderCardComponentHeadOffice: () => renderCardComponentHeadOffice,
@@ -742,6 +754,7 @@ __export(index_exports, {
742
754
  renderCardV5: () => renderCardComponentV5,
743
755
  shouldFlashIcon: () => shouldFlashIcon,
744
756
  strings: () => strings_exports,
757
+ temperatureDeviceStatusIcons: () => temperatureDeviceStatusIcons,
745
758
  timeWindowFromInputYMD: () => timeWindowFromInputYMD,
746
759
  toCSV: () => toCSV,
747
760
  toFixedSafe: () => toFixedSafe,
@@ -1503,11 +1516,18 @@ var DeviceStatusType = {
1503
1516
  MAINTENANCE: "maintenance",
1504
1517
  NO_INFO: "no_info",
1505
1518
  NOT_INSTALLED: "not_installed",
1506
- OFFLINE: "offline"
1519
+ OFFLINE: "offline",
1520
+ WEAK_CONNECTION: "weak_connection"
1521
+ // RFC-0109: Device with unstable/bad connection
1507
1522
  };
1508
1523
  var ConnectionStatusType = {
1524
+ ONLINE: "online",
1509
1525
  CONNECTED: "connected",
1510
- OFFLINE: "offline"
1526
+ // alias for online
1527
+ OFFLINE: "offline",
1528
+ WAITING: "waiting",
1529
+ BAD: "bad"
1530
+ // RFC-0109: Weak/unstable connection
1511
1531
  };
1512
1532
  var deviceStatusIcons = {
1513
1533
  [DeviceStatusType.POWER_ON]: "\u26A1",
@@ -1518,7 +1538,9 @@ var deviceStatusIcons = {
1518
1538
  [DeviceStatusType.MAINTENANCE]: "\u{1F6E0}\uFE0F",
1519
1539
  [DeviceStatusType.NO_INFO]: "\u2753\uFE0F",
1520
1540
  [DeviceStatusType.NOT_INSTALLED]: "\u{1F4E6}",
1521
- [DeviceStatusType.OFFLINE]: "\u{1F534}"
1541
+ [DeviceStatusType.OFFLINE]: "\u{1F534}",
1542
+ [DeviceStatusType.WEAK_CONNECTION]: "\u{1F4F6}"
1543
+ // RFC-0109: Weak signal icon
1522
1544
  };
1523
1545
  var waterDeviceStatusIcons = {
1524
1546
  [DeviceStatusType.POWER_ON]: "\u{1F4A7}",
@@ -1529,7 +1551,9 @@ var waterDeviceStatusIcons = {
1529
1551
  [DeviceStatusType.MAINTENANCE]: "\u{1F6E0}\uFE0F",
1530
1552
  [DeviceStatusType.NO_INFO]: "\u2753\uFE0F",
1531
1553
  [DeviceStatusType.NOT_INSTALLED]: "\u{1F4E6}",
1532
- [DeviceStatusType.OFFLINE]: "\u{1F534}"
1554
+ [DeviceStatusType.OFFLINE]: "\u{1F534}",
1555
+ [DeviceStatusType.WEAK_CONNECTION]: "\u{1F4F6}"
1556
+ // RFC-0109: Weak signal icon
1533
1557
  };
1534
1558
  var temperatureDeviceStatusIcons = {
1535
1559
  [DeviceStatusType.POWER_ON]: "\u{1F321}\uFE0F",
@@ -1540,28 +1564,49 @@ var temperatureDeviceStatusIcons = {
1540
1564
  [DeviceStatusType.MAINTENANCE]: "\u{1F6E0}\uFE0F",
1541
1565
  [DeviceStatusType.NO_INFO]: "\u2753\uFE0F",
1542
1566
  [DeviceStatusType.NOT_INSTALLED]: "\u{1F4E6}",
1543
- [DeviceStatusType.OFFLINE]: "\u{1F534}"
1567
+ [DeviceStatusType.OFFLINE]: "\u{1F534}",
1568
+ [DeviceStatusType.WEAK_CONNECTION]: "\u{1F4F6}"
1569
+ // RFC-0109: Weak signal icon
1544
1570
  };
1545
1571
  var connectionStatusIcons = {
1572
+ [ConnectionStatusType.ONLINE]: "\u{1F7E2}",
1546
1573
  [ConnectionStatusType.CONNECTED]: "\u{1F7E2}",
1547
- [ConnectionStatusType.OFFLINE]: "\u{1F6AB}"
1574
+ [ConnectionStatusType.OFFLINE]: "\u{1F6AB}",
1575
+ [ConnectionStatusType.WAITING]: "\u{1F7E1}",
1576
+ [ConnectionStatusType.BAD]: "\u{1F7E0}"
1577
+ // RFC-0109: Weak connection icon (orange)
1548
1578
  };
1549
1579
  function mapDeviceToConnectionStatus(deviceStatus) {
1550
1580
  if (deviceStatus === DeviceStatusType.NO_INFO || deviceStatus === DeviceStatusType.OFFLINE) {
1551
1581
  return ConnectionStatusType.OFFLINE;
1552
1582
  }
1553
- return ConnectionStatusType.CONNECTED;
1583
+ if (deviceStatus === DeviceStatusType.WEAK_CONNECTION) {
1584
+ return ConnectionStatusType.BAD;
1585
+ }
1586
+ if (deviceStatus === DeviceStatusType.NOT_INSTALLED) {
1587
+ return ConnectionStatusType.WAITING;
1588
+ }
1589
+ return ConnectionStatusType.ONLINE;
1554
1590
  }
1555
- function mapConnectionStatus(rawStatus) {
1556
- const statusLower = String(rawStatus || "").toLowerCase().trim();
1557
- if (statusLower === "online" || statusLower === "ok" || statusLower === "running") {
1591
+ function normalizeConnectionStatus(rawStatus) {
1592
+ if (rawStatus === null || rawStatus === void 0 || rawStatus === "") {
1593
+ return "offline";
1594
+ }
1595
+ const statusLower = String(rawStatus).toLowerCase().trim();
1596
+ if (["online", "ok", "running", "true", "connected", "active", "1"].includes(statusLower)) {
1558
1597
  return "online";
1559
1598
  }
1560
- if (statusLower === "waiting" || statusLower === "connecting" || statusLower === "pending") {
1599
+ if (["waiting", "connecting", "pending"].includes(statusLower)) {
1561
1600
  return "waiting";
1562
1601
  }
1602
+ if (["bad", "weak", "unstable", "poor", "degraded"].includes(statusLower)) {
1603
+ return "bad";
1604
+ }
1563
1605
  return "offline";
1564
1606
  }
1607
+ function mapConnectionStatus(rawStatus) {
1608
+ return normalizeConnectionStatus(rawStatus);
1609
+ }
1565
1610
  function mapDeviceStatusToCardStatus(deviceStatus) {
1566
1611
  const statusMap = {
1567
1612
  [DeviceStatusType.POWER_ON]: "ok",
@@ -1572,12 +1617,14 @@ function mapDeviceStatusToCardStatus(deviceStatus) {
1572
1617
  [DeviceStatusType.MAINTENANCE]: "alert",
1573
1618
  [DeviceStatusType.NO_INFO]: "unknown",
1574
1619
  [DeviceStatusType.NOT_INSTALLED]: "not_installed",
1575
- [DeviceStatusType.OFFLINE]: "offline"
1620
+ [DeviceStatusType.OFFLINE]: "offline",
1621
+ [DeviceStatusType.WEAK_CONNECTION]: "weak"
1622
+ // RFC-0109
1576
1623
  };
1577
1624
  return statusMap[deviceStatus] || "unknown";
1578
1625
  }
1579
1626
  function shouldFlashIcon(deviceStatus) {
1580
- return deviceStatus === DeviceStatusType.POWER_OFF || deviceStatus === DeviceStatusType.WARNING || deviceStatus === DeviceStatusType.FAILURE || deviceStatus === DeviceStatusType.MAINTENANCE || deviceStatus === DeviceStatusType.OFFLINE;
1627
+ return deviceStatus === DeviceStatusType.POWER_OFF || deviceStatus === DeviceStatusType.WARNING || deviceStatus === DeviceStatusType.FAILURE || deviceStatus === DeviceStatusType.MAINTENANCE || deviceStatus === DeviceStatusType.OFFLINE || deviceStatus === DeviceStatusType.WEAK_CONNECTION;
1581
1628
  }
1582
1629
  function isDeviceOffline(deviceStatus) {
1583
1630
  return deviceStatus === DeviceStatusType.NO_INFO || deviceStatus === DeviceStatusType.OFFLINE;
@@ -1585,11 +1632,11 @@ function isDeviceOffline(deviceStatus) {
1585
1632
  function getDeviceStatusIcon(deviceStatus, deviceType = null) {
1586
1633
  const normalizedType = deviceType?.toUpperCase() || "";
1587
1634
  const isWaterDevice = normalizedType === "TANK" || normalizedType === "CAIXA_DAGUA" || normalizedType === "HIDROMETRO" || normalizedType === "HIDROMETRO_AREA_COMUM" || normalizedType === "HIDROMETRO_SHOPPING" || normalizedType.startsWith("HIDROMETRO_");
1588
- const isTemperatureDevice = normalizedType === "TERMOSTATO";
1635
+ const isTemperatureDevice2 = normalizedType === "TERMOSTATO";
1589
1636
  let iconMap;
1590
1637
  if (isWaterDevice) {
1591
1638
  iconMap = waterDeviceStatusIcons;
1592
- } else if (isTemperatureDevice) {
1639
+ } else if (isTemperatureDevice2) {
1593
1640
  iconMap = temperatureDeviceStatusIcons;
1594
1641
  } else {
1595
1642
  iconMap = deviceStatusIcons;
@@ -1625,20 +1672,20 @@ function calculateDeviceStatus({
1625
1672
  limitOfPowerOnAlertWatts,
1626
1673
  limitOfPowerOnFailureWatts
1627
1674
  }) {
1628
- const validConnectionStatuses = ["waiting", "offline", "online"];
1629
- if (!validConnectionStatuses.includes(connectionStatus)) {
1630
- return DeviceStatusType.MAINTENANCE;
1631
- }
1632
- if (connectionStatus === "waiting") {
1675
+ const normalizedStatus = normalizeConnectionStatus(connectionStatus);
1676
+ if (normalizedStatus === "waiting") {
1633
1677
  return DeviceStatusType.NOT_INSTALLED;
1634
1678
  }
1635
- if (connectionStatus === "offline") {
1679
+ if (normalizedStatus === "offline") {
1636
1680
  return DeviceStatusType.NO_INFO;
1637
1681
  }
1638
- if (connectionStatus === "online" && (lastConsumptionValue === null || lastConsumptionValue === void 0)) {
1682
+ if (normalizedStatus === "bad") {
1683
+ return DeviceStatusType.WEAK_CONNECTION;
1684
+ }
1685
+ if (normalizedStatus === "online" && (lastConsumptionValue === null || lastConsumptionValue === void 0)) {
1639
1686
  return DeviceStatusType.POWER_ON;
1640
1687
  }
1641
- if (connectionStatus === "online" && lastConsumptionValue !== null && lastConsumptionValue !== void 0) {
1688
+ if (normalizedStatus === "online" && lastConsumptionValue !== null && lastConsumptionValue !== void 0) {
1642
1689
  const consumption = Number(lastConsumptionValue);
1643
1690
  if (isNaN(consumption)) {
1644
1691
  return DeviceStatusType.MAINTENANCE;
@@ -1663,24 +1710,26 @@ function calculateDeviceStatusWithRanges({
1663
1710
  lastConsumptionValue,
1664
1711
  ranges
1665
1712
  }) {
1666
- const validConnectionStatuses = ["waiting", "offline", "online"];
1667
- if (!validConnectionStatuses.includes(connectionStatus)) {
1668
- return DeviceStatusType.MAINTENANCE;
1669
- }
1670
- if (connectionStatus === "waiting") {
1713
+ const normalizedStatus = normalizeConnectionStatus(connectionStatus);
1714
+ if (normalizedStatus === "waiting") {
1671
1715
  return DeviceStatusType.NOT_INSTALLED;
1672
1716
  }
1673
- if (connectionStatus === "offline") {
1717
+ if (normalizedStatus === "offline") {
1674
1718
  return DeviceStatusType.NO_INFO;
1675
1719
  }
1676
- if (connectionStatus === "online" && (lastConsumptionValue === null || lastConsumptionValue === void 0)) {
1720
+ if (normalizedStatus === "bad") {
1721
+ return DeviceStatusType.WEAK_CONNECTION;
1722
+ }
1723
+ if (normalizedStatus === "online" && (lastConsumptionValue === null || lastConsumptionValue === void 0)) {
1677
1724
  return DeviceStatusType.POWER_ON;
1678
1725
  }
1679
1726
  if (!ranges || !ranges.standbyRange || !ranges.normalRange || !ranges.alertRange || !ranges.failureRange) {
1680
- console.error("[RFC-0077] Invalid ranges object:", ranges);
1727
+ if (normalizedStatus === "online") {
1728
+ return DeviceStatusType.POWER_ON;
1729
+ }
1681
1730
  return DeviceStatusType.MAINTENANCE;
1682
1731
  }
1683
- if (connectionStatus === "online" && lastConsumptionValue !== null && lastConsumptionValue !== void 0) {
1732
+ if (normalizedStatus === "online" && lastConsumptionValue !== null && lastConsumptionValue !== void 0) {
1684
1733
  const consumption = Number(lastConsumptionValue);
1685
1734
  if (isNaN(consumption)) {
1686
1735
  return DeviceStatusType.MAINTENANCE;
@@ -1708,6 +1757,264 @@ function calculateDeviceStatusWithRanges({
1708
1757
  return DeviceStatusType.MAINTENANCE;
1709
1758
  }
1710
1759
 
1760
+ // src/utils/deviceItem.js
1761
+ var DomainType = {
1762
+ ENERGY: "energy",
1763
+ WATER: "water",
1764
+ TEMPERATURE: "temperature"
1765
+ };
1766
+ var DeviceCategory = {
1767
+ ENERGY_METER: "3F_MEDIDOR",
1768
+ TANK: "TANK",
1769
+ CAIXA_DAGUA: "CAIXA_DAGUA",
1770
+ HIDROMETRO: "HIDROMETRO",
1771
+ HIDROMETRO_AREA_COMUM: "HIDROMETRO_AREA_COMUM",
1772
+ HIDROMETRO_SHOPPING: "HIDROMETRO_SHOPPING",
1773
+ TERMOSTATO: "TERMOSTATO",
1774
+ SENSOR_TEMP: "SENSOR_TEMP"
1775
+ };
1776
+ function isTankDevice(deviceType) {
1777
+ const dt = String(deviceType || "").toUpperCase();
1778
+ return dt === "TANK" || dt === "CAIXA_DAGUA";
1779
+ }
1780
+ function isHydrometerDevice(deviceType) {
1781
+ const dt = String(deviceType || "").toUpperCase();
1782
+ return dt.startsWith("HIDROMETRO");
1783
+ }
1784
+ function isTemperatureDevice(deviceType) {
1785
+ const dt = String(deviceType || "").toUpperCase();
1786
+ return dt === "TERMOSTATO" || dt === "SENSOR_TEMP" || dt.includes("TEMP");
1787
+ }
1788
+ function isEnergyDevice(deviceType) {
1789
+ const dt = String(deviceType || "").toUpperCase();
1790
+ return dt === "3F_MEDIDOR" || dt.includes("3F") || dt.includes("MEDIDOR");
1791
+ }
1792
+ function getDomainFromDeviceType(deviceType) {
1793
+ if (isTankDevice(deviceType) || isHydrometerDevice(deviceType)) {
1794
+ return DomainType.WATER;
1795
+ }
1796
+ if (isTemperatureDevice(deviceType)) {
1797
+ return DomainType.TEMPERATURE;
1798
+ }
1799
+ return DomainType.ENERGY;
1800
+ }
1801
+ function extractPowerLimitsForDevice(mapInstantaneousPower, deviceType, mode = "consumption") {
1802
+ if (!mapInstantaneousPower || !deviceType) {
1803
+ return null;
1804
+ }
1805
+ let map = mapInstantaneousPower;
1806
+ if (typeof map === "string") {
1807
+ try {
1808
+ map = JSON.parse(map);
1809
+ } catch (e) {
1810
+ return null;
1811
+ }
1812
+ }
1813
+ const dt = String(deviceType).toUpperCase();
1814
+ let limits = map[dt] || map[deviceType];
1815
+ if (!limits) {
1816
+ for (const key of Object.keys(map)) {
1817
+ if (dt.includes(key.toUpperCase()) || key.toUpperCase().includes(dt)) {
1818
+ limits = map[key];
1819
+ break;
1820
+ }
1821
+ }
1822
+ }
1823
+ if (!limits) {
1824
+ return null;
1825
+ }
1826
+ const prefix = mode === "consumption" ? "consumption" : "instantaneous";
1827
+ return {
1828
+ standbyRange: {
1829
+ down: limits[`${prefix}StandbyDown`] ?? limits.standbyDown ?? 0,
1830
+ up: limits[`${prefix}StandbyUp`] ?? limits.standbyUp ?? 150
1831
+ },
1832
+ normalRange: {
1833
+ down: limits[`${prefix}NormalDown`] ?? limits.normalDown ?? 150,
1834
+ up: limits[`${prefix}NormalUp`] ?? limits.normalUp ?? 800
1835
+ },
1836
+ alertRange: {
1837
+ down: limits[`${prefix}AlertDown`] ?? limits.alertDown ?? 800,
1838
+ up: limits[`${prefix}AlertUp`] ?? limits.alertUp ?? 1200
1839
+ },
1840
+ failureRange: {
1841
+ down: limits[`${prefix}FailureDown`] ?? limits.failureDown ?? 1200,
1842
+ up: limits[`${prefix}FailureUp`] ?? limits.failureUp ?? 99999
1843
+ },
1844
+ source: limits.source || "unknown",
1845
+ tier: limits.tier || 3
1846
+ };
1847
+ }
1848
+ function createDeviceItem(entityId, meta, options = {}) {
1849
+ const {
1850
+ domain,
1851
+ labelWidget = null,
1852
+ apiRow = null,
1853
+ globalMapInstantaneousPower = null,
1854
+ temperatureLimits = null
1855
+ } = options;
1856
+ const deviceType = meta.deviceType || meta.deviceProfile || "";
1857
+ const deviceProfile = meta.deviceProfile || deviceType || "";
1858
+ const effectiveDeviceType = deviceProfile || deviceType || null;
1859
+ const identifier = meta.identifier || "";
1860
+ const label = meta.label || identifier || entityId;
1861
+ const _isTankDevice = isTankDevice(deviceType);
1862
+ const _isHidrometerDevice = isHydrometerDevice(deviceType);
1863
+ const _isTemperatureDevice = isTemperatureDevice(deviceType);
1864
+ const _isEnergyDevice = !_isTankDevice && !_isHidrometerDevice && !_isTemperatureDevice;
1865
+ const effectiveDomain = domain || getDomainFromDeviceType(deviceType);
1866
+ const rawConnectionStatus = meta.connectionStatus;
1867
+ const connectionStatus = normalizeConnectionStatus(rawConnectionStatus);
1868
+ let deviceStatus = DeviceStatusType.NO_INFO;
1869
+ if (_isEnergyDevice && effectiveDomain === DomainType.ENERGY) {
1870
+ const deviceMapLimits = meta.deviceMapInstaneousPower ? extractPowerLimitsForDevice(meta.deviceMapInstaneousPower, effectiveDeviceType) : null;
1871
+ const globalLimits = globalMapInstantaneousPower ? extractPowerLimitsForDevice(globalMapInstantaneousPower, effectiveDeviceType) : null;
1872
+ const ranges = deviceMapLimits || globalLimits;
1873
+ const consumptionValue = meta.consumption ?? apiRow?.value ?? null;
1874
+ deviceStatus = calculateDeviceStatusWithRanges({
1875
+ connectionStatus,
1876
+ lastConsumptionValue: consumptionValue,
1877
+ ranges
1878
+ });
1879
+ } else {
1880
+ if (connectionStatus === "offline") {
1881
+ deviceStatus = DeviceStatusType.NO_INFO;
1882
+ } else if (connectionStatus === "bad") {
1883
+ deviceStatus = DeviceStatusType.WEAK_CONNECTION;
1884
+ } else if (connectionStatus === "waiting") {
1885
+ deviceStatus = DeviceStatusType.NOT_INSTALLED;
1886
+ } else {
1887
+ deviceStatus = DeviceStatusType.POWER_ON;
1888
+ }
1889
+ }
1890
+ let temperatureStatus = null;
1891
+ const temperature = meta.temperature ?? null;
1892
+ if (_isTemperatureDevice && temperature !== null && temperatureLimits) {
1893
+ const { min, max } = temperatureLimits;
1894
+ if (min !== null && max !== null) {
1895
+ if (temperature > max) {
1896
+ temperatureStatus = "above";
1897
+ } else if (temperature < min) {
1898
+ temperatureStatus = "below";
1899
+ } else {
1900
+ temperatureStatus = "ok";
1901
+ }
1902
+ }
1903
+ }
1904
+ let logAnnotations = null;
1905
+ if (meta.log_annotations) {
1906
+ if (typeof meta.log_annotations === "string") {
1907
+ try {
1908
+ logAnnotations = JSON.parse(meta.log_annotations);
1909
+ } catch (e) {
1910
+ logAnnotations = null;
1911
+ }
1912
+ } else {
1913
+ logAnnotations = meta.log_annotations;
1914
+ }
1915
+ }
1916
+ let value = 0;
1917
+ let perc = 0;
1918
+ if (_isTankDevice) {
1919
+ value = meta.waterLevel ?? 0;
1920
+ perc = (meta.waterPercentage ?? 0) * 100;
1921
+ } else if (_isTemperatureDevice) {
1922
+ value = temperature ?? 0;
1923
+ } else {
1924
+ value = meta.consumption ?? apiRow?.value ?? 0;
1925
+ }
1926
+ return {
1927
+ // Identifiers
1928
+ id: meta.ingestionId || entityId,
1929
+ tbId: entityId,
1930
+ ingestionId: meta.ingestionId || null,
1931
+ identifier,
1932
+ deviceIdentifier: identifier,
1933
+ // Display names
1934
+ label,
1935
+ entityLabel: label,
1936
+ name: apiRow?.name || label,
1937
+ // Values
1938
+ value,
1939
+ perc,
1940
+ // Device classification
1941
+ deviceType,
1942
+ deviceProfile,
1943
+ effectiveDeviceType,
1944
+ // Status
1945
+ deviceStatus,
1946
+ connectionStatus,
1947
+ // Relationships
1948
+ slaveId: meta.slaveId ?? apiRow?.slaveId ?? null,
1949
+ centralId: meta.centralId ?? apiRow?.centralId ?? null,
1950
+ centralName: meta.centralName ?? null,
1951
+ gatewayId: apiRow?.gatewayId ?? null,
1952
+ customerId: apiRow?.customerId ?? null,
1953
+ assetId: apiRow?.assetId ?? null,
1954
+ assetName: apiRow?.assetName ?? null,
1955
+ customerName: meta.customerName ?? null,
1956
+ // Timestamps
1957
+ lastActivityTime: meta.lastActivityTime ?? null,
1958
+ lastConnectTime: meta.lastConnectTime ?? null,
1959
+ lastDisconnectTime: meta.lastDisconnectTime ?? null,
1960
+ connectionStatusTime: meta.lastConnectTime ?? null,
1961
+ timeVal: meta.lastActivityTime ?? null,
1962
+ // Annotations
1963
+ log_annotations: logAnnotations,
1964
+ // Grouping
1965
+ labelWidget,
1966
+ groupLabel: labelWidget,
1967
+ // Power limits (for Settings modal and recalculation)
1968
+ mapInstantaneousPower: globalMapInstantaneousPower,
1969
+ deviceMapInstaneousPower: meta.deviceMapInstaneousPower ?? null,
1970
+ consumptionPower: meta.consumption ?? null,
1971
+ // Temperature fields
1972
+ temperature: _isTemperatureDevice ? temperature : null,
1973
+ temperatureMin: temperatureLimits?.min ?? null,
1974
+ temperatureMax: temperatureLimits?.max ?? null,
1975
+ temperatureStatus,
1976
+ // Water fields
1977
+ waterLevel: _isTankDevice ? meta.waterLevel ?? null : null,
1978
+ waterPercentage: _isTankDevice ? meta.waterPercentage ?? null : null,
1979
+ // Internal flags
1980
+ _hasMetadata: true,
1981
+ _hasApiData: apiRow !== null,
1982
+ _matchedBy: apiRow ? meta.ingestionId ? "ingestionId" : "name" : null,
1983
+ _isTankDevice,
1984
+ _isHidrometerDevice,
1985
+ _isTemperatureDevice,
1986
+ _isEnergyDevice,
1987
+ // Placeholder for UI state
1988
+ updatedIdentifiers: {}
1989
+ };
1990
+ }
1991
+ function createDeviceItemsFromMap(metadataMap, options = {}) {
1992
+ const items = [];
1993
+ for (const [entityId, meta] of metadataMap.entries()) {
1994
+ items.push(createDeviceItem(entityId, meta, options));
1995
+ }
1996
+ return items;
1997
+ }
1998
+ function recalculateDeviceStatus(item, newData = {}, options = {}) {
1999
+ const updatedMeta = {
2000
+ ...item,
2001
+ ...newData,
2002
+ connectionStatus: newData.connectionStatus ?? item.connectionStatus,
2003
+ consumption: newData.value ?? newData.consumption ?? item.consumptionPower ?? item.value,
2004
+ deviceMapInstaneousPower: newData.deviceMapInstaneousPower ?? item.deviceMapInstaneousPower
2005
+ };
2006
+ const updatedOptions = {
2007
+ domain: options.domain ?? getDomainFromDeviceType(item.deviceType),
2008
+ labelWidget: item.labelWidget,
2009
+ globalMapInstantaneousPower: options.globalMapInstantaneousPower ?? item.mapInstantaneousPower,
2010
+ temperatureLimits: options.temperatureLimits ?? {
2011
+ min: item.temperatureMin,
2012
+ max: item.temperatureMax
2013
+ }
2014
+ };
2015
+ return createDeviceItem(item.tbId, updatedMeta, updatedOptions);
2016
+ }
2017
+
1711
2018
  // src/thingsboard/utils/buildListItemsThingsboardByUniqueDatasource.ts
1712
2019
  function normalizeAttrKey(raw) {
1713
2020
  const k = String(raw || "").trim();
@@ -4280,14 +4587,14 @@ function renderCardComponentV2({
4280
4587
  const normalizedType = deviceType2?.toUpperCase() || "";
4281
4588
  return typeMap[normalizedType] || "ENERGY";
4282
4589
  };
4283
- const isEnergyDevice = (deviceType2) => {
4590
+ const isEnergyDevice2 = (deviceType2) => {
4284
4591
  const energyDeviceTypes = ["COMPRESSOR", "VENTILADOR", "ESCADA_ROLANTE", "ELEVADOR", "MOTOR", "3F_MEDIDOR", "RELOGIO", "ENTRADA", "SUBESTACAO"];
4285
4592
  const normalizedType = deviceType2?.toUpperCase() || "";
4286
4593
  return energyDeviceTypes.includes(normalizedType);
4287
4594
  };
4288
4595
  const formatCardValue = (value, deviceType2) => {
4289
4596
  const numValue = Number(value) || 0;
4290
- if (isEnergyDevice(deviceType2)) {
4597
+ if (isEnergyDevice2(deviceType2)) {
4291
4598
  return formatEnergy(numValue);
4292
4599
  } else {
4293
4600
  const unit = determineUnit(deviceType2);
@@ -12155,17 +12462,17 @@ function renderCardComponentV5({
12155
12462
  if (category === "tank") return "TANK";
12156
12463
  return category.toUpperCase();
12157
12464
  };
12158
- const isEnergyDevice = (deviceType2) => isEnergyDeviceType(deviceType2);
12159
- const isTemperatureDevice = (deviceType2) => isTemperatureDeviceType(deviceType2);
12465
+ const isEnergyDevice2 = (deviceType2) => isEnergyDeviceType(deviceType2);
12466
+ const isTemperatureDevice2 = (deviceType2) => isTemperatureDeviceType(deviceType2);
12160
12467
  const formatCardValue = (value, deviceType2) => {
12161
12468
  const numValue = Number(value) || 0;
12162
12469
  const category = getDeviceCategory(deviceType2);
12163
- if (isEnergyDevice(deviceType2)) {
12470
+ if (isEnergyDevice2(deviceType2)) {
12164
12471
  if (window.MyIOUtils?.formatEnergyWithSettings) {
12165
12472
  return window.MyIOUtils.formatEnergyWithSettings(numValue);
12166
12473
  }
12167
12474
  return formatEnergy(numValue);
12168
- } else if (isTemperatureDevice(deviceType2)) {
12475
+ } else if (isTemperatureDevice2(deviceType2)) {
12169
12476
  if (window.MyIOUtils?.formatTemperatureWithSettings) {
12170
12477
  return window.MyIOUtils.formatTemperatureWithSettings(numValue);
12171
12478
  }
@@ -12431,10 +12738,10 @@ function renderCardComponentV5({
12431
12738
  }
12432
12739
  return getStaticDeviceImage(nameType);
12433
12740
  };
12434
- const isTankDevice = deviceType === "TANK" || deviceType === "CAIXA_DAGUA";
12741
+ const isTankDevice2 = deviceType === "TANK" || deviceType === "CAIXA_DAGUA";
12435
12742
  const isTermostatoDevice = deviceType?.toUpperCase() === "TERMOSTATO";
12436
- const isEnergyDeviceFlag = isEnergyDevice(deviceType);
12437
- const percentageForDisplay = isTankDevice ? (waterPercentage || 0) * 100 : perc;
12743
+ const isEnergyDeviceFlag = isEnergyDevice2(deviceType);
12744
+ const percentageForDisplay = isTankDevice2 ? (waterPercentage || 0) * 100 : perc;
12438
12745
  const calculateTempStatus2 = () => {
12439
12746
  if (temperatureStatus) return temperatureStatus;
12440
12747
  const currentTemp = Number(val) || 0;
@@ -46025,11 +46332,11 @@ function renderDeviceRow(device, state6, modalId, colors) {
46025
46332
  const deviceName = (device.name || device.label || "").toLowerCase();
46026
46333
  const attrDeviceType = (attrs.deviceType || "").toUpperCase();
46027
46334
  const isWaterDevice = ["HIDROMETRO", "CAIXA_DAGUA", "TANK"].includes(attrDeviceType) || deviceName.includes("hidr") || deviceName.includes("agua") || deviceName.includes("water") || deviceType.includes("water");
46028
- const isEnergyDevice = ["3F_MEDIDOR", "ENTRADA", "MOTOR", "CHILLER", "FANCOIL", "COMPRESSOR", "ESCADA_ROLANTE", "ELEVADOR"].includes(attrDeviceType) || deviceType.includes("energy") || deviceType.includes("meter");
46029
- const isTemperatureDevice = deviceName.includes("temp") || deviceType.includes("temp") || deviceName.includes("sensor") && !isWaterDevice && !isEnergyDevice;
46335
+ const isEnergyDevice2 = ["3F_MEDIDOR", "ENTRADA", "MOTOR", "CHILLER", "FANCOIL", "COMPRESSOR", "ESCADA_ROLANTE", "ELEVADOR"].includes(attrDeviceType) || deviceType.includes("energy") || deviceType.includes("meter");
46336
+ const isTemperatureDevice2 = deviceName.includes("temp") || deviceType.includes("temp") || deviceName.includes("sensor") && !isWaterDevice && !isEnergyDevice2;
46030
46337
  if (state6.deviceTelemetryLoaded) {
46031
46338
  if (telemetry.consumption && telemetry.consumption.ts && telemetry.consumption.value != null) {
46032
- if (isEnergyDevice || !isWaterDevice && !isTemperatureDevice) {
46339
+ if (isEnergyDevice2 || !isWaterDevice && !isTemperatureDevice2) {
46033
46340
  telemetryItems.push({
46034
46341
  label: "consumption",
46035
46342
  value: telemetry.consumption.value.toFixed(1),
@@ -46039,7 +46346,7 @@ function renderDeviceRow(device, state6, modalId, colors) {
46039
46346
  }
46040
46347
  }
46041
46348
  if (telemetry.pulses && telemetry.pulses.ts && telemetry.pulses.value != null) {
46042
- if (isWaterDevice || !isEnergyDevice && !isTemperatureDevice) {
46349
+ if (isWaterDevice || !isEnergyDevice2 && !isTemperatureDevice2) {
46043
46350
  telemetryItems.push({
46044
46351
  label: "pulses",
46045
46352
  value: String(telemetry.pulses.value),
@@ -46049,9 +46356,9 @@ function renderDeviceRow(device, state6, modalId, colors) {
46049
46356
  }
46050
46357
  }
46051
46358
  if (telemetry.temperature && telemetry.temperature.ts && telemetry.temperature.value != null) {
46052
- if (isTemperatureDevice || !isWaterDevice && !isEnergyDevice) {
46359
+ if (isTemperatureDevice2 || !isWaterDevice && !isEnergyDevice2) {
46053
46360
  const tempValue = telemetry.temperature.value;
46054
- const isReasonableTemp = tempValue !== 0 || isTemperatureDevice;
46361
+ const isReasonableTemp = tempValue !== 0 || isTemperatureDevice2;
46055
46362
  if (isReasonableTemp) {
46056
46363
  telemetryItems.push({
46057
46364
  label: "temperature",
@@ -46711,10 +47018,11 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
46711
47018
  document.addEventListener("keydown", escHandler);
46712
47019
  document.getElementById(`${modalId}-load-attrs`)?.addEventListener("click", async () => {
46713
47020
  if (state6.attrsLoading) return;
47021
+ console.log("[UpsellModal] Load attrs clicked - mode:", state6.deviceSelectionMode, "selected:", state6.selectedDevices.length);
46714
47022
  let devicesToLoad;
46715
47023
  if (state6.deviceSelectionMode === "multi" && state6.selectedDevices.length > 0) {
46716
- devicesToLoad = state6.selectedDevices;
46717
- console.log("[UpsellModal] Loading attrs for selected devices:", devicesToLoad.length);
47024
+ devicesToLoad = [...state6.selectedDevices];
47025
+ console.log("[UpsellModal] Loading attrs for SELECTED devices only:", devicesToLoad.length);
46718
47026
  } else {
46719
47027
  const { types: filterTypes, deviceTypes: filterDeviceTypes, deviceProfiles: filterDeviceProfiles } = state6.deviceFilters;
46720
47028
  const searchTerm = state6.deviceSearchTerm.toLowerCase();
@@ -46749,10 +47057,11 @@ function setupEventListeners3(container, state6, modalId, t, onClose) {
46749
47057
  });
46750
47058
  document.getElementById(`${modalId}-load-telemetry`)?.addEventListener("click", async () => {
46751
47059
  if (state6.telemetryLoading) return;
47060
+ console.log("[UpsellModal] Load telemetry clicked - mode:", state6.deviceSelectionMode, "selected:", state6.selectedDevices.length);
46752
47061
  let devicesToLoad;
46753
47062
  if (state6.deviceSelectionMode === "multi" && state6.selectedDevices.length > 0) {
46754
- devicesToLoad = state6.selectedDevices;
46755
- console.log("[UpsellModal] Loading telemetry for selected devices:", devicesToLoad.length);
47063
+ devicesToLoad = [...state6.selectedDevices];
47064
+ console.log("[UpsellModal] Loading telemetry for SELECTED devices only:", devicesToLoad.length);
46756
47065
  } else {
46757
47066
  const { types: filterTypes, deviceTypes: filterDeviceTypes, deviceProfiles: filterDeviceProfiles } = state6.deviceFilters;
46758
47067
  const searchTerm = state6.deviceSearchTerm.toLowerCase();
@@ -47710,8 +48019,10 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47710
48019
  DEFAULT_WATER_GROUP_COLORS,
47711
48020
  DEVICE_COUNT_KEYS,
47712
48021
  DEVICE_TYPE_DOMAIN,
48022
+ DeviceCategory,
47713
48023
  DeviceComparisonTooltip,
47714
48024
  DeviceStatusType,
48025
+ DomainType,
47715
48026
  ENERGY_UNITS,
47716
48027
  EXPORT_DEFAULT_COLORS,
47717
48028
  EXPORT_DOMAIN_ICONS,
@@ -47768,6 +48079,8 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47768
48079
  createConsumptionChartWidget,
47769
48080
  createConsumptionModal,
47770
48081
  createDateRangePicker,
48082
+ createDeviceItem,
48083
+ createDeviceItemsFromMap,
47771
48084
  createDistributionChartWidget,
47772
48085
  createInputDateRangePickerInsideDIV,
47773
48086
  createModalHeader,
@@ -47782,6 +48095,7 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47782
48095
  exportToCSV,
47783
48096
  exportToCSVAll,
47784
48097
  extractMyIOCredentials,
48098
+ extractPowerLimitsForDevice,
47785
48099
  fetchCurrentUserInfo,
47786
48100
  fetchTemperatureData,
47787
48101
  fetchThingsboardCustomerAttrsFromStorage,
@@ -47815,6 +48129,7 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47815
48129
  getDeviceStatusIcon,
47816
48130
  getDeviceStatusInfo,
47817
48131
  getDistributionThemeColors,
48132
+ getDomainFromDeviceType,
47818
48133
  getGroupColor,
47819
48134
  getHashColor,
47820
48135
  getModalHeaderStyles,
@@ -47830,6 +48145,10 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47830
48145
  handleDeviceType,
47831
48146
  interpolateTemperature,
47832
48147
  isDeviceOffline,
48148
+ isEnergyDevice,
48149
+ isHydrometerDevice,
48150
+ isTankDevice,
48151
+ isTemperatureDevice,
47833
48152
  isValidConnectionStatus,
47834
48153
  isValidDeviceStatus,
47835
48154
  isWaterCategory,
@@ -47837,6 +48156,7 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47837
48156
  mapDeviceStatusToCardStatus,
47838
48157
  mapDeviceToConnectionStatus,
47839
48158
  myioExportData,
48159
+ normalizeConnectionStatus,
47840
48160
  normalizeRecipients,
47841
48161
  numbers,
47842
48162
  openContractDevicesModal,
@@ -47856,6 +48176,7 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47856
48176
  openTemperatureSettingsModal,
47857
48177
  openUpsellModal,
47858
48178
  parseInputDateToDate,
48179
+ recalculateDeviceStatus,
47859
48180
  renderCardComponent,
47860
48181
  renderCardComponentEnhanced,
47861
48182
  renderCardComponentHeadOffice,
@@ -47865,6 +48186,7 @@ ${errors.slice(0, 5).join("\n")}` + (errors.length > 5 ? `
47865
48186
  renderCardV5,
47866
48187
  shouldFlashIcon,
47867
48188
  strings,
48189
+ temperatureDeviceStatusIcons,
47868
48190
  timeWindowFromInputYMD,
47869
48191
  toCSV,
47870
48192
  toFixedSafe,