myio-js-library 0.1.144 → 0.1.146

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
@@ -5043,15 +5043,41 @@ function buildDOM(state) {
5043
5043
  function verifyOfflineStatus(entityObject) {
5044
5044
  const lastConnectionTime = new Date(entityObject.lastConnectTime || 0);
5045
5045
  const lastDisconnectTime = new Date(entityObject.lastDisconnectTime || 0);
5046
- if (lastDisconnectTime.getTime() > lastConnectionTime.getTime()) {
5047
- return false;
5048
- }
5049
5046
  const now = /* @__PURE__ */ new Date();
5050
5047
  const fifteenMinutesInMs = 15 * 60 * 1e3;
5051
5048
  const timeSinceConnection = now.getTime() - lastConnectionTime.getTime();
5049
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
5050
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
5051
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] label:", entityObject.labelOrName);
5052
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] deviceStatus (before):", entityObject.deviceStatus);
5053
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastConnectTime (raw):", entityObject.lastConnectTime);
5054
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastDisconnectTime (raw):", entityObject.lastDisconnectTime);
5055
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastConnectionTime (Date):", lastConnectionTime.toISOString());
5056
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastDisconnectTime (Date):", lastDisconnectTime.toISOString());
5057
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] now:", now.toISOString());
5058
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection (ms):", timeSinceConnection);
5059
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection (min):", (timeSinceConnection / 6e4).toFixed(2));
5060
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] disconnect > connect?", lastDisconnectTime.getTime() > lastConnectionTime.getTime());
5061
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection < 15min?", timeSinceConnection < fifteenMinutesInMs);
5062
+ }
5063
+ if (lastDisconnectTime.getTime() > lastConnectionTime.getTime()) {
5064
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
5065
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: false (disconnect > connect)");
5066
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
5067
+ }
5068
+ return false;
5069
+ }
5052
5070
  if (timeSinceConnection < fifteenMinutesInMs) {
5071
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
5072
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: false (connection < 15min ago)");
5073
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
5074
+ }
5053
5075
  return false;
5054
5076
  }
5077
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
5078
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: true (online)");
5079
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
5080
+ }
5055
5081
  return true;
5056
5082
  }
