@teslemetry/api 0.4.0 → 0.5.1

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
@@ -3009,6 +3009,10 @@ var TeslemetryVehicleStream = class extends events.EventEmitter {
3009
3009
  this.fields = event.config.fields;
3010
3010
  });
3011
3011
  }
3012
+ on(event, listener) {
3013
+ this.root.sse.sendCache(this.vin, event, listener);
3014
+ return super.on(event, listener);
3015
+ }
3012
3016
  /** Get the current configuration for the vehicle */
3013
3017
  async getConfig() {
3014
3018
  const { data, response } = await getApiConfigByVin({ path: { vin: this.vin } });
@@ -3080,6 +3084,8 @@ var TeslemetryVehicleStream = class extends events.EventEmitter {
3080
3084
  this.addField(field).catch((error) => {
3081
3085
  this.logger.error(`Failed to add field ${field}:`, error);
3082
3086
  });
3087
+ const data = this.root.sse.cache?.[this.vin]?.data?.[field];
3088
+ if (data !== void 0) callback(data);
3083
3089
  this.data.on(field, callback);
3084
3090
  return () => this.data.off(field, callback);
3085
3091
  }
@@ -3096,16 +3102,69 @@ var TeslemetryStream = class extends events.EventEmitter {
3096
3102
  active = false;
3097
3103
  connected = false;
3098
3104
  vin;
3099
- cache;
3105
+ cache = {};
3106
+ cloudCache;
3107
+ localCache;
3100
3108
  logger;
3101
3109
  vehicles = /* @__PURE__ */ new Map();
3102
3110
  constructor(root, options) {
3103
3111
  super();
3104
3112
  this.root = root;
3105
3113
  this.vin = options?.vin;
3106
- this.cache = options?.cache;
3114
+ if (typeof options?.cache === "boolean") {
3115
+ this.cloudCache = options.cache;
3116
+ this.localCache = options.cache;
3117
+ } else {
3118
+ this.cloudCache = options?.cache?.cloud;
3119
+ this.localCache = options?.cache?.local;
3120
+ }
3107
3121
  this.logger = root.logger;
3108
3122
  if (this.vin) this.getVehicle(this.vin);
3123
+ if (this.localCache) this.startLocalCache();
3124
+ }
3125
+ sendCache(vin, event, listener) {
3126
+ if (this.cache) {
3127
+ const vehicleCache = this.cache[vin];
3128
+ if (event === "connectivity" && vehicleCache.connectivity) for (const networkInterface in vehicleCache.connectivity) {
3129
+ const typedNetworkInterface = networkInterface;
3130
+ const status = vehicleCache.connectivity[typedNetworkInterface];
3131
+ if (status !== void 0) listener({
3132
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3133
+ vin,
3134
+ networkInterface: typedNetworkInterface,
3135
+ status,
3136
+ isCache: true
3137
+ });
3138
+ }
3139
+ else if (event === "state" && vehicleCache.state) listener({
3140
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3141
+ vin,
3142
+ state: vehicleCache.state,
3143
+ isCache: true
3144
+ });
3145
+ else if (event === "data" && vehicleCache.data) listener({
3146
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3147
+ vin,
3148
+ data: vehicleCache.data,
3149
+ isCache: true
3150
+ });
3151
+ else if (event === "errors" && vehicleCache.errors) listener({
3152
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3153
+ vin,
3154
+ errors: vehicleCache.errors,
3155
+ isCache: true
3156
+ });
3157
+ else if (event === "alerts" && vehicleCache.alerts) listener({
3158
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3159
+ vin,
3160
+ alerts: vehicleCache.alerts,
3161
+ isCache: true
3162
+ });
3163
+ }
3164
+ }
3165
+ on(event, listener) {
3166
+ for (const vin in this.cache) this.sendCache(vin, event, listener);
3167
+ return super.on(event, listener);
3109
3168
  }
3110
3169
  getVehicle(vin) {
3111
3170
  if (!this.vehicles.has(vin)) new TeslemetryVehicleStream(this.root, vin);
@@ -3122,7 +3181,7 @@ var TeslemetryStream = class extends events.EventEmitter {
3122
3181
  const sse = await getSseByVin_({
3123
3182
  client: this.root.client,
3124
3183
  path: { vin: this.vin || "" },
3125
- query: { cache: this.cache }
3184
+ query: { cache: this.cloudCache }
3126
3185
  });
3127
3186
  this.logger.info(`Connected to stream`);
3128
3187
  retries = 0;
@@ -3181,6 +3240,48 @@ var TeslemetryStream = class extends events.EventEmitter {
3181
3240
  else if ("config" in event) vehicle.emit("config", event);
3182
3241
  }
3183
3242
  }
3243
+ cacheState(event) {
3244
+ this.cache[event.vin] ??= {};
3245
+ this.cache[event.vin].state = event.state;
3246
+ }
3247
+ cacheData(event) {
3248
+ this.cache[event.vin] ??= { data: {} };
3249
+ this.cache[event.vin].data = {
3250
+ ...this.cache[event.vin].data,
3251
+ ...event.data
3252
+ };
3253
+ }
3254
+ cacheErrors(event) {
3255
+ this.cache[event.vin] ??= {};
3256
+ this.cache[event.vin].errors = event.errors;
3257
+ }
3258
+ cacheAlerts(event) {
3259
+ this.cache[event.vin] ??= {};
3260
+ this.cache[event.vin].alerts = event.alerts;
3261
+ }
3262
+ cacheConnectivity(event) {
3263
+ this.cache[event.vin] ??= {};
3264
+ this.cache[event.vin].connectivity ??= {};
3265
+ this.cache[event.vin].connectivity[event.networkInterface] = event.status;
3266
+ }
3267
+ startLocalCache() {
3268
+ this.localCache = true;
3269
+ this.on("state", this.cacheState);
3270
+ this.on("data", this.cacheData);
3271
+ this.on("errors", this.cacheErrors);
3272
+ this.on("alerts", this.cacheAlerts);
3273
+ this.on("connectivity", this.cacheConnectivity);
3274
+ this.logger.info(`Started local cache`);
3275
+ }
3276
+ stopLocalCache() {
3277
+ this.localCache = false;
3278
+ this.off("state", this.cacheState);
3279
+ this.off("data", this.cacheData);
3280
+ this.off("errors", this.cacheErrors);
3281
+ this.off("alerts", this.cacheAlerts);
3282
+ this.off("connectivity", this.cacheConnectivity);
3283
+ this.logger.info(`Stopped local cache`);
3284
+ }
3184
3285
  };
3185
3286
 
3186
3287
  //#endregion
@@ -3245,6 +3346,15 @@ var TeslemetryChargingApi = class {
3245
3346
  var TeslemetryEnergyApi = class extends events.EventEmitter {
3246
3347
  root;
3247
3348
  siteId;
3349
+ refreshDelay = 3e4;
3350
+ refreshInterval = {
3351
+ siteInfo: null,
3352
+ liveStatus: null
3353
+ };
3354
+ refreshClients = {
3355
+ siteInfo: /* @__PURE__ */ new Set(),
3356
+ liveStatus: /* @__PURE__ */ new Set()
3357
+ };
3248
3358
  constructor(root, siteId) {
3249
3359
  if (root.api.energySites.has(siteId)) throw new Error("Energy site already exists");
3250
3360
  super();
@@ -3386,6 +3496,34 @@ var TeslemetryEnergyApi = class extends events.EventEmitter {
3386
3496
  });
3387
3497
  return data;
3388
3498
  }
3499
+ requestPolling(endpoint) {
3500
+ if (!this.refreshInterval[endpoint]) switch (endpoint) {
3501
+ case "siteInfo":
3502
+ this.refreshInterval[endpoint] = setInterval(() => {
3503
+ this.getSiteInfo();
3504
+ }, this.refreshDelay);
3505
+ this.getSiteInfo();
3506
+ break;
3507
+ case "liveStatus":
3508
+ this.refreshInterval[endpoint] = setInterval(() => {
3509
+ this.getLiveStatus();
3510
+ }, this.refreshDelay);
3511
+ this.getLiveStatus();
3512
+ break;
3513
+ default: throw new Error(`Invalid endpoint: ${endpoint}`);
3514
+ }
3515
+ const symbol = Symbol("refreshClient");
3516
+ this.refreshClients[endpoint].add(symbol);
3517
+ return () => {
3518
+ this.refreshClients[endpoint].delete(symbol);
3519
+ if (this.refreshClients[endpoint].size === 0) {
3520
+ if (this.refreshInterval[endpoint]) {
3521
+ clearInterval(this.refreshInterval[endpoint]);
3522
+ this.refreshInterval[endpoint] = null;
3523
+ }
3524
+ }
3525
+ };
3526
+ }
3389
3527
  };
3390
3528
 
3391
3529
  //#endregion
@@ -4907,7 +5045,7 @@ const consoleLogger = {
4907
5045
 
4908
5046
  //#endregion
4909
5047
  //#region package.json
4910
- var version = "0.4.0";
5048
+ var version = "0.5.1";
4911
5049
 
4912
5050
  //#endregion
4913
5051
  //#region src/Teslemetry.ts
@@ -4968,27 +5106,25 @@ var Teslemetry = class {
4968
5106
  * @returns A promise that resolves to an object containing vehicle and energy site names, API, and SSE instances.
4969
5107
  */
4970
5108
  async createProducts() {
4971
- const { data } = await getApi1Products({ client: this.client });
4972
- const result = {
4973
- vehicles: {},
4974
- energySites: {}
5109
+ const { data } = await getApiMetadata({ client: this.client });
5110
+ return {
5111
+ vehicles: Object.fromEntries(Object.entries(data.vehicles).map(([vin, metadata]) => [vin, {
5112
+ name: metadata.name ?? useTeslaModel(vin),
5113
+ vin,
5114
+ api: this.api.getVehicle(vin),
5115
+ sse: this.sse.getVehicle(vin),
5116
+ metadata
5117
+ }])),
5118
+ energySites: Object.fromEntries(Object.entries(data.energy_sites ?? {}).map(([id, metadata]) => {
5119
+ const siteId = Number(id);
5120
+ return [id, {
5121
+ name: metadata.name ?? "Unnamed",
5122
+ id: siteId,
5123
+ api: this.api.getEnergySite(siteId),
5124
+ metadata
5125
+ }];
5126
+ }))
4975
5127
  };
4976
- data.response?.forEach((product) => {
4977
- if (product.device_type === "vehicle") result.vehicles[product.vin] = {
4978
- name: product.display_name ?? useTeslaModel(product.vin),
4979
- vin: product.vin,
4980
- api: this.api.getVehicle(product.vin),
4981
- sse: this.sse.getVehicle(product.vin),
4982
- product
4983
- };
4984
- if (product.device_type === "energy") result.energySites[product.energy_site_id] = {
4985
- name: product.site_name ?? "Unnamed",
4986
- site: product.energy_site_id,
4987
- api: this.api.getEnergySite(product.energy_site_id),
4988
- product
4989
- };
4990
- });
4991
- return result;
4992
5128
  }
4993
5129
  /**
4994
5130
  * Get a vehicle API instance for the specified VIN.