@apocaliss92/nodedreame 1.12.4 → 1.12.5

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
@@ -134,7 +134,7 @@ module.exports = __toCommonJS(index_exports);
134
134
 
135
135
  // src/support/version.ts
136
136
  var LIBRARY_NAME = "nodedreame";
137
- var LIBRARY_VERSION = "1.12.4";
137
+ var LIBRARY_VERSION = "1.12.5";
138
138
 
139
139
  // src/transport/errors.ts
140
140
  var DreameError = class extends Error {
@@ -1980,50 +1980,93 @@ var VACUUM_CONSUMABLES = [
1980
1980
  key: "main-brush",
1981
1981
  label: "Main Brush",
1982
1982
  life: { siid: 9, piid: 2 },
1983
+ timeLeft: { siid: 9, piid: 1, unit: "hours" },
1983
1984
  reset: { siid: 9, aiid: 1 }
1984
1985
  },
1985
1986
  {
1986
1987
  key: "side-brush",
1987
1988
  label: "Side Brush",
1988
1989
  life: { siid: 10, piid: 2 },
1990
+ timeLeft: { siid: 10, piid: 1, unit: "hours" },
1989
1991
  reset: { siid: 10, aiid: 1 }
1990
1992
  },
1991
- { key: "filter", label: "Filter", life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },
1992
- { key: "sensor", label: "Sensor", life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },
1993
+ {
1994
+ key: "filter",
1995
+ label: "Filter",
1996
+ life: { siid: 11, piid: 1 },
1997
+ timeLeft: { siid: 11, piid: 2, unit: "hours" },
1998
+ reset: { siid: 11, aiid: 1 }
1999
+ },
2000
+ {
2001
+ key: "sensor",
2002
+ label: "Sensor Dirty",
2003
+ life: { siid: 16, piid: 1 },
2004
+ timeLeft: { siid: 16, piid: 2, unit: "hours" },
2005
+ reset: { siid: 16, aiid: 1 }
2006
+ },
1993
2007
  {
1994
2008
  key: "tank-filter",
1995
2009
  label: "Tank Filter",
1996
2010
  life: { siid: 17, piid: 1 },
2011
+ timeLeft: { siid: 17, piid: 2, unit: "hours" },
1997
2012
  reset: { siid: 17, aiid: 1 }
1998
2013
  },
1999
- { key: "mop-pad", label: "Mop Pad", life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },
2014
+ {
2015
+ key: "mop-pad",
2016
+ label: "Mop Pad",
2017
+ life: { siid: 18, piid: 1 },
2018
+ timeLeft: { siid: 18, piid: 2, unit: "hours" },
2019
+ reset: { siid: 18, aiid: 1 }
2020
+ },
2000
2021
  {
2001
2022
  key: "silver-ion",
2002
2023
  label: "Silver-ion",
2003
2024
  life: { siid: 19, piid: 2 },
2025
+ timeLeft: { siid: 19, piid: 1, unit: "days" },
2004
2026
  reset: { siid: 19, aiid: 1 }
2005
2027
  },
2006
2028
  {
2007
2029
  key: "detergent",
2008
2030
  label: "Detergent",
2009
2031
  life: { siid: 20, piid: 1 },
2032
+ timeLeft: { siid: 20, piid: 2, unit: "days" },
2010
2033
  reset: { siid: 20, aiid: 1 }
2011
2034
  },
2012
- { key: "squeegee", label: "Squeegee", life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },
2035
+ {
2036
+ key: "squeegee",
2037
+ label: "Squeegee",
2038
+ life: { siid: 24, piid: 1 },
2039
+ timeLeft: { siid: 24, piid: 2, unit: "days" },
2040
+ reset: { siid: 24, aiid: 1 }
2041
+ },
2013
2042
  {
2014
2043
  key: "deodorizer",
2015
2044
  label: "Deodorizer",
2016
2045
  life: { siid: 29, piid: 2 },
2046
+ timeLeft: { siid: 29, piid: 1, unit: "days" },
2017
2047
  reset: { siid: 29, aiid: 1 }
2018
2048
  },
2019
- { key: "wheel", label: "Wheel", life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },
2049
+ {
2050
+ key: "wheel",
2051
+ label: "Wheel Dirty",
2052
+ life: { siid: 30, piid: 2 },
2053
+ timeLeft: { siid: 30, piid: 1, unit: "days" },
2054
+ reset: { siid: 30, aiid: 1 }
2055
+ },
2020
2056
  {
2021
2057
  key: "scale-inhibitor",
2022
2058
  label: "Scale Inhibitor",
2023
2059
  life: { siid: 31, piid: 2 },
2060
+ timeLeft: { siid: 31, piid: 1, unit: "days" },
2024
2061
  reset: { siid: 31, aiid: 1 }
2025
2062
  },
2026
- { key: "dust-bag", label: "Dust Bag", life: { siid: 27, piid: 17 }, reset: null }
2063
+ {
2064
+ key: "dust-bag",
2065
+ label: "Dust Bag",
2066
+ life: { siid: 27, piid: 17 },
2067
+ timeLeft: null,
2068
+ reset: null
2069
+ }
2027
2070
  ];