5057
5083
  function paint(root, state) {
@@ -15403,7 +15429,13 @@ var SettingsModalView = class {
15403
15429
  render(initialData) {
15404
15430
  this.originalActiveElement = document.activeElement;
15405
15431
  document.body.appendChild(this.container);
15406
- this.populateForm(initialData);
15432
+ let formData = { ...initialData };
15433
+ if (initialData.deviceMapInstaneousPower && typeof initialData.deviceMapInstaneousPower === "object") {
15434
+ console.log("[SettingsModalView] Configura\xE7\xE3o salva encontrada (Device Scope). Processando...");
15435
+ const flatLimits = this.parseDeviceSavedLimits(initialData.deviceMapInstaneousPower);
15436
+ formData = { ...formData, ...flatLimits };
15437
+ }
15438
+ this.populateForm(formData);
15407
15439
  this.attachEventListeners();
15408
15440
  this.setupAccessibility();
15409
15441
  this.setupFocusTrap();
@@ -16972,6 +17004,42 @@ var SettingsModalView = class {
16972
17004
  }
16973
17005
  }
16974
17006
  }
17007
+ /**
17008
+ * Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
17009
+ * para os campos planos do formulário (ex: standbyLimitUpConsumption)
17010
+ */
17011
+ parseDeviceSavedLimits(deviceJson) {
17012
+ const extracted = {};
17013
+ try {
17014
+ if (!deviceJson || !deviceJson.limitsByInstantaneoustPowerType) return extracted;
17015
+ const consumptionGroup = deviceJson.limitsByInstantaneoustPowerType.find(
17016
+ (g) => g.telemetryType === "consumption"
17017
+ );
17018
+ const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
17019
+ if (!deviceItem?.limitsByDeviceStatus) return extracted;
17020
+ const mapPrefix = {
17021
+ "standBy": "standby",
17022
+ "normal": "normal",
17023
+ "alert": "alert",
17024
+ "failure": "failure"
17025
+ };
17026
+ deviceItem.limitsByDeviceStatus.forEach((status) => {
17027
+ const prefix = mapPrefix[status.deviceStatusName];
17028
+ if (prefix && status.limitsValues) {
17029
+ const { baseValue, topValue } = status.limitsValues;
17030
+ if (baseValue !== null && baseValue !== void 0) {
17031
+ extracted[`${prefix}LimitDownConsumption`] = baseValue;
17032
+ }
17033
+ if (topValue !== null && topValue !== void 0) {
17034
+ extracted[`${prefix}LimitUpConsumption`] = topValue;
17035
+ }
17036
+ }
17037
+ });
17038
+ } catch (e) {
17039
+ console.warn("[SettingsModalView] Erro ao processar deviceMapInstaneousPower:", e);
17040
+ }
17041
+ return extracted;
17042
+ }
16975
17043
  attachEventListeners() {
16976
17044
  this.form.addEventListener("submit", (event) => {
16977
17045
  event.preventDefault();
@@ -17243,11 +17311,25 @@ var DefaultSettingsPersister = class {
17243
17311
  }
17244
17312
  async saveServerScopeAttributes(deviceId, attributes) {
17245
17313
  try {
17246
- const mapInstantaneousPower = this.buildMapInstantaneousPower(attributes);
17247
- const payload = {
17248
- mapInstantaneousPower
17249
- };
17250
- console.log("[SettingsPersister] RFC-0080: Saving mapInstantaneousPower as JSON:", payload);
17314
+ const payload = { ...attributes };
17315
+ const effectiveDeviceType = this.getEffectiveDeviceType();
17316
+ const deviceJson = this.buildDevicePowerJson(payload, effectiveDeviceType);
17317
+ if (deviceJson) {
17318
+ payload.deviceMapInstaneousPower = deviceJson;
17319
+ }
17320
+ const flatKeysToRemove = [
17321
+ "telemetryType",
17322
+ "standbyLimitDownConsumption",
17323
+ "standbyLimitUpConsumption",
17324
+ "normalLimitDownConsumption",
17325
+ "normalLimitUpConsumption",
17326
+ "alertLimitDownConsumption",
17327
+ "alertLimitUpConsumption",
17328
+ "failureLimitDownConsumption",
17329
+ "failureLimitUpConsumption"
17330
+ ];
17331
+ flatKeysToRemove.forEach((key) => delete payload[key]);
17332
+ console.log("[SettingsPersister] Saving Server Scope Attributes:", payload);
17251
17333
  const res = await fetch(
17252
17334
  `${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`,
17253
17335
  {
@@ -17264,13 +17346,70 @@ var DefaultSettingsPersister = class {
17264
17346
  }
17265
17347
  return {
17266
17348
  ok: true,
17267
- updatedKeys: ["mapInstantaneousPower"]
17349
+ updatedKeys: Object.keys(payload)
17268
17350
  };
17269
17351
  } catch (error) {
17270
17352
  console.error("[SettingsPersister] Attributes save failed:", error);
17271
17353
  return { ok: false, error: this.mapError(error) };
17272
17354
  }
17273
17355
  }
17356
+ /**
17357
+ * Constrói o JSON Reduzido (apenas o deviceType atual) para salvar no Device.
17358
+ * Retorna null se nenhum campo de potência estiver presente.
17359
+ */
17360
+ buildDevicePowerJson(formData, deviceType) {
17361
+ const statuses = ["standby", "normal", "alert", "failure"];
17362
+ const hasPowerData = statuses.some(
17363
+ (status) => formData[`${status}LimitDownConsumption`] !== void 0 && formData[`${status}LimitDownConsumption`] !== "" || formData[`${status}LimitUpConsumption`] !== void 0 && formData[`${status}LimitUpConsumption`] !== ""
17364
+ );
17365
+ if (!hasPowerData) return null;
17366
+ const statusMap = {
17367
+ "standby": "standBy",
17368
+ "normal": "normal",
17369
+ "alert": "alert",
17370
+ "failure": "failure"
17371
+ };
17372
+ const limitsList = [];
17373
+ statuses.forEach((statusKey) => {
17374
+ const down = formData[`${statusKey}LimitDownConsumption`];
17375
+ const up = formData[`${statusKey}LimitUpConsumption`];
17376
+ if (down !== void 0 && down !== "" || up !== void 0 && up !== "") {
17377
+ limitsList.push({
17378
+ deviceStatusName: statusMap[statusKey],
17379
+ limitsValues: {
17380
+ // Converte para Number ou null
17381
+ baseValue: down !== "" && down !== void 0 ? Number(down) : null,
17382
+ topValue: up !== "" && up !== void 0 ? Number(up) : null
17383
+ }
17384
+ });
17385
+ }
17386
+ });
17387
+ if (limitsList.length === 0) return null;
17388
+ return {
17389
+ version: "1.0.0",
17390
+ limitsByInstantaneoustPowerType: [
17391
+ {
17392
+ telemetryType: "consumption",
17393
+ itemsByDeviceType: [
17394
+ {
17395
+ deviceType,
17396
+ // Gera um nome descritivo interno
17397
+ name: `deviceMapInstaneousPower${this.formatDeviceTypeName(deviceType)}`,
17398
+ description: "Override manual configurado via Dashboard",
17399
+ limitsByDeviceStatus: limitsList
17400
+ }
17401
+ ]
17402
+ }
17403
+ ]
17404
+ };
17405
+ }
17406
+ /**
17407
+ * Helper para formatar o nome do tipo (PascalCase) para uso no campo "name" do JSON
17408
+ */
17409
+ formatDeviceTypeName(deviceType) {
17410
+ if (!deviceType) return "";
17411
+ return deviceType.charAt(0).toUpperCase() + deviceType.slice(1).toLowerCase();
17412
+ }
17274
17413
  /**
17275
17414
  * RFC-0086: Build mapInstantaneousPower JSON structure from form data
17276
17415
  * IMPORTANT: When saving to a DEVICE, only include entries for the specific deviceType
@@ -17327,25 +17466,6 @@ var DefaultSettingsPersister = class {
17327
17466
  console.log(`[SettingsPersister] RFC-0086: Built mapInstantaneousPower for deviceType=${effectiveDeviceType}:`, result);
17328
17467
  return result;
17329
17468
  }
17330
- /**
17331
- * Format device type name for display (e.g., ELEVADOR -> Elevador)
17332
- */
17333
- formatDeviceTypeName(deviceType) {
17334
- const map = {
17335
- "ELEVADOR": "Elevator",
17336
- "ESCADA_ROLANTE": "Escalator",
17337
- "MOTOR": "Motor",
17338
- "BOMBA": "Pump",
17339
- "3F_MEDIDOR": "3FMedidor",
17340
- "CHILLER": "Chiller",
17341
- "FANCOIL": "Fancoil",
17342
- "AR_CONDICIONADO": "AirConditioner",
17343
- "HVAC": "HVAC",
17344
- "HIDROMETRO": "Hidrometro",
17345
- "TERMOSTATO": "Termostato"
17346
- };
17347
- return map[deviceType] || deviceType;
17348
- }
17349
17469
  sanitizeLabel(label) {
17350
17470
  return label.trim().slice(0, 255).replace(/[\x00-\x1F\x7F]/g, "");
17351
17471
  }
@@ -17482,18 +17602,16 @@ var DefaultSettingsFetcher = class {
17482
17602
  }
17483
17603
  const attributesArray = await response.json();
17484
17604
  const attributes = {};
17485
- const settingsNamespace = "myio.settings.energy.";
17486
17605
  for (const attr of attributesArray) {
17487
17606
  if (attr.key && attr.value !== void 0 && attr.value !== null && attr.value !== "") {
17488
- if (attr.key.startsWith(settingsNamespace)) {
17489
- const key = attr.key.replace(settingsNamespace, "");
17490
- if (key !== "__version") {
17491
- attributes[key] = attr.value;
17492
- }
17493
- } else if (attr.key === "floor") {
17607
+ if (attr.key === "floor") {
17494
17608
  attributes.floor = attr.value;
17495
17609
  } else if (attr.key === "identifier") {
17496
17610
  attributes.identifier = attr.value;
17611
+ } else if (attr.key === "mapInstantaneousPower") {
17612
+ attributes.mapInstantaneousPower = attr.value;
17613
+ } else if (attr.key === "deviceMapInstaneousPower") {
17614
+ attributes.deviceMapInstaneousPower = attr.value;
17497
17615
  }
17498
17616
  }
17499
17617
  }
@@ -17515,23 +17633,12 @@ var DefaultSettingsFetcher = class {
17515
17633
  }
17516
17634
  return merged;
17517
17635
  }
17518
- /**
17519
- * Utility method to validate and sanitize fetched data
17520
- */
17521
17636
  static sanitizeFetchedData(data) {
17522
17637
  const sanitized = {};
17523
17638
  const stringFields = [
17524
17639
  "label",
17525
17640
  "floor",
17526
- "identifier",
17527
- "alertLimitDownConsumption",
17528
- "alertLimitUpConsumption",
17529
- "failureLimitDownConsumption",
17530
- "failureLimitUpConsumption",
17531
- "normalLimitDownConsumption",
17532
- "normalLimitUpConsumption",
17533
- "standbyLimitDownConsumption",
17534
- "standbyLimitUpConsumption"
17641
+ "identifier"
17535
17642
  ];
17536
17643
  for (const field of stringFields) {
17537
17644
  if (data[field] && typeof data[field] === "string") {
@@ -17547,6 +17654,12 @@ var DefaultSettingsFetcher = class {
17547
17654
  }
17548
17655
  }
17549
17656
  }
17657
+ const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
17658
+ for (const field of objectFields) {
17659
+ if (data[field] && typeof data[field] === "object") {
17660
+ sanitized[field] = data[field];
17661
+ }
17662
+ }
17550
17663
  return sanitized;
17551
17664
  }
17552
17665
  };
@@ -17596,8 +17709,9 @@ var SettingsController = class {
17596
17709
  // Pass connection info for display
17597
17710
  onSave: this.handleSave.bind(this),
17598
17711
  onClose: this.handleClose.bind(this),
17599
- mapInstantaneousPower: params.mapInstantaneousPower
17600
- // RFC-0077: Pass instantaneous power map for Power Limits feature
17712
+ mapInstantaneousPower: params.mapInstantaneousPower,
17713
+ // RFC-0077: Pass instantaneous power map for Power Limits feature,
17714
+ deviceMapInstaneousPower: params.deviceMapInstaneousPower
17601
17715
  });
17602
17716
  }
17603
17717
  async show() {
package/dist/index.d.cts CHANGED
@@ -1634,6 +1634,7 @@ interface OpenDashboardPopupSettingsParams {
1634
1634
  customerName?: string;
1635
1635
  customerId?: string;
1636
1636
  mapInstantaneousPower?: object;
1637
+ deviceMapInstaneousPower?: object;
1637
1638
  connectionData?: {
1638
1639
  centralName?: string;
1639
1640
  connectionStatusTime?: string;
package/dist/index.js CHANGED
@@ -4935,15 +4935,41 @@ function buildDOM(state) {
4935
4935
  function verifyOfflineStatus(entityObject) {
4936
4936
  const lastConnectionTime = new Date(entityObject.lastConnectTime || 0);
4937
4937
  const lastDisconnectTime = new Date(entityObject.lastDisconnectTime || 0);
4938
- if (lastDisconnectTime.getTime() > lastConnectionTime.getTime()) {
4939
- return false;
4940
- }
4941
4938
  const now = /* @__PURE__ */ new Date();
4942
4939
  const fifteenMinutesInMs = 15 * 60 * 1e3;
4943
4940
  const timeSinceConnection = now.getTime() - lastConnectionTime.getTime();
4941
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
4942
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
4943
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] label:", entityObject.labelOrName);
4944
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] deviceStatus (before):", entityObject.deviceStatus);
4945
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastConnectTime (raw):", entityObject.lastConnectTime);
4946
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastDisconnectTime (raw):", entityObject.lastDisconnectTime);
4947
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastConnectionTime (Date):", lastConnectionTime.toISOString());
4948
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] lastDisconnectTime (Date):", lastDisconnectTime.toISOString());
4949
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] now:", now.toISOString());
4950
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection (ms):", timeSinceConnection);
4951
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection (min):", (timeSinceConnection / 6e4).toFixed(2));
4952
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] disconnect > connect?", lastDisconnectTime.getTime() > lastConnectionTime.getTime());
4953
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] timeSinceConnection < 15min?", timeSinceConnection < fifteenMinutesInMs);
4954
+ }
4955
+ if (lastDisconnectTime.getTime() > lastConnectionTime.getTime()) {
4956
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
4957
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: false (disconnect > connect)");
4958
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
4959
+ }
4960
+ return false;
4961
+ }
4944
4962
  if (timeSinceConnection < fifteenMinutesInMs) {
4963
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
4964
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: false (connection < 15min ago)");
4965
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
4966
+ }
4945
4967
  return false;
