myio-js-library 0.1.526 → 0.1.528

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -53,6 +53,8 @@ __export(src_exports, {
53
53
  AlarmsSummaryTooltip: () => AlarmsSummaryTooltip,
54
54
  AnnotationIndicator: () => AnnotationIndicator,
55
55
  AnnotationTooltip: () => AnnotationTooltip,
56
+ BAKED_PROFILE_KEYS: () => BAKED_PROFILE_KEYS,
57
+ BAKED_PROFILE_VERSION: () => BAKED_PROFILE_VERSION,
56
58
  BASDashboardController: () => BASDashboardController,
57
59
  BASDashboardView: () => BASDashboardView,
58
60
  BAS_DASHBOARD_CSS_PREFIX: () => BAS_DASHBOARD_CSS_PREFIX,
@@ -375,6 +377,7 @@ __export(src_exports, {
375
377
  createAmbienteGroupModal: () => createAmbienteGroupModal,
376
378
  createAnnotationIndicator: () => createAnnotationIndicator,
377
379
  createBASDashboard: () => createBASDashboard,
380
+ createBakedProfileSource: () => createBakedProfileSource,
378
381
  createButtonBar: () => createButtonBar,
379
382
  createConsumption7DaysChart: () => createConsumption7DaysChart,
380
383
  createConsumptionChartWidget: () => createConsumptionChartWidget,
@@ -486,8 +489,10 @@ __export(src_exports, {
486
489
  fetchThingsboardCustomerAttrsFromStorage: () => fetchThingsboardCustomerAttrsFromStorage,
487
490
  fetchThingsboardCustomerServerScopeAttrs: () => fetchThingsboardCustomerServerScopeAttrs,
488
491
  fetchTicketsForDevice: () => fetchTicketsForDevice,
492
+ findNode: () => findNode,
489
493
  findValue: () => findValue,
490
494
  findValueWithDefault: () => findValueWithDefault,
495
+ flattenTree: () => flattenTree,
491
496
  fmtPerc: () => fmtPerc,
492
497
  fmtPercLegacy: () => fmtPerc2,
493
498
  formatAlarmRelativeTime: () => formatAlarmRelativeTime,
@@ -620,6 +625,7 @@ __export(src_exports, {
620
625
  isAlarmActive: () => isAlarmActive,
621
626
  isAnnotationOverdue: () => isOverdue,
622
627
  isAssetCode: () => isAssetCode,
628
+ isBakedStale: () => isBakedStale,
623
629
  isConnectionStale: () => isConnectionStale,
624
630
  isCustomerCode: () => isCustomerCode,
625
631
  isDeviceIconType: () => isDeviceIconType,
@@ -640,6 +646,7 @@ __export(src_exports, {
640
646
  isValidDeviceStatus: () => isValidDeviceStatus,
641
647
  isValidTimeFormat: () => isValidTimeFormat,
642
648
  isWaterCategory: () => isWaterCategory,
649
+ liftProfileToTree: () => liftProfileToTree,
643
650
  listDomains: () => listDomains,
644
651
  listGroups: () => listGroups,
645
652
  mapConnectionStatus: () => mapConnectionStatus,
@@ -728,8 +735,11 @@ __export(src_exports, {
728
735
  reopenFreshdeskTicket: () => reopenTicket,
729
736
  resolveActiveProfile: () => resolveActiveProfile,
730
737
  resolveCategory: () => resolveCategory,
738
+ resolveClassification: () => resolveClassification,
731
739
  resolveGroup: () => resolveGroup,
732
740
  resolvePercentDecimals: () => resolvePercentDecimals,
741
+ resolveSubcategory: () => resolveSubcategory,
742
+ resolveWithFallback: () => resolveWithFallback,
733
743
  schedCreateDateInput: () => createDateInput,
734
744
  schedCreateNumberInput: () => createNumberInput,
735
745
  schedCreateSelect: () => createSelect,
@@ -758,6 +768,7 @@ __export(src_exports, {
758
768
  updateFreshdeskTicket: () => updateTicket,
759
769
  upsertAlarmAnnotation: () => upsertAlarmAnnotation,
760
770
  validateProfile: () => validateProfile,
771
+ validateTree: () => validateTree,
761
772
  version: () => version,
762
773
  waterDeviceStatusIcons: () => waterDeviceStatusIcons,
763
774
  writeFreshdeskSyncedAtToTB: () => writeFreshdeskSyncedAtToTB,
@@ -768,7 +779,7 @@ module.exports = __toCommonJS(src_exports);
768
779
  // package.json
769
780
  var package_default = {
770
781
  name: "myio-js-library",
771
- version: "0.1.526",
782
+ version: "0.1.528",
772
783
  description: "A clean, standalone JS SDK for MYIO projects",
773
784
  license: "MIT",
774
785
  repository: "github:gh-myio/myio-js-library",
@@ -815,6 +826,8 @@ var package_default = {
815
826
  "build:umd": "rollup -c",
816
827
  "minify:umd": "terser dist/myio-js-library.umd.js -o dist/myio-js-library.umd.min.js --comments=false",
817
828
  postbuild: "node scripts/verify-dts.js",
829
+ "gen:baked-profile": "node scripts/gen-baked-profile.mjs",
830
+ prebuild: "node scripts/gen-baked-profile.mjs --check",
818
831
  build: "npm run clean && npm run build:tsup && npm run build:umd && npm run minify:umd",
819
832
  lint: "eslint .",
820
833
  test: "vitest run",
@@ -3123,7 +3136,7 @@ var DEFAULT_DEVICE_CLASSIFICATION_PROFILE = {
3123
3136
  name: "elevadores",
3124
3137
  // ELEVADORES_DEVICE_TYPES_SET + buildSummary ELEVADOR_PATTERNS
3125
3138
  deviceProfiles: ["ELEVADOR"],
3126
- combinedContains: ["ELEVADOR"],
3139
+ profileContains: ["ELEVADOR"],
3127
3140
  identifierFallback: {
3128
3141
  // ELEVADORES_IDENTIFIERS_SET + buildSummary id prefix ELV-
3129
3142
  identifierEquals: ["ELV", "ELEVADOR", "ELEVADORES"],
@@ -3134,7 +3147,7 @@ var DEFAULT_DEVICE_CLASSIFICATION_PROFILE = {
3134
3147
  name: "escadas_rolantes",
3135
3148
  // ESCADAS_DEVICE_TYPES_SET + buildSummary ESCADA_PATTERNS
3136
3149
  deviceProfiles: ["ESCADA_ROLANTE"],
3137
- combinedContains: ["ESCADA", "ROLANTE"],
3150
+ profileContains: ["ESCADA", "ROLANTE"],
3138
3151
  identifierFallback: {
3139
3152
  // ESCADAS_IDENTIFIERS_SET + buildSummary id prefix ESC-
3140
3153
  identifierEquals: ["ESC", "ESCADA", "ESCADASROLANTES"],
@@ -3145,17 +3158,21 @@ var DEFAULT_DEVICE_CLASSIFICATION_PROFILE = {
3145
3158
  name: "climatizacao",
3146
3159
  // CLIMATIZACAO_DEVICE_TYPES_SET
3147
3160
  deviceProfiles: ["CHILLER", "AR_CONDICIONADO", "HVAC", "FANCOIL"],
3148
- // buildSummary CLIMATIZACAO_PATTERNS (superset of the deviceProfiles)
3149
- combinedContains: [
3161
+ // buildSummary CLIMATIZACAO_PATTERNS (superset of the deviceProfiles).
3162
+ // NB: BOMBA_HIDRAULICA/BOMBASHIDRAULICAS foram REMOVIDOS (2026-07-23):
3163
+ // bomba hidráulica é recalque de água, não climatização — deve cair em
3164
+ // 'outros' (igual a BOMBA_INCENDIO). BOMBA_CAG continua em climatização
3165
+ // via `conditional`/`identifierFallback` (CAG), sem depender destas
3166
+ // strings. Alinhado a equipmentCategory.js (hvacProfiles NÃO inclui bomba
3167
+ // hidráulica) e a openUpsellModal (BOMBA_HIDRAULICA = energy_common_area).
3168
+ profileContains: [
3150
3169
  "CHILLER",
3151
3170
  "FANCOIL",
3152
3171
  "HVAC",
3153
3172
  "AR_CONDICIONADO",
3154
3173
  "COMPRESSOR",
3155
3174
  "VENTILADOR",
3156
- "CLIMATIZA",
3157
- "BOMBA_HIDRAULICA",
3158
- "BOMBASHIDRAULICAS"
3175
+ "CLIMATIZA"
3159
3176
  ],
3160
3177
  // CLIMATIZACAO conditional (perfis BOMBA/MOTOR) + identifier requirement
3161
3178
  conditional: {
@@ -3240,9 +3257,6 @@ function profileOf(item) {
3240
3257
  function identifierOf(item) {
3241
3258
  return up(item?.identifier ?? "").trim();
3242
3259
  }
3243
- function combinedOf(item) {
3244
- return `${up(item?.labelWidget ?? "")} ${up(item?.deviceProfile ?? "")} ${up(item?.label ?? "")}`;
3245
- }
3246
3260
  function matchesIdentifier(id, m) {
3247
3261
  if (!m) return false;
3248
3262
  if (m.identifierEquals) {
@@ -3297,13 +3311,12 @@ function resolveCategory(item, profile = getActiveProfile(), domain = "energy")
3297
3311
  }
3298
3312
  }
3299
3313
  }
3300
- const combined = combinedOf(item);
3301
3314
  const id = identifierOf(item);
3302
3315
  for (const rule of rules) {
3303
- if (rule.combinedContains) {
3304
- for (const pattern of rule.combinedContains) {
3305
- if (combined.includes(up(pattern))) {
3306
- return { category: rule.name, matchedBy: "combined" };
3316
+ if (dp && rule.profileContains) {
3317
+ for (const pattern of rule.profileContains) {
3318
+ if (dp.includes(up(pattern))) {
3319
+ return { category: rule.name, matchedBy: "profileContains" };
3307
3320
  }
3308
3321
  }
3309
3322
  }
@@ -3379,6 +3392,12 @@ function validateProfile(profile) {
3379
3392
  if (!Array.isArray(r.deviceProfiles)) {
3380
3393
  errors.push(`${domain}.categories.rules["${r?.name}"]: missing deviceProfiles array`);
3381
3394
  }
3395
+ if (r.profileContains !== void 0 && !Array.isArray(r.profileContains)) {
3396
+ errors.push(`${domain}.categories.rules["${r?.name}"].profileContains: must be an array`);
3397
+ }
3398
+ if (r.combinedContains !== void 0 && !Array.isArray(r.combinedContains)) {
3399
+ errors.push(`${domain}.categories.rules["${r?.name}"].combinedContains: must be an array`);
3400
+ }
3382
3401
  }
3383
3402
  const inlineFallbacks = catRules.filter((r) => r.fallback === true).length;
3384
3403
  const hasOutros = cats.fallback && cats.fallback.name === "outros" ? 1 : 0;
@@ -3419,7 +3438,13 @@ function normalizeProfile(raw) {
3419
3438
  }
3420
3439
  for (const r of dom.categories.rules ?? []) {
3421
3440
  r.deviceProfiles = upArr(r.deviceProfiles) ?? [];
3422
- r.combinedContains = upArr(r.combinedContains);
3441
+ const _pc = upArr(r.profileContains);
3442
+ const _legacy = upArr(r.combinedContains);
3443
+ if (_pc || _legacy) {
3444
+ const merged = [..._pc ?? [], ..._legacy ?? []];
3445
+ r.profileContains = merged.filter((v, i) => merged.indexOf(v) === i);
3446
+ }
3447
+ delete r.combinedContains;
3423
3448
  if (r.conditional) {
3424
3449
  r.conditional.deviceProfiles = upArr(r.conditional.deviceProfiles) ?? upArr(r.conditional.deviceTypes) ?? [];
3425
3450
  delete r.conditional.deviceTypes;
@@ -3463,8 +3488,364 @@ function listGroups(profile = getActiveProfile(), domain = "") {
3463
3488
  return rules.map((r) => ({ key: r.name, fallback: !!r.fallback }));
3464
3489
  }
3465
3490
 
3491
+ // src/utils/devices/classificationNodeTree.ts
3492
+ var up2 = (v) => String(v ?? "").toUpperCase();
3493
+ var profileOf2 = (i) => up2(i?.deviceProfile ?? "");
3494
+ var identifierOf2 = (i) => up2(i?.identifier ?? "").trim();
3495
+ function matchesIdentifier2(id, m) {
3496
+ if (!m) return false;
3497
+ for (const v of m.identifierEquals ?? []) if (id === up2(v)) return true;
3498
+ for (const v of m.identifierExact ?? []) if (id === up2(v)) return true;
3499
+ for (const v of m.identifierContains ?? []) if (id.includes(up2(v))) return true;
3500
+ for (const p of m.identifierPrefixes ?? []) if (id.startsWith(up2(p))) return true;
3501
+ return false;
3502
+ }
3503
+ function matchExact(dp, rules) {
3504
+ if (!rules || !dp) return false;
3505
+ for (const c of rules.deviceProfiles ?? []) if (dp === up2(c)) return true;
3506
+ return false;
3507
+ }
3508
+ function matchLoose(dp, id, rules) {
3509
+ if (!rules) return null;
3510
+ if (dp) {
3511
+ for (const p of rules.profileContains ?? []) if (dp.includes(up2(p))) return "profileContains";
3512
+ }
3513
+ const cond = rules.conditional;
3514
+ if (cond && (cond.deviceProfiles ?? []).some((t) => dp === up2(t))) {
3515
+ if (matchesIdentifier2(id, cond)) return "conditional";
3516
+ }
3517
+ if (matchesIdentifier2(id, rules)) return "identifier";
3518
+ return null;
3519
+ }
3520
+ function byOrder(a, b) {
3521
+ const d = (a.order ?? 0) - (b.order ?? 0);
3522
+ return d !== 0 ? d : String(a.key).localeCompare(String(b.key));
3523
+ }
3524
+ function classifiable(nodes) {
3525
+ return nodes.filter((n) => n.role !== "residual" && n.role !== "total");
3526
+ }
3527
+ function resolveClassification(item, tree) {
3528
+ if (!Array.isArray(tree) || tree.length === 0) return null;
3529
+ const dp = profileOf2(item);
3530
+ const id = identifierOf2(item);
3531
+ for (const n of [...tree].sort(byOrder)) {
3532
+ if (n.role !== "ocultos") continue;
3533
+ if (matchExact(dp, n.rules) || matchLoose(dp, id, n.rules)) {
3534
+ return { key: n.key, nodePath: [n.key], matchedBy: "ocultos" };
3535
+ }
3536
+ }
3537
+ const path = [];
3538
+ let level = classifiable(tree).filter((n) => n.role !== "ocultos");
3539
+ let matchedBy = "fallback";
3540
+ let current = null;
3541
+ while (true) {
3542
+ const siblings = [...level].sort(byOrder);
3543
+ let hit = null;
3544
+ let hitBy = null;
3545
+ for (const n of siblings) {
3546
+ if (matchExact(dp, n.rules)) {
3547
+ hit = n;
3548
+ hitBy = "deviceProfile";
3549
+ break;
3550
+ }
3551
+ }
3552
+ if (!hit) {
3553
+ for (const n of siblings) {
3554
+ const by = matchLoose(dp, id, n.rules);
3555
+ if (by) {
3556
+ hit = n;
3557
+ hitBy = by;
3558
+ break;
3559
+ }
3560
+ }
3561
+ }
3562
+ if (!hit) {
3563
+ const fb = siblings.find((n) => n.role === "fallback");
3564
+ if (fb) {
3565
+ hit = fb;
3566
+ hitBy = "fallback";
3567
+ }
3568
+ }
3569
+ if (!hit) break;
3570
+ current = hit;
3571
+ matchedBy = hitBy ?? "fallback";
3572
+ path.push(hit.key);
3573
+ const kids = classifiable(hit.children ?? []).filter((n) => n.role !== "ocultos");
3574
+ if (kids.length === 0) break;
3575
+ level = kids;
3576
+ }
3577
+ if (!current) return null;
3578
+ return { key: current.key, nodePath: path, matchedBy };
3579
+ }
3580
+ function resolveSubcategory(item, groupKey, tree) {
3581
+ const group = findNode(tree, groupKey);
3582
+ if (!group) return null;
3583
+ const kids = classifiable(group.children ?? []);
3584
+ if (kids.length === 0) return null;
3585
+ const r = resolveClassification(item, kids);
3586
+ return r ? { ...r, nodePath: [groupKey, ...r.nodePath] } : null;
3587
+ }
3588
+ function findNode(tree, key) {
3589
+ for (const n of tree ?? []) {
3590
+ if (n.key === key) return n;
3591
+ const deep = findNode(n.children ?? [], key);
3592
+ if (deep) return deep;
3593
+ }
3594
+ return null;
3595
+ }
3596
+ function flattenTree(tree) {
3597
+ const out = [];
3598
+ for (const n of [...tree ?? []].sort(byOrder)) {
3599
+ out.push(n);
3600
+ out.push(...flattenTree(n.children ?? []));
3601
+ }
3602
+ return out;
3603
+ }
3604
+ function validateTree(tree) {
3605
+ const errors = [];
3606
+ const seenKeys = /* @__PURE__ */ new Map();
3607
+ const seenProfiles = /* @__PURE__ */ new Map();
3608
+ const walkLevel = (nodes, parentPath) => {
3609
+ const fallbacks = nodes.filter((n) => n.role === "fallback");
3610
+ if (fallbacks.length > 1) {
3611
+ errors.push(
3612
+ `${parentPath}: ${fallbacks.length} n\xF3s role:'fallback' entre irm\xE3os (esperado no m\xE1ximo 1)`
3613
+ );
3614
+ }
3615
+ if (fallbacks.length === 1) {
3616
+ const fb = fallbacks[0];
3617
+ const maxOther = nodes.filter((n) => n !== fb && n.role !== "ocultos" && n.role !== "residual" && n.role !== "total").reduce((m, n) => Math.max(m, n.order ?? 0), Number.NEGATIVE_INFINITY);
3618
+ if (maxOther !== Number.NEGATIVE_INFINITY && (fb.order ?? 0) <= maxOther) {
3619
+ errors.push(
3620
+ `${parentPath}: fallback "${fb.key}" tem order ${fb.order} <= ${maxOther} de um irm\xE3o real \u2014 o catch-all sombrearia a regra`
3621
+ );
3622
+ }
3623
+ }
3624
+ for (const n of nodes) {
3625
+ const path = `${parentPath}/${n.key || "<sem key>"}`;
3626
+ if (!n.key) errors.push(`${path}: key vazia`);
3627
+ if (!n.label) errors.push(`${path}: label vazio`);
3628
+ if (!Number.isInteger(n.order)) {
3629
+ errors.push(`${path}: order ausente ou n\xE3o-inteiro (RFC-0207 \xA7B.1-1 exige expl\xEDcito)`);
3630
+ }
3631
+ seenKeys.set(n.key, (seenKeys.get(n.key) ?? 0) + 1);
3632
+ for (const dp of n.rules?.deviceProfiles ?? []) {
3633
+ const k = up2(dp);
3634
+ const owner = seenProfiles.get(k);
3635
+ if (owner && owner !== n.key) {
3636
+ errors.push(`aloca\xE7\xE3o n\xE3o-\xFAnica: deviceProfile "${k}" em "${owner}" e "${n.key}"`);
3637
+ } else {
3638
+ seenProfiles.set(k, n.key);
3639
+ }
3640
+ }
3641
+ if (n.children?.length) walkLevel(n.children, path);
3642
+ }
3643
+ };
3644
+ walkLevel(tree ?? [], "");
3645
+ for (const [key, count] of seenKeys) {
3646
+ if (count > 1) errors.push(`key duplicada na \xE1rvore: "${key}" (${count}x)`);
3647
+ }
3648
+ for (const n of flattenTree(tree ?? [])) {
3649
+ const refs = [...n.formula?.of ?? [], ...n.formula?.subtract ?? []];
3650
+ if (n.formula?.from) refs.push(n.formula.from);
3651
+ for (const ref of refs) {
3652
+ if (!seenKeys.has(ref)) errors.push(`formula de "${n.key}" referencia key inexistente "${ref}"`);
3653
+ }
3654
+ }
3655
+ return errors;
3656
+ }
3657
+ var LABELS = {
3658
+ ocultos: "Ocultos",
3659
+ entrada: "Entrada",
3660
+ lojas: "Lojas",
3661
+ areacomum: "\xC1rea Comum",
3662
+ caixadagua: "Caixa d'\xC1gua",
3663
+ banheiros: "Banheiros",
3664
+ climatizavel: "Climatiz\xE1vel",
3665
+ nao_climatizavel: "N\xE3o Climatiz\xE1vel",
3666
+ climatizacao: "Climatiza\xE7\xE3o",
3667
+ elevadores: "Elevadores",
3668
+ escadas_rolantes: "Escadas Rolantes",
3669
+ outros: "Outros Equipamentos"
3670
+ };
3671
+ function labelFor(key) {
3672
+ return LABELS[key] ?? key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " ");
3673
+ }
3674
+ function liftProfileToTree(profile = getActiveProfile(), domain = "energy") {
3675
+ const dom = profile?.domains?.[domain];
3676
+ if (!dom) return [];
3677
+ const tree = [];
3678
+ let order = 0;
3679
+ tree.push({
3680
+ key: "ocultos",
3681
+ label: labelFor("ocultos"),
3682
+ role: "ocultos",
3683
+ order: order++,
3684
+ rules: { profileContains: [...dom.groups?.ocultosProfilePatterns ?? []] }
3685
+ });
3686
+ const groupRules = (dom.groups?.rules ?? []).filter((r) => !r.fallback);
3687
+ const groupFallback = (dom.groups?.rules ?? []).find((r) => r.fallback);
3688
+ const cats = dom.categories;
3689
+ for (const r of groupRules) {
3690
+ tree.push({
3691
+ key: r.name,
3692
+ label: labelFor(r.name),
3693
+ role: r.name === "entrada" ? "entrada" : "consumer",
3694
+ order: order++,
3695
+ rules: { deviceProfiles: [...r.deviceProfiles ?? []] }
3696
+ });
3697
+ }
3698
+ if (cats) {
3699
+ const lojas = tree.find((n) => n.key === "lojas");
3700
+ if (lojas && cats.storeDeviceProfile) {
3701
+ lojas.rules = lojas.rules ?? {};
3702
+ const list = lojas.rules.deviceProfiles ?? [];
3703
+ if (!list.some((v) => up2(v) === up2(cats.storeDeviceProfile))) list.push(cats.storeDeviceProfile);
3704
+ lojas.rules.deviceProfiles = list;
3705
+ }
3706
+ for (const r of cats.rules) {
3707
+ tree.push({
3708
+ key: r.name,
3709
+ label: labelFor(r.name),
3710
+ role: "consumer",
3711
+ order: order++,
3712
+ rules: {
3713
+ deviceProfiles: [...r.deviceProfiles ?? []],
3714
+ profileContains: r.profileContains ? [...r.profileContains] : void 0,
3715
+ identifierExact: r.identifierFallback?.identifierEquals ? [...r.identifierFallback.identifierEquals] : void 0,
3716
+ identifierContains: r.identifierFallback?.identifierContains ? [...r.identifierFallback.identifierContains] : void 0,
3717
+ identifierPrefixes: r.identifierFallback?.identifierPrefixes ? [...r.identifierFallback.identifierPrefixes] : void 0,
3718
+ conditional: r.conditional ? { ...r.conditional } : void 0
3719
+ }
3720
+ });
3721
+ }
3722
+ tree.push({
3723
+ key: cats.fallback?.name ?? "outros",
3724
+ label: labelFor(cats.fallback?.name ?? "outros"),
3725
+ role: "fallback",
3726
+ order: order++
3727
+ });
3728
+ if (groupFallback) {
3729
+ const consumers = tree.filter((n) => n.role === "consumer" || n.role === "fallback").map((n) => n.key);
3730
+ tree.push({
3731
+ key: groupFallback.name,
3732
+ label: labelFor(groupFallback.name),
3733
+ role: "residual",
3734
+ order: order++,
3735
+ formula: { op: "subtract", from: "entrada", subtract: consumers }
3736
+ });
3737
+ }
3738
+ } else if (groupFallback) {
3739
+ tree.push({
3740
+ key: groupFallback.name,
3741
+ label: labelFor(groupFallback.name),
3742
+ role: "fallback",
3743
+ order: order++
3744
+ });
3745
+ }
3746
+ return tree;
3747
+ }
3748
+
3749
+ // src/utils/devices/bakedProfile.generated.ts
3750
+ var BAKED_PROFILE_VERSION = "d861298603d0";
3751
+ var BAKED_PROFILE_KEYS = Object.freeze([
3752
+ "energy",
3753
+ "energy.categories.climatizacao",
3754
+ "energy.categories.elevadores",
3755
+ "energy.categories.escadas_rolantes",
3756
+ "energy.categories.lojas",
3757
+ "energy.categories.outros",
3758
+ "energy.groups.areacomum",
3759
+ "energy.groups.entrada",
3760
+ "energy.groups.lojas",
3761
+ "energy.groups.ocultos",
3762
+ "temperature",
3763
+ "temperature.groups.climatizavel",
3764
+ "temperature.groups.nao_climatizavel",
3765
+ "temperature.groups.ocultos",
3766
+ "water",
3767
+ "water.groups.areacomum",
3768
+ "water.groups.caixadagua",
3769
+ "water.groups.entrada",
3770
+ "water.groups.lojas",
3771
+ "water.groups.ocultos"
3772
+ ]);
3773
+
3774
+ // src/utils/devices/profileSource.ts
3775
+ function createBakedProfileSource() {
3776
+ return {
3777
+ name: "baked",
3778
+ async resolve() {
3779
+ return {
3780
+ version: BAKED_PROFILE_VERSION,
3781
+ source: "baked",
3782
+ profile: DEFAULT_DEVICE_CLASSIFICATION_PROFILE,
3783
+ degraded: true,
3784
+ reason: "baked-default"
3785
+ };
3786
+ }
3787
+ };
3788
+ }
3789
+ function withTimeout(p, ms) {
3790
+ if (!ms || ms <= 0) return p;
3791
+ return new Promise((resolve, reject) => {
3792
+ const t = setTimeout(() => reject(new Error(`profile source timeout after ${ms}ms`)), ms);
3793
+ p.then(
3794
+ (v) => {
3795
+ clearTimeout(t);
3796
+ resolve(v);
3797
+ },
3798
+ (e) => {
3799
+ clearTimeout(t);
3800
+ reject(e);
3801
+ }
3802
+ );
3803
+ });
3804
+ }
3805
+ async function resolveWithFallback(primary, opts) {
3806
+ const baked = opts.baked ?? createBakedProfileSource();
3807
+ const logger = opts.logger ?? console;
3808
+ const degrade = async (reason) => {
3809
+ const floor = await baked.resolve(opts.customerId);
3810
+ const info = {
3811
+ source: "baked",
3812
+ reason,
3813
+ customerId: opts.customerId,
3814
+ bakedVersion: floor.version
3815
+ };
3816
+ logger.warn(`[RFC-0207] profile degraded \u2192 baked ${JSON.stringify(info)}`);
3817
+ try {
3818
+ opts.onDegraded?.(info);
3819
+ } catch {
3820
+ }
3821
+ return { ...floor, degraded: true, reason };
3822
+ };
3823
+ let resolved;
3824
+ try {
3825
+ resolved = await withTimeout(primary.resolve(opts.customerId), opts.timeoutMs ?? 0);
3826
+ } catch (err) {
3827
+ return degrade(`${primary.name}:${err?.message || "resolve threw"}`);
3828
+ }
3829
+ if (!resolved || !resolved.profile) {
3830
+ return degrade(`${primary.name}:empty-response`);
3831
+ }
3832
+ const errors = validateProfile(resolved.profile);
3833
+ if (errors.length > 0) {
3834
+ return degrade(`${primary.name}:invalid-profile:${errors.slice(0, 3).join("; ")}`);
3835
+ }
3836
+ return {
3837
+ ...resolved,
3838
+ profile: normalizeProfile(resolved.profile),
3839
+ degraded: resolved.degraded ?? false
3840
+ };
3841
+ }
3842
+ function isBakedStale(resolved, authoredVersion) {
3843
+ if (!authoredVersion) return false;
3844
+ return resolved.source === "baked" && resolved.version !== authoredVersion;
3845
+ }
3846
+
3466
3847
  // src/utils/devices/classificationTree.ts
3467
- function up2(s) {
3848
+ function up3(s) {
3468
3849
  return String(s ?? "").trim().toUpperCase();
3469
3850
  }
3470
3851
  function isUsable(node) {
@@ -3476,7 +3857,7 @@ function collectProfiles(node) {
3476
3857
  const kids = [...n.children ?? []].filter(isUsable).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
3477
3858
  for (const child of kids) {
3478
3859
  if ((child.entityType ?? "").toUpperCase() === "PROFILE") {
3479
- const key = up2(child.entityKey);
3860
+ const key = up3(child.entityKey);
3480
3861
  if (key && !out.includes(key)) out.push(key);
3481
3862
  }
3482
3863
  walk(child);
@@ -6155,8 +6536,8 @@ function openDeviceProfileModal(params) {
6155
6536
  onClose
6156
6537
  } = params;
6157
6538
  void customerId;
6158
- const working = deepClone(
6159
- params.profile || DEFAULT_DEVICE_CLASSIFICATION_PROFILE
6539
+ const working = normalizeProfile(
6540
+ deepClone(params.profile || DEFAULT_DEVICE_CLASSIFICATION_PROFILE)
6160
6541
  );
6161
6542
  working.domains = working.domains || {};
6162
6543
  if (!working.domains.energy) {
@@ -6328,11 +6709,13 @@ function openDeviceProfileModal(params) {
6328
6709
  accent: accentFor(r.name),
6329
6710
  infoIcon: "\u{1F527}",
6330
6711
  infoHtml: cardInfoText(
6331
- `Breakdown <b>${escHtml(r.name)}</b> (preced\xEAncia): 1) deviceProfile exato; 2) identifier cont\xE9m/prefixo.`
6712
+ `Breakdown <b>${escHtml(r.name)}</b> (preced\xEAncia): 1) <b>deviceProfile</b> exato; 2) <b>deviceProfile cont\xE9m</b> (substring); 3) identifier cont\xE9m/prefixo.<br><br>
6713
+ S\xF3 o <b>deviceProfile</b> e o <b>identifier</b> s\xE3o avaliados \u2014 nunca o nome/label do device.`
6332
6714
  )
6333
6715
  },
6334
6716
  `
6335
6717
  <div class="mdp-field"><label>deviceProfile</label>${chipList(`cat-${i}-dp`, r.deviceProfiles || [], ro)}</div>
6718
+ <div class="mdp-field"><label>deviceProfile cont\xE9m</label>${chipList(`cat-${i}-pc`, r.profileContains || [], ro)}</div>
6336
6719
  <div class="mdp-field"><label>identifier cont\xE9m</label>${chipList(`cat-${i}-idc`, r.identifierFallback?.identifierContains || [], ro)}</div>
6337
6720
  <div class="mdp-field"><label>identifier prefixo</label>${chipList(`cat-${i}-idp`, r.identifierFallback?.identifierPrefixes || [], ro)}</div>`
6338
6721
  )
@@ -6388,11 +6771,11 @@ function openDeviceProfileModal(params) {
6388
6771
  }
6389
6772
  const cats = d.categories;
6390
6773
  if (!cats) return null;
6391
- m = key.match(/^cat-(\d+)-(dp|cc|idc|idp)$/);
6774
+ m = key.match(/^cat-(\d+)-(dp|pc|idc|idp)$/);
6392
6775
  if (m) {
6393
6776
  const rule = cats.rules[Number(m[1])];
6394
6777
  if (m[2] === "dp") return rule.deviceProfiles = rule.deviceProfiles || [];
6395
- if (m[2] === "cc") return rule.combinedContains = rule.combinedContains || [];
6778
+ if (m[2] === "pc") return rule.profileContains = rule.profileContains || [];
6396
6779
  rule.identifierFallback = rule.identifierFallback || {};
6397
6780
  if (m[2] === "idc")
6398
6781
  return rule.identifierFallback.identifierContains = rule.identifierFallback.identifierContains || [];
@@ -68806,14 +69189,14 @@ var DefaultSettingsPersister = class {
68806
69189
  const limitsList = [];
68807
69190
  statuses.forEach((statusKey) => {
68808
69191
  const down = formData[`${statusKey}LimitDownConsumption`];
68809
- const up3 = formData[`${statusKey}LimitUpConsumption`];
68810
- if (down !== void 0 && down !== "" || up3 !== void 0 && up3 !== "") {
69192
+ const up4 = formData[`${statusKey}LimitUpConsumption`];
69193
+ if (down !== void 0 && down !== "" || up4 !== void 0 && up4 !== "") {
68811
69194
  limitsList.push({
68812
69195
  deviceStatusName: statusMap[statusKey],
68813
69196
  limitsValues: {
68814
69197
  // Converte para Number ou null
68815
69198
  baseValue: down !== "" && down !== void 0 ? Number(down) : null,
68816
- topValue: up3 !== "" && up3 !== void 0 ? Number(up3) : null
69199
+ topValue: up4 !== "" && up4 !== void 0 ? Number(up4) : null
68817
69200
  }
68818
69201
  });
68819
69202
  }
@@ -86061,10 +86444,17 @@ function _applyTheme(root) {
86061
86444
  }
86062
86445
  function _formatValue(value, domain) {
86063
86446
  const cfg = DOMAIN_CFG[domain] || DOMAIN_CFG.energy;
86064
- const dec = domain === "temperature" ? 1 : 2;
86447
+ const U = typeof window !== "undefined" ? window.MyIOUtils : void 0;
86448
+ if (domain === "energy" && typeof U?.formatEnergyWithSettings === "function") {
86449
+ return U.formatEnergyWithSettings(value);
86450
+ }
86451
+ if (domain === "water" && typeof U?.formatWaterWithSettings === "function") {
86452
+ return U.formatWaterWithSettings(value);
86453
+ }
86454
+ const dec = domain === "temperature" ? 1 : 3;
86065
86455
  const ptBR = (v, d) => v.toLocaleString("pt-BR", { minimumFractionDigits: d, maximumFractionDigits: d });
86066
86456
  if (cfg.threshold && cfg.unitLarge && Math.abs(value) >= cfg.threshold) {
86067
- return `${ptBR(value / cfg.threshold, 2)} ${cfg.unitLarge}`;
86457
+ return `${ptBR(value / cfg.threshold, 3)} ${cfg.unitLarge}`;
86068
86458
  }
86069
86459
  return `${ptBR(value, dec)} ${cfg.unit}`;
86070
86460
  }
@@ -86199,7 +86589,7 @@ async function _fetchSeriesTotals(domain, boundaries, viewGran, fetchGran) {
86199
86589
  async function _fetchDayData(domain) {
86200
86590
  const boundaries = _buildDayBoundaries(_periodStart, _periodEnd);
86201
86591
  const labels = boundaries.map((b) => b.label);
86202
- const totals = await _fetchSeriesTotals(domain, boundaries, "1d", "1d") ?? await _fetchTotalsThrottled(domain, boundaries, "1d");
86592
+ const totals = await _fetchSeriesTotals(domain, boundaries, "1h", "1h") ?? await _fetchTotalsThrottled(domain, boundaries, "1d");
86203
86593
  return { labels, totals };
86204
86594
  }
86205
86595
  async function _fetchHourData(domain, dateISO) {
@@ -86247,7 +86637,9 @@ async function _fetchPrevYearTotals(domain, gran, boundaries) {
86247
86637
  pe.setFullYear(pe.getFullYear() - 1);
86248
86638
  return { ...b, startTs: ps.getTime(), endTs: pe.getTime() };
86249
86639
  });
86250
- const viaSeries = await _fetchSeriesTotals(domain, prevBoundaries, gran, gran === "1M" ? "1d" : gran);
86640
+ const seriesViewGran = gran === "1d" ? "1h" : gran;
86641
+ const seriesFetchGran = gran === "1M" ? "1d" : gran === "1d" ? "1h" : gran;
86642
+ const viaSeries = await _fetchSeriesTotals(domain, prevBoundaries, seriesViewGran, seriesFetchGran);
86251
86643
  if (viaSeries) return viaSeries;
86252
86644
  const fetchFn = _options.fetchConsumption;
86253
86645
  const totals = new Array(prevBoundaries.length).fill(0);
@@ -155415,6 +155807,8 @@ var version = package_default.version || "0.0.0";
155415
155807
  AlarmsSummaryTooltip,
155416
155808
  AnnotationIndicator,
155417
155809
  AnnotationTooltip,
155810
+ BAKED_PROFILE_KEYS,
155811
+ BAKED_PROFILE_VERSION,
155418
155812
  BASDashboardController,
155419
155813
  BASDashboardView,
155420
155814
  BAS_DASHBOARD_CSS_PREFIX,
@@ -155737,6 +156131,7 @@ var version = package_default.version || "0.0.0";
155737
156131
  createAmbienteGroupModal,
155738
156132
  createAnnotationIndicator,
155739
156133
  createBASDashboard,
156134
+ createBakedProfileSource,
155740
156135
  createButtonBar,
155741
156136
  createConsumption7DaysChart,
155742
156137
  createConsumptionChartWidget,
@@ -155848,8 +156243,10 @@ var version = package_default.version || "0.0.0";
155848
156243
  fetchThingsboardCustomerAttrsFromStorage,
155849
156244
  fetchThingsboardCustomerServerScopeAttrs,
155850
156245
  fetchTicketsForDevice,
156246
+ findNode,
155851
156247
  findValue,
155852
156248
  findValueWithDefault,
156249
+ flattenTree,
155853
156250
  fmtPerc,
155854
156251
  fmtPercLegacy,
155855
156252
  formatAlarmRelativeTime,
@@ -155982,6 +156379,7 @@ var version = package_default.version || "0.0.0";
155982
156379
  isAlarmActive,
155983
156380
  isAnnotationOverdue,
155984
156381
  isAssetCode,
156382
+ isBakedStale,
155985
156383
  isConnectionStale,
155986
156384
  isCustomerCode,
155987
156385
  isDeviceIconType,
@@ -156002,6 +156400,7 @@ var version = package_default.version || "0.0.0";
156002
156400
  isValidDeviceStatus,
156003
156401
  isValidTimeFormat,
156004
156402
  isWaterCategory,
156403
+ liftProfileToTree,
156005
156404
  listDomains,
156006
156405
  listGroups,
156007
156406
  mapConnectionStatus,
@@ -156090,8 +156489,11 @@ var version = package_default.version || "0.0.0";
156090
156489
  reopenFreshdeskTicket,
156091
156490
  resolveActiveProfile,
156092
156491
  resolveCategory,
156492
+ resolveClassification,
156093
156493
  resolveGroup,
156094
156494
  resolvePercentDecimals,
156495
+ resolveSubcategory,
156496
+ resolveWithFallback,
156095
156497
  schedCreateDateInput,
156096
156498
  schedCreateNumberInput,
156097
156499
  schedCreateSelect,
@@ -156120,6 +156522,7 @@ var version = package_default.version || "0.0.0";
156120
156522
  updateFreshdeskTicket,
156121
156523
  upsertAlarmAnnotation,
156122
156524
  validateProfile,
156525
+ validateTree,
156123
156526
  version,
156124
156527
  waterDeviceStatusIcons,
156125
156528
  writeFreshdeskSyncedAtToTB,