myio-js-library 0.1.143 → 0.1.145

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.
@@ -4701,6 +4701,12 @@
4701
4701
  function getIconSvg(deviceType) {
4702
4702
  return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
4703
4703
  }
4704
+ function formatValueByDomain(value, domain) {
4705
+ if (domain === "water") {
4706
+ return formatWaterVolumeM3(value);
4707
+ }
4708
+ return formatEnergy(value);
4709
+ }
4704
4710
  function formatUpdateDate(timeVal) {
4705
4711
  let telemetryTimeFormatted = "N/A";
4706
4712
  let timeSinceLastTelemetry = "";
@@ -4949,7 +4955,7 @@
4949
4955
  const chip = root.querySelector(".chip");
4950
4956
  chip.className = `chip ${statusInfo.chipClass}`;
4951
4957
  chip.textContent = statusInfo.label;
4952
- const primaryValue = formatEnergy(entityObject.val);
4958
+ const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
4953
4959
  const numSpan = root.querySelector(".myio-ho-card__value .num");
4954
4960
  root.querySelector(".myio-ho-card__value .unit");
4955
4961
  numSpan.textContent = primaryValue;
@@ -15113,7 +15119,13 @@ ${rangeText}`;
15113
15119
  render(initialData) {
15114
15120
  this.originalActiveElement = document.activeElement;
15115
15121
  document.body.appendChild(this.container);
15116
- this.populateForm(initialData);
15122
+ let formData = { ...initialData };
15123
+ if (initialData.deviceMapInstaneousPower && typeof initialData.deviceMapInstaneousPower === "object") {
15124
+ console.log("[SettingsModalView] Configura\xE7\xE3o salva encontrada (Device Scope). Processando...");
15125
+ const flatLimits = this.parseDeviceSavedLimits(initialData.deviceMapInstaneousPower);
15126
+ formData = { ...formData, ...flatLimits };
15127
+ }
15128
+ this.populateForm(formData);
15117
15129
  this.attachEventListeners();
15118
15130
  this.setupAccessibility();
15119
15131
  this.setupFocusTrap();
@@ -16682,6 +16694,42 @@ ${rangeText}`;
16682
16694
  }
16683
16695
  }
16684
16696
  }