4946
4968
  }
4969
+ if (String(entityObject.labelOrName || "").toLowerCase().includes("chiller 1")) {
4970
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] RESULT: true (online)");
4971
+ console.log("[DEBUG CHILLER 1 - verifyOfflineStatus] ========================================");
4972
+ }
4947
4973
  return true;
4948
4974
  }
4949
4975
  function paint(root, state) {
@@ -15295,7 +15321,13 @@ var SettingsModalView = class {
15295
15321
  render(initialData) {
15296
15322
  this.originalActiveElement = document.activeElement;
15297
15323
  document.body.appendChild(this.container);
15298
- this.populateForm(initialData);
15324
+ let formData = { ...initialData };
15325
+ if (initialData.deviceMapInstaneousPower && typeof initialData.deviceMapInstaneousPower === "object") {
15326
+ console.log("[SettingsModalView] Configura\xE7\xE3o salva encontrada (Device Scope). Processando...");
15327
+ const flatLimits = this.parseDeviceSavedLimits(initialData.deviceMapInstaneousPower);
15328
+ formData = { ...formData, ...flatLimits };
15329
+ }
15330
+ this.populateForm(formData);
15299
15331
  this.attachEventListeners();
15300
15332
  this.setupAccessibility();
15301
15333
  this.setupFocusTrap();
@@ -16864,6 +16896,42 @@ var SettingsModalView = class {
16864
16896
  }
16865
16897
  }
16866
16898
  }
16899
+ /**
16900
+ * Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
16901
+ * para os campos planos do formulário (ex: standbyLimitUpConsumption)
16902
+ */
16903
+ parseDeviceSavedLimits(deviceJson) {
16904
+ const extracted = {};
16905
+ try {
16906
+ if (!deviceJson || !deviceJson.limitsByInstantaneoustPowerType) return extracted;
16907
+ const consumptionGroup = deviceJson.limitsByInstantaneoustPowerType.find(
16908
+ (g) => g.telemetryType === "consumption"
16909
+ );
16910
+ const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
16911
+ if (!deviceItem?.limitsByDeviceStatus) return extracted;
16912
+ const mapPrefix = {
16913
+ "standBy": "standby",
16914
+ "normal": "normal",
16915
+ "alert": "alert",
16916
+ "failure": "failure"
16917
+ };
16918
+ deviceItem.limitsByDeviceStatus.forEach((status) => {
16919
+ const prefix = mapPrefix[status.deviceStatusName];
16920
+ if (prefix && status.limitsValues) {
16921
+ const { baseValue, topValue } = status.limitsValues;
16922
+ if (baseValue !== null && baseValue !== void 0) {
16923
+ extracted[`${prefix}LimitDownConsumption`] = baseValue;
16924
+ }
16925
+ if (topValue !== null && topValue !== void 0) {
16926
+ extracted[`${prefix}LimitUpConsumption`] = topValue;
16927
+ }
16928
+ }
16929
+ });
16930
+ } catch (e) {
16931
+ console.warn("[SettingsModalView] Erro ao processar deviceMapInstaneousPower:", e);
16932
+ }
16933
+ return extracted;
16934
+ }
16867
16935
  attachEventListeners() {
16868
16936
  this.form.addEventListener("submit", (event) => {
16869
16937
  event.preventDefault();
@@ -17135,11 +17203,25 @@ var DefaultSettingsPersister = class {
17135
17203
  }
17136
17204
  async saveServerScopeAttributes(deviceId, attributes) {
17137
17205
  try {
17138
- const mapInstantaneousPower = this.buildMapInstantaneousPower(attributes);
17139
- const payload = {
17140
- mapInstantaneousPower
17141
- };
17142
- console.log("[SettingsPersister] RFC-0080: Saving mapInstantaneousPower as JSON:", payload);
17206
+ const payload = { ...attributes };
17207
+ const effectiveDeviceType = this.getEffectiveDeviceType();
17208
+ const deviceJson = this.buildDevicePowerJson(payload, effectiveDeviceType);
17209
+ if (deviceJson) {
17210
+ payload.deviceMapInstaneousPower = deviceJson;
17211
+ }
17212
+ const flatKeysToRemove = [
17213
+ "telemetryType",
17214
+ "standbyLimitDownConsumption",
17215
+ "standbyLimitUpConsumption",
17216
+ "normalLimitDownConsumption",
17217
+ "normalLimitUpConsumption",
17218
+ "alertLimitDownConsumption",
17219
+ "alertLimitUpConsumption",
17220
+ "failureLimitDownConsumption",
17221
+ "failureLimitUpConsumption"
17222
+ ];
17223
+ flatKeysToRemove.forEach((key) => delete payload[key]);
17224
+ console.log("[SettingsPersister] Saving Server Scope Attributes:", payload);
17143
17225
  const res = await fetch(
17144
17226
  `${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`,
17145
17227
  {
@@ -17156,13 +17238,70 @@ var DefaultSettingsPersister = class {
17156
17238
  }
17157
17239
  return {
17158
17240
  ok: true,
17159
- updatedKeys: ["mapInstantaneousPower"]
17241
+ updatedKeys: Object.keys(payload)
17160
17242
  };
17161
17243
  } catch (error) {
17162
17244
  console.error("[SettingsPersister] Attributes save failed:", error);
17163
17245
  return { ok: false, error: this.mapError(error) };
17164
17246
  }
17165
17247
  }
17248
+ /**
17249
+ * Constrói o JSON Reduzido (apenas o deviceType atual) para salvar no Device.
17250
+ * Retorna null se nenhum campo de potência estiver presente.
17251
+ */
17252
+ buildDevicePowerJson(formData, deviceType) {
17253
+ const statuses = ["standby", "normal", "alert", "failure"];
17254
+ const hasPowerData = statuses.some(
17255
+ (status) => formData[`${status}LimitDownConsumption`] !== void 0 && formData[`${status}LimitDownConsumption`] !== "" || formData[`${status}LimitUpConsumption`] !== void 0 && formData[`${status}LimitUpConsumption`] !== ""
17256
+ );
17257
+ if (!hasPowerData) return null;
17258
+ const statusMap = {
17259
+ "standby": "standBy",
17260
+ "normal": "normal",
17261
+ "alert": "alert",
17262
+ "failure": "failure"
17263
+ };
17264
+ const limitsList = [];
17265
+ statuses.forEach((statusKey) => {
17266
+ const down = formData[`${statusKey}LimitDownConsumption`];
17267
+ const up = formData[`${statusKey}LimitUpConsumption`];
17268
+ if (down !== void 0 && down !== "" || up !== void 0 && up !== "") {
17269
+ limitsList.push({
17270
+ deviceStatusName: statusMap[statusKey],
17271
+ limitsValues: {
17272
+ // Converte para Number ou null
17273
+ baseValue: down !== "" && down !== void 0 ? Number(down) : null,
17274
+ topValue: up !== "" && up !== void 0 ? Number(up) : null
17275
+ }
17276
+ });
17277
+ }
17278
+ });
17279
+ if (limitsList.length === 0) return null;
17280
+ return {
17281
+ version: "1.0.0",
17282
+ limitsByInstantaneoustPowerType: [
17283
+ {
17284
+ telemetryType: "consumption",
17285
+ itemsByDeviceType: [
17286
+ {
17287
+ deviceType,
17288
+ // Gera um nome descritivo interno
17289
+ name: `deviceMapInstaneousPower${this.formatDeviceTypeName(deviceType)}`,
17290
+ description: "Override manual configurado via Dashboard",
17291
+ limitsByDeviceStatus: limitsList
17292
+ }
17293
+ ]
17294
+ }
17295
+ ]
17296
+ };
17297
+ }
17298
+ /**
17299
+ * Helper para formatar o nome do tipo (PascalCase) para uso no campo "name" do JSON
17300
+ */
17301
+ formatDeviceTypeName(deviceType) {
17302
+ if (!deviceType) return "";
17303
+ return deviceType.charAt(0).toUpperCase() + deviceType.slice(1).toLowerCase();
17304
+ }
17166
17305
  /**
17167
17306
  * RFC-0086: Build mapInstantaneousPower JSON structure from form data
17168
17307
  * IMPORTANT: When saving to a DEVICE, only include entries for the specific deviceType
@@ -17219,25 +17358,6 @@ var DefaultSettingsPersister = class {
17219
17358
  console.log(`[SettingsPersister] RFC-0086: Built mapInstantaneousPower for deviceType=${effectiveDeviceType}:`, result);
17220
17359
  return result;
17221
17360
  }
17222
- /**
17223
- * Format device type name for display (e.g., ELEVADOR -> Elevador)
17224
- */
17225
- formatDeviceTypeName(deviceType) {
17226
- const map = {
17227
- "ELEVADOR": "Elevator",
17228
- "ESCADA_ROLANTE": "Escalator",
17229
- "MOTOR": "Motor",
17230
- "BOMBA": "Pump",
17231
- "3F_MEDIDOR": "3FMedidor",
17232
- "CHILLER": "Chiller",
17233
- "FANCOIL": "Fancoil",
17234
- "AR_CONDICIONADO": "AirConditioner",
17235
- "HVAC": "HVAC",
17236
- "HIDROMETRO": "Hidrometro",
17237
- "TERMOSTATO": "Termostato"
17238
- };
17239
- return map[deviceType] || deviceType;
17240
- }
17241
17361
  sanitizeLabel(label) {
17242
17362
  return label.trim().slice(0, 255).replace(/[\x00-\x1F\x7F]/g, "");
17243
17363
  }
@@ -17374,18 +17494,16 @@ var DefaultSettingsFetcher = class {
17374
17494
  }
17375
17495
  const attributesArray = await response.json();
17376
17496
  const attributes = {};
17377
- const settingsNamespace = "myio.settings.energy.";
17378
17497
  for (const attr of attributesArray) {
17379
17498
  if (attr.key && attr.value !== void 0 && attr.value !== null && attr.value !== "") {
17380
- if (attr.key.startsWith(settingsNamespace)) {
17381
- const key = attr.key.replace(settingsNamespace, "");
17382
- if (key !== "__version") {
17383
- attributes[key] = attr.value;
17384
- }
17385
- } else if (attr.key === "floor") {
17499
+ if (attr.key === "floor") {
17386
17500
  attributes.floor = attr.value;
17387
17501
  } else if (attr.key === "identifier") {
17388
17502
  attributes.identifier = attr.value;
17503
+ } else if (attr.key === "mapInstantaneousPower") {
17504
+ attributes.mapInstantaneousPower = attr.value;
17505
+ } else if (attr.key === "deviceMapInstaneousPower") {
17506
+ attributes.deviceMapInstaneousPower = attr.value;
17389
17507
  }
17390
17508
  }
17391
17509
  }
@@ -17407,23 +17525,12 @@ var DefaultSettingsFetcher = class {
17407
17525
  }
17408
17526
  return merged;
17409
17527
  }
17410
- /**
17411
- * Utility method to validate and sanitize fetched data
17412
- */
17413
17528
  static sanitizeFetchedData(data) {
17414
17529
  const sanitized = {};
17415
17530
  const stringFields = [
17416
17531
  "label",
17417
17532
  "floor",
17418
- "identifier",
17419
- "alertLimitDownConsumption",
17420
- "alertLimitUpConsumption",
17421
- "failureLimitDownConsumption",
17422
- "failureLimitUpConsumption",
17423
- "normalLimitDownConsumption",
17424
- "normalLimitUpConsumption",
17425
- "standbyLimitDownConsumption",
17426
- "standbyLimitUpConsumption"
17533
+ "identifier"
17427
17534
  ];
17428
17535
  for (const field of stringFields) {
17429
17536
  if (data[field] && typeof data[field] === "string") {
@@ -17439,6 +17546,12 @@ var DefaultSettingsFetcher = class {
17439
17546
  }
17440
17547
  }
17441
17548
  }
17549
+ const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
17550
+ for (const field of objectFields) {
17551
+ if (data[field] && typeof data[field] === "object") {
17552
+ sanitized[field] = data[field];
17553
+ }
17554
+ }
17442
17555
  return sanitized;
17443
17556
  }
17444
17557
  };
@@ -17488,8 +17601,9 @@ var SettingsController = class {
17488
17601
  // Pass connection info for display
17489
17602
  onSave: this.handleSave.bind(this),
17490
17603
  onClose: this.handleClose.bind(this),
17491
- mapInstantaneousPower: params.mapInstantaneousPower
17492
- // RFC-0077: Pass instantaneous power map for Power Limits feature
17604
+ mapInstantaneousPower: params.mapInstantaneousPower,
17605
+ // RFC-0077: Pass instantaneous power map for Power Limits feature,
17606
+ deviceMapInstaneousPower: params.deviceMapInstaneousPower
17493
17607
  });
17494
17608
  }
17495
17609
  async show() {