@teslemetry/api 0.6.9 → 0.6.10

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 CHANGED
@@ -101,9 +101,8 @@ vehicle.sse.onSignal("PackCurrent", (val) => console.log("Current:", val));
101
101
  vehicle.sse.onSignal("ChargerVoltage", (val) => console.log("Voltage:", val));
102
102
 
103
103
  // Monitor connection status
104
- teslemetry.sse.onConnection((isConnected) => {
105
- console.log(isConnected ? "Stream Connected" : "Stream Disconnected");
106
- });
104
+ teslemetry.sse.on("connect", () => console.log("Stream Connected"));
105
+ teslemetry.sse.on("disconnect", () => console.log("Stream Disconnected"));
107
106
 
108
107
  // Start streaming (connects to the shared Teslemetry stream)
109
108
  await teslemetry.sse.connect();
package/dist/index.cjs CHANGED
@@ -2989,12 +2989,23 @@ const getSseByVin_ = (options) => (options.client ?? client).sse.get({
2989
2989
 
2990
2990
  //#endregion
2991
2991
  //#region src/TeslemetryVehicleStream.ts
2992
+ /** Simple deferred promise pattern */
2993
+ var Deferred = class {
2994
+ promise;
2995
+ resolve;
2996
+ reject;
2997
+ constructor() {
2998
+ this.promise = new Promise((resolve, reject) => {
2999
+ this.resolve = resolve;
3000
+ this.reject = reject;
3001
+ });
3002
+ }
3003
+ };
2992
3004
  var TeslemetryVehicleStream = class extends events.EventEmitter {
2993
3005
  root;
2994
3006
  vin;
2995
3007
  fields = {};
2996
- _pendingFields = {};
2997
- _debounceTimeout = null;
3008
+ _fieldUpdateBatch = null;
2998
3009
  logger;
2999
3010
  data;
3000
3011
  constructor(root, vin) {
@@ -3025,25 +3036,45 @@ var TeslemetryVehicleStream = class extends events.EventEmitter {
3025
3036
  return;
3026
3037
  } else throw new Error(`Failed to get config: ${response.statusText}`);
3027
3038
  }
3028
- /** Safely add field configuration to the vehicle */
3029
- async updateFields(fields) {
3030
- this._pendingFields = {
3031
- ...this._pendingFields,
3039
+ /** Safely add field configuration to the vehicle
3040
+ * Returns a promise that resolves when the debounced update completes.
3041
+ * Multiple calls within the debounce window share the same promise.
3042
+ */
3043
+ updateFields(fields) {
3044
+ if (!this._fieldUpdateBatch) this._fieldUpdateBatch = {
3045
+ fields: {},
3046
+ deferred: new Deferred(),
3047
+ timeout: null
3048
+ };
3049
+ this._fieldUpdateBatch.fields = {
3050
+ ...this._fieldUpdateBatch.fields,
3032
3051
  ...fields
3033
3052
  };
3034
- if (this._debounceTimeout) clearTimeout(this._debounceTimeout);
3035
- this._debounceTimeout = setTimeout(async () => {
3036
- const data = await this.patchConfig(this._pendingFields);
3053
+ clearTimeout(this._fieldUpdateBatch.timeout);
3054
+ this._fieldUpdateBatch.timeout = setTimeout(() => this._flushFieldUpdate(), 1e3);
3055
+ return this._fieldUpdateBatch.deferred.promise;
3056
+ }
3057
+ /** Flush pending field updates to the API */
3058
+ async _flushFieldUpdate() {
3059
+ const batch = this._fieldUpdateBatch;
3060
+ this._fieldUpdateBatch = null;
3061
+ try {
3062
+ const data = await this.patchConfig(batch.fields);
3037
3063
  if (data?.updated_vehicles) {
3038
- this.logger.info(`Updated ${Object.keys(this._pendingFields).length} streaming fields for ${this.vin}`);
3064
+ this.logger.info(`Updated ${Object.keys(batch.fields).length} streaming fields for ${this.vin}`);
3039
3065
  this.fields = {
3040
3066
  ...this.fields,
3041
- ...this._pendingFields
3067
+ ...batch.fields
3042
3068
  };
3043
- this._pendingFields = {};
3044
- } else this.logger.error(`Error updating streaming config for ${this.vin}`, data);
3045
- this._debounceTimeout = null;
3046
- }, 1e3);
3069
+ batch.deferred.resolve();
3070
+ } else {
3071
+ const error = /* @__PURE__ */ new Error(`Error updating streaming config for ${this.vin}`);
3072
+ this.logger.error(error.message, data);
3073
+ batch.deferred.reject(error);
3074
+ }
3075
+ } catch (error) {
3076
+ batch.deferred.reject(error);
3077
+ }
3047
3078
  }
3048
3079
  /** Modify the field configuration of the vehicle */
3049
3080
  async patchConfig(fields) {
@@ -3067,15 +3098,15 @@ var TeslemetryVehicleStream = class extends events.EventEmitter {
3067
3098
  * Add a field to the vehicles streaming configuration
3068
3099
  * @param field Vehicle Signal
3069
3100
  * @param interval
3070
- * @returns
3101
+ * @returns Promise that resolves when the field is added
3071
3102
  */
3072
- async addField(field, interval) {
3103
+ addField(field, interval) {
3073
3104
  if (this.fields && this.fields[field] && (interval === void 0 || this.fields[field].interval_seconds === interval)) {
3074
3105
  this.logger.debug(`Streaming field ${field} already enabled @ ${this.fields[field]?.interval_seconds || "default"}s`);
3075
- return;
3106
+ return Promise.resolve();
3076
3107
  }
3077
3108
  const value = interval !== void 0 ? { interval_seconds: interval } : null;
3078
- this.updateFields({ [field]: value });
3109
+ return this.updateFields({ [field]: value });
3079
3110
  }
3080
3111
  /**
3081
3112
  * Helper to enable and listen for specific field signals
@@ -3084,8 +3115,8 @@ var TeslemetryVehicleStream = class extends events.EventEmitter {
3084
3115
  * @returns Off function to stop the listener
3085
3116
  */
3086
3117
  onSignal(field, callback) {
3087
- this.addField(field).catch((error) => {
3088
- this.logger.error(`Failed to add field ${field}:`, error);
3118
+ this.addField(field).catch(() => {
3119
+ this.logger.error(`Failed to add field ${field}`);
3089
3120
  });
3090
3121
  const data = this.root.sse.cache?.[this.vin]?.data?.[field];
3091
3122
  if (data !== void 0) callback(data);
@@ -3228,6 +3259,7 @@ var TeslemetryStream = class extends events.EventEmitter {
3228
3259
  else if ("credits" in event) this.emit("credits", event);
3229
3260
  else if ("vehicle_data" in event) this.emit("vehicle_data", event);
3230
3261
  else if ("config" in event) this.emit("config", event);
3262
+ this.emit("all", event);
3231
3263
  const vehicle = this.vehicles.get(event.vin);
3232
3264
  if (vehicle) {
3233
3265
  if ("state" in event) vehicle.emit("state", event);
@@ -5220,7 +5252,7 @@ const consoleLogger = {
5220
5252
 
5221
5253
  //#endregion
5222
5254
  //#region package.json
5223
- var version = "0.6.9";
5255
+ var version = "0.6.10";
5224
5256
 
5225
5257
  //#endregion
5226
5258
  //#region src/Teslemetry.ts
package/dist/index.d.cts CHANGED
@@ -14188,6 +14188,7 @@ interface TeslemetryStreamOptions {
14188
14188
  };
14189
14189
  }
14190
14190
  type TeslemetryStreamEventMap$1 = {
14191
+ all: SseEvent;
14191
14192
  state: SseState;
14192
14193
  data: SseData;
14193
14194
  errors: SseErrors;
@@ -14267,16 +14268,20 @@ declare class TeslemetryVehicleStream extends EventEmitter {
14267
14268
  private root;
14268
14269
  vin: string;
14269
14270
  fields: FieldsRequest;
14270
- private _pendingFields;
14271
- private _debounceTimeout;
14271
+ private _fieldUpdateBatch;
14272
14272
  logger: Logger;
14273
14273
  data: TeslemetryVehicleStreamData;
14274
14274
  constructor(root: Teslemetry, vin: string);
14275
14275
  get cache(): VehicleCache;
14276
14276
  /** Get the current configuration for the vehicle */
14277
14277
  getConfig(): Promise<void>;
14278
- /** Safely add field configuration to the vehicle */
14278
+ /** Safely add field configuration to the vehicle
14279
+ * Returns a promise that resolves when the debounced update completes.
14280
+ * Multiple calls within the debounce window share the same promise.
14281
+ */
14279
14282
  updateFields(fields: FieldsRequest): Promise<void>;
14283
+ /** Flush pending field updates to the API */
14284
+ private _flushFieldUpdate;
14280
14285
  /** Modify the field configuration of the vehicle */
14281
14286
  patchConfig(fields: FieldsRequest): Promise<{
14282
14287
  updated_vehicles?: number;
@@ -14293,7 +14298,7 @@ declare class TeslemetryVehicleStream extends EventEmitter {
14293
14298
  * Add a field to the vehicles streaming configuration
14294
14299
  * @param field Vehicle Signal
14295
14300
  * @param interval
14296
- * @returns
14301
+ * @returns Promise that resolves when the field is added
14297
14302
  */
14298
14303
  addField(field: Signals, interval?: number): Promise<void>;
14299
14304
  /**
package/dist/index.d.mts CHANGED
@@ -14188,6 +14188,7 @@ interface TeslemetryStreamOptions {
14188
14188
  };
14189
14189
  }
14190
14190
  type TeslemetryStreamEventMap$1 = {
14191
+ all: SseEvent;
14191
14192
  state: SseState;
14192
14193
  data: SseData;
14193
14194
  errors: SseErrors;
@@ -14267,16 +14268,20 @@ declare class TeslemetryVehicleStream extends EventEmitter {
14267
14268
  private root;
14268
14269
  vin: string;
14269
14270
  fields: FieldsRequest;
14270
- private _pendingFields;
14271
- private _debounceTimeout;
14271
+ private _fieldUpdateBatch;
14272
14272
  logger: Logger;
14273
14273
  data: TeslemetryVehicleStreamData;
14274
14274
  constructor(root: Teslemetry, vin: string);
14275
14275
  get cache(): VehicleCache;
14276
14276
  /** Get the current configuration for the vehicle */
14277
14277
  getConfig(): Promise<void>;
14278
- /** Safely add field configuration to the vehicle */
14278
+ /** Safely add field configuration to the vehicle
14279
+ * Returns a promise that resolves when the debounced update completes.
14280
+ * Multiple calls within the debounce window share the same promise.
14281
+ */
14279
14282
  updateFields(fields: FieldsRequest): Promise<void>;
14283
+ /** Flush pending field updates to the API */
14284
+ private _flushFieldUpdate;
14280
14285
  /** Modify the field configuration of the vehicle */
14281
14286
  patchConfig(fields: FieldsRequest): Promise<{
14282
14287
  updated_vehicles?: number;
@@ -14293,7 +14298,7 @@ declare class TeslemetryVehicleStream extends EventEmitter {
14293
14298
  * Add a field to the vehicles streaming configuration
14294
14299
  * @param field Vehicle Signal
14295
14300
  * @param interval
14296
- * @returns
14301
+ * @returns Promise that resolves when the field is added
14297
14302
  */
14298
14303
  addField(field: Signals, interval?: number): Promise<void>;
14299
14304
  /**
package/dist/index.mjs CHANGED
@@ -2989,12 +2989,23 @@ const getSseByVin_ = (options) => (options.client ?? client).sse.get({
2989
2989
 
2990
2990
  //#endregion
2991
2991
  //#region src/TeslemetryVehicleStream.ts
2992
+ /** Simple deferred promise pattern */
2993
+ var Deferred = class {
2994
+ promise;
2995
+ resolve;
2996
+ reject;
2997
+ constructor() {
2998
+ this.promise = new Promise((resolve, reject) => {
2999
+ this.resolve = resolve;
3000
+ this.reject = reject;
3001
+ });
3002
+ }
3003
+ };
2992
3004
  var TeslemetryVehicleStream = class extends EventEmitter {
2993
3005
  root;
2994
3006
  vin;
2995
3007
  fields = {};
2996
- _pendingFields = {};
2997
- _debounceTimeout = null;
3008
+ _fieldUpdateBatch = null;
2998
3009
  logger;
2999
3010
  data;
3000
3011
  constructor(root, vin) {
@@ -3025,25 +3036,45 @@ var TeslemetryVehicleStream = class extends EventEmitter {
3025
3036
  return;
3026
3037
  } else throw new Error(`Failed to get config: ${response.statusText}`);
3027
3038
  }
3028
- /** Safely add field configuration to the vehicle */
3029
- async updateFields(fields) {
3030
- this._pendingFields = {
3031
- ...this._pendingFields,
3039
+ /** Safely add field configuration to the vehicle
3040
+ * Returns a promise that resolves when the debounced update completes.
3041
+ * Multiple calls within the debounce window share the same promise.
3042
+ */
3043
+ updateFields(fields) {
3044
+ if (!this._fieldUpdateBatch) this._fieldUpdateBatch = {
3045
+ fields: {},
3046
+ deferred: new Deferred(),
3047
+ timeout: null
3048
+ };
3049
+ this._fieldUpdateBatch.fields = {
3050
+ ...this._fieldUpdateBatch.fields,
3032
3051
  ...fields
3033
3052
  };
3034
- if (this._debounceTimeout) clearTimeout(this._debounceTimeout);
3035
- this._debounceTimeout = setTimeout(async () => {
3036
- const data = await this.patchConfig(this._pendingFields);
3053
+ clearTimeout(this._fieldUpdateBatch.timeout);
3054
+ this._fieldUpdateBatch.timeout = setTimeout(() => this._flushFieldUpdate(), 1e3);
3055
+ return this._fieldUpdateBatch.deferred.promise;
3056
+ }
3057
+ /** Flush pending field updates to the API */
3058
+ async _flushFieldUpdate() {
3059
+ const batch = this._fieldUpdateBatch;
3060
+ this._fieldUpdateBatch = null;
3061
+ try {
3062
+ const data = await this.patchConfig(batch.fields);
3037
3063
  if (data?.updated_vehicles) {
3038
- this.logger.info(`Updated ${Object.keys(this._pendingFields).length} streaming fields for ${this.vin}`);
3064
+ this.logger.info(`Updated ${Object.keys(batch.fields).length} streaming fields for ${this.vin}`);
3039
3065
  this.fields = {
3040
3066
  ...this.fields,
3041
- ...this._pendingFields
3067
+ ...batch.fields
3042
3068
  };
3043
- this._pendingFields = {};
3044
- } else this.logger.error(`Error updating streaming config for ${this.vin}`, data);
3045
- this._debounceTimeout = null;
3046
- }, 1e3);
3069
+ batch.deferred.resolve();
3070
+ } else {
3071
+ const error = /* @__PURE__ */ new Error(`Error updating streaming config for ${this.vin}`);
3072
+ this.logger.error(error.message, data);
3073
+ batch.deferred.reject(error);
3074
+ }
3075
+ } catch (error) {
3076
+ batch.deferred.reject(error);
3077
+ }
3047
3078
  }
3048
3079
  /** Modify the field configuration of the vehicle */
3049
3080
  async patchConfig(fields) {
@@ -3067,15 +3098,15 @@ var TeslemetryVehicleStream = class extends EventEmitter {
3067
3098
  * Add a field to the vehicles streaming configuration
3068
3099
  * @param field Vehicle Signal
3069
3100
  * @param interval
3070
- * @returns
3101
+ * @returns Promise that resolves when the field is added
3071
3102
  */
3072
- async addField(field, interval) {
3103
+ addField(field, interval) {
3073
3104
  if (this.fields && this.fields[field] && (interval === void 0 || this.fields[field].interval_seconds === interval)) {
3074
3105
  this.logger.debug(`Streaming field ${field} already enabled @ ${this.fields[field]?.interval_seconds || "default"}s`);
3075
- return;
3106
+ return Promise.resolve();
3076
3107
  }
3077
3108
  const value = interval !== void 0 ? { interval_seconds: interval } : null;
3078
- this.updateFields({ [field]: value });
3109
+ return this.updateFields({ [field]: value });
3079
3110
  }
3080
3111
  /**
3081
3112
  * Helper to enable and listen for specific field signals
@@ -3084,8 +3115,8 @@ var TeslemetryVehicleStream = class extends EventEmitter {
3084
3115
  * @returns Off function to stop the listener
3085
3116
  */
3086
3117
  onSignal(field, callback) {
3087
- this.addField(field).catch((error) => {
3088
- this.logger.error(`Failed to add field ${field}:`, error);
3118
+ this.addField(field).catch(() => {
3119
+ this.logger.error(`Failed to add field ${field}`);
3089
3120
  });
3090
3121
  const data = this.root.sse.cache?.[this.vin]?.data?.[field];
3091
3122
  if (data !== void 0) callback(data);
@@ -3228,6 +3259,7 @@ var TeslemetryStream = class extends EventEmitter {
3228
3259
  else if ("credits" in event) this.emit("credits", event);
3229
3260
  else if ("vehicle_data" in event) this.emit("vehicle_data", event);
3230
3261
  else if ("config" in event) this.emit("config", event);
3262
+ this.emit("all", event);
3231
3263
  const vehicle = this.vehicles.get(event.vin);
3232
3264
  if (vehicle) {
3233
3265
  if ("state" in event) vehicle.emit("state", event);
@@ -5220,7 +5252,7 @@ const consoleLogger = {
5220
5252
 
5221
5253
  //#endregion
5222
5254
  //#region package.json
5223
- var version = "0.6.9";
5255
+ var version = "0.6.10";
5224
5256
 
5225
5257
  //#endregion
5226
5258
  //#region src/Teslemetry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teslemetry/api",
3
- "version": "0.6.9",
3
+ "version": "0.6.10",
4
4
  "description": "API client for Teslemetry",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",