16697
+ /**
16698
+ * Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
16699
+ * para os campos planos do formulário (ex: standbyLimitUpConsumption)
16700
+ */
16701
+ parseDeviceSavedLimits(deviceJson) {
16702
+ const extracted = {};
16703
+ try {
16704
+ if (!deviceJson || !deviceJson.limitsByInstantaneoustPowerType) return extracted;
16705
+ const consumptionGroup = deviceJson.limitsByInstantaneoustPowerType.find(
16706
+ (g) => g.telemetryType === "consumption"
16707
+ );
16708
+ const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
16709
+ if (!deviceItem?.limitsByDeviceStatus) return extracted;
16710
+ const mapPrefix = {
16711
+ "standBy": "standby",
16712
+ "normal": "normal",
16713
+ "alert": "alert",
16714
+ "failure": "failure"
16715
+ };
16716
+ deviceItem.limitsByDeviceStatus.forEach((status) => {
16717
+ const prefix = mapPrefix[status.deviceStatusName];
16718
+ if (prefix && status.limitsValues) {
16719
+ const { baseValue, topValue } = status.limitsValues;
16720
+ if (baseValue !== null && baseValue !== void 0) {
16721
+ extracted[`${prefix}LimitDownConsumption`] = baseValue;
16722
+ }
16723
+ if (topValue !== null && topValue !== void 0) {
16724
+ extracted[`${prefix}LimitUpConsumption`] = topValue;
16725
+ }
16726
+ }
16727
+ });
16728
+ } catch (e) {
16729
+ console.warn("[SettingsModalView] Erro ao processar deviceMapInstaneousPower:", e);
16730
+ }
16731
+ return extracted;
16732
+ }
16685
16733
  attachEventListeners() {
16686
16734
  this.form.addEventListener("submit", (event) => {
16687
16735
  event.preventDefault();
@@ -16953,11 +17001,25 @@ ${rangeText}`;
16953
17001
  }
16954
17002
  async saveServerScopeAttributes(deviceId, attributes) {
16955
17003
  try {
16956
- const mapInstantaneousPower = this.buildMapInstantaneousPower(attributes);
16957
- const payload = {
16958
- mapInstantaneousPower
16959
- };
16960
- console.log("[SettingsPersister] RFC-0080: Saving mapInstantaneousPower as JSON:", payload);
17004
+ const payload = { ...attributes };
17005
+ const effectiveDeviceType = this.getEffectiveDeviceType();
17006
+ const deviceJson = this.buildDevicePowerJson(payload, effectiveDeviceType);
17007
+ if (deviceJson) {
17008
+ payload.deviceMapInstaneousPower = deviceJson;
17009
+ }
17010
+ const flatKeysToRemove = [
17011
+ "telemetryType",
17012
+ "standbyLimitDownConsumption",
17013
+ "standbyLimitUpConsumption",
17014
+ "normalLimitDownConsumption",
17015
+ "normalLimitUpConsumption",
17016
+ "alertLimitDownConsumption",
17017
+ "alertLimitUpConsumption",
17018
+ "failureLimitDownConsumption",
17019
+ "failureLimitUpConsumption"
17020
+ ];
17021
+ flatKeysToRemove.forEach((key) => delete payload[key]);
17022
+ console.log("[SettingsPersister] Saving Server Scope Attributes:", payload);
16961
17023
  const res = await fetch(
16962
17024
  `${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`,
16963
17025
  {
@@ -16974,13 +17036,70 @@ ${rangeText}`;
16974
17036
  }
16975
17037
  return {
16976
17038
  ok: true,
16977
- updatedKeys: ["mapInstantaneousPower"]
17039
+ updatedKeys: Object.keys(payload)
16978
17040
  };
16979
17041
  } catch (error) {
16980
17042
  console.error("[SettingsPersister] Attributes save failed:", error);
16981
17043
  return { ok: false, error: this.mapError(error) };
16982
17044
  }
16983
17045
  }
17046
+ /**
17047
+ * Constrói o JSON Reduzido (apenas o deviceType atual) para salvar no Device.
17048
+ * Retorna null se nenhum campo de potência estiver presente.
17049
+ */
17050
+ buildDevicePowerJson(formData, deviceType) {
17051
+ const statuses = ["standby", "normal", "alert", "failure"];
17052
+ const hasPowerData = statuses.some(
17053
+ (status) => formData[`${status}LimitDownConsumption`] !== void 0 && formData[`${status}LimitDownConsumption`] !== "" || formData[`${status}LimitUpConsumption`] !== void 0 && formData[`${status}LimitUpConsumption`] !== ""
17054
+ );
17055
+ if (!hasPowerData) return null;
17056
+ const statusMap = {
17057
+ "standby": "standBy",
17058
+ "normal": "normal",
17059
+ "alert": "alert",
17060
+ "failure": "failure"
17061
+ };
17062
+ const limitsList = [];
17063
+ statuses.forEach((statusKey) => {
17064
+ const down = formData[`${statusKey}LimitDownConsumption`];
17065
+ const up = formData[`${statusKey}LimitUpConsumption`];
17066
+ if (down !== void 0 && down !== "" || up !== void 0 && up !== "") {
17067
+ limitsList.push({
17068
+ deviceStatusName: statusMap[statusKey],
17069
+ limitsValues: {
17070
+ // Converte para Number ou null
17071
+ baseValue: down !== "" && down !== void 0 ? Number(down) : null,
17072
+ topValue: up !== "" && up !== void 0 ? Number(up) : null
17073
+ }
17074
+ });
17075
+ }
17076
+ });
17077
+ if (limitsList.length === 0) return null;
17078
+ return {
17079
+ version: "1.0.0",
17080
+ limitsByInstantaneoustPowerType: [
17081
+ {
17082
+ telemetryType: "consumption",
17083
+ itemsByDeviceType: [
17084
+ {
17085
+ deviceType,
17086
+ // Gera um nome descritivo interno
17087
+ name: `deviceMapInstaneousPower${this.formatDeviceTypeName(deviceType)}`,
17088
+ description: "Override manual configurado via Dashboard",
17089
+ limitsByDeviceStatus: limitsList
17090
+ }
17091
+ ]
17092
+ }
17093
+ ]
17094
+ };
17095
+ }
17096
+ /**
17097
+ * Helper para formatar o nome do tipo (PascalCase) para uso no campo "name" do JSON
17098
+ */
17099
+ formatDeviceTypeName(deviceType) {
17100
+ if (!deviceType) return "";
17101
+ return deviceType.charAt(0).toUpperCase() + deviceType.slice(1).toLowerCase();
17102
+ }
16984
17103
  /**
16985
17104
  * RFC-0086: Build mapInstantaneousPower JSON structure from form data
16986
17105
  * IMPORTANT: When saving to a DEVICE, only include entries for the specific deviceType
@@ -17037,25 +17156,6 @@ ${rangeText}`;
17037
17156
  console.log(`[SettingsPersister] RFC-0086: Built mapInstantaneousPower for deviceType=${effectiveDeviceType}:`, result);
17038
17157
  return result;
17039
17158
  }
17040
- /**
17041
- * Format device type name for display (e.g., ELEVADOR -> Elevador)
17042
- */
17043
- formatDeviceTypeName(deviceType) {
17044
- const map = {
17045
- "ELEVADOR": "Elevator",
17046
- "ESCADA_ROLANTE": "Escalator",
17047
- "MOTOR": "Motor",
17048
- "BOMBA": "Pump",
17049
- "3F_MEDIDOR": "3FMedidor",
17050
- "CHILLER": "Chiller",
17051
- "FANCOIL": "Fancoil",
17052
- "AR_CONDICIONADO": "AirConditioner",
17053
- "HVAC": "HVAC",
17054
- "HIDROMETRO": "Hidrometro",
17055
- "TERMOSTATO": "Termostato"
17056
- };
17057
- return map[deviceType] || deviceType;
17058
- }
17059
17159
  sanitizeLabel(label) {
17060
17160
  return label.trim().slice(0, 255).replace(/[\x00-\x1F\x7F]/g, "");
17061
17161
  }
@@ -17192,18 +17292,16 @@ ${rangeText}`;
17192
17292
  }
17193
17293
  const attributesArray = await response.json();
17194
17294
  const attributes = {};
17195
- const settingsNamespace = "myio.settings.energy.";
17196
17295
  for (const attr of attributesArray) {
17197
17296
  if (attr.key && attr.value !== void 0 && attr.value !== null && attr.value !== "") {
17198
- if (attr.key.startsWith(settingsNamespace)) {
17199
- const key = attr.key.replace(settingsNamespace, "");
17200
- if (key !== "__version") {
17201
- attributes[key] = attr.value;
17202
- }
17203
- } else if (attr.key === "floor") {
17297
+ if (attr.key === "floor") {
17204
17298
  attributes.floor = attr.value;
17205
17299
  } else if (attr.key === "identifier") {
17206
17300
  attributes.identifier = attr.value;
17301
+ } else if (attr.key === "mapInstantaneousPower") {
17302
+ attributes.mapInstantaneousPower = attr.value;
17303
+ } else if (attr.key === "deviceMapInstaneousPower") {
17304
+ attributes.deviceMapInstaneousPower = attr.value;
17207
17305
  }
17208
17306
  }
17209
17307
  }
@@ -17225,23 +17323,12 @@ ${rangeText}`;
17225
17323
  }
