myio-js-library 0.1.321 → 0.1.324

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
@@ -680,6 +680,7 @@ __export(index_exports, {
680
680
  createDeviceItem: () => createDeviceItem,
681
681
  createDeviceItemsFromMap: () => createDeviceItemsFromMap,
682
682
  createDistributionChartWidget: () => createDistributionChartWidget,
683
+ createEnergyPanelComponent: () => createEnergyPanelComponent,
683
684
  createFilterModalComponent: () => createFilterModalComponent,
684
685
  createFooterComponent: () => createFooterComponent,
685
686
  createHeaderComponent: () => createHeaderComponent,
@@ -689,6 +690,7 @@ __export(index_exports, {
689
690
  createMenuComponent: () => createMenuComponent,
690
691
  createModalHeader: () => createModalHeader,
691
692
  createTelemetryGridComponent: () => createTelemetryGridComponent,
693
+ createWaterPanelComponent: () => createWaterPanelComponent,
692
694
  decodePayload: () => decodePayload,
693
695
  decodePayloadBase64Xor: () => decodePayloadBase64Xor,
694
696
  detectContext: () => detectContext,
@@ -1775,8 +1777,8 @@ function normalizeConnectionStatus(rawStatus) {
1775
1777
  }
1776
1778
  return "offline";
1777
1779
  }
1778
- function isTelemetryStale(telemetryTimestamp, lastActivityTime = null, delayMins = 1440) {
1779
- const timestamp = telemetryTimestamp && telemetryTimestamp > 0 ? telemetryTimestamp : lastActivityTime && lastActivityTime > 0 ? lastActivityTime : null;
1780
+ function isTelemetryStale(telemetryTimestamp, delayMins) {
1781
+ const timestamp = telemetryTimestamp && telemetryTimestamp > 0 ? telemetryTimestamp : null;
1780
1782
  if (!timestamp) {
1781
1783
  return false;
1782
1784
  }
@@ -1786,12 +1788,8 @@ function isTelemetryStale(telemetryTimestamp, lastActivityTime = null, delayMins
1786
1788
  const timeSinceUpdate = now.getTime() - lastUpdate.getTime();
1787
1789
  return timeSinceUpdate > delayMs;
1788
1790
  }
1789
- function isConnectionStale({
1790
- connectionStatusTs = null,
1791
- lastActivityTime = null,
1792
- delayTimeConnectionInMins = 1440
1793
- } = {}) {
1794
- return isTelemetryStale(connectionStatusTs, lastActivityTime, delayTimeConnectionInMins);
1791
+ function isConnectionStale({ connectionStatusTs = null, delayTimeConnectionInMins } = {}) {
1792
+ return isTelemetryStale(connectionStatusTs, delayTimeConnectionInMins);
1795
1793
  }
1796
1794
  function mapDeviceStatusToCardStatus(deviceStatus) {
1797
1795
  const statusMap = {
@@ -1857,41 +1855,33 @@ function calculateDeviceStatus({
1857
1855
  domain = "energy",
1858
1856
  telemetryValue = null,
1859
1857
  telemetryTimestamp = null,
1860
- // v3: MUST be domain-specific (consumptionTs, pulsesTs, etc.)
1861
- lastActivityTime = null,
1862
- // v3: NOT USED as fallback anymore
1858
+ // MUST be domain-specific (consumptionTs, pulsesTs, etc.)
1863
1859
  ranges = null,
1864
1860
  // RFC-0110 v2: Dual threshold configuration
1865
- delayTimeConnectionInMins = 1440,
1866
- // For 'online' status (24h default)
1867
- shortDelayMins = 60,
1868
- // For 'offline'/'bad' status (60 mins default)
1861
+ delayTimeConnectionInMins,
1862
+ // For 'online' status
1863
+ shortDelayMins,
1864
+ // For 'offline'/'bad' status
1869
1865
  // Legacy parameters (backward compatibility)
1870
1866
  lastConsumptionValue = null,
1871
1867
  limitOfPowerOnStandByWatts = null,
1872
1868
  limitOfPowerOnAlertWatts = null,
1873
- limitOfPowerOnFailureWatts = null,
1874
- // @deprecated - kept for backward compatibility
1875
- lastConnectTime = null,
1876
- lastDisconnectTime = null
1869
+ limitOfPowerOnFailureWatts = null
1877
1870
  }) {
1878
1871
  const normalizedStatus = normalizeConnectionStatus(connectionStatus);
1879
1872
  if (normalizedStatus === "waiting") {
1880
1873
  return DeviceStatusType.NOT_INSTALLED;
1881
1874
  }
1882
1875
  const hasTelemetryTs = telemetryTimestamp !== null && telemetryTimestamp !== void 0 && telemetryTimestamp > 0;
1883
- const hasLastActivityTime = lastActivityTime !== null && lastActivityTime !== void 0 && lastActivityTime > 0;
1884
- const effectiveTimestampForRecovery = hasTelemetryTs ? telemetryTimestamp : hasLastActivityTime ? lastActivityTime : null;
1885
- const hasEffectiveTimestampForRecovery = effectiveTimestampForRecovery !== null;
1886
1876
  if (normalizedStatus === "bad") {
1887
- const hasRecentTelemetry = hasEffectiveTimestampForRecovery && !isTelemetryStale(effectiveTimestampForRecovery, null, shortDelayMins);
1877
+ const hasRecentTelemetry = hasTelemetryTs && !isTelemetryStale(telemetryTimestamp, shortDelayMins);
1888
1878
  if (hasRecentTelemetry) {
1889
1879
  } else {
1890
1880
  return DeviceStatusType.WEAK_CONNECTION;
1891
1881
  }
1892
1882
  }
1893
1883
  if (normalizedStatus === "offline") {
1894
- const hasRecentTelemetry = hasEffectiveTimestampForRecovery && !isTelemetryStale(effectiveTimestampForRecovery, null, shortDelayMins);
1884
+ const hasRecentTelemetry = hasTelemetryTs && !isTelemetryStale(telemetryTimestamp, shortDelayMins);
1895
1885
  if (!hasRecentTelemetry) {
1896
1886
  return DeviceStatusType.OFFLINE;
1897
1887
  }
@@ -1900,7 +1890,7 @@ function calculateDeviceStatus({
1900
1890
  if (!hasTelemetryTs) {
1901
1891
  return DeviceStatusType.OFFLINE;
1902
1892
  }
1903
- const telemetryStale = isTelemetryStale(telemetryTimestamp, null, delayTimeConnectionInMins);
1893
+ const telemetryStale = isTelemetryStale(telemetryTimestamp, delayTimeConnectionInMins);
1904
1894
  if (telemetryStale) {
1905
1895
  return DeviceStatusType.OFFLINE;
1906
1896
  }
@@ -1953,31 +1943,19 @@ function calculateDeviceStatusWithRanges({
1953
1943
  ranges,
1954
1944
  // RFC-0110: New parameters
1955
1945
  telemetryTimestamp = null,
1956
- lastActivityTime = null,
1957
- delayTimeConnectionInMins = 1440,
1958
- // @deprecated - kept for backward compatibility
1959
- lastConnectTime = null,
1960
- lastDisconnectTime = null
1946
+ delayTimeConnectionInMins
1961
1947
  }) {
1962
1948
  return calculateDeviceStatus({
1963
1949
  connectionStatus,
1964
1950
  domain: "energy",
1965
1951
  telemetryValue: lastConsumptionValue,
1966
- telemetryTimestamp: telemetryTimestamp || lastConnectTime || lastDisconnectTime,
1967
- lastActivityTime,
1952
+ telemetryTimestamp,
1968
1953
  ranges,
1969
1954
  delayTimeConnectionInMins
1970
1955
  });
1971
1956
  }
1972
1957
  function calculateDeviceStatusMasterRules(options = {}) {
1973
- const {
1974
- connectionStatus = "",
1975
- telemetryTimestamp = null,
1976
- delayMins = 1440,
1977
- // 24h for online status
1978
- domain = "energy"
1979
- } = options;
1980
- const SHORT_DELAY_MINS = 60;
1958
+ const { connectionStatus, telemetryTimestamp = null, delayMins, domain, SHORT_DELAY_MINS } = options;
1981
1959
  const now = Date.now();
1982
1960
  const normalizedStatus = (connectionStatus || "").toLowerCase().trim();
1983
1961
  if (normalizedStatus === "waiting" || normalizedStatus === "connecting" || normalizedStatus === "pending") {
@@ -2132,9 +2110,6 @@ function createDeviceItem(entityId, meta, options = {}) {
2132
2110
  domain: "energy",
2133
2111
  telemetryValue: consumptionValue,
2134
2112
  telemetryTimestamp: telemetryTs,
2135
- // v3: MUST be consumptionTs, NOT lastActivityTime
2136
- lastActivityTime: null,
2137
- // v3: NOT used as fallback
2138
2113
  ranges,
2139
2114
  delayTimeConnectionInMins
2140
2115
  });
@@ -2145,8 +2120,8 @@ function createDeviceItem(entityId, meta, options = {}) {
2145
2120
  deviceStatus = DeviceStatusType.OFFLINE;
2146
2121
  } else {
2147
2122
  const SHORT_DELAY_MINS = 60;
2148
- const hasRecentTelemetry = !isTelemetryStale(telemetryTs, null, SHORT_DELAY_MINS);
2149
- const staleTelemetryLong = isTelemetryStale(telemetryTs, null, delayTimeConnectionInMins);
2123
+ const hasRecentTelemetry = !isTelemetryStale(telemetryTs, SHORT_DELAY_MINS);
2124
+ const staleTelemetryLong = isTelemetryStale(telemetryTs, delayTimeConnectionInMins);
2150
2125
  if (connectionStatus === "bad") {
2151
2126
  deviceStatus = hasRecentTelemetry ? DeviceStatusType.POWER_ON : DeviceStatusType.WEAK_CONNECTION;
2152
2127
  } else if (connectionStatus === "offline") {
@@ -50833,7 +50808,22 @@ function createConsumption7DaysChart(config) {
50833
50808
  "#65a30d",
50834
50809
  "#d97706",
50835
50810
  "#be185d",
50836
- "#0d9488"
50811
+ "#0d9488",
50812
+ "#7c3aed",
50813
+ "#059669",
50814
+ "#db2777",
50815
+ "#9333ea",
50816
+ "#0284c7",
50817
+ "#ca8a04",
50818
+ "#4f46e5",
50819
+ "#c026d3",
50820
+ "#14b8a6",
50821
+ "#f97316",
50822
+ "#6366f1",
50823
+ "#84cc16",
50824
+ "#ec4899",
50825
+ "#06b6d4",
50826
+ "#a855f7"
50837
50827
  ];
50838
50828
  datasets = Object.entries(data.shoppingData).map(([shoppingId, values], index) => ({
50839
50829
  label: data.shoppingNames?.[shoppingId] || shoppingId,
@@ -66578,6 +66568,1629 @@ var CustomerCardV1 = class {
66578
66568
  function createCustomerCardV1(params) {
66579
66569
  return new CustomerCardV1(params);
66580
66570
  }
66571
+
66572
+ // src/components/energy-panel/EnergyPanelController.ts
66573
+ var EnergyPanelController = class {
66574
+ state;
66575
+ params;
66576
+ onStateChange = null;
66577
+ constructor(params) {
66578
+ this.params = params;
66579
+ this.state = {
66580
+ theme: params.theme || "light",
66581
+ period: params.period || 7,
66582
+ vizMode: params.vizMode || "total",
66583
+ chartType: params.chartType || "line",
66584
+ selectedShoppingIds: params.selectedShoppingIds || [],
66585
+ summary: params.initialSummary || null,
66586
+ isLoading: false,
66587
+ error: null
66588
+ };
66589
+ }
66590
+ setOnStateChange(callback) {
66591
+ this.onStateChange = callback;
66592
+ }
66593
+ notifyStateChange() {
66594
+ if (this.onStateChange) {
66595
+ this.onStateChange({ ...this.state });
66596
+ }
66597
+ }
66598
+ getState() {
66599
+ return { ...this.state };
66600
+ }
66601
+ getSummary() {
66602
+ return this.state.summary ? { ...this.state.summary } : null;
66603
+ }
66604
+ updateSummary(data) {
66605
+ this.state.summary = { ...data };
66606
+ this.notifyStateChange();
66607
+ }
66608
+ setTheme(mode) {
66609
+ if (this.state.theme !== mode) {
66610
+ this.state.theme = mode;
66611
+ this.notifyStateChange();
66612
+ }
66613
+ }
66614
+ setPeriod(days) {
66615
+ if (this.state.period !== days) {
66616
+ this.state.period = days;
66617
+ this.params.onPeriodChange?.(days);
66618
+ this.notifyStateChange();
66619
+ }
66620
+ }
66621
+ setVizMode(mode) {
66622
+ if (this.state.vizMode !== mode) {
66623
+ this.state.vizMode = mode;
66624
+ this.params.onVizModeChange?.(mode);
66625
+ this.notifyStateChange();
66626
+ }
66627
+ }
66628
+ setChartType(type) {
66629
+ if (this.state.chartType !== type) {
66630
+ this.state.chartType = type;
66631
+ this.notifyStateChange();
66632
+ }
66633
+ }
66634
+ applyShoppingFilter(ids) {
66635
+ this.state.selectedShoppingIds = [...ids];
66636
+ this.params.onFilterChange?.(ids);
66637
+ this.notifyStateChange();
66638
+ }
66639
+ clearFilters() {
66640
+ this.state.selectedShoppingIds = [];
66641
+ this.params.onFilterChange?.([]);
66642
+ this.notifyStateChange();
66643
+ }
66644
+ setLoading(loading) {
66645
+ this.state.isLoading = loading;
66646
+ this.notifyStateChange();
66647
+ }
66648
+ setError(error) {
66649
+ this.state.error = error;
66650
+ this.notifyStateChange();
66651
+ }
66652
+ };
66653
+
66654
+ // src/components/energy-panel/styles.ts
66655
+ var ENERGY_PANEL_STYLES = `
66656
+ .energy-panel-wrap {
66657
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
66658
+ color: #1e293b;
66659
+ background-color: #f8fafc;
66660
+ padding: 16px;
66661
+ border-radius: 12px;
66662
+ }
66663
+
66664
+ .energy-panel-wrap[data-theme="dark"] {
66665
+ color: #e2e8f0;
66666
+ background-color: #1e293b;
66667
+ }
66668
+
66669
+ .energy-panel__cards {
66670
+ display: grid;
66671
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
66672
+ gap: 16px;
66673
+ margin-bottom: 20px;
66674
+ }
66675
+
66676
+ .energy-panel__card {
66677
+ background-color: #ffffff;
66678
+ padding: 16px;
66679
+ border-radius: 12px;
66680
+ display: flex;
66681
+ align-items: center;
66682
+ border: 1px solid #e2e8f0;
66683
+ transition: box-shadow 0.2s ease;
66684
+ }
66685
+
66686
+ .energy-panel__card:hover {
66687
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
66688
+ }
66689
+
66690
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__card {
66691
+ background-color: #334155;
66692
+ border-color: #475569;
66693
+ }
66694
+
66695
+ .energy-panel__card-icon {
66696
+ font-size: 2.5em;
66697
+ margin-right: 16px;
66698
+ }
66699
+
66700
+ .energy-panel__card-label {
66701
+ font-size: 13px;
66702
+ color: #64748b;
66703
+ font-weight: 500;
66704
+ }
66705
+
66706
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__card-label {
66707
+ color: #94a3b8;
66708
+ }
66709
+
66710
+ .energy-panel__card-value {
66711
+ font-size: 1.6em;
66712
+ font-weight: 700;
66713
+ color: #2563eb;
66714
+ }
66715
+
66716
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__card-value {
66717
+ color: #60a5fa;
66718
+ }
66719
+
66720
+ .energy-panel__card-count {
66721
+ font-size: 12px;
66722
+ color: #94a3b8;
66723
+ }
66724
+
66725
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__card-count {
66726
+ color: #64748b;
66727
+ }
66728
+
66729
+ .energy-panel__chart-section {
66730
+ background-color: #ffffff;
66731
+ padding: 16px;
66732
+ border-radius: 12px;
66733
+ border: 1px solid #e2e8f0;
66734
+ margin-bottom: 20px;
66735
+ }
66736
+
66737
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__chart-section {
66738
+ background-color: #334155;
66739
+ border-color: #475569;
66740
+ }
66741
+
66742
+ .energy-panel__chart-header {
66743
+ display: flex;
66744
+ justify-content: space-between;
66745
+ align-items: center;
66746
+ flex-wrap: wrap;
66747
+ gap: 12px;
66748
+ margin-bottom: 16px;
66749
+ padding-bottom: 12px;
66750
+ border-bottom: 1px solid #e2e8f0;
66751
+ }
66752
+
66753
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__chart-header {
66754
+ border-bottom-color: #475569;
66755
+ }
66756
+
66757
+ .energy-panel__chart-title-group {
66758
+ display: flex;
66759
+ align-items: center;
66760
+ gap: 12px;
66761
+ flex-wrap: wrap;
66762
+ }
66763
+
66764
+ .energy-panel__chart-header h3 {
66765
+ margin: 0;
66766
+ font-size: 15px;
66767
+ font-weight: 600;
66768
+ color: #1e293b;
66769
+ }
66770
+
66771
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__chart-header h3 {
66772
+ color: #f1f5f9;
66773
+ }
66774
+
66775
+ /* Tab styles */
66776
+ .energy-panel__chart-tabs {
66777
+ display: flex;
66778
+ gap: 2px;
66779
+ background: #f1f5f9;
66780
+ padding: 3px;
66781
+ border-radius: 8px;
66782
+ }
66783
+
66784
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__chart-tabs {
66785
+ background: #1e293b;
66786
+ }
66787
+
66788
+ .energy-panel__tab {
66789
+ display: flex;
66790
+ align-items: center;
66791
+ justify-content: center;
66792
+ padding: 6px 10px;
66793
+ border: none;
66794
+ background: transparent;
66795
+ color: #64748b;
66796
+ cursor: pointer;
66797
+ border-radius: 6px;
66798
+ transition: all 0.2s ease;
66799
+ font-size: 12px;
66800
+ font-weight: 500;
66801
+ }
66802
+
66803
+ .energy-panel__tab:hover {
66804
+ color: #1e293b;
66805
+ background: rgba(0, 0, 0, 0.05);
66806
+ }
66807
+
66808
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__tab:hover {
66809
+ color: #e2e8f0;
66810
+ background: rgba(255, 255, 255, 0.1);
66811
+ }
66812
+
66813
+ .energy-panel__tab.active {
66814
+ background: #2563eb;
66815
+ color: white;
66816
+ box-shadow: 0 1px 3px rgba(37, 99, 235, 0.3);
66817
+ }
66818
+
66819
+ .energy-panel__tab svg {
66820
+ width: 14px;
66821
+ height: 14px;
66822
+ }
66823
+
66824
+ /* Controls */
66825
+ .energy-panel__chart-controls {
66826
+ display: flex;
66827
+ align-items: center;
66828
+ gap: 8px;
66829
+ }
66830
+
66831
+ .energy-panel__chart-controls select {
66832
+ padding: 6px 12px;
66833
+ border-radius: 6px;
66834
+ border: 1px solid #e2e8f0;
66835
+ background-color: #ffffff;
66836
+ color: #1e293b;
66837
+ font-size: 12px;
66838
+ font-weight: 500;
66839
+ cursor: pointer;
66840
+ min-width: 180px;
66841
+ }
66842
+
66843
+ .energy-panel__chart-controls select:focus {
66844
+ outline: none;
66845
+ border-color: #2563eb;
66846
+ }
66847
+
66848
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__chart-controls select {
66849
+ background-color: #1e293b;
66850
+ border-color: #475569;
66851
+ color: #e2e8f0;
66852
+ }
66853
+
66854
+ .energy-panel__period-select {
66855
+ min-width: 100px !important;
66856
+ }
66857
+
66858
+ .energy-panel__distribution-mode {
66859
+ min-width: 220px !important;
66860
+ }
66861
+
66862
+ .energy-panel__maximize-btn {
66863
+ display: flex;
66864
+ align-items: center;
66865
+ justify-content: center;
66866
+ width: 32px;
66867
+ height: 32px;
66868
+ padding: 0;
66869
+ border: 1px solid #e2e8f0;
66870
+ border-radius: 6px;
66871
+ background: transparent;
66872
+ color: #64748b;
66873
+ cursor: pointer;
66874
+ font-size: 16px;
66875
+ transition: all 0.2s ease;
66876
+ }
66877
+
66878
+ .energy-panel__maximize-btn:hover {
66879
+ background: #2563eb;
66880
+ border-color: #2563eb;
66881
+ color: white;
66882
+ }
66883
+
66884
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__maximize-btn {
66885
+ border-color: #475569;
66886
+ color: #94a3b8;
66887
+ }
66888
+
66889
+ .energy-panel-wrap[data-theme="dark"] .energy-panel__maximize-btn:hover {
66890
+ background: #2563eb;
66891
+ border-color: #2563eb;
66892
+ color: white;
66893
+ }
66894
+
66895
+ /* Chart containers */
66896
+ .energy-panel__consumption-chart,
66897
+ .energy-panel__distribution-chart {
66898
+ min-height: 220px;
66899
+ position: relative;
66900
+ }
66901
+
66902
+ .energy-panel__consumption-chart canvas,
66903
+ .energy-panel__distribution-chart canvas {
66904
+ width: 100% !important;
66905
+ }
66906
+
66907
+ /* Responsive */
66908
+ @media (max-width: 768px) {
66909
+ .energy-panel__chart-header {
66910
+ flex-direction: column;
66911
+ align-items: flex-start;
66912
+ }
66913
+
66914
+ .energy-panel__chart-title-group {
66915
+ width: 100%;
66916
+ flex-wrap: wrap;
66917
+ }
66918
+
66919
+ .energy-panel__chart-controls {
66920
+ width: 100%;
66921
+ justify-content: flex-start;
66922
+ }
66923
+
66924
+ .energy-panel__distribution-mode {
66925
+ flex: 1;
66926
+ }
66927
+ }
66928
+ `;
66929
+ function injectEnergyPanelStyles() {
66930
+ if (!document.getElementById("energy-panel-styles")) {
66931
+ const styleTag = document.createElement("style");
66932
+ styleTag.id = "energy-panel-styles";
66933
+ styleTag.textContent = ENERGY_PANEL_STYLES;
66934
+ document.head.appendChild(styleTag);
66935
+ }
66936
+ }
66937
+
66938
+ // src/components/energy-panel/EnergyPanelView.ts
66939
+ var EnergyPanelView = class {
66940
+ params;
66941
+ controller;
66942
+ root = null;
66943
+ // Chart widget instances (using existing reusable components)
66944
+ consumptionWidget = null;
66945
+ distributionWidget = null;
66946
+ // Unique IDs for this panel instance
66947
+ panelId;
66948
+ constructor(params, controller) {
66949
+ this.params = params;
66950
+ this.controller = controller;
66951
+ this.panelId = `energy-panel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
66952
+ this.controller.setOnStateChange((state6) => this.onStateChange(state6));
66953
+ }
66954
+ render() {
66955
+ injectEnergyPanelStyles();
66956
+ const state6 = this.controller.getState();
66957
+ this.root = document.createElement("div");
66958
+ this.root.className = "energy-panel-wrap";
66959
+ this.root.setAttribute("data-theme", state6.theme);
66960
+ this.root.setAttribute("data-domain", "energy");
66961
+ this.root.innerHTML = this.buildHTML(state6);
66962
+ this.bindEvents();
66963
+ setTimeout(() => this.initializeCharts(), 0);
66964
+ return this.root;
66965
+ }
66966
+ buildHTML(state6) {
66967
+ const { showCards = true, showConsumptionChart = true, showDistributionChart = true } = this.params;
66968
+ return `
66969
+ <div class="energy-panel">
66970
+ ${showCards ? this.buildCardsHTML(state6) : ""}
66971
+ ${showConsumptionChart ? this.buildConsumptionChartContainerHTML() : ""}
66972
+ ${showDistributionChart ? this.buildDistributionChartContainerHTML() : ""}
66973
+ </div>
66974
+ `;
66975
+ }
66976
+ buildCardsHTML(state6) {
66977
+ const summary = state6.summary;
66978
+ const storesValue = summary?.storesTotal ?? 0;
66979
+ const equipmentsValue = summary?.equipmentsTotal ?? 0;
66980
+ return `
66981
+ <div class="energy-panel__cards">
66982
+ <div class="energy-panel__card" data-type="stores">
66983
+ <div class="energy-panel__card-icon">&#127968;</div>
66984
+ <div class="energy-panel__card-content">
66985
+ <div class="energy-panel__card-label">Consumo Lojas</div>
66986
+ <div class="energy-panel__card-value">${this.formatEnergy(storesValue)}</div>
66987
+ <div class="energy-panel__card-count">${summary?.byCategory?.lojas?.count || 0} medidores</div>
66988
+ </div>
66989
+ </div>
66990
+ <div class="energy-panel__card" data-type="equipments">
66991
+ <div class="energy-panel__card-icon">&#x2699;&#xfe0f;</div>
66992
+ <div class="energy-panel__card-content">
66993
+ <div class="energy-panel__card-label">Consumo Equipamentos</div>
66994
+ <div class="energy-panel__card-value">${this.formatEnergy(equipmentsValue)}</div>
66995
+ <div class="energy-panel__card-count">${this.getEquipmentCount(summary)} equipamentos</div>
66996
+ </div>
66997
+ </div>
66998
+ </div>
66999
+ `;
67000
+ }
67001
+ /**
67002
+ * Container for consumption chart widget (RFC-0098)
67003
+ * The createConsumptionChartWidget will inject its own HTML structure
67004
+ */
67005
+ buildConsumptionChartContainerHTML() {
67006
+ return `
67007
+ <div class="energy-panel__chart-section">
67008
+ <div id="${this.panelId}-consumption-chart" class="energy-panel__consumption-chart"></div>
67009
+ </div>
67010
+ `;
67011
+ }
67012
+ /**
67013
+ * Container for distribution chart widget (RFC-0102)
67014
+ * The createDistributionChartWidget will inject its own HTML structure
67015
+ */
67016
+ buildDistributionChartContainerHTML() {
67017
+ return `
67018
+ <div class="energy-panel__chart-section">
67019
+ <div id="${this.panelId}-distribution-chart" class="energy-panel__distribution-chart"></div>
67020
+ </div>
67021
+ `;
67022
+ }
67023
+ formatEnergy(value) {
67024
+ if (value >= 1e3) {
67025
+ return `${(value / 1e3).toFixed(2)} MWh`;
67026
+ }
67027
+ return `${value.toFixed(1)} kWh`;
67028
+ }
67029
+ getEquipmentCount(summary) {
67030
+ if (!summary?.byCategory) return 0;
67031
+ const { climatizacao, elevadores, escadas, outros } = summary.byCategory;
67032
+ return (climatizacao?.count || 0) + (elevadores?.count || 0) + (escadas?.count || 0) + (outros?.count || 0);
67033
+ }
67034
+ bindEvents() {
67035
+ }
67036
+ /**
67037
+ * Initialize chart widgets using the existing reusable components
67038
+ */
67039
+ async initializeCharts() {
67040
+ if (!this.root) return;
67041
+ const state6 = this.controller.getState();
67042
+ const { showConsumptionChart = true, showDistributionChart = true } = this.params;
67043
+ if (showConsumptionChart) {
67044
+ await this.initializeConsumptionWidget(state6);
67045
+ }
67046
+ if (showDistributionChart) {
67047
+ await this.initializeDistributionWidget(state6);
67048
+ }
67049
+ }
67050
+ /**
67051
+ * Initialize the consumption chart using createConsumptionChartWidget
67052
+ * This provides: settings modal, ideal range, viz modes, chart types
67053
+ */
67054
+ async initializeConsumptionWidget(state6) {
67055
+ const containerId = `${this.panelId}-consumption-chart`;
67056
+ const container = this.root?.querySelector(`#${containerId}`);
67057
+ if (!container) {
67058
+ console.warn("[EnergyPanel] Consumption chart container not found");
67059
+ return;
67060
+ }
67061
+ try {
67062
+ this.consumptionWidget = createConsumptionChartWidget({
67063
+ domain: "energy",
67064
+ containerId,
67065
+ title: "Consumo de Energia",
67066
+ unit: "kWh",
67067
+ unitLarge: "MWh",
67068
+ thresholdForLargeUnit: 1e3,
67069
+ theme: state6.theme,
67070
+ defaultPeriod: state6.period,
67071
+ defaultChartType: "line",
67072
+ defaultVizMode: "total",
67073
+ // Settings modal options
67074
+ showSettingsButton: true,
67075
+ showMaximizeButton: true,
67076
+ showVizModeTabs: true,
67077
+ showChartTypeTabs: true,
67078
+ chartHeight: 280,
67079
+ // Data fetching - use params callback or mock data
67080
+ fetchData: this.params.fetchConsumptionData || this.createMockFetchData(),
67081
+ // Callbacks
67082
+ onMaximizeClick: () => {
67083
+ this.params.onMaximizeClick?.();
67084
+ },
67085
+ onSettingsClick: () => {
67086
+ console.log("[EnergyPanel] Settings clicked");
67087
+ },
67088
+ onDataLoaded: (data) => {
67089
+ console.log("[EnergyPanel] Consumption data loaded:", data.labels?.length, "days");
67090
+ },
67091
+ onError: (error) => {
67092
+ console.error("[EnergyPanel] Consumption chart error:", error);
67093
+ },
67094
+ // Ideal range (from customer settings or orchestrator)
67095
+ idealRange: this.params.idealRange || void 0,
67096
+ // ThingsBoard container reference
67097
+ $container: this.params.$container
67098
+ });
67099
+ await this.consumptionWidget.render();
67100
+ console.log("[EnergyPanel] Consumption widget initialized");
67101
+ } catch (error) {
67102
+ console.error("[EnergyPanel] Failed to initialize consumption widget:", error);
67103
+ }
67104
+ }
67105
+ /**
67106
+ * Initialize the distribution chart using createDistributionChartWidget
67107
+ * This provides: mode selector, horizontal bars, shopping colors
67108
+ */
67109
+ async initializeDistributionWidget(state6) {
67110
+ const containerId = `${this.panelId}-distribution-chart`;
67111
+ const container = this.root?.querySelector(`#${containerId}`);
67112
+ if (!container) {
67113
+ console.warn("[EnergyPanel] Distribution chart container not found");
67114
+ return;
67115
+ }
67116
+ try {
67117
+ this.distributionWidget = createDistributionChartWidget({
67118
+ domain: "energy",
67119
+ containerId,
67120
+ title: "Distribuicao de Consumo",
67121
+ unit: "kWh",
67122
+ unitLarge: "MWh",
67123
+ thresholdForLargeUnit: 1e3,
67124
+ theme: state6.theme,
67125
+ chartHeight: 300,
67126
+ showHeader: true,
67127
+ showModeSelector: true,
67128
+ showSettingsButton: false,
67129
+ showMaximizeButton: false,
67130
+ defaultMode: "groups",
67131
+ // Energy-specific modes
67132
+ modes: [
67133
+ { value: "groups", label: "Por Grupos de Equipamentos" },
67134
+ { value: "elevators", label: "Elevadores por Shopping" },
67135
+ { value: "escalators", label: "Escadas Rolantes por Shopping" },
67136
+ { value: "hvac", label: "Climatizacao por Shopping" },
67137
+ { value: "others", label: "Outros Equipamentos por Shopping" },
67138
+ { value: "stores", label: "Lojas por Shopping" }
67139
+ ],
67140
+ // Data fetching
67141
+ fetchDistribution: this.params.fetchDistributionData || this.createMockFetchDistribution(),
67142
+ // Callbacks
67143
+ onModeChange: (mode) => {
67144
+ console.log("[EnergyPanel] Distribution mode changed:", mode);
67145
+ },
67146
+ onDataLoaded: (data) => {
67147
+ console.log("[EnergyPanel] Distribution data loaded:", Object.keys(data).length, "categories");
67148
+ },
67149
+ onError: (error) => {
67150
+ console.error("[EnergyPanel] Distribution chart error:", error);
67151
+ },
67152
+ // Get shopping colors from orchestrator
67153
+ getShoppingColors: () => {
67154
+ return window.MyIOOrchestrator?.getShoppingColors?.() || null;
67155
+ }
67156
+ });
67157
+ await this.distributionWidget.render();
67158
+ console.log("[EnergyPanel] Distribution widget initialized");
67159
+ } catch (error) {
67160
+ console.error("[EnergyPanel] Failed to initialize distribution widget:", error);
67161
+ }
67162
+ }
67163
+ /**
67164
+ * Create mock data fetcher for showcase/demo purposes
67165
+ */
67166
+ createMockFetchData() {
67167
+ return async (period) => {
67168
+ const labels = [];
67169
+ const dailyTotals = [];
67170
+ const today = /* @__PURE__ */ new Date();
67171
+ for (let i = period - 1; i >= 0; i--) {
67172
+ const date = new Date(today);
67173
+ date.setDate(date.getDate() - i);
67174
+ labels.push(date.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" }));
67175
+ const baseValue = 500 + Math.random() * 500;
67176
+ const variation = (Math.random() - 0.5) * 200;
67177
+ dailyTotals.push(Math.max(100, baseValue + variation));
67178
+ }
67179
+ const shoppingData = {
67180
+ "shopping-1": dailyTotals.map((v) => v * 0.35),
67181
+ "shopping-2": dailyTotals.map((v) => v * 0.25),
67182
+ "shopping-3": dailyTotals.map((v) => v * 0.25),
67183
+ "shopping-4": dailyTotals.map((v) => v * 0.15)
67184
+ };
67185
+ const shoppingNames = {
67186
+ "shopping-1": "Shopping Aricanduva",
67187
+ "shopping-2": "Shopping Interlagos",
67188
+ "shopping-3": "Shopping Tucuruvi",
67189
+ "shopping-4": "Shopping Penha"
67190
+ };
67191
+ return {
67192
+ labels,
67193
+ dailyTotals,
67194
+ shoppingData,
67195
+ shoppingNames,
67196
+ fetchTimestamp: Date.now()
67197
+ };
67198
+ };
67199
+ }
67200
+ /**
67201
+ * Create mock distribution data fetcher for showcase/demo purposes
67202
+ */
67203
+ createMockFetchDistribution() {
67204
+ const state6 = this.controller.getState();
67205
+ const summary = state6.summary;
67206
+ return async (mode) => {
67207
+ if (mode === "groups") {
67208
+ if (summary?.byCategory) {
67209
+ return {
67210
+ "Lojas": summary.byCategory.lojas?.total || 0,
67211
+ "Climatizacao": summary.byCategory.climatizacao?.total || 0,
67212
+ "Elevadores": summary.byCategory.elevadores?.total || 0,
67213
+ "Escadas Rolantes": summary.byCategory.escadas?.total || 0,
67214
+ "Outros": summary.byCategory.outros?.total || 0
67215
+ };
67216
+ }
67217
+ return {
67218
+ "Lojas": 12500,
67219
+ "Climatizacao": 8500,
67220
+ "Elevadores": 3200,
67221
+ "Escadas Rolantes": 2100,
67222
+ "Outros": 1800
67223
+ };
67224
+ }
67225
+ if (mode === "elevators") {
67226
+ return {
67227
+ "Shopping Aricanduva": 2400,
67228
+ "Shopping Interlagos": 1800,
67229
+ "Shopping Tucuruvi": 1500,
67230
+ "Shopping Penha": 1200,
67231
+ "Shopping Tatuape": 900
67232
+ };
67233
+ }
67234
+ if (mode === "escalators") {
67235
+ return {
67236
+ "Shopping Aricanduva": 1800,
67237
+ "Shopping Interlagos": 1400,
67238
+ "Shopping Tucuruvi": 1100,
67239
+ "Shopping Penha": 800
67240
+ };
67241
+ }
67242
+ if (mode === "hvac") {
67243
+ return {
67244
+ "Shopping Aricanduva": 5200,
67245
+ "Shopping Interlagos": 4100,
67246
+ "Shopping Tucuruvi": 3800,
67247
+ "Shopping Penha": 3200,
67248
+ "Shopping Tatuape": 2800,
67249
+ "Shopping Santana": 2400
67250
+ };
67251
+ }
67252
+ if (mode === "others") {
67253
+ return {
67254
+ "Shopping Aricanduva": 1200,
67255
+ "Shopping Interlagos": 900,
67256
+ "Shopping Tucuruvi": 700
67257
+ };
67258
+ }
67259
+ if (mode === "stores") {
67260
+ return {
67261
+ "Shopping Aricanduva": 8500,
67262
+ "Shopping Interlagos": 7200,
67263
+ "Shopping Tucuruvi": 6800,
67264
+ "Shopping Penha": 5500,
67265
+ "Shopping Tatuape": 5100,
67266
+ "Shopping Santana": 4800,
67267
+ "Shopping Campo Limpo": 4200
67268
+ };
67269
+ }
67270
+ return null;
67271
+ };
67272
+ }
67273
+ onStateChange(state6) {
67274
+ if (!this.root) return;
67275
+ this.root.setAttribute("data-theme", state6.theme);
67276
+ if (this.consumptionWidget) {
67277
+ this.consumptionWidget.setTheme(state6.theme);
67278
+ }
67279
+ if (this.distributionWidget) {
67280
+ this.distributionWidget.setTheme(state6.theme);
67281
+ }
67282
+ this.updateCards(state6);
67283
+ }
67284
+ updateCards(state6) {
67285
+ const summary = state6.summary;
67286
+ if (!summary || !this.root) return;
67287
+ const storesCard = this.root.querySelector('[data-type="stores"] .energy-panel__card-value');
67288
+ const equipmentsCard = this.root.querySelector('[data-type="equipments"] .energy-panel__card-value');
67289
+ if (storesCard) {
67290
+ storesCard.textContent = this.formatEnergy(summary.storesTotal);
67291
+ }
67292
+ if (equipmentsCard) {
67293
+ equipmentsCard.textContent = this.formatEnergy(summary.equipmentsTotal);
67294
+ }
67295
+ }
67296
+ /**
67297
+ * Public method to refresh consumption chart data
67298
+ */
67299
+ async refreshConsumptionChart() {
67300
+ if (this.consumptionWidget) {
67301
+ await this.consumptionWidget.refresh(true);
67302
+ }
67303
+ }
67304
+ /**
67305
+ * Public method to refresh distribution chart data
67306
+ */
67307
+ async refreshDistributionChart() {
67308
+ if (this.distributionWidget) {
67309
+ await this.distributionWidget.refresh();
67310
+ }
67311
+ }
67312
+ /**
67313
+ * Public method to set ideal range on consumption chart
67314
+ */
67315
+ setIdealRange(range) {
67316
+ if (this.consumptionWidget) {
67317
+ this.consumptionWidget.setIdealRange(range ? {
67318
+ min: range.min,
67319
+ max: range.max,
67320
+ label: range.label,
67321
+ enabled: true
67322
+ } : null);
67323
+ }
67324
+ }
67325
+ destroy() {
67326
+ if (this.consumptionWidget) {
67327
+ this.consumptionWidget.destroy();
67328
+ this.consumptionWidget = null;
67329
+ }
67330
+ if (this.distributionWidget) {
67331
+ this.distributionWidget.destroy();
67332
+ this.distributionWidget = null;
67333
+ }
67334
+ this.root?.remove();
67335
+ this.root = null;
67336
+ }
67337
+ };
67338
+
67339
+ // src/components/energy-panel/createEnergyPanelComponent.ts
67340
+ function createEnergyPanelComponent(params) {
67341
+ if (!params.container) {
67342
+ throw new Error("[EnergyPanel] container is required");
67343
+ }
67344
+ const controller = new EnergyPanelController(params);
67345
+ const view = new EnergyPanelView(params, controller);
67346
+ const element = view.render();
67347
+ params.container.appendChild(element);
67348
+ return {
67349
+ element,
67350
+ updateSummary: (data) => controller.updateSummary(data),
67351
+ getSummary: () => controller.getSummary(),
67352
+ setTheme: (mode) => controller.setTheme(mode),
67353
+ getTheme: () => controller.getState().theme,
67354
+ setPeriod: (days) => controller.setPeriod(days),
67355
+ getPeriod: () => controller.getState().period,
67356
+ setVizMode: (mode) => controller.setVizMode(mode),
67357
+ getVizMode: () => controller.getState().vizMode,
67358
+ setChartType: (type) => controller.setChartType(type),
67359
+ getChartType: () => controller.getState().chartType,
67360
+ applyShoppingFilter: (ids) => controller.applyShoppingFilter(ids),
67361
+ getSelectedShoppingIds: () => [...controller.getState().selectedShoppingIds],
67362
+ clearFilters: () => controller.clearFilters(),
67363
+ refresh: () => {
67364
+ params.onRefresh?.();
67365
+ },
67366
+ openFullscreen: () => {
67367
+ params.onMaximizeClick?.();
67368
+ },
67369
+ destroy: () => view.destroy()
67370
+ };
67371
+ }
67372
+
67373
+ // src/components/water-panel/WaterPanelController.ts
67374
+ var WaterPanelController = class {
67375
+ state;
67376
+ params;
67377
+ onStateChange = null;
67378
+ constructor(params) {
67379
+ this.params = params;
67380
+ this.state = {
67381
+ theme: params.theme || "light",
67382
+ period: params.period || 7,
67383
+ vizMode: params.vizMode || "total",
67384
+ chartType: params.chartType || "line",
67385
+ selectedShoppingIds: params.selectedShoppingIds || [],
67386
+ summary: params.initialSummary || null,
67387
+ isLoading: false,
67388
+ error: null
67389
+ };
67390
+ }
67391
+ // Observer pattern
67392
+ setOnStateChange(callback) {
67393
+ this.onStateChange = callback;
67394
+ }
67395
+ notifyStateChange() {
67396
+ if (this.onStateChange) {
67397
+ this.onStateChange({ ...this.state });
67398
+ }
67399
+ }
67400
+ // Getters
67401
+ getState() {
67402
+ return { ...this.state };
67403
+ }
67404
+ getSummary() {
67405
+ return this.state.summary ? { ...this.state.summary } : null;
67406
+ }
67407
+ // Data updates
67408
+ updateSummary(data) {
67409
+ const total = data.total || data.storesTotal + data.commonAreaTotal;
67410
+ const storesPercentage = total > 0 ? data.storesTotal / total * 100 : 0;
67411
+ const commonAreaPercentage = total > 0 ? data.commonAreaTotal / total * 100 : 0;
67412
+ this.state.summary = {
67413
+ ...data,
67414
+ total,
67415
+ storesPercentage: data.storesPercentage ?? storesPercentage,
67416
+ commonAreaPercentage: data.commonAreaPercentage ?? commonAreaPercentage
67417
+ };
67418
+ this.notifyStateChange();
67419
+ }
67420
+ // Config updates
67421
+ setTheme(mode) {
67422
+ if (this.state.theme !== mode) {
67423
+ this.state.theme = mode;
67424
+ this.notifyStateChange();
67425
+ }
67426
+ }
67427
+ setPeriod(days) {
67428
+ if (this.state.period !== days) {
67429
+ this.state.period = days;
67430
+ this.params.onPeriodChange?.(days);
67431
+ this.notifyStateChange();
67432
+ }
67433
+ }
67434
+ setVizMode(mode) {
67435
+ if (this.state.vizMode !== mode) {
67436
+ this.state.vizMode = mode;
67437
+ this.params.onVizModeChange?.(mode);
67438
+ this.notifyStateChange();
67439
+ }
67440
+ }
67441
+ setChartType(type) {
67442
+ if (this.state.chartType !== type) {
67443
+ this.state.chartType = type;
67444
+ this.notifyStateChange();
67445
+ }
67446
+ }
67447
+ // Filter updates
67448
+ applyShoppingFilter(ids) {
67449
+ this.state.selectedShoppingIds = [...ids];
67450
+ this.params.onFilterChange?.(ids);
67451
+ this.notifyStateChange();
67452
+ }
67453
+ clearFilters() {
67454
+ this.state.selectedShoppingIds = [];
67455
+ this.params.onFilterChange?.([]);
67456
+ this.notifyStateChange();
67457
+ }
67458
+ // Loading state
67459
+ setLoading(loading) {
67460
+ this.state.isLoading = loading;
67461
+ this.notifyStateChange();
67462
+ }
67463
+ setError(error) {
67464
+ this.state.error = error;
67465
+ this.notifyStateChange();
67466
+ }
67467
+ };
67468
+
67469
+ // src/components/water-panel/styles.ts
67470
+ var WATER_PANEL_STYLES = `
67471
+ .water-panel-wrap {
67472
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
67473
+ color: #1e293b;
67474
+ background-color: #f8fafc;
67475
+ padding: 16px;
67476
+ border-radius: 12px;
67477
+ }
67478
+
67479
+ .water-panel-wrap[data-theme="dark"] {
67480
+ color: #e2e8f0;
67481
+ background-color: #1e293b;
67482
+ }
67483
+
67484
+ .water-panel__cards {
67485
+ display: grid;
67486
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
67487
+ gap: 16px;
67488
+ margin-bottom: 20px;
67489
+ }
67490
+
67491
+ .water-panel__card {
67492
+ background-color: #ffffff;
67493
+ padding: 16px;
67494
+ border-radius: 12px;
67495
+ display: flex;
67496
+ align-items: center;
67497
+ border: 1px solid #e2e8f0;
67498
+ transition: box-shadow 0.2s ease;
67499
+ }
67500
+
67501
+ .water-panel__card:hover {
67502
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
67503
+ }
67504
+
67505
+ .water-panel-wrap[data-theme="dark"] .water-panel__card {
67506
+ background-color: #334155;
67507
+ border-color: #475569;
67508
+ }
67509
+
67510
+ .water-panel__card--total {
67511
+ border-left: 4px solid #0288d1;
67512
+ }
67513
+
67514
+ .water-panel__card-icon {
67515
+ font-size: 2.5em;
67516
+ margin-right: 16px;
67517
+ }
67518
+
67519
+ .water-panel__card-content {
67520
+ flex: 1;
67521
+ }
67522
+
67523
+ .water-panel__card-label {
67524
+ font-size: 13px;
67525
+ color: #64748b;
67526
+ font-weight: 500;
67527
+ }
67528
+
67529
+ .water-panel-wrap[data-theme="dark"] .water-panel__card-label {
67530
+ color: #94a3b8;
67531
+ }
67532
+
67533
+ .water-panel__card-value {
67534
+ font-size: 1.6em;
67535
+ font-weight: 700;
67536
+ color: #0288d1;
67537
+ }
67538
+
67539
+ .water-panel-wrap[data-theme="dark"] .water-panel__card-value {
67540
+ color: #38bdf8;
67541
+ }
67542
+
67543
+ .water-panel__card-trend {
67544
+ font-size: 12px;
67545
+ color: #64748b;
67546
+ margin-top: 2px;
67547
+ }
67548
+
67549
+ .water-panel-wrap[data-theme="dark"] .water-panel__card-trend {
67550
+ color: #94a3b8;
67551
+ }
67552
+
67553
+ .water-panel__card-count {
67554
+ font-size: 12px;
67555
+ color: #94a3b8;
67556
+ margin-top: 4px;
67557
+ }
67558
+
67559
+ .water-panel-wrap[data-theme="dark"] .water-panel__card-count {
67560
+ color: #64748b;
67561
+ }
67562
+
67563
+ .water-panel__chart-section {
67564
+ background-color: #ffffff;
67565
+ padding: 16px;
67566
+ border-radius: 12px;
67567
+ border: 1px solid #e2e8f0;
67568
+ margin-bottom: 20px;
67569
+ }
67570
+
67571
+ .water-panel-wrap[data-theme="dark"] .water-panel__chart-section {
67572
+ background-color: #334155;
67573
+ border-color: #475569;
67574
+ }
67575
+
67576
+ .water-panel__chart-header {
67577
+ display: flex;
67578
+ justify-content: space-between;
67579
+ align-items: center;
67580
+ flex-wrap: wrap;
67581
+ gap: 12px;
67582
+ margin-bottom: 16px;
67583
+ padding-bottom: 12px;
67584
+ border-bottom: 1px solid #e2e8f0;
67585
+ }
67586
+
67587
+ .water-panel-wrap[data-theme="dark"] .water-panel__chart-header {
67588
+ border-bottom-color: #475569;
67589
+ }
67590
+
67591
+ .water-panel__chart-title-group {
67592
+ display: flex;
67593
+ align-items: center;
67594
+ gap: 12px;
67595
+ flex-wrap: wrap;
67596
+ }
67597
+
67598
+ .water-panel__chart-header h3 {
67599
+ margin: 0;
67600
+ font-size: 15px;
67601
+ font-weight: 600;
67602
+ color: #1e293b;
67603
+ }
67604
+
67605
+ .water-panel-wrap[data-theme="dark"] .water-panel__chart-header h3 {
67606
+ color: #f1f5f9;
67607
+ }
67608
+
67609
+ /* Tab styles */
67610
+ .water-panel__chart-tabs {
67611
+ display: flex;
67612
+ gap: 2px;
67613
+ background: #f1f5f9;
67614
+ padding: 3px;
67615
+ border-radius: 8px;
67616
+ }
67617
+
67618
+ .water-panel-wrap[data-theme="dark"] .water-panel__chart-tabs {
67619
+ background: #1e293b;
67620
+ }
67621
+
67622
+ .water-panel__tab {
67623
+ display: flex;
67624
+ align-items: center;
67625
+ justify-content: center;
67626
+ padding: 6px 10px;
67627
+ border: none;
67628
+ background: transparent;
67629
+ color: #64748b;
67630
+ cursor: pointer;
67631
+ border-radius: 6px;
67632
+ transition: all 0.2s ease;
67633
+ font-size: 12px;
67634
+ font-weight: 500;
67635
+ }
67636
+
67637
+ .water-panel__tab:hover {
67638
+ color: #1e293b;
67639
+ background: rgba(0, 0, 0, 0.05);
67640
+ }
67641
+
67642
+ .water-panel-wrap[data-theme="dark"] .water-panel__tab:hover {
67643
+ color: #e2e8f0;
67644
+ background: rgba(255, 255, 255, 0.1);
67645
+ }
67646
+
67647
+ .water-panel__tab.active {
67648
+ background: #0288d1;
67649
+ color: white;
67650
+ box-shadow: 0 1px 3px rgba(2, 136, 209, 0.3);
67651
+ }
67652
+
67653
+ .water-panel__tab svg {
67654
+ width: 14px;
67655
+ height: 14px;
67656
+ }
67657
+
67658
+ /* Controls */
67659
+ .water-panel__chart-controls {
67660
+ display: flex;
67661
+ align-items: center;
67662
+ gap: 8px;
67663
+ }
67664
+
67665
+ .water-panel__chart-controls select {
67666
+ padding: 6px 12px;
67667
+ border-radius: 6px;
67668
+ border: 1px solid #e2e8f0;
67669
+ background-color: #ffffff;
67670
+ color: #1e293b;
67671
+ font-size: 12px;
67672
+ font-weight: 500;
67673
+ cursor: pointer;
67674
+ min-width: 180px;
67675
+ }
67676
+
67677
+ .water-panel__chart-controls select:focus {
67678
+ outline: none;
67679
+ border-color: #0288d1;
67680
+ }
67681
+
67682
+ .water-panel-wrap[data-theme="dark"] .water-panel__chart-controls select {
67683
+ background-color: #1e293b;
67684
+ border-color: #475569;
67685
+ color: #e2e8f0;
67686
+ }
67687
+
67688
+ .water-panel__period-select {
67689
+ min-width: 100px !important;
67690
+ }
67691
+
67692
+ .water-panel__distribution-mode {
67693
+ min-width: 200px !important;
67694
+ }
67695
+
67696
+ .water-panel__maximize-btn {
67697
+ display: flex;
67698
+ align-items: center;
67699
+ justify-content: center;
67700
+ width: 32px;
67701
+ height: 32px;
67702
+ padding: 0;
67703
+ border: 1px solid #e2e8f0;
67704
+ border-radius: 6px;
67705
+ background: transparent;
67706
+ color: #64748b;
67707
+ cursor: pointer;
67708
+ font-size: 16px;
67709
+ transition: all 0.2s ease;
67710
+ }
67711
+
67712
+ .water-panel__maximize-btn:hover {
67713
+ background: #0288d1;
67714
+ border-color: #0288d1;
67715
+ color: white;
67716
+ }
67717
+
67718
+ .water-panel-wrap[data-theme="dark"] .water-panel__maximize-btn {
67719
+ border-color: #475569;
67720
+ color: #94a3b8;
67721
+ }
67722
+
67723
+ .water-panel-wrap[data-theme="dark"] .water-panel__maximize-btn:hover {
67724
+ background: #0288d1;
67725
+ border-color: #0288d1;
67726
+ color: white;
67727
+ }
67728
+
67729
+ /* Chart containers */
67730
+ .water-panel__consumption-chart,
67731
+ .water-panel__distribution-chart {
67732
+ min-height: 220px;
67733
+ position: relative;
67734
+ }
67735
+
67736
+ .water-panel__consumption-chart canvas,
67737
+ .water-panel__distribution-chart canvas {
67738
+ width: 100% !important;
67739
+ }
67740
+
67741
+ /* Responsive */
67742
+ @media (max-width: 768px) {
67743
+ .water-panel__chart-header {
67744
+ flex-direction: column;
67745
+ align-items: flex-start;
67746
+ }
67747
+
67748
+ .water-panel__chart-title-group {
67749
+ width: 100%;
67750
+ flex-wrap: wrap;
67751
+ }
67752
+
67753
+ .water-panel__chart-controls {
67754
+ width: 100%;
67755
+ justify-content: flex-start;
67756
+ }
67757
+
67758
+ .water-panel__distribution-mode {
67759
+ flex: 1;
67760
+ }
67761
+ }
67762
+ `;
67763
+ function injectWaterPanelStyles() {
67764
+ if (typeof document === "undefined") return;
67765
+ const id = "myio-water-panel-styles";
67766
+ if (document.getElementById(id)) return;
67767
+ const style = document.createElement("style");
67768
+ style.id = id;
67769
+ style.textContent = WATER_PANEL_STYLES;
67770
+ document.head.appendChild(style);
67771
+ }
67772
+
67773
+ // src/components/water-panel/WaterPanelView.ts
67774
+ var WaterPanelView = class {
67775
+ params;
67776
+ controller;
67777
+ root = null;
67778
+ // Chart widget instances (using existing reusable components)
67779
+ consumptionWidget = null;
67780
+ distributionWidget = null;
67781
+ // Unique IDs for this panel instance
67782
+ panelId;
67783
+ constructor(params, controller) {
67784
+ this.params = params;
67785
+ this.controller = controller;
67786
+ this.panelId = `water-panel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
67787
+ this.controller.setOnStateChange((state6) => this.onStateChange(state6));
67788
+ }
67789
+ render() {
67790
+ injectWaterPanelStyles();
67791
+ const state6 = this.controller.getState();
67792
+ this.root = document.createElement("div");
67793
+ this.root.className = "water-panel-wrap";
67794
+ this.root.setAttribute("data-theme", state6.theme);
67795
+ this.root.setAttribute("data-domain", "water");
67796
+ this.root.innerHTML = this.buildHTML(state6);
67797
+ this.bindEvents();
67798
+ setTimeout(() => this.initializeCharts(), 0);
67799
+ return this.root;
67800
+ }
67801
+ buildHTML(state6) {
67802
+ const { showCards = true, showConsumptionChart = true, showDistributionChart = true } = this.params;
67803
+ return `
67804
+ <div class="water-panel">
67805
+ ${showCards ? this.buildCardsHTML(state6) : ""}
67806
+ ${showConsumptionChart ? this.buildConsumptionChartContainerHTML() : ""}
67807
+ ${showDistributionChart ? this.buildDistributionChartContainerHTML() : ""}
67808
+ </div>
67809
+ `;
67810
+ }
67811
+ buildCardsHTML(state6) {
67812
+ const summary = state6.summary;
67813
+ const storesValue = summary?.storesTotal ?? 0;
67814
+ const commonAreaValue = summary?.commonAreaTotal ?? 0;
67815
+ const totalValue = summary?.total ?? 0;
67816
+ const storesPercent = summary?.storesPercentage ?? 0;
67817
+ const commonAreaPercent = summary?.commonAreaPercentage ?? 0;
67818
+ return `
67819
+ <div class="water-panel__cards">
67820
+ <div class="water-panel__card" data-type="stores">
67821
+ <div class="water-panel__card-icon">&#x1F4A7;</div>
67822
+ <div class="water-panel__card-content">
67823
+ <div class="water-panel__card-label">Consumo Lojas</div>
67824
+ <div class="water-panel__card-value">${this.formatWater(storesValue)}</div>
67825
+ <div class="water-panel__card-trend">${storesPercent.toFixed(1)}% do total</div>
67826
+ <div class="water-panel__card-count">${summary?.deviceCount || 0} hidrometros</div>
67827
+ </div>
67828
+ </div>
67829
+ <div class="water-panel__card" data-type="common-area">
67830
+ <div class="water-panel__card-icon">&#x1F6BF;</div>
67831
+ <div class="water-panel__card-content">
67832
+ <div class="water-panel__card-label">Consumo Area Comum</div>
67833
+ <div class="water-panel__card-value">${this.formatWater(commonAreaValue)}</div>
67834
+ <div class="water-panel__card-trend">${commonAreaPercent.toFixed(1)}% do total</div>
67835
+ <div class="water-panel__card-count">Banheiros, limpeza, etc.</div>
67836
+ </div>
67837
+ </div>
67838
+ <div class="water-panel__card water-panel__card--total" data-type="total">
67839
+ <div class="water-panel__card-icon">&#x1F4CA;</div>
67840
+ <div class="water-panel__card-content">
67841
+ <div class="water-panel__card-label">Consumo Total</div>
67842
+ <div class="water-panel__card-value">${this.formatWater(totalValue)}</div>
67843
+ <div class="water-panel__card-trend">Lojas + Area Comum</div>
67844
+ <div class="water-panel__card-count">${summary?.deviceCount || 0} dispositivos</div>
67845
+ </div>
67846
+ </div>
67847
+ </div>
67848
+ `;
67849
+ }
67850
+ /**
67851
+ * Container for consumption chart widget (RFC-0098)
67852
+ * The createConsumptionChartWidget will inject its own HTML structure
67853
+ */
67854
+ buildConsumptionChartContainerHTML() {
67855
+ return `
67856
+ <div class="water-panel__chart-section">
67857
+ <div id="${this.panelId}-consumption-chart" class="water-panel__consumption-chart"></div>
67858
+ </div>
67859
+ `;
67860
+ }
67861
+ /**
67862
+ * Container for distribution chart widget (RFC-0102)
67863
+ * The createDistributionChartWidget will inject its own HTML structure
67864
+ */
67865
+ buildDistributionChartContainerHTML() {
67866
+ return `
67867
+ <div class="water-panel__chart-section">
67868
+ <div id="${this.panelId}-distribution-chart" class="water-panel__distribution-chart"></div>
67869
+ </div>
67870
+ `;
67871
+ }
67872
+ formatWater(value) {
67873
+ if (value >= 1e3) {
67874
+ return `${(value / 1e3).toFixed(2)} k m3`;
67875
+ }
67876
+ return `${value.toFixed(1)} m3`;
67877
+ }
67878
+ bindEvents() {
67879
+ }
67880
+ /**
67881
+ * Initialize chart widgets using the existing reusable components
67882
+ */
67883
+ async initializeCharts() {
67884
+ if (!this.root) return;
67885
+ const state6 = this.controller.getState();
67886
+ const { showConsumptionChart = true, showDistributionChart = true } = this.params;
67887
+ if (showConsumptionChart) {
67888
+ await this.initializeConsumptionWidget(state6);
67889
+ }
67890
+ if (showDistributionChart) {
67891
+ await this.initializeDistributionWidget(state6);
67892
+ }
67893
+ }
67894
+ /**
67895
+ * Initialize the consumption chart using createConsumptionChartWidget
67896
+ * This provides: settings modal, ideal range, viz modes, chart types
67897
+ */
67898
+ async initializeConsumptionWidget(state6) {
67899
+ const containerId = `${this.panelId}-consumption-chart`;
67900
+ const container = this.root?.querySelector(`#${containerId}`);
67901
+ if (!container) {
67902
+ console.warn("[WaterPanel] Consumption chart container not found");
67903
+ return;
67904
+ }
67905
+ try {
67906
+ this.consumptionWidget = createConsumptionChartWidget({
67907
+ domain: "water",
67908
+ containerId,
67909
+ title: "Consumo de Agua",
67910
+ unit: "m3",
67911
+ unitLarge: "k m3",
67912
+ thresholdForLargeUnit: 1e3,
67913
+ theme: state6.theme,
67914
+ defaultPeriod: state6.period,
67915
+ defaultChartType: "line",
67916
+ defaultVizMode: "total",
67917
+ // Settings modal options
67918
+ showSettingsButton: true,
67919
+ showMaximizeButton: true,
67920
+ showVizModeTabs: true,
67921
+ showChartTypeTabs: true,
67922
+ chartHeight: 280,
67923
+ // Data fetching - use params callback or mock data
67924
+ fetchData: this.params.fetchConsumptionData || this.createMockFetchData(),
67925
+ // Callbacks
67926
+ onMaximizeClick: () => {
67927
+ this.params.onMaximizeClick?.();
67928
+ },
67929
+ onSettingsClick: () => {
67930
+ console.log("[WaterPanel] Settings clicked");
67931
+ },
67932
+ onDataLoaded: (data) => {
67933
+ console.log("[WaterPanel] Consumption data loaded:", data.labels?.length, "days");
67934
+ },
67935
+ onError: (error) => {
67936
+ console.error("[WaterPanel] Consumption chart error:", error);
67937
+ },
67938
+ // Ideal range (from customer settings or orchestrator)
67939
+ idealRange: this.params.idealRange || void 0,
67940
+ // ThingsBoard container reference
67941
+ $container: this.params.$container
67942
+ });
67943
+ await this.consumptionWidget.render();
67944
+ console.log("[WaterPanel] Consumption widget initialized");
67945
+ } catch (error) {
67946
+ console.error("[WaterPanel] Failed to initialize consumption widget:", error);
67947
+ }
67948
+ }
67949
+ /**
67950
+ * Initialize the distribution chart using createDistributionChartWidget
67951
+ * This provides: mode selector, horizontal bars, shopping colors
67952
+ */
67953
+ async initializeDistributionWidget(state6) {
67954
+ const containerId = `${this.panelId}-distribution-chart`;
67955
+ const container = this.root?.querySelector(`#${containerId}`);
67956
+ if (!container) {
67957
+ console.warn("[WaterPanel] Distribution chart container not found");
67958
+ return;
67959
+ }
67960
+ try {
67961
+ this.distributionWidget = createDistributionChartWidget({
67962
+ domain: "water",
67963
+ containerId,
67964
+ title: "Distribuicao de Consumo",
67965
+ unit: "m3",
67966
+ unitLarge: "k m3",
67967
+ thresholdForLargeUnit: 1e3,
67968
+ theme: state6.theme,
67969
+ chartHeight: 300,
67970
+ showHeader: true,
67971
+ showModeSelector: true,
67972
+ showSettingsButton: false,
67973
+ showMaximizeButton: false,
67974
+ defaultMode: "groups",
67975
+ // Water-specific modes
67976
+ modes: [
67977
+ { value: "groups", label: "Lojas vs Area Comum" },
67978
+ { value: "stores", label: "Lojas por Shopping" },
67979
+ { value: "common", label: "Area Comum por Shopping" }
67980
+ ],
67981
+ // Data fetching
67982
+ fetchDistribution: this.params.fetchDistributionData || this.createMockFetchDistribution(),
67983
+ // Callbacks
67984
+ onModeChange: (mode) => {
67985
+ console.log("[WaterPanel] Distribution mode changed:", mode);
67986
+ },
67987
+ onDataLoaded: (data) => {
67988
+ console.log("[WaterPanel] Distribution data loaded:", Object.keys(data).length, "categories");
67989
+ },
67990
+ onError: (error) => {
67991
+ console.error("[WaterPanel] Distribution chart error:", error);
67992
+ },
67993
+ // Get shopping colors from orchestrator
67994
+ getShoppingColors: () => {
67995
+ return window.MyIOOrchestrator?.getShoppingColors?.() || null;
67996
+ }
67997
+ });
67998
+ await this.distributionWidget.render();
67999
+ console.log("[WaterPanel] Distribution widget initialized");
68000
+ } catch (error) {
68001
+ console.error("[WaterPanel] Failed to initialize distribution widget:", error);
68002
+ }
68003
+ }
68004
+ /**
68005
+ * Create mock data fetcher for showcase/demo purposes
68006
+ */
68007
+ createMockFetchData() {
68008
+ return async (period) => {
68009
+ const labels = [];
68010
+ const dailyTotals = [];
68011
+ const today = /* @__PURE__ */ new Date();
68012
+ for (let i = period - 1; i >= 0; i--) {
68013
+ const date = new Date(today);
68014
+ date.setDate(date.getDate() - i);
68015
+ labels.push(date.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" }));
68016
+ const baseValue = 50 + Math.random() * 50;
68017
+ const variation = (Math.random() - 0.5) * 20;
68018
+ dailyTotals.push(Math.max(10, baseValue + variation));
68019
+ }
68020
+ const shoppingData = {
68021
+ "shopping-1": dailyTotals.map((v) => v * 0.35),
68022
+ "shopping-2": dailyTotals.map((v) => v * 0.25),
68023
+ "shopping-3": dailyTotals.map((v) => v * 0.25),
68024
+ "shopping-4": dailyTotals.map((v) => v * 0.15)
68025
+ };
68026
+ const shoppingNames = {
68027
+ "shopping-1": "Shopping Aricanduva",
68028
+ "shopping-2": "Shopping Interlagos",
68029
+ "shopping-3": "Shopping Tucuruvi",
68030
+ "shopping-4": "Shopping Penha"
68031
+ };
68032
+ return {
68033
+ labels,
68034
+ dailyTotals,
68035
+ shoppingData,
68036
+ shoppingNames,
68037
+ fetchTimestamp: Date.now()
68038
+ };
68039
+ };
68040
+ }
68041
+ /**
68042
+ * Create mock distribution data fetcher for showcase/demo purposes
68043
+ */
68044
+ createMockFetchDistribution() {
68045
+ const state6 = this.controller.getState();
68046
+ const summary = state6.summary;
68047
+ return async (mode) => {
68048
+ if (mode === "groups") {
68049
+ const storesTotal = summary?.storesTotal || 850;
68050
+ const commonAreaTotal = summary?.commonAreaTotal || 650;
68051
+ return {
68052
+ "Lojas": storesTotal,
68053
+ "Area Comum": commonAreaTotal
68054
+ };
68055
+ }
68056
+ if (mode === "stores") {
68057
+ return {
68058
+ "Shopping Aricanduva": 180,
68059
+ "Shopping Interlagos": 150,
68060
+ "Shopping Tucuruvi": 140,
68061
+ "Shopping Penha": 120,
68062
+ "Shopping Tatuape": 110,
68063
+ "Shopping Santana": 100
68064
+ };
68065
+ }
68066
+ if (mode === "common") {
68067
+ return {
68068
+ "Shopping Aricanduva": 150,
68069
+ "Shopping Interlagos": 130,
68070
+ "Shopping Tucuruvi": 120,
68071
+ "Shopping Penha": 100,
68072
+ "Shopping Tatuape": 90
68073
+ };
68074
+ }
68075
+ return null;
68076
+ };
68077
+ }
68078
+ onStateChange(state6) {
68079
+ if (!this.root) return;
68080
+ this.root.setAttribute("data-theme", state6.theme);
68081
+ if (this.consumptionWidget) {
68082
+ this.consumptionWidget.setTheme(state6.theme);
68083
+ }
68084
+ if (this.distributionWidget) {
68085
+ this.distributionWidget.setTheme(state6.theme);
68086
+ }
68087
+ this.updateCards(state6);
68088
+ }
68089
+ updateCards(state6) {
68090
+ const summary = state6.summary;
68091
+ if (!summary || !this.root) return;
68092
+ const storesCard = this.root.querySelector('[data-type="stores"] .water-panel__card-value');
68093
+ const storesTrend = this.root.querySelector('[data-type="stores"] .water-panel__card-trend');
68094
+ if (storesCard) {
68095
+ storesCard.textContent = this.formatWater(summary.storesTotal);
68096
+ }
68097
+ if (storesTrend) {
68098
+ storesTrend.textContent = `${summary.storesPercentage.toFixed(1)}% do total`;
68099
+ }
68100
+ const commonAreaCard = this.root.querySelector('[data-type="common-area"] .water-panel__card-value');
68101
+ const commonAreaTrend = this.root.querySelector('[data-type="common-area"] .water-panel__card-trend');
68102
+ if (commonAreaCard) {
68103
+ commonAreaCard.textContent = this.formatWater(summary.commonAreaTotal);
68104
+ }
68105
+ if (commonAreaTrend) {
68106
+ commonAreaTrend.textContent = `${summary.commonAreaPercentage.toFixed(1)}% do total`;
68107
+ }
68108
+ const totalCard = this.root.querySelector('[data-type="total"] .water-panel__card-value');
68109
+ if (totalCard) {
68110
+ totalCard.textContent = this.formatWater(summary.total);
68111
+ }
68112
+ }
68113
+ /**
68114
+ * Public method to refresh consumption chart data
68115
+ */
68116
+ async refreshConsumptionChart() {
68117
+ if (this.consumptionWidget) {
68118
+ await this.consumptionWidget.refresh(true);
68119
+ }
68120
+ }
68121
+ /**
68122
+ * Public method to refresh distribution chart data
68123
+ */
68124
+ async refreshDistributionChart() {
68125
+ if (this.distributionWidget) {
68126
+ await this.distributionWidget.refresh();
68127
+ }
68128
+ }
68129
+ /**
68130
+ * Public method to set ideal range on consumption chart
68131
+ */
68132
+ setIdealRange(range) {
68133
+ if (this.consumptionWidget) {
68134
+ this.consumptionWidget.setIdealRange(range ? {
68135
+ min: range.min,
68136
+ max: range.max,
68137
+ label: range.label,
68138
+ enabled: true
68139
+ } : null);
68140
+ }
68141
+ }
68142
+ destroy() {
68143
+ if (this.consumptionWidget) {
68144
+ this.consumptionWidget.destroy();
68145
+ this.consumptionWidget = null;
68146
+ }
68147
+ if (this.distributionWidget) {
68148
+ this.distributionWidget.destroy();
68149
+ this.distributionWidget = null;
68150
+ }
68151
+ this.root?.remove();
68152
+ this.root = null;
68153
+ }
68154
+ };
68155
+
68156
+ // src/components/water-panel/createWaterPanelComponent.ts
68157
+ function createWaterPanelComponent(params) {
68158
+ if (!params.container) {
68159
+ throw new Error("[WaterPanel] container is required");
68160
+ }
68161
+ const controller = new WaterPanelController(params);
68162
+ const view = new WaterPanelView(params, controller);
68163
+ const element = view.render();
68164
+ params.container.appendChild(element);
68165
+ return {
68166
+ element,
68167
+ // Data methods
68168
+ updateSummary: (data) => controller.updateSummary(data),
68169
+ getSummary: () => controller.getSummary(),
68170
+ // Config methods
68171
+ setTheme: (mode) => controller.setTheme(mode),
68172
+ getTheme: () => controller.getState().theme,
68173
+ setPeriod: (days) => controller.setPeriod(days),
68174
+ getPeriod: () => controller.getState().period,
68175
+ setVizMode: (mode) => controller.setVizMode(mode),
68176
+ getVizMode: () => controller.getState().vizMode,
68177
+ setChartType: (type) => controller.setChartType(type),
68178
+ getChartType: () => controller.getState().chartType,
68179
+ // Filter methods
68180
+ applyShoppingFilter: (ids) => controller.applyShoppingFilter(ids),
68181
+ getSelectedShoppingIds: () => [...controller.getState().selectedShoppingIds],
68182
+ clearFilters: () => controller.clearFilters(),
68183
+ // Actions
68184
+ refresh: () => {
68185
+ params.onRefresh?.();
68186
+ },
68187
+ openFullscreen: () => {
68188
+ params.onMaximizeClick?.();
68189
+ },
68190
+ // Lifecycle
68191
+ destroy: () => view.destroy()
68192
+ };
68193
+ }
66581
68194
  // Annotate the CommonJS export names for ESM import in node:
66582
68195
  0 && (module.exports = {
66583
68196
  ANNOTATION_TYPE_COLORS,
@@ -66714,6 +68327,7 @@ function createCustomerCardV1(params) {
66714
68327
  createDeviceItem,
66715
68328
  createDeviceItemsFromMap,
66716
68329
  createDistributionChartWidget,
68330
+ createEnergyPanelComponent,
66717
68331
  createFilterModalComponent,
66718
68332
  createFooterComponent,
66719
68333
  createHeaderComponent,
@@ -66723,6 +68337,7 @@ function createCustomerCardV1(params) {
66723
68337
  createMenuComponent,
66724
68338
  createModalHeader,
66725
68339
  createTelemetryGridComponent,
68340
+ createWaterPanelComponent,
66726
68341
  decodePayload,
66727
68342
  decodePayloadBase64Xor,
66728
68343
  detectContext,