myio-js-library 0.1.5 → 0.1.7
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 +21 -3
- package/dist/index.cjs +29 -4
- package/dist/index.d.cts +5 -4
- package/dist/index.js +29 -4
- package/dist/myio-js-library.umd.js +29 -4
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -354,16 +354,34 @@ numbers.toFixedSafe(Infinity); // "—"
|
|
|
354
354
|
|
|
355
355
|
### Energy Formatting Utilities
|
|
356
356
|
|
|
357
|
-
#### `formatEnergy(value: number, unit
|
|
357
|
+
#### `formatEnergy(value: number, unit?: string): string`
|
|
358
358
|
|
|
359
|
-
Formats energy values with Brazilian locale formatting and appropriate units.
|
|
359
|
+
Formats energy values with Brazilian locale formatting and appropriate units. If no unit is provided, automatically selects the most appropriate unit based on the value magnitude.
|
|
360
|
+
|
|
361
|
+
**Parameters:**
|
|
362
|
+
- `value: number` - The energy value to format
|
|
363
|
+
- `unit?: string` - Optional unit ('kWh', 'MWh', 'GWh'). If not provided, automatically determined based on value
|
|
364
|
+
|
|
365
|
+
**Auto-unit selection:**
|
|
366
|
+
- Values ≥ 1,000,000: Converts to GWh
|
|
367
|
+
- Values ≥ 1,000: Converts to MWh
|
|
368
|
+
- Values < 1,000: Uses kWh
|
|
360
369
|
|
|
361
370
|
```javascript
|
|
362
371
|
import { formatEnergy } from 'myio-js-library';
|
|
363
372
|
|
|
373
|
+
// With explicit unit
|
|
364
374
|
formatEnergy(1234.56, 'kWh'); // "1.234,56 kWh"
|
|
365
375
|
formatEnergy(1000, 'MWh'); // "1.000,00 MWh"
|
|
366
|
-
|
|
376
|
+
|
|
377
|
+
// Auto-unit selection
|
|
378
|
+
formatEnergy(500); // "500,00 kWh"
|
|
379
|
+
formatEnergy(1500); // "1,50 MWh"
|
|
380
|
+
formatEnergy(2500000); // "2,50 GWh"
|
|
381
|
+
|
|
382
|
+
// Invalid values
|
|
383
|
+
formatEnergy(null); // "-"
|
|
384
|
+
formatEnergy(NaN); // "-"
|
|
367
385
|
```
|
|
368
386
|
|
|
369
387
|
#### `formatAllInSameUnit(values: Array<{value: number, unit: string}>, targetUnit: string): string[]`
|
package/dist/index.cjs
CHANGED
|
@@ -74,20 +74,45 @@ function formatEnergy(value, unit) {
|
|
|
74
74
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
75
75
|
return "-";
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
let adjustedValue = value;
|
|
78
|
+
let adjustedUnit = unit;
|
|
79
|
+
if (!adjustedUnit) {
|
|
80
|
+
if (value >= 1e6) {
|
|
81
|
+
adjustedValue = value / 1e6;
|
|
82
|
+
adjustedUnit = "GWh";
|
|
83
|
+
} else if (value >= 1e3) {
|
|
84
|
+
adjustedValue = value / 1e3;
|
|
85
|
+
adjustedUnit = "MWh";
|
|
86
|
+
} else {
|
|
87
|
+
adjustedUnit = "kWh";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const formattedValue = adjustedValue.toLocaleString("pt-BR", {
|
|
78
91
|
minimumFractionDigits: 2,
|
|
79
92
|
maximumFractionDigits: 2
|
|
80
93
|
});
|
|
81
|
-
return `${formattedValue} ${
|
|
94
|
+
return `${formattedValue} ${adjustedUnit}`;
|
|
82
95
|
}
|
|
83
|
-
function formatAllInSameUnit(values, targetUnit) {
|
|
96
|
+
function formatAllInSameUnit(values, targetUnit, sourceUnit = "kWh") {
|
|
84
97
|
const unitMultipliers = {
|
|
85
98
|
"kWh": 1,
|
|
86
99
|
"MWh": 1e3,
|
|
87
100
|
"GWh": 1e6
|
|
88
101
|
};
|
|
89
102
|
const targetMultiplier = unitMultipliers[targetUnit] || 1;
|
|
90
|
-
|
|
103
|
+
if (typeof values[0] === "number") {
|
|
104
|
+
const numberValues = values;
|
|
105
|
+
const sourceMultiplier = unitMultipliers[sourceUnit] || 1;
|
|
106
|
+
return numberValues.map((value) => {
|
|
107
|
+
if (value === null || value === void 0 || isNaN(value)) {
|
|
108
|
+
return "-";
|
|
109
|
+
}
|
|
110
|
+
const convertedValue = value * sourceMultiplier / targetMultiplier;
|
|
111
|
+
return formatEnergy(convertedValue, targetUnit);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const objectValues = values;
|
|
115
|
+
return objectValues.map((item) => {
|
|
91
116
|
if (item.value === null || item.value === void 0 || isNaN(item.value)) {
|
|
92
117
|
return "-";
|
|
93
118
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Formats energy values with appropriate units (kWh, MWh, GWh) using Brazilian locale formatting
|
|
3
3
|
* @param value - The energy value to format
|
|
4
|
-
* @param unit -
|
|
4
|
+
* @param unit - Optional unit of the energy value ('kWh', 'MWh', 'GWh')
|
|
5
5
|
* @returns Formatted energy string with Brazilian locale number formatting
|
|
6
6
|
*/
|
|
7
|
-
declare function formatEnergy(value: number, unit
|
|
7
|
+
declare function formatEnergy(value: number, unit?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Formats all energy values to the same unit for consistent display
|
|
10
|
-
* @param values - Array of energy values with their units
|
|
10
|
+
* @param values - Array of energy values with their units OR array of numbers (assumes kWh)
|
|
11
11
|
* @param targetUnit - Target unit to convert all values to ('kWh', 'MWh', 'GWh')
|
|
12
|
+
* @param sourceUnit - Source unit when values is an array of numbers (defaults to 'kWh')
|
|
12
13
|
* @returns Array of formatted energy strings in the target unit
|
|
13
14
|
*/
|
|
14
15
|
declare function formatAllInSameUnit(values: Array<{
|
|
15
16
|
value: number;
|
|
16
17
|
unit: string;
|
|
17
|
-
}
|
|
18
|
+
}> | number[], targetUnit: string, sourceUnit?: string): string[];
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Formats a percentage value with Brazilian locale formatting
|
package/dist/index.js
CHANGED
|
@@ -9,20 +9,45 @@ function formatEnergy(value, unit) {
|
|
|
9
9
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
10
10
|
return "-";
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
let adjustedValue = value;
|
|
13
|
+
let adjustedUnit = unit;
|
|
14
|
+
if (!adjustedUnit) {
|
|
15
|
+
if (value >= 1e6) {
|
|
16
|
+
adjustedValue = value / 1e6;
|
|
17
|
+
adjustedUnit = "GWh";
|
|
18
|
+
} else if (value >= 1e3) {
|
|
19
|
+
adjustedValue = value / 1e3;
|
|
20
|
+
adjustedUnit = "MWh";
|
|
21
|
+
} else {
|
|
22
|
+
adjustedUnit = "kWh";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const formattedValue = adjustedValue.toLocaleString("pt-BR", {
|
|
13
26
|
minimumFractionDigits: 2,
|
|
14
27
|
maximumFractionDigits: 2
|
|
15
28
|
});
|
|
16
|
-
return `${formattedValue} ${
|
|
29
|
+
return `${formattedValue} ${adjustedUnit}`;
|
|
17
30
|
}
|
|
18
|
-
function formatAllInSameUnit(values, targetUnit) {
|
|
31
|
+
function formatAllInSameUnit(values, targetUnit, sourceUnit = "kWh") {
|
|
19
32
|
const unitMultipliers = {
|
|
20
33
|
"kWh": 1,
|
|
21
34
|
"MWh": 1e3,
|
|
22
35
|
"GWh": 1e6
|
|
23
36
|
};
|
|
24
37
|
const targetMultiplier = unitMultipliers[targetUnit] || 1;
|
|
25
|
-
|
|
38
|
+
if (typeof values[0] === "number") {
|
|
39
|
+
const numberValues = values;
|
|
40
|
+
const sourceMultiplier = unitMultipliers[sourceUnit] || 1;
|
|
41
|
+
return numberValues.map((value) => {
|
|
42
|
+
if (value === null || value === void 0 || isNaN(value)) {
|
|
43
|
+
return "-";
|
|
44
|
+
}
|
|
45
|
+
const convertedValue = value * sourceMultiplier / targetMultiplier;
|
|
46
|
+
return formatEnergy(convertedValue, targetUnit);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const objectValues = values;
|
|
50
|
+
return objectValues.map((item) => {
|
|
26
51
|
if (item.value === null || item.value === void 0 || isNaN(item.value)) {
|
|
27
52
|
return "-";
|
|
28
53
|
}
|
|
@@ -15,20 +15,45 @@
|
|
|
15
15
|
if (value === null || value === void 0 || isNaN(value)) {
|
|
16
16
|
return "-";
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
let adjustedValue = value;
|
|
19
|
+
let adjustedUnit = unit;
|
|
20
|
+
if (!adjustedUnit) {
|
|
21
|
+
if (value >= 1e6) {
|
|
22
|
+
adjustedValue = value / 1e6;
|
|
23
|
+
adjustedUnit = "GWh";
|
|
24
|
+
} else if (value >= 1e3) {
|
|
25
|
+
adjustedValue = value / 1e3;
|
|
26
|
+
adjustedUnit = "MWh";
|
|
27
|
+
} else {
|
|
28
|
+
adjustedUnit = "kWh";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const formattedValue = adjustedValue.toLocaleString("pt-BR", {
|
|
19
32
|
minimumFractionDigits: 2,
|
|
20
33
|
maximumFractionDigits: 2
|
|
21
34
|
});
|
|
22
|
-
return `${formattedValue} ${
|
|
35
|
+
return `${formattedValue} ${adjustedUnit}`;
|
|
23
36
|
}
|
|
24
|
-
function formatAllInSameUnit(values, targetUnit) {
|
|
37
|
+
function formatAllInSameUnit(values, targetUnit, sourceUnit = "kWh") {
|
|
25
38
|
const unitMultipliers = {
|
|
26
39
|
"kWh": 1,
|
|
27
40
|
"MWh": 1e3,
|
|
28
41
|
"GWh": 1e6
|
|
29
42
|
};
|
|
30
43
|
const targetMultiplier = unitMultipliers[targetUnit] || 1;
|
|
31
|
-
|
|
44
|
+
if (typeof values[0] === "number") {
|
|
45
|
+
const numberValues = values;
|
|
46
|
+
const sourceMultiplier = unitMultipliers[sourceUnit] || 1;
|
|
47
|
+
return numberValues.map((value) => {
|
|
48
|
+
if (value === null || value === void 0 || isNaN(value)) {
|
|
49
|
+
return "-";
|
|
50
|
+
}
|
|
51
|
+
const convertedValue = value * sourceMultiplier / targetMultiplier;
|
|
52
|
+
return formatEnergy(convertedValue, targetUnit);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const objectValues = values;
|
|
56
|
+
return objectValues.map((item) => {
|
|
32
57
|
if (item.value === null || item.value === void 0 || isNaN(item.value)) {
|
|
33
58
|
return "-";
|
|
34
59
|
}
|
|
@@ -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"-"}const formattedValue=value.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} ${unit}`}function formatAllInSameUnit(values,targetUnit){const unitMultipliers={kWh:1,MWh:1e3,GWh:1e6};const targetMultiplier=unitMultipliers[targetUnit]||1;return values.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&¤t===0){return{value:0,type:"neutral"}}if(prev===0&¤t>0){return{value:100,type:"increase"}}if(prev===0&¤t<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)}async function getEntityInfoAndAttributesTB(deviceId,opts){if(!deviceId)throw new Error("getEntityInfoAndAttributesTB: deviceId is required");const{jwt:jwt,baseUrl:baseUrl="",scope:scope="SERVER_SCOPE",attributeKeys:attributeKeys=["floor","NumLoja","IDMedidor","deviceId","guid","maxDailyConsumption","maxNightConsumption"],fetcher:fetcher=globalThis.fetch?.bind(globalThis)}=opts||{};if(!jwt)throw new Error("getEntityInfoAndAttributesTB: opts.jwt (Bearer token) is required");if(!fetcher)throw new Error("getEntityInfoAndAttributesTB: no fetch implementation available");const headers={"Content-Type":"application/json","X-Authorization":`Bearer ${jwt}`};const base=baseUrl.endsWith("/")?baseUrl.slice(0,-1):baseUrl;const deviceRes=await fetcher(`${base}/api/device/${encodeURIComponent(deviceId)}`,{headers:headers});if(!deviceRes.ok){throw new Error(`Failed to fetch device: HTTP ${deviceRes.status} ${deviceRes.statusText}`)}const device=await deviceRes.json();const label=device?.label||device?.name||"Sem etiqueta";const attrUrl=`${base}/api/plugins/telemetry/DEVICE/${encodeURIComponent(deviceId)}/values/attributes?scope=${encodeURIComponent(scope)}`;const attrRes=await fetcher(attrUrl,{headers:headers});if(!attrRes.ok){throw new Error(`Failed to fetch attributes: HTTP ${attrRes.status} ${attrRes.statusText}`)}const attributes=await attrRes.json();const map=new Map;for(const a of attributes)map.set(a.key,a.value);const getStr=k=>{const v=map.get(k);if(v==null)return"";return typeof v==="string"?v:String(v)};const getNum=k=>{const v=map.get(k);if(v==null)return 0;const n=typeof v==="string"?Number(v.replace(",",".")):Number(v);return Number.isFinite(n)?n:0};return{label:label,andar:getStr("floor")||"",numeroLoja:getStr("NumLoja")||"",identificadorMedidor:getStr("IDMedidor")||"",identificadorDispositivo:getStr("deviceId")||"",guid:getStr("guid")||"",consumoDiario:getNum("maxDailyConsumption"),consumoMadrugada:getNum("maxNightConsumption")}}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}async function fetchWithRetry(url,options={}){const{retries:retries=0,retryDelay:retryDelay=100,timeout:timeout=1e4,retryCondition:retryCondition,...passThrough}=options;const baseInit={...passThrough,timeout:timeout};let attempt=0;while(true){try{const res=await withTimeout(fetch(url,baseInit),timeout);if(!res.ok){const doRetry=typeof retryCondition==="function"&&retryCondition(null,res)||res.status>=500;if(doRetry&&attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}const msg=`HTTP ${res.status}: ${res.statusText||""}`.trim();throw new Error(msg)}return res}catch(err){if(err&&err.message==="Request timeout"){if(attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}throw err}const doRetry=typeof retryCondition==="function"&&retryCondition(err,void 0)||isRetryableNetworkError(err);if(doRetry&&attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}throw err}}}var http=fetchWithRetry;function withTimeout(promise,ms){return new Promise((resolve,reject)=>{const t=setTimeout(()=>reject(new Error("Request timeout")),ms);promise.then(v=>{clearTimeout(t);resolve(v)},e=>{clearTimeout(t);reject(e)})})}function delay(ms){return new Promise(r=>setTimeout(r,ms))}function expBackoff(base,attempt){return base*Math.pow(2,attempt)}function isRetryableNetworkError(err){if(!err)return false;const msg=String(err.message||"").toLowerCase();return msg.includes("network")||err.name==="AbortError"}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.fetchWithRetry=fetchWithRetry;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.getEntityInfoAndAttributesTB=getEntityInfoAndAttributesTB;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.http=http;exports.isWaterCategory=isWaterCategory;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.parseInputDateToDate=parseInputDateToDate;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&¤t===0){return{value:0,type:"neutral"}}if(prev===0&¤t>0){return{value:100,type:"increase"}}if(prev===0&¤t<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)}async function getEntityInfoAndAttributesTB(deviceId,opts){if(!deviceId)throw new Error("getEntityInfoAndAttributesTB: deviceId is required");const{jwt:jwt,baseUrl:baseUrl="",scope:scope="SERVER_SCOPE",attributeKeys:attributeKeys=["floor","NumLoja","IDMedidor","deviceId","guid","maxDailyConsumption","maxNightConsumption"],fetcher:fetcher=globalThis.fetch?.bind(globalThis)}=opts||{};if(!jwt)throw new Error("getEntityInfoAndAttributesTB: opts.jwt (Bearer token) is required");if(!fetcher)throw new Error("getEntityInfoAndAttributesTB: no fetch implementation available");const headers={"Content-Type":"application/json","X-Authorization":`Bearer ${jwt}`};const base=baseUrl.endsWith("/")?baseUrl.slice(0,-1):baseUrl;const deviceRes=await fetcher(`${base}/api/device/${encodeURIComponent(deviceId)}`,{headers:headers});if(!deviceRes.ok){throw new Error(`Failed to fetch device: HTTP ${deviceRes.status} ${deviceRes.statusText}`)}const device=await deviceRes.json();const label=device?.label||device?.name||"Sem etiqueta";const attrUrl=`${base}/api/plugins/telemetry/DEVICE/${encodeURIComponent(deviceId)}/values/attributes?scope=${encodeURIComponent(scope)}`;const attrRes=await fetcher(attrUrl,{headers:headers});if(!attrRes.ok){throw new Error(`Failed to fetch attributes: HTTP ${attrRes.status} ${attrRes.statusText}`)}const attributes=await attrRes.json();const map=new Map;for(const a of attributes)map.set(a.key,a.value);const getStr=k=>{const v=map.get(k);if(v==null)return"";return typeof v==="string"?v:String(v)};const getNum=k=>{const v=map.get(k);if(v==null)return 0;const n=typeof v==="string"?Number(v.replace(",",".")):Number(v);return Number.isFinite(n)?n:0};return{label:label,andar:getStr("floor")||"",numeroLoja:getStr("NumLoja")||"",identificadorMedidor:getStr("IDMedidor")||"",identificadorDispositivo:getStr("deviceId")||"",guid:getStr("guid")||"",consumoDiario:getNum("maxDailyConsumption"),consumoMadrugada:getNum("maxNightConsumption")}}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}async function fetchWithRetry(url,options={}){const{retries:retries=0,retryDelay:retryDelay=100,timeout:timeout=1e4,retryCondition:retryCondition,...passThrough}=options;const baseInit={...passThrough,timeout:timeout};let attempt=0;while(true){try{const res=await withTimeout(fetch(url,baseInit),timeout);if(!res.ok){const doRetry=typeof retryCondition==="function"&&retryCondition(null,res)||res.status>=500;if(doRetry&&attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}const msg=`HTTP ${res.status}: ${res.statusText||""}`.trim();throw new Error(msg)}return res}catch(err){if(err&&err.message==="Request timeout"){if(attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}throw err}const doRetry=typeof retryCondition==="function"&&retryCondition(err,void 0)||isRetryableNetworkError(err);if(doRetry&&attempt<retries){await delay(expBackoff(retryDelay,attempt));attempt++;continue}throw err}}}var http=fetchWithRetry;function withTimeout(promise,ms){return new Promise((resolve,reject)=>{const t=setTimeout(()=>reject(new Error("Request timeout")),ms);promise.then(v=>{clearTimeout(t);resolve(v)},e=>{clearTimeout(t);reject(e)})})}function delay(ms){return new Promise(r=>setTimeout(r,ms))}function expBackoff(base,attempt){return base*Math.pow(2,attempt)}function isRetryableNetworkError(err){if(!err)return false;const msg=String(err.message||"").toLowerCase();return msg.includes("network")||err.name==="AbortError"}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.fetchWithRetry=fetchWithRetry;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.getEntityInfoAndAttributesTB=getEntityInfoAndAttributesTB;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.http=http;exports.isWaterCategory=isWaterCategory;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.parseInputDateToDate=parseInputDateToDate;exports.strings=strings_exports;exports.timeWindowFromInputYMD=timeWindowFromInputYMD;exports.toCSV=toCSV;exports.toFixedSafe=toFixedSafe});
|