2028
2071
  function consumableSpec(key2) {
2029
2072
  return VACUUM_CONSUMABLES.find((c) => c.key === key2);
@@ -4895,6 +4938,12 @@ var OssFetcher = class {
4895
4938
  };
4896
4939
 
4897
4940
  // src/models/vacuum/vacuum-device.ts
4941
+ function consumableProps(spec) {
4942
+ return spec.timeLeft === null ? [spec.life] : [spec.life, { siid: spec.timeLeft.siid, piid: spec.timeLeft.piid }];
4943
+ }
4944
+ function asError(err) {
4945
+ return err instanceof Error ? err : new Error(String(err));
4946
+ }
4898
4947
  var SUCTION = enumLookup([
4899
4948
  0 /* Quiet */,
4900
4949
  1 /* Standard */,
@@ -5052,25 +5101,72 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
5052
5101
  });
5053
5102
  }
5054
5103
  /**
5055
- * Seed EVERY consumable life property from the cloud shadow (no robot wake) so
5056
- * {@link supportedConsumables} reflects the full set on a docked robot. The
5057
- * consumable life values are static settings the robot rarely re-pushes over
5058
- * MQTT, so a fresh connect surfaces only the actively-changing ones without this.
5104
+ * Time left before a consumable needs attention, in the unit the robot counts
5105
+ * it in (see {@link ConsumableSpec.timeLeft}), or `null` when the model does
5106
+ * not report it or the key has no time-left twin.
5107
+ */
5108
+ consumableTimeLeft(key2) {
5109
+ const spec = consumableSpec(key2);
5110
+ if (spec === void 0 || spec.timeLeft === null) return null;
5111
+ const value = this.#num(spec.timeLeft.siid, spec.timeLeft.piid);
5112
+ return value === null ? null : { value, unit: spec.timeLeft.unit };
5113
+ }
5114
+ /**
5115
+ * Seed EVERY consumable life property AND its time-left twin from the cloud
5116
+ * shadow (no robot wake), in one call, so {@link supportedConsumables} and
5117
+ * {@link consumableTimeLeft} reflect the full set on a docked robot. The
5118
+ * robot rarely re-pushes these over MQTT, so a consumer that wants them fresh
5119
+ * must call this again — the library does not poll them.
5059
5120
  */
5060
5121
  async refreshConsumables() {
5061
- await this.refreshCachedProperties(VACUUM_CONSUMABLES.map((c) => c.life));
5122
+ await this.refreshCachedProperties(VACUUM_CONSUMABLES.flatMap(consumableProps));
5062
5123
  }
5063
5124
  /**
5064
5125
  * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset
5065
- * action. Rejects when the model exposes no reset action for the key (e.g.
5066
- * `dust-bag`) or the key is unknown.
5126
+ * action, then re-read that consumable's life % and time left ONCE so the
5127
+ * cache — and every getter after this resolves — reflects the reset. The
5128
+ * re-read is live; if the robot does not answer it falls back to the cloud
5129
+ * shadow (which may still hold the pre-reset value until the robot reports),
5130
+ * and `refreshedFrom` says which one answered.
5131
+ *
5132
+ * Rejects when the model exposes no reset action for the key (e.g.
5133
+ * `dust-bag`), the key is unknown, or the action itself fails. A failed
5134
+ * RE-READ does not reject: the reset happened, and the result says the values
5135
+ * are unknown (`refreshedFrom: null`).
5067
5136
  */
5068
5137
  async resetConsumable(key2) {
5069
5138
  const spec = consumableSpec(key2);
5070
5139
  if (spec === void 0 || spec.reset === null) {
5071
5140
  throw new DreameError(`resetConsumable: no reset action for consumable "${key2}"`);
5072
5141
  }
5073
- return this.callAction(spec.reset.siid, spec.reset.aiid, []);
5142
+ const actionResult = await this.callAction(spec.reset.siid, spec.reset.aiid, []);
5143
+ const refresh2 = await this.#rereadConsumable(spec);
5144
+ const fresh = refresh2.refreshedFrom !== null;
5145
+ return {
5146
+ key: key2,
5147
+ actionResult,
5148
+ refreshedFrom: refresh2.refreshedFrom,
5149
+ leftPct: fresh ? this.consumableLeftPct(key2) : null,
5150
+ timeLeft: fresh ? this.consumableTimeLeft(key2) : null,
5151
+ refreshError: refresh2.error
5152
+ };
5153
+ }
5154
+ /** One targeted re-read of a consumable's properties: live, else the cloud shadow. */
5155
+ async #rereadConsumable(spec) {
5156
+ const props = consumableProps(spec);
5157
+ let liveError;
5158
+ try {
5159
+ await this.refreshProperties(props);
5160
+ return { refreshedFrom: "device", error: null };
5161
+ } catch (err) {
5162
+ liveError = asError(err);
5163
+ }
5164
+ try {
5165
+ await this.refreshCachedProperties(props);
5166
+ return { refreshedFrom: "cloud-shadow", error: liveError };
5167
+ } catch (err) {
5168
+ return { refreshedFrom: null, error: asError(err) };
5169
+ }
5074
5170
  }
5075
5171
  // -- AI obstacle-detection ----------------------------------------------
5076
5172
  /**
@@ -5438,7 +5534,9 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
5438
5534
  break;
5439
5535
  }
5440
5536
  if (Date.now() + pollMs >= deadline) {
5441
- throw new DreameError("wifi signal map not available (none stored yet, or request timed out)");
5537
+ throw new DreameError(
5538
+ "wifi signal map not available (none stored yet, or request timed out)"
5539
+ );
5442
5540
  }
5443
5541
  await new Promise((r) => setTimeout(r, pollMs));
5444
5542
  }