17226
17324
  return merged;
17227
17325
  }
17228
- /**
17229
- * Utility method to validate and sanitize fetched data
17230
- */
17231
17326
  static sanitizeFetchedData(data) {
17232
17327
  const sanitized = {};
17233
17328
  const stringFields = [
17234
17329
  "label",
17235
17330
  "floor",
17236
- "identifier",
17237
- "alertLimitDownConsumption",
17238
- "alertLimitUpConsumption",
17239
- "failureLimitDownConsumption",
17240
- "failureLimitUpConsumption",
17241
- "normalLimitDownConsumption",
17242
- "normalLimitUpConsumption",
17243
- "standbyLimitDownConsumption",
17244
- "standbyLimitUpConsumption"
17331
+ "identifier"
17245
17332
  ];
17246
17333
  for (const field of stringFields) {
17247
17334
  if (data[field] && typeof data[field] === "string") {
@@ -17257,6 +17344,12 @@ ${rangeText}`;
17257
17344
  }
17258
17345
  }
17259
17346
  }
17347
+ const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
17348
+ for (const field of objectFields) {
17349
+ if (data[field] && typeof data[field] === "object") {
17350
+ sanitized[field] = data[field];
17351
+ }
17352
+ }
17260
17353
  return sanitized;
17261
17354
  }
17262
17355
  };
@@ -17306,8 +17399,9 @@ ${rangeText}`;
17306
17399
  // Pass connection info for display
17307
17400
  onSave: this.handleSave.bind(this),
17308
17401
  onClose: this.handleClose.bind(this),
17309
- mapInstantaneousPower: params.mapInstantaneousPower
17310
- // RFC-0077: Pass instantaneous power map for Power Limits feature
17402
+ mapInstantaneousPower: params.mapInstantaneousPower,
17403
+ // RFC-0077: Pass instantaneous power map for Power Limits feature,
17404
+ deviceMapInstaneousPower: params.deviceMapInstaneousPower
17311
17405
  });
17312
17406
  }
17313
17407
  async show() {