myio-js-library 0.1.13 → 0.1.14

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/README.md CHANGED
@@ -570,16 +570,17 @@ calcDeltaPercent(100, 100); // { value: 0, type: "neutral" }
570
570
  calcDeltaPercent(0, 100); // { value: 100, type: "increase" }
571
571
  ```
572
572
 
573
- ##### `formatEnergyByGroup(value: number, group: string): string`
573
+ ##### `formatWaterByGroup(value: number, group: string): string`
574
574
 
575
- Formats energy/water values based on group type (from MAIN_WATER controller).
575
+ Formats water group totals in m³. For values 1000, it returns the value in thousands of m³ with a simplified `x 10³` suffix.
576
576
 
577
+ **Examples**
577
578
  ```javascript
578
- import { formatEnergyByGroup } from 'myio-js-library';
579
+ import { formatWaterByGroup } from 'myio-js-library';
579
580
 
580
- formatEnergyByGroup(178, "Caixas D'Água"); // "1,78 m.c.a."
581
- formatEnergyByGroup(12.345, "Lojas"); // "12,35 M³"
582
- formatEnergyByGroup(1000000, "Lojas"); // "1,00(GWh scale)"
581
+ formatWaterByGroup(178, "Caixas D'Água"); // "1,78 m.c.a."
582
+ formatWaterByGroup(750, "Lojas"); // "750,00 M³"
583
+ formatWaterByGroup(2500, "Lojas"); // "2,50x 10³ "
583
584
  ```
584
585
 
585
586
  #### Water Date Utilities
package/dist/index.cjs CHANGED
@@ -43,9 +43,9 @@ __export(index_exports, {
43
43
  formatDateToYMD: () => formatDateToYMD,
44
44
  formatDateWithTimezoneOffset: () => formatDateWithTimezoneOffset,
45
45
  formatEnergy: () => formatEnergy,
46
- formatEnergyByGroup: () => formatEnergyByGroup,
47
46
  formatNumberReadable: () => formatNumberReadable,
48
47
  formatTankHeadFromCm: () => formatTankHeadFromCm,
48
+ formatWaterByGroup: () => formatWaterByGroup,
49
49
  formatWaterVolumeM3: () => formatWaterVolumeM3,
50
50
  getAvailableContexts: () => getAvailableContexts,
51
51
  getDateRangeArray: () => getDateRangeArray,
@@ -189,18 +189,15 @@ function calcDeltaPercent(prev, current) {
189
189
  return { value: 0, type: "neutral" };
190
190
  }
191
191
  }
192
- function formatEnergyByGroup(value, group) {
192
+ function formatWaterByGroup(value, group) {
193
193
  if (value === null || value === void 0 || isNaN(value)) {
194
194
  return "-";
195
195
  }
196
196
  if (group === "Caixas D'\xC1gua") {
197
197
  return formatTankHeadFromCm(value);
198
198
  }
199
- if (value >= 1e6) {
200
- return formatWaterVolumeM3(value / 1e6) + " (GWh scale)";
201
- }
202
199
  if (value >= 1e3) {
203
- return formatWaterVolumeM3(value / 1e3) + " (MWh scale)";
200
+ return formatWaterVolumeM3(value / 1e3) + " x 10\xB3 ";
204
201
  }
205
202
  return formatWaterVolumeM3(value);
206
203
  }
@@ -1428,9 +1425,9 @@ function renderCardComponent({
1428
1425
  formatDateToYMD,
1429
1426
  formatDateWithTimezoneOffset,
1430
1427
  formatEnergy,
1431
- formatEnergyByGroup,
1432
1428
  formatNumberReadable,
1433
1429
  formatTankHeadFromCm,
1430
+ formatWaterByGroup,
1434
1431
  formatWaterVolumeM3,
1435
1432
  getAvailableContexts,
1436
1433
  getDateRangeArray,
package/dist/index.d.cts CHANGED
@@ -59,12 +59,12 @@ declare function calcDeltaPercent(prev: number, current: number): {
59
59
  type: 'increase' | 'decrease' | 'neutral';
60
60
  };
61
61
  /**
62
- * Formats energy/water values based on group type (from MAIN_WATER controller)
63
- * @param value - The value to format
62
+ * Formats water values based on group type (from MAIN_WATER controller)
63
+ * @param value - The value to format in cubic meters
64
64
  * @param group - The group type ('Caixas D\'Água', 'Lojas', 'Área Comum')
65
65
  * @returns Formatted string with appropriate unit
66
66
  */
67
- declare function formatEnergyByGroup(value: number, group: string): string;
67
+ declare function formatWaterByGroup(value: number, group: string): string;
68
68
  /**
69
69
  * Formats all values in the same unit for consistent display
70
70
  * @param values - Array of values to format
@@ -386,4 +386,4 @@ declare function renderCardComponent({ entityObject, handleActionDashboard, hand
386
386
  handleClickCard: any;
387
387
  }): any;
388
388
 
389
- export { type StoreRow, type TimedValue, type WaterRow, addDetectionContext, addNamespace, averageByDay, buildWaterReportCSV, buildWaterStoresCSV, calcDeltaPercent, classify, classifyWaterLabel, classifyWaterLabels, decodePayload, decodePayloadBase64Xor, detectDeviceType, determineInterval, exportToCSV, exportToCSVAll, findValue, fmtPerc$1 as fmtPerc, fmtPerc as fmtPercLegacy, formatAllInSameUnit, formatAllInSameWaterUnit, formatDateForInput, formatDateToYMD, formatDateWithTimezoneOffset, formatEnergy, formatEnergyByGroup, formatNumberReadable, formatTankHeadFromCm, formatWaterVolumeM3, getAvailableContexts, getDateRangeArray, getSaoPauloISOString, getSaoPauloISOStringFixed, getValueByDatakey, getValueByDatakeyLegacy, getWaterCategories, groupByDay, isWaterCategory, normalizeRecipients, numbers, parseInputDateToDate, renderCardComponent, strings, timeWindowFromInputYMD, toCSV, toFixedSafe };
389
+ export { type StoreRow, type TimedValue, type WaterRow, addDetectionContext, addNamespace, averageByDay, buildWaterReportCSV, buildWaterStoresCSV, calcDeltaPercent, classify, classifyWaterLabel, classifyWaterLabels, decodePayload, decodePayloadBase64Xor, detectDeviceType, determineInterval, exportToCSV, exportToCSVAll, findValue, fmtPerc$1 as fmtPerc, fmtPerc as fmtPercLegacy, formatAllInSameUnit, formatAllInSameWaterUnit, formatDateForInput, formatDateToYMD, formatDateWithTimezoneOffset, formatEnergy, formatNumberReadable, formatTankHeadFromCm, formatWaterByGroup, formatWaterVolumeM3, getAvailableContexts, getDateRangeArray, getSaoPauloISOString, getSaoPauloISOStringFixed, getValueByDatakey, getValueByDatakeyLegacy, getWaterCategories, groupByDay, isWaterCategory, normalizeRecipients, numbers, parseInputDateToDate, renderCardComponent, strings, timeWindowFromInputYMD, toCSV, toFixedSafe };
package/dist/index.js CHANGED
@@ -126,18 +126,15 @@ function calcDeltaPercent(prev, current) {
126
126
  return { value: 0, type: "neutral" };
127
127
  }
128
128
  }
129
- function formatEnergyByGroup(value, group) {
129
+ function formatWaterByGroup(value, group) {
130
130
  if (value === null || value === void 0 || isNaN(value)) {
131
131
  return "-";
132
132
  }
133
133
  if (group === "Caixas D'\xC1gua") {
134
134
  return formatTankHeadFromCm(value);
135
135
  }
136
- if (value >= 1e6) {
137
- return formatWaterVolumeM3(value / 1e6) + " (GWh scale)";
138
- }
139
136
  if (value >= 1e3) {
140
- return formatWaterVolumeM3(value / 1e3) + " (MWh scale)";
137
+ return formatWaterVolumeM3(value / 1e3) + " x 10\xB3 ";
141
138
  }
142
139
  return formatWaterVolumeM3(value);
143
140
  }
@@ -1364,9 +1361,9 @@ export {
1364
1361
  formatDateToYMD,
1365
1362
  formatDateWithTimezoneOffset,
1366
1363
  formatEnergy,
1367
- formatEnergyByGroup,
1368
1364
  formatNumberReadable,
1369
1365
  formatTankHeadFromCm,
1366
+ formatWaterByGroup,
1370
1367
  formatWaterVolumeM3,
1371
1368
  getAvailableContexts,
1372
1369
  getDateRangeArray,
@@ -132,18 +132,15 @@
132
132
  return { value: 0, type: "neutral" };
133
133
  }
134
134
  }
135
- function formatEnergyByGroup(value, group) {
135
+ function formatWaterByGroup(value, group) {
136
136
  if (value === null || value === void 0 || isNaN(value)) {
137
137
  return "-";
138
138
  }
139
139
  if (group === "Caixas D'\xC1gua") {
140
140
  return formatTankHeadFromCm(value);
141
141
  }
142
- if (value >= 1e6) {
143
- return formatWaterVolumeM3(value / 1e6) + " (GWh scale)";
144
- }
145
142
  if (value >= 1e3) {
146
- return formatWaterVolumeM3(value / 1e3) + " (MWh scale)";
143
+ return formatWaterVolumeM3(value / 1e3) + " x 10\xB3 ";
147
144
  }
148
145
  return formatWaterVolumeM3(value);
149
146
  }
@@ -1370,9 +1367,9 @@
1370
1367
  exports.formatDateToYMD = formatDateToYMD;
1371
1368
  exports.formatDateWithTimezoneOffset = formatDateWithTimezoneOffset;
1372
1369
  exports.formatEnergy = formatEnergy;
1373
- exports.formatEnergyByGroup = formatEnergyByGroup;
1374
1370
  exports.formatNumberReadable = formatNumberReadable;
1375
1371
  exports.formatTankHeadFromCm = formatTankHeadFromCm;
1372
+ exports.formatWaterByGroup = formatWaterByGroup;
1376
1373
  exports.formatWaterVolumeM3 = formatWaterVolumeM3;
1377
1374
  exports.getAvailableContexts = getAvailableContexts;
1378
1375
  exports.getDateRangeArray = getDateRangeArray;
@@ -1 +1 @@
1
- (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.MyIOLibrary={}))})(this,function(exports){"use strict";var __defProp=Object.defineProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};function formatEnergy(value,unit){if(value===null||value===void 0||isNaN(value)){return"-"}let adjustedValue=value;let adjustedUnit=unit;if(!adjustedUnit){if(value>=1e6){adjustedValue=value/1e6;adjustedUnit="GWh"}else if(value>=1e3){adjustedValue=value/1e3;adjustedUnit="MWh"}else{adjustedUnit="kWh"}}const formattedValue=adjustedValue.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} ${adjustedUnit}`}function formatAllInSameUnit(values,targetUnit,sourceUnit="kWh"){const unitMultipliers={kWh:1,MWh:1e3,GWh:1e6};const targetMultiplier=unitMultipliers[targetUnit]||1;if(typeof values[0]==="number"){const numberValues=values;const sourceMultiplier=unitMultipliers[sourceUnit]||1;return numberValues.map(value=>{if(value===null||value===void 0||isNaN(value)){return"-"}const convertedValue=value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}const objectValues=values;return objectValues.map(item=>{if(item.value===null||item.value===void 0||isNaN(item.value)){return"-"}const sourceMultiplier=unitMultipliers[item.unit]||1;const convertedValue=item.value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}function fmtPerc(value){if(value===null||value===void 0||isNaN(value)||!isFinite(value)){return"-"}const percentage=value*100;return percentage.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2})+"%"}function formatNumberReadable(value,locale="pt-BR",minimumFractionDigits=2,maximumFractionDigits=2){const n=typeof value==="string"?Number(value.replace(",",".")):Number(value);if(!Number.isFinite(n)){return"-"}const safe=Object.is(n,-0)?0:n;return safe.toLocaleString(locale,{minimumFractionDigits:minimumFractionDigits,maximumFractionDigits:maximumFractionDigits})}function formatWaterVolumeM3(value,locale="pt-BR"){if(value===null||value===void 0||isNaN(value)){return"-"}const formattedValue=value.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} M³`}function formatTankHeadFromCm(valueCm,locale="pt-BR"){if(valueCm===null||valueCm===void 0||isNaN(valueCm)){return"-"}const valueMeters=valueCm/100;const formattedValue=valueMeters.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} m.c.a.`}function calcDeltaPercent(prev,current){if(prev===null||prev===void 0||isNaN(prev)||current===null||current===void 0||isNaN(current)){return{value:0,type:"neutral"}}if(prev===0&&current===0){return{value:0,type:"neutral"}}if(prev===0&&current>0){return{value:100,type:"increase"}}if(prev===0&&current<0){return{value:100,type:"decrease"}}const percentChange=(current-prev)/prev*100;if(percentChange>0){return{value:percentChange,type:"increase"}}else if(percentChange<0){return{value:Math.abs(percentChange),type:"decrease"}}else{return{value:0,type:"neutral"}}}function formatEnergyByGroup(value,group){if(value===null||value===void 0||isNaN(value)){return"-"}if(group==="Caixas D'Água"){return formatTankHeadFromCm(value)}if(value>=1e6){return formatWaterVolumeM3(value/1e6)+" (GWh scale)"}if(value>=1e3){return formatWaterVolumeM3(value/1e3)+" (MWh scale)"}return formatWaterVolumeM3(value)}function formatAllInSameWaterUnit(values){const max=Math.max(...values.filter(v=>!isNaN(v)&&v!==null&&v!==void 0));let divisor=1;let unit="M³";if(max>=1e6){divisor=1e6;unit="M³"}else if(max>=1e3){divisor=1e3;unit="M³"}return{format:val=>{if(val===null||val===void 0||isNaN(val)){return"-"}return(val/divisor).toFixed(2)+" "+unit},unit:unit}}function formatDateToYMD(date){if(!date){return""}const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const year=dateObj.getFullYear();const month=String(dateObj.getMonth()+1).padStart(2,"0");const day=String(dateObj.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function determineInterval(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);if(isNaN(start.getTime())||isNaN(end.getTime())){return"day"}const diffMs=end.getTime()-start.getTime();const diffDays=diffMs/(1e3*60*60*24);if(diffDays<=1){return"hour"}else if(diffDays<=7){return"day"}else if(diffDays<=31){return"week"}else if(diffDays<=365){return"month"}else{return"year"}}function getSaoPauloISOString(date,edge="start"){const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const saoPauloOffset=-3;const saoPauloDate=new Date(dateObj.getTime()+saoPauloOffset*60*60*1e3);if(edge==="start"){saoPauloDate.setHours(0,0,0,0)}else{saoPauloDate.setHours(23,59,59,999)}const utcDate=new Date(saoPauloDate.getTime()-saoPauloOffset*60*60*1e3);return utcDate.toISOString()}function getDateRangeArray(startDate,endDate,interval="day"){const start=new Date(startDate);const end=new Date(endDate);const dates=[];if(isNaN(start.getTime())||isNaN(end.getTime())||start>end){return dates}const current=new Date(start);while(current<=end){dates.push(new Date(current));switch(interval){case"day":current.setDate(current.getDate()+1);break;case"week":current.setDate(current.getDate()+7);break;case"month":current.setMonth(current.getMonth()+1);break;case"year":current.setFullYear(current.getFullYear()+1);break;default:current.setDate(current.getDate()+1)}}return dates}function formatDateForInput(date){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function parseInputDateToDate(inputDateStr){if(!inputDateStr){return null}const parts=inputDateStr.split("-");if(parts.length!==3){return null}const year=parseInt(parts[0],10);const month=parseInt(parts[1],10)-1;const day=parseInt(parts[2],10);if(isNaN(year)||isNaN(month)||isNaN(day)){return null}return new Date(year,month,day,0,0,0,0)}function timeWindowFromInputYMD(startYmd,endYmd,tzOffset="-03:00"){if(!startYmd||!endYmd){return{startTs:0,endTs:0}}const startParts=startYmd.split("-");const endParts=endYmd.split("-");if(startParts.length!==3||endParts.length!==3){return{startTs:0,endTs:0}}const startDate=new Date(parseInt(startParts[0],10),parseInt(startParts[1],10)-1,parseInt(startParts[2],10),0,0,0,0);const endDate=new Date(parseInt(endParts[0],10),parseInt(endParts[1],10)-1,parseInt(endParts[2],10),23,59,59,999);return{startTs:startDate.getTime(),endTs:endDate.getTime()}}function formatDateWithTimezoneOffset(date,endOfDay=false,tzOffset="-03:00"){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");let hours,minutes,seconds,milliseconds;if(endOfDay){hours="23";minutes="59";seconds="59";milliseconds="999"}else{hours=String(date.getHours()).padStart(2,"0");minutes=String(date.getMinutes()).padStart(2,"0");seconds=String(date.getSeconds()).padStart(2,"0");milliseconds=String(date.getMilliseconds()).padStart(3,"0")}return`${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${tzOffset}`}function getSaoPauloISOStringFixed(dateStr,endOfDay=false){if(!dateStr)return"";if(endOfDay){return`${dateStr}T23:59:59.999-03:00`}else{return`${dateStr}T00:00:00.000-03:00`}}function averageByDay(data){if(!data||data.length===0){return[]}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});const result=Object.entries(grouped).map(([day,values])=>{const sum=values.reduce((acc,val)=>acc+val,0);const average=sum/values.length;return{day:day,average:average}});result.sort((a,b)=>a.day.localeCompare(b.day));return result}function groupByDay(data){if(!data||data.length===0){return{}}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});return grouped}function exportToCSV(data,headers,filename){if(!data||data.length===0){return""}const csvHeaders=headers.join(",");const csvRows=data.map(row=>headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue}).join(","));const csvContent=[csvHeaders,...csvRows].join("\n");return csvContent}function exportToCSVAll(storesData,headers,filename){if(!storesData||Object.keys(storesData).length===0){return""}const csvRows=[];const csvHeaders=["Store",...headers].join(",");csvRows.push(csvHeaders);Object.entries(storesData).forEach(([storeName,storeData])=>{if(!storeData||storeData.length===0){return}storeData.forEach(row=>{let formattedStoreName=storeName;if(storeName.includes(",")||storeName.includes('"')||storeName.includes("\n")){formattedStoreName=`"${storeName.replace(/"/g,'""')}"`}const csvRow=[formattedStoreName,...headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue})].join(",");csvRows.push(csvRow)})});return csvRows.join("\n")}function buildWaterReportCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumptionStr=String(row.totalConsumption).replace(",",".");const consumption=Number(consumptionStr)||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);if(meta.name&&meta.identifier){csvRows.push(["Loja:",meta.name,meta.identifier])}csvRows.push(["Data","Dia da Semana","Consumo Médio (m³)","Consumo Mínimo (m³)","Consumo Máximo (m³)","Consumo (m³)"]);rows.forEach(row=>{csvRows.push([row.formattedDate,row.day,String(row.avgConsumption),String(row.minDemand),String(row.maxDemand),String(row.totalConsumption)])});return csvRows.map(row=>row.join(";")).join("\n")}function buildWaterStoresCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);csvRows.push(["Loja","Identificador","Consumo"]);rows.forEach(row=>{const label=row.entityLabel||row.deviceName||"-";const deviceId=row.deviceId||"-";const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;const formattedConsumption=consumption!==null&&consumption!==void 0?formatNumberReadable(consumption):"0,00";csvRows.push([label,deviceId,formattedConsumption])});return csvRows.map(row=>row.join(";")).join("\n")}function toCSV(rows,delimiter=";"){if(!rows||rows.length===0){return""}return rows.map(row=>row.map(cell=>{const value=String(cell);if(value.includes(delimiter)||value.includes('"')||value.includes("\n")){return`"${value.replace(/"/g,'""')}"`}return value}).join(delimiter)).join("\n")}function classify(entity,criteria){if(!entity||!criteria){return{category:"unknown",confidence:0}}let category="unknown";let subcategory;let confidence=0;if(entity.type){switch(entity.type.toLowerCase()){case"consumption":category="energy_consumption";confidence=.9;break;case"generation":category="energy_generation";confidence=.9;break;case"storage":category="energy_storage";confidence=.9;break;case"distribution":category="energy_distribution";confidence=.8;break;default:category="energy_other";confidence=.5}}if(entity.powerRating){const power=parseFloat(entity.powerRating);if(!isNaN(power)){if(power<1e3){subcategory="small_scale"}else if(power<1e4){subcategory="medium_scale"}else{subcategory="large_scale"}confidence=Math.min(confidence+.1,1)}}return{category:category,subcategory:subcategory,confidence:confidence}}function classifyWaterLabel(label){if(!label){console.warn('classifyWaterLabel: empty label, defaulting to "Lojas"');return"Lojas"}const normalizedLabel=label.toLowerCase().trim();if(/rel[óo]gio|caixa|superior|inferior|nível_terraço/.test(normalizedLabel)){return"Caixas D'Água"}if(/administra|bomba|chiller|adm/.test(normalizedLabel)){return"Área Comum"}return"Lojas"}function classifyWaterLabels(labels){const counts={"Caixas D'Água":0,Lojas:0,"Área Comum":0,total:0};labels.forEach(label=>{const category=classifyWaterLabel(label);counts[category]++;counts.total++});return counts}function getWaterCategories(){return["Caixas D'Água","Lojas","Área Comum"]}function isWaterCategory(label,category){return classifyWaterLabel(label)===category}function getValueByDatakey(data,datakey){if(!data||!datakey){return void 0}if(Array.isArray(data)){for(const item of data){const value=getValueByDatakey(item,datakey);if(value!==void 0){return value}}return void 0}if(typeof data==="object"&&data!==null){const keys=datakey.split(".");let current=data;for(const key of keys){if(current===null||current===void 0){return void 0}if(key.includes("[")&&key.includes("]")){const arrayKey=key.substring(0,key.indexOf("["));const indexMatch=key.match(/\[(\d+)\]/);if(indexMatch){const index=parseInt(indexMatch[1],10);current=current[arrayKey];if(Array.isArray(current)&&index>=0&&index<current.length){current=current[index]}else{return void 0}}else{return void 0}}else{current=current[key]}}return current}return void 0}function getValueByDatakeyLegacy(dataList,dataSourceNameTarget,dataKeyTarget){if(!Array.isArray(dataList)||!dataSourceNameTarget||!dataKeyTarget){return void 0}for(const item of dataList){if(item&&item.dataSourceName===dataSourceNameTarget&&item.dataKey===dataKeyTarget){return item.value}}return void 0}function findValue(data,keyOrPath,legacyDataKey){if(legacyDataKey!==void 0){return getValueByDatakeyLegacy(data,keyOrPath,legacyDataKey)}return getValueByDatakey(data,keyOrPath)}var contexts={building:name=>{const upper=name.toUpperCase();if(upper.includes("COMPRESSOR"))return"COMPRESSOR";if(upper.includes("VENT"))return"VENTILADOR";if(upper.includes("AUTOMATICO")||upper.includes("AUTOMÁTICO"))return"SELETOR_AUTO_MANUAL";if(upper.includes("TERMOSTATO"))return"TERMOSTATO";if(upper.includes("3F"))return"3F_MEDIDOR";if(upper.includes("TERMO")||upper.includes("TEMP"))return"TERMOSTATO";if(upper.includes("HIDR"))return"HIDROMETRO";if(upper.includes("ABRE"))return"SOLENOIDE";if(upper.includes("RECALQUE"))return"MOTOR";if(upper.includes("AUTOMACAO")||upper.includes("AUTOMAÇÃO"))return"GLOBAL_AUTOMACAO";if(upper.includes("AC"))return"CONTROLE REMOTO";if(upper.includes("SCD"))return"CAIXA_D_AGUA";return"default"},mall:name=>{const upper=name.toUpperCase();if(upper.includes("CHILLER"))return"CHILLER";if(upper.includes("ESCADA"))return"ESCADA_ROLANTE";if(upper.includes("LOJA"))return"LOJA_SENSOR";if(upper.includes("ILUMINACAO")||upper.includes("ILUMINAÇÃO"))return"ILUMINACAO";return"default"}};function detectDeviceType(name,context="building"){if(typeof name!=="string"){throw new Error("Device name must be a string.")}const detectFunction=contexts[context];if(!detectFunction){console.warn(`[myio-js-library] Context "${context}" not found. Using default fallback.`);return contexts.building(name)}return detectFunction(name)}function getAvailableContexts(){return Object.keys(contexts)}function addDetectionContext(contextName,detectFunction){if(typeof contextName!=="string"){throw new Error("Context name must be a string.")}if(typeof detectFunction!=="function"){throw new Error("Detection function must be a function.")}contexts[contextName]=detectFunction}function addNamespace(payload,namespace=""){if(!payload||typeof payload!=="object"||Array.isArray(payload)){throw new Error("Payload must be an object.")}const keys=Object.keys(payload);const suffix=namespace.trim()?` (${namespace.trim()})`:"";return keys.reduce((acc,key)=>{acc[`${key}${suffix}`]=payload[key];return acc},{})}var numbers_exports={};__export(numbers_exports,{fmtPerc:()=>fmtPerc2,toFixedSafe:()=>toFixedSafe});function fmtPerc2(x,digits=2){if(!Number.isFinite(x))return"—";return(x*100).toFixed(digits)+"%"}function toFixedSafe(x,digits=2){if(!Number.isFinite(x))return"—";return x.toFixed(digits)}var strings_exports={};__export(strings_exports,{normalizeRecipients:()=>normalizeRecipients});function normalizeRecipients(val){if(val===null||val===void 0||val==="")return"";if(Object.prototype.toString.call(val)==="[object Array]"){return val.filter(Boolean).join(",")}let s=String(val).trim();if(/^\s*\[/.test(s)){try{const arr=JSON.parse(s);if(Object.prototype.toString.call(arr)==="[object Array]"){return arr.filter(Boolean).join(",")}}catch{}}s=s.replace(/[;\s]+/g,",");s=s.replace(/,+/g,",").replace(/^,|,$/g,"");return s}function decodePayload(encoded,key){const bytes=base64ToBytesStrict(encoded);if(bytes.length===0)return"";if(key===""||key===void 0||key===null){return(new TextDecoder).decode(bytes)}if(typeof key==="number"&&Number.isFinite(key)){const k=key&255;for(let i=0;i<bytes.length;i++)bytes[i]^=k;return(new TextDecoder).decode(bytes)}const keyStr=String(key);const keyBytes=(new TextEncoder).encode(keyStr);if(keyBytes.length===0){return(new TextDecoder).decode(bytes)}for(let i=0;i<bytes.length;i++){bytes[i]^=keyBytes[i%keyBytes.length]}return(new TextDecoder).decode(bytes)}function decodePayloadBase64Xor(encoded,xorKey=73){const bytes=base64ToBytesStrict(encoded);for(let i=0;i<bytes.length;i++)bytes[i]^=xorKey&255;return(new TextDecoder).decode(bytes)}function base64ToBytesStrict(b64){if(b64===""||b64===void 0||b64===null)return new Uint8Array;const s=String(b64).replace(/\s+/g,"");const re=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;if(!re.test(s))throw new Error("Invalid base64");if(typeof Buffer!=="undefined"&&Buffer.from){return Uint8Array.from(Buffer.from(s,"base64"))}const bin=atob(s);const out=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)out[i]=bin.charCodeAt(i);return out}function renderCardComponent({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard}){const{entityId:entityId,labelOrName:labelOrName,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},isOn:isOn=false,perc:perc=0,group:group,connectionStatus:connectionStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timaVal:timaVal,valType:valType}=entityObject;const MyIO=typeof MyIOLibrary!=="undefined"&&MyIOLibrary||typeof window!=="undefined"&&window.MyIOLibrary||{formatEnergyByGroup:(v,g)=>`${v} kWh`,formatNumberReadable:n=>Number(n??0).toFixed(1)};let valFormatted=MyIO.formatEnergyByGroup(val);if(valType==="ENERGY"){valFormatted=MyIO.formatEnergyByGroup(val)}else if(valType==="WATER"){valFormatted=`${val} m³`}else if(valType==="TANK"){valFormatted=`${val} m.c.a`}else{valFormatted=val}const percFormatted=MyIO.formatNumberReadable(perc);if(!document.getElementById("myio-card-styles")){const style=document.createElement("style");style.id="myio-card-styles";style.textContent=` \n.device-card-centered,\n.clickable {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: transform .2s;\n min-height: 140px;\n box-sizing: border-box;\n overflow: hidden;\n}\n\n.device-card-centered:hover,\n.clickable:hover {\n transform: scale(1.05);\n}\n\n.device-title-row {\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 4px;\n padding: 0 4px;\n min-height: 22px;\n}\n\n.device-title {\n font-weight: 700;\n font-size: .85rem;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n}\n\n.device-image {\n max-height: 44px;\n width: auto;\n margin: 4px 0;\n display: block;\n}\n\n.device-data-row {\n display: flex;\n justify-content: center;\n align-items: center;\n margin-top: auto;\n margin-bottom: 6px;\n gap: 6px;\n width: 100%;\n}\n\n.consumption-main {\n font-size: .9rem;\n font-weight: 700;\n color: #28a745;\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: center;\n white-space: nowrap;\n}\n\n.device-title-percent {\n font-size: .75rem;\n color: rgba(0, 0, 0, .45);\n font-weight: 500;\n}\n\n.flash {\n animation: flash 1s infinite;\n color: #ff9800;\n}\n\n@keyframes flash {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: .2;\n }\n 100% {\n opacity: 1;\n }\n}\n\n.card-actions {\n width: 10%;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n}\n\n.card-action img {\n width: 24px;\n height: 24px;\n transition: transform .2s ease;\n cursor: pointer;\n}\n\n.card-action img:hover {\n transform: scale(1.15);\n}\n\n.device-card-centered.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n}\n\n\n.device-info {\n position: absolute;\n top: 8px; /* distância do topo */\n right: 8px; /* distância da direita */\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 50%;\n transition: background 0.2s;\n}\n\n.device-info:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n@keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n font-size: 1.2rem;\n} \n.device-card-centered {\n perspective: 1000px; /* perspectiva para 3D */\n}\n\n.device-card-inner {\n position: relative;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n}\n\n.device-card-front {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: flex-start; /* topo */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n}\n\n.device-card-back {\n display: flex;\n flex-direction: column; /* muda para coluna se quiser empilhar elementos verticalmente */\n justify-content: flex-start; /* conteúdo começa do topo */\n align-items: center; /* centraliza horizontalmente */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n transform: rotateY(180deg); /* mantém flip */\n padding: 10px; /* opcional: espaço interno */\n gap: 10px; /* opcional: espaço entre elementos */\n}\n\n\n\n.device-info {\n position: absolute;\n top: 8px; /* distância do topo */\n right: 8px; /* distância da direita */\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 50%;\n transition: background 0.2s;l\n}\n.device-card-back {\n transform: rotateY(180deg); /* faz o flip funcionar */\n}\n\n.device-card-front .device-info,\n.device-card-back .device-info {\n position: absolute; /* tira do fluxo do flex */\n top: 0; /* topo do card */\n right: 8px; /* canto direito */\n margin: 0; /* remove qualquer margem que empurre o botão */\n display: flex; /* mantém o conteúdo do botão centralizado */\n align-items: center; \n justify-content: center; \n padding: 4px; \n border-radius: 50%; \n cursor: pointer;\n z-index: 10; \n}\n\n\n.device-card-back {\n\n display: flex;\n flex-direction: column; /* empilha os elementos */\n justify-content: flex-start;\n align-items: flex-start; /* tudo à esquerda por padrão */\n padding: 10px;\n gap: 10px;\n height: 100%;\n margin-left: -10px; \n}\n\n.device-card-back .value-container {\n display: flex;\n flex-direction: row; /* ícone e valor lado a lado */\n align-items: center;\n gap: 5px;\n color: #c19efc; /* roxo clarinho */\n align-self: center; /* centraliza horizontalmente no card */\n}\n\n.device-card-back #lastconsumptionTime {\n\n display: flex;\n flex-direction: column;\n align-items: flex-start; /* mantém à esquerda */\n gap: 2px;\n font-size: 11px;\n font-weight: bold;\n color: black;\n}\n\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n.flash-icon.flash {\n animation: icon-blink 1s infinite;\n}\n\n@keyframes icon-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.2; transform: scale(1.2); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n}\n\n.device-card-centered.online .flash-icon {\n color: #28a745 !important; /* verde premium para online */\n}\n `;document.head.appendChild(style)}let formattedDateVal;let formattedDate;let Infcolor="#5cb85c";if(connectionStatusTime){const date=new Date(connectionStatusTime);const day=String(date.getDate()).padStart(2,"0");const month=String(date.getMonth()+1).padStart(2,"0");const year=date.getFullYear();const hours=String(date.getHours()).padStart(2,"0");const minutes=String(date.getMinutes()).padStart(2,"0");formattedDate=`Online: (${day}/${month}/${year} - ${hours}:${minutes})`;const datVal=new Date(timaVal);const dayVal=String(datVal.getDate()).padStart(2,"0");const monthVal=String(datVal.getMonth()+1).padStart(2,"0");const yearVal=date.getFullYear();const hoursVal=String(date.getHours()).padStart(2,"0");const minutesVal=String(date.getMinutes()).padStart(2,"0");const now=new Date;const diffMs=now-datVal;const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMs/(1e3*60*60));const diffDays=Math.floor(diffMs/(1e3*60*60*24));let diffText="";if(diffMinutes<60){diffText=`${diffMinutes} minuto${diffMinutes!==1?"s":""}`}else if(diffHours<24){diffText=`${diffHours} hora${diffHours!==1?"s":""}`}else{diffText=`${diffDays} dia${diffDays!==1?"s":""} `}formattedDateVal=`${dayVal}/${monthVal}/${yearVal} - ${hoursVal}:${minutesVal} (${diffText})`;if(diffHours>=24){Infcolor="#cc2900"}else if(diffMinutes>=30){Infcolor="#e89105"}}else{Infcolor="#d6dcdd"}function normalizeString(str){return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/8Ezn8qVBJ3jXD0iDfnEAZ0MZhAP1b5Ts","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType);const img=deviceImages[nameType]||defaultImage;const html=`\n <div class="device-card-centered clickable ${connectionStatus==="offline"?"offline":""}"\n data-entity-id="${entityId}"\n data-entity-label="${labelOrName}"\n data-entity-type="${entityType}"\n data-entity-slaveid="${slaveId}"\n data-entity-ingestionid="${ingestionId}"\n data-entity-consumption="${val}"\n data-entity-centralid="${centralId}"\n data-entity-updated-identifiers='${JSON.stringify(updatedIdentifiers)}'>\n <div class="device-card-inner" style="width:100%; height:100%; transform-style: preserve-3d; transition: transform 0.6s;">\n <div class="device-card-front">\n\n <div class="card-actions" >\n ${typeof handleActionDashboard==="function"?`\n <div class="card-action action-dashboard" data-action="dashboard" title="Dashboard">\n <img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>\n </div>`:``}\n ${typeof handleActionReport==="function"?`\n <div class="card-action action-report" data-action="report" title="Relatório">\n <img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>\n </div>`:``}\n ${typeof handleActionSettings==="function"?`\n <div class="card-action action-settings" data-action="settings" title="Configurações">\n <img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>\n </div>`:``}\n ${typeof handleSelect==="function"?`\n <input class="card-action action-checker" data-action="checker" title="Selecionar" type="checkbox">`:``}\n </div>\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;width:85%">\n <div class="device-title-row">\n <span class="device-title" title="${labelOrName}">\n ${String(labelOrName??"").length>15?String(labelOrName).slice(0,15)+"…":String(labelOrName??"")}\n </span>\n </div>\n <img class="device-image" src="${img}" />\n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${connectionStatus==="offline"?"flash":isOn?"flash":""}">\n ${connectionStatus==="offline"?"🚨":"⚡"}\n </span>\n <span class="consumption-value" data-entity-consumption="${val}">${valFormatted}</span>\n <span class="device-title-percent">(${percFormatted}%)</span>\n </div>\n </div>\n </div>\n \n ${handInfo?`\n <button id="infoButtom-front" class="device-info info-button">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>`:``}\n </div>\n <div class="device-card-back" >\n <div id="status-bar"> \n <div id="status-information">\n <div style="font-size: 0.85rem; font-weight: bold; line-height: 1;"><span>Central: ${centralName}</span></div>\n <div style="display: flex; flex-direction: row; gap: 4px; font-weight: bold; align-items: center;">\n <div style="font-size: 12px; font-weight: bold; line-height: 1;"><span> ${formattedDate}</span></div>\n <div style="font-size: 10px; line-height: 1;"><span></span></div> \n </div>\n </div>\n <button id="infoButtom-back" class="device-info" style="background:none; border:none; cursor:pointer;">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>\n </div>\n\n <div class="value-container">\n <svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#dea404">\n <path d="m456-200 174-340H510v-220L330-420h126v220Zm24 120q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n <div><span>${MyIO.formatEnergyByGroup(val/1e3)||"-"}</span></div>\n </div>\n\n <div id="lastconsumptionTime">\n <div style="font-size: 12px; font-weight: bold; color: black; line-height: 1;">Ultima Telemetria:</div>\n <div style="font-size: 11px; font-weight: bold; color: black; line-height: 1;">${formattedDateVal}</div>\n </div>\n </div>\n </div>\n </div> \n </div>\n \n `;const $card=$(html);if(typeof handleActionDashboard==="function"){$card.find(".action-dashboard").on("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)})}if(typeof handleActionReport==="function"){$card.find(".action-report").on("click",e=>{e.stopPropagation();handleActionReport(entityObject)})}if(typeof handleActionSettings==="function"){$card.find(".action-settings").on("click",e=>{e.stopPropagation();handleActionSettings(entityObject)})}if(typeof handleSelect==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleSelect(entityObject)})}if(typeof handleClickCard==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleClickCard(entityObject)})}$card.on("click",e=>{if(!$(e.target).closest(".card-action").length){if(typeof handleClickCard==="function"){handleClickCard(entityObject)}else if(typeof handleActionDashboard==="function"){handleActionDashboard(entityObject)}}});$card.find("#infoButtom-front").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").addClass("flipped")});$card.find("#infoButtom-back").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").removeClass("flipped")});return $card}exports.addDetectionContext=addDetectionContext;exports.addNamespace=addNamespace;exports.averageByDay=averageByDay;exports.buildWaterReportCSV=buildWaterReportCSV;exports.buildWaterStoresCSV=buildWaterStoresCSV;exports.calcDeltaPercent=calcDeltaPercent;exports.classify=classify;exports.classifyWaterLabel=classifyWaterLabel;exports.classifyWaterLabels=classifyWaterLabels;exports.decodePayload=decodePayload;exports.decodePayloadBase64Xor=decodePayloadBase64Xor;exports.detectDeviceType=detectDeviceType;exports.determineInterval=determineInterval;exports.exportToCSV=exportToCSV;exports.exportToCSVAll=exportToCSVAll;exports.findValue=findValue;exports.fmtPerc=fmtPerc;exports.fmtPercLegacy=fmtPerc2;exports.formatAllInSameUnit=formatAllInSameUnit;exports.formatAllInSameWaterUnit=formatAllInSameWaterUnit;exports.formatDateForInput=formatDateForInput;exports.formatDateToYMD=formatDateToYMD;exports.formatDateWithTimezoneOffset=formatDateWithTimezoneOffset;exports.formatEnergy=formatEnergy;exports.formatEnergyByGroup=formatEnergyByGroup;exports.formatNumberReadable=formatNumberReadable;exports.formatTankHeadFromCm=formatTankHeadFromCm;exports.formatWaterVolumeM3=formatWaterVolumeM3;exports.getAvailableContexts=getAvailableContexts;exports.getDateRangeArray=getDateRangeArray;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.isWaterCategory=isWaterCategory;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.parseInputDateToDate=parseInputDateToDate;exports.renderCardComponent=renderCardComponent;exports.strings=strings_exports;exports.timeWindowFromInputYMD=timeWindowFromInputYMD;exports.toCSV=toCSV;exports.toFixedSafe=toFixedSafe});
1
+ (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.MyIOLibrary={}))})(this,function(exports){"use strict";var __defProp=Object.defineProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};function formatEnergy(value,unit){if(value===null||value===void 0||isNaN(value)){return"-"}let adjustedValue=value;let adjustedUnit=unit;if(!adjustedUnit){if(value>=1e6){adjustedValue=value/1e6;adjustedUnit="GWh"}else if(value>=1e3){adjustedValue=value/1e3;adjustedUnit="MWh"}else{adjustedUnit="kWh"}}const formattedValue=adjustedValue.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} ${adjustedUnit}`}function formatAllInSameUnit(values,targetUnit,sourceUnit="kWh"){const unitMultipliers={kWh:1,MWh:1e3,GWh:1e6};const targetMultiplier=unitMultipliers[targetUnit]||1;if(typeof values[0]==="number"){const numberValues=values;const sourceMultiplier=unitMultipliers[sourceUnit]||1;return numberValues.map(value=>{if(value===null||value===void 0||isNaN(value)){return"-"}const convertedValue=value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}const objectValues=values;return objectValues.map(item=>{if(item.value===null||item.value===void 0||isNaN(item.value)){return"-"}const sourceMultiplier=unitMultipliers[item.unit]||1;const convertedValue=item.value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}function fmtPerc(value){if(value===null||value===void 0||isNaN(value)||!isFinite(value)){return"-"}const percentage=value*100;return percentage.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2})+"%"}function formatNumberReadable(value,locale="pt-BR",minimumFractionDigits=2,maximumFractionDigits=2){const n=typeof value==="string"?Number(value.replace(",",".")):Number(value);if(!Number.isFinite(n)){return"-"}const safe=Object.is(n,-0)?0:n;return safe.toLocaleString(locale,{minimumFractionDigits:minimumFractionDigits,maximumFractionDigits:maximumFractionDigits})}function formatWaterVolumeM3(value,locale="pt-BR"){if(value===null||value===void 0||isNaN(value)){return"-"}const formattedValue=value.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} M³`}function formatTankHeadFromCm(valueCm,locale="pt-BR"){if(valueCm===null||valueCm===void 0||isNaN(valueCm)){return"-"}const valueMeters=valueCm/100;const formattedValue=valueMeters.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} m.c.a.`}function calcDeltaPercent(prev,current){if(prev===null||prev===void 0||isNaN(prev)||current===null||current===void 0||isNaN(current)){return{value:0,type:"neutral"}}if(prev===0&&current===0){return{value:0,type:"neutral"}}if(prev===0&&current>0){return{value:100,type:"increase"}}if(prev===0&&current<0){return{value:100,type:"decrease"}}const percentChange=(current-prev)/prev*100;if(percentChange>0){return{value:percentChange,type:"increase"}}else if(percentChange<0){return{value:Math.abs(percentChange),type:"decrease"}}else{return{value:0,type:"neutral"}}}function formatWaterByGroup(value,group){if(value===null||value===void 0||isNaN(value)){return"-"}if(group==="Caixas D'Água"){return formatTankHeadFromCm(value)}if(value>=1e3){return formatWaterVolumeM3(value/1e3)+" x 10³ "}return formatWaterVolumeM3(value)}function formatAllInSameWaterUnit(values){const max=Math.max(...values.filter(v=>!isNaN(v)&&v!==null&&v!==void 0));let divisor=1;let unit="M³";if(max>=1e6){divisor=1e6;unit="M³"}else if(max>=1e3){divisor=1e3;unit="M³"}return{format:val=>{if(val===null||val===void 0||isNaN(val)){return"-"}return(val/divisor).toFixed(2)+" "+unit},unit:unit}}function formatDateToYMD(date){if(!date){return""}const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const year=dateObj.getFullYear();const month=String(dateObj.getMonth()+1).padStart(2,"0");const day=String(dateObj.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function determineInterval(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);if(isNaN(start.getTime())||isNaN(end.getTime())){return"day"}const diffMs=end.getTime()-start.getTime();const diffDays=diffMs/(1e3*60*60*24);if(diffDays<=1){return"hour"}else if(diffDays<=7){return"day"}else if(diffDays<=31){return"week"}else if(diffDays<=365){return"month"}else{return"year"}}function getSaoPauloISOString(date,edge="start"){const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const saoPauloOffset=-3;const saoPauloDate=new Date(dateObj.getTime()+saoPauloOffset*60*60*1e3);if(edge==="start"){saoPauloDate.setHours(0,0,0,0)}else{saoPauloDate.setHours(23,59,59,999)}const utcDate=new Date(saoPauloDate.getTime()-saoPauloOffset*60*60*1e3);return utcDate.toISOString()}function getDateRangeArray(startDate,endDate,interval="day"){const start=new Date(startDate);const end=new Date(endDate);const dates=[];if(isNaN(start.getTime())||isNaN(end.getTime())||start>end){return dates}const current=new Date(start);while(current<=end){dates.push(new Date(current));switch(interval){case"day":current.setDate(current.getDate()+1);break;case"week":current.setDate(current.getDate()+7);break;case"month":current.setMonth(current.getMonth()+1);break;case"year":current.setFullYear(current.getFullYear()+1);break;default:current.setDate(current.getDate()+1)}}return dates}function formatDateForInput(date){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function parseInputDateToDate(inputDateStr){if(!inputDateStr){return null}const parts=inputDateStr.split("-");if(parts.length!==3){return null}const year=parseInt(parts[0],10);const month=parseInt(parts[1],10)-1;const day=parseInt(parts[2],10);if(isNaN(year)||isNaN(month)||isNaN(day)){return null}return new Date(year,month,day,0,0,0,0)}function timeWindowFromInputYMD(startYmd,endYmd,tzOffset="-03:00"){if(!startYmd||!endYmd){return{startTs:0,endTs:0}}const startParts=startYmd.split("-");const endParts=endYmd.split("-");if(startParts.length!==3||endParts.length!==3){return{startTs:0,endTs:0}}const startDate=new Date(parseInt(startParts[0],10),parseInt(startParts[1],10)-1,parseInt(startParts[2],10),0,0,0,0);const endDate=new Date(parseInt(endParts[0],10),parseInt(endParts[1],10)-1,parseInt(endParts[2],10),23,59,59,999);return{startTs:startDate.getTime(),endTs:endDate.getTime()}}function formatDateWithTimezoneOffset(date,endOfDay=false,tzOffset="-03:00"){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");let hours,minutes,seconds,milliseconds;if(endOfDay){hours="23";minutes="59";seconds="59";milliseconds="999"}else{hours=String(date.getHours()).padStart(2,"0");minutes=String(date.getMinutes()).padStart(2,"0");seconds=String(date.getSeconds()).padStart(2,"0");milliseconds=String(date.getMilliseconds()).padStart(3,"0")}return`${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${tzOffset}`}function getSaoPauloISOStringFixed(dateStr,endOfDay=false){if(!dateStr)return"";if(endOfDay){return`${dateStr}T23:59:59.999-03:00`}else{return`${dateStr}T00:00:00.000-03:00`}}function averageByDay(data){if(!data||data.length===0){return[]}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});const result=Object.entries(grouped).map(([day,values])=>{const sum=values.reduce((acc,val)=>acc+val,0);const average=sum/values.length;return{day:day,average:average}});result.sort((a,b)=>a.day.localeCompare(b.day));return result}function groupByDay(data){if(!data||data.length===0){return{}}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});return grouped}function exportToCSV(data,headers,filename){if(!data||data.length===0){return""}const csvHeaders=headers.join(",");const csvRows=data.map(row=>headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue}).join(","));const csvContent=[csvHeaders,...csvRows].join("\n");return csvContent}function exportToCSVAll(storesData,headers,filename){if(!storesData||Object.keys(storesData).length===0){return""}const csvRows=[];const csvHeaders=["Store",...headers].join(",");csvRows.push(csvHeaders);Object.entries(storesData).forEach(([storeName,storeData])=>{if(!storeData||storeData.length===0){return}storeData.forEach(row=>{let formattedStoreName=storeName;if(storeName.includes(",")||storeName.includes('"')||storeName.includes("\n")){formattedStoreName=`"${storeName.replace(/"/g,'""')}"`}const csvRow=[formattedStoreName,...headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue})].join(",");csvRows.push(csvRow)})});return csvRows.join("\n")}function buildWaterReportCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumptionStr=String(row.totalConsumption).replace(",",".");const consumption=Number(consumptionStr)||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);if(meta.name&&meta.identifier){csvRows.push(["Loja:",meta.name,meta.identifier])}csvRows.push(["Data","Dia da Semana","Consumo Médio (m³)","Consumo Mínimo (m³)","Consumo Máximo (m³)","Consumo (m³)"]);rows.forEach(row=>{csvRows.push([row.formattedDate,row.day,String(row.avgConsumption),String(row.minDemand),String(row.maxDemand),String(row.totalConsumption)])});return csvRows.map(row=>row.join(";")).join("\n")}function buildWaterStoresCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);csvRows.push(["Loja","Identificador","Consumo"]);rows.forEach(row=>{const label=row.entityLabel||row.deviceName||"-";const deviceId=row.deviceId||"-";const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;const formattedConsumption=consumption!==null&&consumption!==void 0?formatNumberReadable(consumption):"0,00";csvRows.push([label,deviceId,formattedConsumption])});return csvRows.map(row=>row.join(";")).join("\n")}function toCSV(rows,delimiter=";"){if(!rows||rows.length===0){return""}return rows.map(row=>row.map(cell=>{const value=String(cell);if(value.includes(delimiter)||value.includes('"')||value.includes("\n")){return`"${value.replace(/"/g,'""')}"`}return value}).join(delimiter)).join("\n")}function classify(entity,criteria){if(!entity||!criteria){return{category:"unknown",confidence:0}}let category="unknown";let subcategory;let confidence=0;if(entity.type){switch(entity.type.toLowerCase()){case"consumption":category="energy_consumption";confidence=.9;break;case"generation":category="energy_generation";confidence=.9;break;case"storage":category="energy_storage";confidence=.9;break;case"distribution":category="energy_distribution";confidence=.8;break;default:category="energy_other";confidence=.5}}if(entity.powerRating){const power=parseFloat(entity.powerRating);if(!isNaN(power)){if(power<1e3){subcategory="small_scale"}else if(power<1e4){subcategory="medium_scale"}else{subcategory="large_scale"}confidence=Math.min(confidence+.1,1)}}return{category:category,subcategory:subcategory,confidence:confidence}}function classifyWaterLabel(label){if(!label){console.warn('classifyWaterLabel: empty label, defaulting to "Lojas"');return"Lojas"}const normalizedLabel=label.toLowerCase().trim();if(/rel[óo]gio|caixa|superior|inferior|nível_terraço/.test(normalizedLabel)){return"Caixas D'Água"}if(/administra|bomba|chiller|adm/.test(normalizedLabel)){return"Área Comum"}return"Lojas"}function classifyWaterLabels(labels){const counts={"Caixas D'Água":0,Lojas:0,"Área Comum":0,total:0};labels.forEach(label=>{const category=classifyWaterLabel(label);counts[category]++;counts.total++});return counts}function getWaterCategories(){return["Caixas D'Água","Lojas","Área Comum"]}function isWaterCategory(label,category){return classifyWaterLabel(label)===category}function getValueByDatakey(data,datakey){if(!data||!datakey){return void 0}if(Array.isArray(data)){for(const item of data){const value=getValueByDatakey(item,datakey);if(value!==void 0){return value}}return void 0}if(typeof data==="object"&&data!==null){const keys=datakey.split(".");let current=data;for(const key of keys){if(current===null||current===void 0){return void 0}if(key.includes("[")&&key.includes("]")){const arrayKey=key.substring(0,key.indexOf("["));const indexMatch=key.match(/\[(\d+)\]/);if(indexMatch){const index=parseInt(indexMatch[1],10);current=current[arrayKey];if(Array.isArray(current)&&index>=0&&index<current.length){current=current[index]}else{return void 0}}else{return void 0}}else{current=current[key]}}return current}return void 0}function getValueByDatakeyLegacy(dataList,dataSourceNameTarget,dataKeyTarget){if(!Array.isArray(dataList)||!dataSourceNameTarget||!dataKeyTarget){return void 0}for(const item of dataList){if(item&&item.dataSourceName===dataSourceNameTarget&&item.dataKey===dataKeyTarget){return item.value}}return void 0}function findValue(data,keyOrPath,legacyDataKey){if(legacyDataKey!==void 0){return getValueByDatakeyLegacy(data,keyOrPath,legacyDataKey)}return getValueByDatakey(data,keyOrPath)}var contexts={building:name=>{const upper=name.toUpperCase();if(upper.includes("COMPRESSOR"))return"COMPRESSOR";if(upper.includes("VENT"))return"VENTILADOR";if(upper.includes("AUTOMATICO")||upper.includes("AUTOMÁTICO"))return"SELETOR_AUTO_MANUAL";if(upper.includes("TERMOSTATO"))return"TERMOSTATO";if(upper.includes("3F"))return"3F_MEDIDOR";if(upper.includes("TERMO")||upper.includes("TEMP"))return"TERMOSTATO";if(upper.includes("HIDR"))return"HIDROMETRO";if(upper.includes("ABRE"))return"SOLENOIDE";if(upper.includes("RECALQUE"))return"MOTOR";if(upper.includes("AUTOMACAO")||upper.includes("AUTOMAÇÃO"))return"GLOBAL_AUTOMACAO";if(upper.includes("AC"))return"CONTROLE REMOTO";if(upper.includes("SCD"))return"CAIXA_D_AGUA";return"default"},mall:name=>{const upper=name.toUpperCase();if(upper.includes("CHILLER"))return"CHILLER";if(upper.includes("ESCADA"))return"ESCADA_ROLANTE";if(upper.includes("LOJA"))return"LOJA_SENSOR";if(upper.includes("ILUMINACAO")||upper.includes("ILUMINAÇÃO"))return"ILUMINACAO";return"default"}};function detectDeviceType(name,context="building"){if(typeof name!=="string"){throw new Error("Device name must be a string.")}const detectFunction=contexts[context];if(!detectFunction){console.warn(`[myio-js-library] Context "${context}" not found. Using default fallback.`);return contexts.building(name)}return detectFunction(name)}function getAvailableContexts(){return Object.keys(contexts)}function addDetectionContext(contextName,detectFunction){if(typeof contextName!=="string"){throw new Error("Context name must be a string.")}if(typeof detectFunction!=="function"){throw new Error("Detection function must be a function.")}contexts[contextName]=detectFunction}function addNamespace(payload,namespace=""){if(!payload||typeof payload!=="object"||Array.isArray(payload)){throw new Error("Payload must be an object.")}const keys=Object.keys(payload);const suffix=namespace.trim()?` (${namespace.trim()})`:"";return keys.reduce((acc,key)=>{acc[`${key}${suffix}`]=payload[key];return acc},{})}var numbers_exports={};__export(numbers_exports,{fmtPerc:()=>fmtPerc2,toFixedSafe:()=>toFixedSafe});function fmtPerc2(x,digits=2){if(!Number.isFinite(x))return"—";return(x*100).toFixed(digits)+"%"}function toFixedSafe(x,digits=2){if(!Number.isFinite(x))return"—";return x.toFixed(digits)}var strings_exports={};__export(strings_exports,{normalizeRecipients:()=>normalizeRecipients});function normalizeRecipients(val){if(val===null||val===void 0||val==="")return"";if(Object.prototype.toString.call(val)==="[object Array]"){return val.filter(Boolean).join(",")}let s=String(val).trim();if(/^\s*\[/.test(s)){try{const arr=JSON.parse(s);if(Object.prototype.toString.call(arr)==="[object Array]"){return arr.filter(Boolean).join(",")}}catch{}}s=s.replace(/[;\s]+/g,",");s=s.replace(/,+/g,",").replace(/^,|,$/g,"");return s}function decodePayload(encoded,key){const bytes=base64ToBytesStrict(encoded);if(bytes.length===0)return"";if(key===""||key===void 0||key===null){return(new TextDecoder).decode(bytes)}if(typeof key==="number"&&Number.isFinite(key)){const k=key&255;for(let i=0;i<bytes.length;i++)bytes[i]^=k;return(new TextDecoder).decode(bytes)}const keyStr=String(key);const keyBytes=(new TextEncoder).encode(keyStr);if(keyBytes.length===0){return(new TextDecoder).decode(bytes)}for(let i=0;i<bytes.length;i++){bytes[i]^=keyBytes[i%keyBytes.length]}return(new TextDecoder).decode(bytes)}function decodePayloadBase64Xor(encoded,xorKey=73){const bytes=base64ToBytesStrict(encoded);for(let i=0;i<bytes.length;i++)bytes[i]^=xorKey&255;return(new TextDecoder).decode(bytes)}function base64ToBytesStrict(b64){if(b64===""||b64===void 0||b64===null)return new Uint8Array;const s=String(b64).replace(/\s+/g,"");const re=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;if(!re.test(s))throw new Error("Invalid base64");if(typeof Buffer!=="undefined"&&Buffer.from){return Uint8Array.from(Buffer.from(s,"base64"))}const bin=atob(s);const out=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)out[i]=bin.charCodeAt(i);return out}function renderCardComponent({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard}){const{entityId:entityId,labelOrName:labelOrName,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},isOn:isOn=false,perc:perc=0,group:group,connectionStatus:connectionStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timaVal:timaVal,valType:valType}=entityObject;const MyIO=typeof MyIOLibrary!=="undefined"&&MyIOLibrary||typeof window!=="undefined"&&window.MyIOLibrary||{formatEnergyByGroup:(v,g)=>`${v} kWh`,formatNumberReadable:n=>Number(n??0).toFixed(1)};let valFormatted=MyIO.formatEnergyByGroup(val);if(valType==="ENERGY"){valFormatted=MyIO.formatEnergyByGroup(val)}else if(valType==="WATER"){valFormatted=`${val} m³`}else if(valType==="TANK"){valFormatted=`${val} m.c.a`}else{valFormatted=val}const percFormatted=MyIO.formatNumberReadable(perc);if(!document.getElementById("myio-card-styles")){const style=document.createElement("style");style.id="myio-card-styles";style.textContent=` \n.device-card-centered,\n.clickable {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: transform .2s;\n min-height: 140px;\n box-sizing: border-box;\n overflow: hidden;\n}\n\n.device-card-centered:hover,\n.clickable:hover {\n transform: scale(1.05);\n}\n\n.device-title-row {\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 4px;\n padding: 0 4px;\n min-height: 22px;\n}\n\n.device-title {\n font-weight: 700;\n font-size: .85rem;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n}\n\n.device-image {\n max-height: 44px;\n width: auto;\n margin: 4px 0;\n display: block;\n}\n\n.device-data-row {\n display: flex;\n justify-content: center;\n align-items: center;\n margin-top: auto;\n margin-bottom: 6px;\n gap: 6px;\n width: 100%;\n}\n\n.consumption-main {\n font-size: .9rem;\n font-weight: 700;\n color: #28a745;\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: center;\n white-space: nowrap;\n}\n\n.device-title-percent {\n font-size: .75rem;\n color: rgba(0, 0, 0, .45);\n font-weight: 500;\n}\n\n.flash {\n animation: flash 1s infinite;\n color: #ff9800;\n}\n\n@keyframes flash {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: .2;\n }\n 100% {\n opacity: 1;\n }\n}\n\n.card-actions {\n width: 10%;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n}\n\n.card-action img {\n width: 24px;\n height: 24px;\n transition: transform .2s ease;\n cursor: pointer;\n}\n\n.card-action img:hover {\n transform: scale(1.15);\n}\n\n.device-card-centered.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n}\n\n\n.device-info {\n position: absolute;\n top: 8px; /* distância do topo */\n right: 8px; /* distância da direita */\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 50%;\n transition: background 0.2s;\n}\n\n.device-info:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n@keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n font-size: 1.2rem;\n} \n.device-card-centered {\n perspective: 1000px; /* perspectiva para 3D */\n}\n\n.device-card-inner {\n position: relative;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n}\n\n.device-card-front {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: flex-start; /* topo */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n}\n\n.device-card-back {\n display: flex;\n flex-direction: column; /* muda para coluna se quiser empilhar elementos verticalmente */\n justify-content: flex-start; /* conteúdo começa do topo */\n align-items: center; /* centraliza horizontalmente */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n transform: rotateY(180deg); /* mantém flip */\n padding: 10px; /* opcional: espaço interno */\n gap: 10px; /* opcional: espaço entre elementos */\n}\n\n\n\n.device-info {\n position: absolute;\n top: 8px; /* distância do topo */\n right: 8px; /* distância da direita */\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 50%;\n transition: background 0.2s;l\n}\n.device-card-back {\n transform: rotateY(180deg); /* faz o flip funcionar */\n}\n\n.device-card-front .device-info,\n.device-card-back .device-info {\n position: absolute; /* tira do fluxo do flex */\n top: 0; /* topo do card */\n right: 8px; /* canto direito */\n margin: 0; /* remove qualquer margem que empurre o botão */\n display: flex; /* mantém o conteúdo do botão centralizado */\n align-items: center; \n justify-content: center; \n padding: 4px; \n border-radius: 50%; \n cursor: pointer;\n z-index: 10; \n}\n\n\n.device-card-back {\n\n display: flex;\n flex-direction: column; /* empilha os elementos */\n justify-content: flex-start;\n align-items: flex-start; /* tudo à esquerda por padrão */\n padding: 10px;\n gap: 10px;\n height: 100%;\n margin-left: -10px; \n}\n\n.device-card-back .value-container {\n display: flex;\n flex-direction: row; /* ícone e valor lado a lado */\n align-items: center;\n gap: 5px;\n color: #c19efc; /* roxo clarinho */\n align-self: center; /* centraliza horizontalmente no card */\n}\n\n.device-card-back #lastconsumptionTime {\n\n display: flex;\n flex-direction: column;\n align-items: flex-start; /* mantém à esquerda */\n gap: 2px;\n font-size: 11px;\n font-weight: bold;\n color: black;\n}\n\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n.flash-icon.flash {\n animation: icon-blink 1s infinite;\n}\n\n@keyframes icon-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.2; transform: scale(1.2); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n}\n\n.device-card-centered.online .flash-icon {\n color: #28a745 !important; /* verde premium para online */\n}\n `;document.head.appendChild(style)}let formattedDateVal;let formattedDate;let Infcolor="#5cb85c";if(connectionStatusTime){const date=new Date(connectionStatusTime);const day=String(date.getDate()).padStart(2,"0");const month=String(date.getMonth()+1).padStart(2,"0");const year=date.getFullYear();const hours=String(date.getHours()).padStart(2,"0");const minutes=String(date.getMinutes()).padStart(2,"0");formattedDate=`Online: (${day}/${month}/${year} - ${hours}:${minutes})`;const datVal=new Date(timaVal);const dayVal=String(datVal.getDate()).padStart(2,"0");const monthVal=String(datVal.getMonth()+1).padStart(2,"0");const yearVal=date.getFullYear();const hoursVal=String(date.getHours()).padStart(2,"0");const minutesVal=String(date.getMinutes()).padStart(2,"0");const now=new Date;const diffMs=now-datVal;const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMs/(1e3*60*60));const diffDays=Math.floor(diffMs/(1e3*60*60*24));let diffText="";if(diffMinutes<60){diffText=`${diffMinutes} minuto${diffMinutes!==1?"s":""}`}else if(diffHours<24){diffText=`${diffHours} hora${diffHours!==1?"s":""}`}else{diffText=`${diffDays} dia${diffDays!==1?"s":""} `}formattedDateVal=`${dayVal}/${monthVal}/${yearVal} - ${hoursVal}:${minutesVal} (${diffText})`;if(diffHours>=24){Infcolor="#cc2900"}else if(diffMinutes>=30){Infcolor="#e89105"}}else{Infcolor="#d6dcdd"}function normalizeString(str){return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/8Ezn8qVBJ3jXD0iDfnEAZ0MZhAP1b5Ts","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType);const img=deviceImages[nameType]||defaultImage;const html=`\n <div class="device-card-centered clickable ${connectionStatus==="offline"?"offline":""}"\n data-entity-id="${entityId}"\n data-entity-label="${labelOrName}"\n data-entity-type="${entityType}"\n data-entity-slaveid="${slaveId}"\n data-entity-ingestionid="${ingestionId}"\n data-entity-consumption="${val}"\n data-entity-centralid="${centralId}"\n data-entity-updated-identifiers='${JSON.stringify(updatedIdentifiers)}'>\n <div class="device-card-inner" style="width:100%; height:100%; transform-style: preserve-3d; transition: transform 0.6s;">\n <div class="device-card-front">\n\n <div class="card-actions" >\n ${typeof handleActionDashboard==="function"?`\n <div class="card-action action-dashboard" data-action="dashboard" title="Dashboard">\n <img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>\n </div>`:``}\n ${typeof handleActionReport==="function"?`\n <div class="card-action action-report" data-action="report" title="Relatório">\n <img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>\n </div>`:``}\n ${typeof handleActionSettings==="function"?`\n <div class="card-action action-settings" data-action="settings" title="Configurações">\n <img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>\n </div>`:``}\n ${typeof handleSelect==="function"?`\n <input class="card-action action-checker" data-action="checker" title="Selecionar" type="checkbox">`:``}\n </div>\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;width:85%">\n <div class="device-title-row">\n <span class="device-title" title="${labelOrName}">\n ${String(labelOrName??"").length>15?String(labelOrName).slice(0,15)+"…":String(labelOrName??"")}\n </span>\n </div>\n <img class="device-image" src="${img}" />\n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${connectionStatus==="offline"?"flash":isOn?"flash":""}">\n ${connectionStatus==="offline"?"🚨":"⚡"}\n </span>\n <span class="consumption-value" data-entity-consumption="${val}">${valFormatted}</span>\n <span class="device-title-percent">(${percFormatted}%)</span>\n </div>\n </div>\n </div>\n \n ${handInfo?`\n <button id="infoButtom-front" class="device-info info-button">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>`:``}\n </div>\n <div class="device-card-back" >\n <div id="status-bar"> \n <div id="status-information">\n <div style="font-size: 0.85rem; font-weight: bold; line-height: 1;"><span>Central: ${centralName}</span></div>\n <div style="display: flex; flex-direction: row; gap: 4px; font-weight: bold; align-items: center;">\n <div style="font-size: 12px; font-weight: bold; line-height: 1;"><span> ${formattedDate}</span></div>\n <div style="font-size: 10px; line-height: 1;"><span></span></div> \n </div>\n </div>\n <button id="infoButtom-back" class="device-info" style="background:none; border:none; cursor:pointer;">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>\n </div>\n\n <div class="value-container">\n <svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#dea404">\n <path d="m456-200 174-340H510v-220L330-420h126v220Zm24 120q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n <div><span>${MyIO.formatEnergyByGroup(val/1e3)||"-"}</span></div>\n </div>\n\n <div id="lastconsumptionTime">\n <div style="font-size: 12px; font-weight: bold; color: black; line-height: 1;">Ultima Telemetria:</div>\n <div style="font-size: 11px; font-weight: bold; color: black; line-height: 1;">${formattedDateVal}</div>\n </div>\n </div>\n </div>\n </div> \n </div>\n \n `;const $card=$(html);if(typeof handleActionDashboard==="function"){$card.find(".action-dashboard").on("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)})}if(typeof handleActionReport==="function"){$card.find(".action-report").on("click",e=>{e.stopPropagation();handleActionReport(entityObject)})}if(typeof handleActionSettings==="function"){$card.find(".action-settings").on("click",e=>{e.stopPropagation();handleActionSettings(entityObject)})}if(typeof handleSelect==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleSelect(entityObject)})}if(typeof handleClickCard==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleClickCard(entityObject)})}$card.on("click",e=>{if(!$(e.target).closest(".card-action").length){if(typeof handleClickCard==="function"){handleClickCard(entityObject)}else if(typeof handleActionDashboard==="function"){handleActionDashboard(entityObject)}}});$card.find("#infoButtom-front").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").addClass("flipped")});$card.find("#infoButtom-back").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").removeClass("flipped")});return $card}exports.addDetectionContext=addDetectionContext;exports.addNamespace=addNamespace;exports.averageByDay=averageByDay;exports.buildWaterReportCSV=buildWaterReportCSV;exports.buildWaterStoresCSV=buildWaterStoresCSV;exports.calcDeltaPercent=calcDeltaPercent;exports.classify=classify;exports.classifyWaterLabel=classifyWaterLabel;exports.classifyWaterLabels=classifyWaterLabels;exports.decodePayload=decodePayload;exports.decodePayloadBase64Xor=decodePayloadBase64Xor;exports.detectDeviceType=detectDeviceType;exports.determineInterval=determineInterval;exports.exportToCSV=exportToCSV;exports.exportToCSVAll=exportToCSVAll;exports.findValue=findValue;exports.fmtPerc=fmtPerc;exports.fmtPercLegacy=fmtPerc2;exports.formatAllInSameUnit=formatAllInSameUnit;exports.formatAllInSameWaterUnit=formatAllInSameWaterUnit;exports.formatDateForInput=formatDateForInput;exports.formatDateToYMD=formatDateToYMD;exports.formatDateWithTimezoneOffset=formatDateWithTimezoneOffset;exports.formatEnergy=formatEnergy;exports.formatNumberReadable=formatNumberReadable;exports.formatTankHeadFromCm=formatTankHeadFromCm;exports.formatWaterByGroup=formatWaterByGroup;exports.formatWaterVolumeM3=formatWaterVolumeM3;exports.getAvailableContexts=getAvailableContexts;exports.getDateRangeArray=getDateRangeArray;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.isWaterCategory=isWaterCategory;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.parseInputDateToDate=parseInputDateToDate;exports.renderCardComponent=renderCardComponent;exports.strings=strings_exports;exports.timeWindowFromInputYMD=timeWindowFromInputYMD;exports.toCSV=toCSV;exports.toFixedSafe=toFixedSafe});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myio-js-library",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A clean, standalone JS SDK for MYIO projects",
5
5
  "license": "MIT",
6
6
  "repository": "github:gh-myio/myio-js-library",