tesla-fleet-api 0.4.0 → 0.6.0

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.
Files changed (215) hide show
  1. package/README.md +269 -1
  2. package/dist/_virtual/_rolldown/runtime.cjs +10 -0
  3. package/dist/bluetooth/adapters/shared.cjs +38 -0
  4. package/dist/bluetooth/adapters/shared.mjs +36 -0
  5. package/dist/bluetooth/async.cjs +121 -0
  6. package/dist/bluetooth/async.d.cts +10 -0
  7. package/dist/bluetooth/async.d.mts +10 -0
  8. package/dist/bluetooth/async.mjs +116 -0
  9. package/dist/bluetooth/broadcast.cjs +153 -0
  10. package/dist/bluetooth/broadcast.d.cts +59 -0
  11. package/dist/bluetooth/broadcast.d.mts +59 -0
  12. package/dist/bluetooth/broadcast.mjs +151 -0
  13. package/dist/bluetooth/constants.cjs +96 -0
  14. package/dist/bluetooth/constants.d.cts +68 -0
  15. package/dist/bluetooth/constants.d.mts +68 -0
  16. package/dist/bluetooth/constants.mjs +70 -0
  17. package/dist/bluetooth/framing.cjs +125 -0
  18. package/dist/bluetooth/framing.d.cts +45 -0
  19. package/dist/bluetooth/framing.d.mts +45 -0
  20. package/dist/bluetooth/framing.mjs +122 -0
  21. package/dist/bluetooth/homey.cjs +165 -0
  22. package/dist/bluetooth/homey.d.cts +84 -0
  23. package/dist/bluetooth/homey.d.mts +84 -0
  24. package/dist/bluetooth/homey.mjs +159 -0
  25. package/dist/bluetooth/names.cjs +24 -0
  26. package/dist/bluetooth/names.d.cts +10 -0
  27. package/dist/bluetooth/names.d.mts +10 -0
  28. package/dist/bluetooth/names.mjs +20 -0
  29. package/dist/bluetooth/noble.cjs +178 -0
  30. package/dist/bluetooth/noble.d.cts +58 -0
  31. package/dist/bluetooth/noble.d.mts +58 -0
  32. package/dist/bluetooth/noble.mjs +177 -0
  33. package/dist/bluetooth/streamglue.cjs +157 -0
  34. package/dist/bluetooth/streamglue.d.cts +37 -0
  35. package/dist/bluetooth/streamglue.d.mts +37 -0
  36. package/dist/bluetooth/streamglue.mjs +154 -0
  37. package/dist/bluetooth/teslabluetooth.cjs +67 -0
  38. package/dist/bluetooth/teslabluetooth.d.cts +41 -0
  39. package/dist/bluetooth/teslabluetooth.d.mts +41 -0
  40. package/dist/bluetooth/teslabluetooth.mjs +66 -0
  41. package/dist/bluetooth/transport.cjs +10 -0
  42. package/dist/bluetooth/transport.d.cts +97 -0
  43. package/dist/bluetooth/transport.d.mts +97 -0
  44. package/dist/bluetooth/transport.mjs +10 -0
  45. package/dist/bluetooth/vehiclebluetooth.cjs +1272 -0
  46. package/dist/bluetooth/vehiclebluetooth.d.cts +495 -0
  47. package/dist/bluetooth/vehiclebluetooth.d.mts +495 -0
  48. package/dist/bluetooth/vehiclebluetooth.mjs +1270 -0
  49. package/dist/bluetooth/verify.cjs +93 -0
  50. package/dist/bluetooth/verify.d.cts +35 -0
  51. package/dist/bluetooth/verify.d.mts +35 -0
  52. package/dist/bluetooth/verify.mjs +91 -0
  53. package/dist/bluetooth.cjs +52 -0
  54. package/dist/bluetooth.d.cts +10 -0
  55. package/dist/bluetooth.d.mts +10 -0
  56. package/dist/bluetooth.mjs +9 -0
  57. package/dist/commands.cjs +1080 -85
  58. package/dist/commands.d.cts +585 -54
  59. package/dist/commands.d.mts +583 -51
  60. package/dist/commands.mjs +1084 -93
  61. package/dist/const.cjs +22 -0
  62. package/dist/const.d.cts +21 -0
  63. package/dist/const.d.mts +21 -0
  64. package/dist/const.mjs +20 -1
  65. package/dist/energy.cjs +24 -19
  66. package/dist/energy.d.cts +21 -15
  67. package/dist/energy.d.mts +21 -15
  68. package/dist/energy.mjs +24 -19
  69. package/dist/energyisland.cjs +38 -0
  70. package/dist/energyisland.mjs +37 -0
  71. package/dist/energyparsers.cjs +118 -0
  72. package/dist/energyparsers.d.cts +39 -0
  73. package/dist/energyparsers.d.mts +39 -0
  74. package/dist/energyparsers.mjs +116 -0
  75. package/dist/energyspecific.cjs +375 -17
  76. package/dist/energyspecific.d.cts +199 -19
  77. package/dist/energyspecific.d.mts +197 -16
  78. package/dist/energyspecific.mjs +370 -18
  79. package/dist/exceptions.cjs +3 -2
  80. package/dist/exceptions.d.cts +3 -2
  81. package/dist/exceptions.d.mts +3 -2
  82. package/dist/exceptions.mjs +3 -2
  83. package/dist/funnel.cjs +297 -0
  84. package/dist/funnel.d.cts +118 -0
  85. package/dist/funnel.d.mts +118 -0
  86. package/dist/funnel.mjs +292 -0
  87. package/dist/index.cjs +61 -0
  88. package/dist/index.d.cts +22 -8
  89. package/dist/index.d.mts +22 -8
  90. package/dist/index.mjs +19 -6
  91. package/dist/log.cjs +75 -0
  92. package/dist/log.d.cts +4 -0
  93. package/dist/log.d.mts +4 -0
  94. package/dist/log.mjs +71 -0
  95. package/dist/oauth.cjs +143 -0
  96. package/dist/oauth.d.cts +69 -0
  97. package/dist/oauth.d.mts +69 -0
  98. package/dist/oauth.mjs +143 -0
  99. package/dist/powerwall/client.cjs +818 -0
  100. package/dist/powerwall/client.d.cts +297 -0
  101. package/dist/powerwall/client.d.mts +297 -0
  102. package/dist/powerwall/client.mjs +807 -0
  103. package/dist/powerwall/components.cjs +188 -0
  104. package/dist/powerwall/components.d.cts +45 -0
  105. package/dist/powerwall/components.d.mts +45 -0
  106. package/dist/powerwall/components.mjs +182 -0
  107. package/dist/powerwall/energysite.cjs +216 -0
  108. package/dist/powerwall/energysite.d.cts +104 -0
  109. package/dist/powerwall/energysite.d.mts +104 -0
  110. package/dist/powerwall/energysite.mjs +214 -0
  111. package/dist/powerwall/exceptions.cjs +59 -0
  112. package/dist/powerwall/exceptions.d.cts +38 -0
  113. package/dist/powerwall/exceptions.d.mts +38 -0
  114. package/dist/powerwall/exceptions.mjs +51 -0
  115. package/dist/powerwall/gateway.cjs +102 -0
  116. package/dist/powerwall/gateway.d.cts +57 -0
  117. package/dist/powerwall/gateway.d.mts +57 -0
  118. package/dist/powerwall/gateway.mjs +101 -0
  119. package/dist/powerwall/merge.cjs +163 -0
  120. package/dist/powerwall/merge.d.cts +63 -0
  121. package/dist/powerwall/merge.d.mts +63 -0
  122. package/dist/powerwall/merge.mjs +159 -0
  123. package/dist/powerwall/models.d.cts +95 -0
  124. package/dist/powerwall/models.d.mts +95 -0
  125. package/dist/powerwall/queries.cjs +17 -0
  126. package/dist/powerwall/queries.mjs +10 -0
  127. package/dist/powerwall/transport.cjs +232 -0
  128. package/dist/powerwall/transport.mjs +227 -0
  129. package/dist/powerwall.cjs +47 -0
  130. package/dist/powerwall.d.cts +9 -0
  131. package/dist/powerwall.d.mts +9 -0
  132. package/dist/powerwall.mjs +8 -0
  133. package/dist/router/base.cjs +203 -0
  134. package/dist/router/base.d.cts +112 -0
  135. package/dist/router/base.d.mts +112 -0
  136. package/dist/router/base.mjs +200 -0
  137. package/dist/router/energysite.cjs +65 -0
  138. package/dist/router/energysite.d.cts +47 -0
  139. package/dist/router/energysite.d.mts +47 -0
  140. package/dist/router/energysite.mjs +60 -0
  141. package/dist/router.cjs +13 -0
  142. package/dist/router.d.cts +3 -1
  143. package/dist/router.d.mts +3 -1
  144. package/dist/router.mjs +3 -1
  145. package/dist/signing/crypto.cjs +28 -0
  146. package/dist/signing/crypto.mjs +26 -1
  147. package/dist/signing/energy.cjs +123 -0
  148. package/dist/signing/energy.d.cts +45 -0
  149. package/dist/signing/energy.d.mts +45 -0
  150. package/dist/signing/energy.mjs +120 -0
  151. package/dist/signing/jws.cjs +229 -0
  152. package/dist/signing/jws.d.cts +45 -0
  153. package/dist/signing/jws.d.mts +45 -0
  154. package/dist/signing/jws.mjs +211 -0
  155. package/dist/signing/keys.cjs +191 -0
  156. package/dist/signing/keys.d.cts +21 -0
  157. package/dist/signing/keys.d.mts +21 -0
  158. package/dist/signing/keys.mjs +182 -0
  159. package/dist/signing/session.cjs +104 -30
  160. package/dist/signing/session.d.cts +60 -23
  161. package/dist/signing/session.d.mts +60 -23
  162. package/dist/signing/session.mjs +105 -31
  163. package/dist/tariff.cjs +27 -0
  164. package/dist/tariff.d.cts +11 -0
  165. package/dist/tariff.d.mts +11 -0
  166. package/dist/tariff.mjs +27 -1
  167. package/dist/teslafleetapi.cjs +256 -55
  168. package/dist/teslafleetapi.d.cts +110 -14
  169. package/dist/teslafleetapi.d.mts +110 -14
  170. package/dist/teslafleetapi.mjs +258 -57
  171. package/dist/teslemetry.cjs +152 -5
  172. package/dist/teslemetry.d.cts +95 -5
  173. package/dist/teslemetry.d.mts +93 -3
  174. package/dist/teslemetry.mjs +151 -6
  175. package/dist/teslemetryenergy.cjs +287 -0
  176. package/dist/teslemetryenergy.d.cts +154 -0
  177. package/dist/teslemetryenergy.d.mts +154 -0
  178. package/dist/teslemetryenergy.mjs +282 -0
  179. package/dist/teslemetryvehicle.cjs +407 -0
  180. package/dist/teslemetryvehicle.d.cts +238 -0
  181. package/dist/teslemetryvehicle.d.mts +238 -0
  182. package/dist/teslemetryvehicle.mjs +405 -0
  183. package/dist/tessie.cjs +42 -5
  184. package/dist/tessie.d.cts +51 -5
  185. package/dist/tessie.d.mts +49 -3
  186. package/dist/tessie.mjs +42 -5
  187. package/dist/tessievehicle.cjs +514 -0
  188. package/dist/tessievehicle.d.cts +333 -0
  189. package/dist/tessievehicle.d.mts +333 -0
  190. package/dist/tessievehicle.mjs +513 -0
  191. package/dist/types/live_status.d.cts +16 -16
  192. package/dist/types/live_status.d.mts +16 -16
  193. package/dist/types/responses.d.cts +5 -1
  194. package/dist/types/responses.d.mts +5 -1
  195. package/dist/types/vehicle.d.cts +7 -1
  196. package/dist/types/vehicle.d.mts +7 -1
  197. package/dist/types.d.cts +3 -3
  198. package/dist/types.d.mts +3 -3
  199. package/dist/util.cjs +41 -0
  200. package/dist/util.d.cts +18 -0
  201. package/dist/util.d.mts +18 -0
  202. package/dist/util.mjs +40 -0
  203. package/dist/vehicle.cjs +79 -36
  204. package/dist/vehicle.d.cts +42 -26
  205. package/dist/vehicle.d.mts +42 -26
  206. package/dist/vehicle.mjs +80 -37
  207. package/dist/vehiclesigned.cjs +20 -5
  208. package/dist/vehiclesigned.d.cts +14 -5
  209. package/dist/vehiclesigned.d.mts +14 -5
  210. package/dist/vehiclesigned.mjs +20 -5
  211. package/dist/vehiclespecific.cjs +126 -15
  212. package/dist/vehiclespecific.d.cts +82 -18
  213. package/dist/vehiclespecific.d.mts +82 -18
  214. package/dist/vehiclespecific.mjs +126 -15
  215. package/package.json +56 -3
package/dist/index.mjs CHANGED
@@ -1,14 +1,27 @@
1
- import Commands from "./commands.mjs";
1
+ import Charging from "./charging.mjs";
2
+ import Commands, { DAY_OF_WEEK_BITS, days_of_week_mask, infotainment_command_name, vcsec_command_name } from "./commands.mjs";
2
3
  import Energy from "./energy.mjs";
3
- import EnergySpecific from "./energyspecific.mjs";
4
+ import EnergySpecific, { MAX_DISPATCH_DURATION_SECONDS, UNSIGNED_DEVICE_COMMANDS, validate_dispatch_battery_power } from "./energyspecific.mjs";
5
+ import Partner from "./partner.mjs";
4
6
  import TeslaFleetApi from "./teslafleetapi.mjs";
5
- import Teslemetry from "./teslemetry.mjs";
7
+ import TeslaFleetOAuth from "./oauth.mjs";
8
+ import Teslemetry, { REGISTER_URL, register_client } from "./teslemetry.mjs";
6
9
  import Tessie from "./tessie.mjs";
10
+ import User from "./user.mjs";
7
11
  import Vehicle from "./vehicle.mjs";
8
12
  import VehicleSigned from "./vehiclesigned.mjs";
9
13
  import VehicleSpecific from "./vehiclespecific.mjs";
14
+ import { TeslemetryVehicle, TeslemetryVehicleTelemetry, TeslemetryVehicles } from "./teslemetryvehicle.mjs";
15
+ import { TessieVehicle, TessieVehicles } from "./tessievehicle.mjs";
16
+ import { ENERGY_SIGNATURE_TTL_SECONDS, EnergyDeviceSigner } from "./signing/energy.mjs";
17
+ import { DEFAULT_PAIRING_POLL_INTERVAL, DEFAULT_PAIRING_TIMEOUT, TeslemetryEnergySite, TeslemetryEnergySites } from "./teslemetryenergy.mjs";
18
+ import { parse_authorized_clients, parse_gateway_address } from "./energyparsers.mjs";
19
+ import { CLOSURE_STATES, FieldPath, LOCK_STATES, ObservationFunnel, TeslemetryStreamPublisher, VehicleDataResultPublisher } from "./funnel.mjs";
10
20
  import { Session } from "./signing/session.mjs";
21
+ import { jws_exports } from "./signing/jws.mjs";
22
+ import { ec_private_key_from_pem, generate_ec_private_key_pem, generate_rsa_private_key_pem, rsa_public_der_pkcs1 } from "./signing/keys.mjs";
11
23
  import { AESDecryptAuthFault, AuthorizedClientPairingTimedOut, AuthorizedClientWaitExpired, BadGateway, BluetoothCloudUnavailableError, BluetoothCommandFailed, BluetoothTimeout, BluetoothTransportError, BluetoothUnconfirmedCommand, ClientClosedRequest, CouldNotHashMetadataFault, CouldNotRetrieveKeyFault, CouldNotRetrieveTokenFault, DeviceUnexpectedResponse, ECDSAInputFault, ECDSASignatureFault, EnergyGatewayUnreachable, Forbidden, GatewayTimeout, INFORMATION_FAULTS, IVIncorrectLengthFault, IVSmallerThanExpectedFault, IncorrectEpochFault, InsufficientCredits, InternalServerError, InvalidAuthCode, InvalidCommand, InvalidField, InvalidMethod, InvalidRedirectUrl, InvalidRegion, InvalidRequest, InvalidResource, InvalidResponse, InvalidScope, InvalidTeslemetryToken, InvalidTessieToken, InvalidToken, InvalidTokenFault, KEY_REJECTED_FAULTS, LibraryError, LocalEntityResultFault, LocalEntityStartFault, Locked, LoginRequired, MESSAGE_FAULTS, MissingTeslemetryToken, MissingTessieToken, MissingToken, MobileAccessDisabled, NotAcceptable, NotAllowed, NotFound, NotOnWhitelistFault, NotProvisionedWithIdentityFault, OAuthExpired, OAuthInvalid, PaymentRequired, PreconditionFailed, PrivateKeyError, RateLimited, ResourceUnavailableForLegalReasons, ResponseError, SIGNED_MESSAGE_INFORMATION_FAULTS, ServiceUnavailable, SessionInfoAuthenticationFault, SignatureTooShortFault, SignedCommandRequired, SignedCommandResponseReplayed, SignedMessageInformationFault, SignedMessageInformationFaultAESDecryptAuth, SignedMessageInformationFaultCouldNotHashMetadata, SignedMessageInformationFaultCouldNotRetrieveKey, SignedMessageInformationFaultCouldNotRetrieveToken, SignedMessageInformationFaultECDSAInput, SignedMessageInformationFaultECDSASignature, SignedMessageInformationFaultIVIncorrectLength, SignedMessageInformationFaultIVSmallerThanExpected, SignedMessageInformationFaultIncorrectEpoch, SignedMessageInformationFaultInvalidToken, SignedMessageInformationFaultLocalEntityResult, SignedMessageInformationFaultLocalEntityStart, SignedMessageInformationFaultNotOnWhitelist, SignedMessageInformationFaultNotProvisionedWithIdentity, SignedMessageInformationFaultSignatureTooShort, SignedMessageInformationFaultTimeExpired, SignedMessageInformationFaultTokenAndCounterInvalid, SignedMessageInformationFaultTokenIsIncorrectLength, SignedMessageInformationFaultUnknown, SigningDisabled, SubscriptionRequired, TeslaFleetConnectionError, TeslaFleetError, TeslaFleetInformationFault, TeslaFleetMessageFault, TeslaFleetMessageFaultBadParameter, TeslaFleetMessageFaultBusy, TeslaFleetMessageFaultCommandRequiresAccountCredentials, TeslaFleetMessageFaultCouldNotHashMetadata, TeslaFleetMessageFaultDecoding, TeslaFleetMessageFaultFieldExceedsMTU, TeslaFleetMessageFaultIVIncorrectLength, TeslaFleetMessageFaultInactiveKey, TeslaFleetMessageFaultIncorrectEpoch, TeslaFleetMessageFaultInsufficientPrivileges, TeslaFleetMessageFaultInternal, TeslaFleetMessageFaultInvalidCommand, TeslaFleetMessageFaultInvalidDomains, TeslaFleetMessageFaultInvalidKeyHandle, TeslaFleetMessageFaultInvalidSignature, TeslaFleetMessageFaultInvalidTokenOrCounter, TeslaFleetMessageFaultKeychainIsFull, TeslaFleetMessageFaultNotProvisionedWithIdentity, TeslaFleetMessageFaultRemoteAccessDisabled, TeslaFleetMessageFaultRemoteServiceAccessDisabled, TeslaFleetMessageFaultRepeatedCounter, TeslaFleetMessageFaultRequiresResponseEncryption, TeslaFleetMessageFaultResponseSizeExceedsMTU, TeslaFleetMessageFaultTimeExpired, TeslaFleetMessageFaultTimeToLiveTooLong, TeslaFleetMessageFaultTimeout, TeslaFleetMessageFaultUnknownKeyId, TeslaFleetMessageFaultWrongPersonalization, TeslemetryRegistrationError, TimeExpiredFault, TokenAndCounterInvalidFault, TokenIsIncorrectLengthFault, UnauthorizedClient, UnknownFault, UnsupportedVehicle, VehicleOffline, VehicleSubscriptionRequired, WHITELIST_OPERATION_STATUS, WhitelistOperationAttemptingToAddExistingKey, WhitelistOperationAttemptingToAddKeyWithServiceRole, WhitelistOperationAttemptingToAddKeyWithoutRole, WhitelistOperationAttemptingToDemoteSuperiorToOneself, WhitelistOperationAttemptingToElevateOthersAboveOneself, WhitelistOperationAttemptingToRemoveOwnPermissions, WhitelistOperationCouldNotStartLocalEntityAuth, WhitelistOperationFMAttemptingToAddPermanentKey, WhitelistOperationFMAttemptingToRemovePermanentKey, WhitelistOperationFMModifyingOutsideOfFMode, WhitelistOperationInvalidPublicKey, WhitelistOperationKeychainWhileFSFull, WhitelistOperationKeyfobSlotsFull, WhitelistOperationLocalEntityAuthFailedCancelled, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForTap, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForUIAck, WhitelistOperationLocalEntityAuthFailedUIDenied, WhitelistOperationLocalEntityAuthFailedValetMode, WhitelistOperationNoPermissionToAdd, WhitelistOperationNoPermissionToChangePermissions, WhitelistOperationNoPermissionToRemove, WhitelistOperationNoPermissionToRemoveOneself, WhitelistOperationNonServiceKeyAttemptingToAddServiceTech, WhitelistOperationNotAllowedToAddUnlessOnReader, WhitelistOperationPublicKeyNotOnWhitelist, WhitelistOperationServiceKeyAttemptingToAddServiceTechOutsideServiceMode, WhitelistOperationStatus, WhitelistOperationUndocumentedError, WhitelistOperationWhitelistFull, is_key_rejected, raise_for_status } from "./exceptions.mjs";
12
- import { getTariffPeriods } from "./tariff.mjs";
13
- import { AuthorizationRole, AuthorizedClientKeyType, AuthorizedClientState, AuthorizedClientType, AuthorizedVerificationType, AutoSeat, BluetoothVehicleData, CabinOverheatProtectionTemp, ClimateKeeperMode, ClimateModes, ClosureState, DeviceType, DistanceUnit, EnergyDeviceIdentifierType, EnergyDisplayFormat, EnergyExportMode, EnergyIslandMode, EnergyOperationMode, Level, Method, Models, PhoneFontSize, SERVERS, Scope, SeatHeaterLevel, Seats, SunRoofCommand, TOKEN_URLS, TemperatureUnit, TeslaEnergyPeriod, TimeDisplayFormat, TirePressureUnit, Trunk, VehicleDataEndpoint, VehicleImageType, WindowCommand, is_valid_region } from "./const.mjs";
14
- export { AESDecryptAuthFault, AuthorizationRole, AuthorizedClientKeyType, AuthorizedClientPairingTimedOut, AuthorizedClientState, AuthorizedClientType, AuthorizedClientWaitExpired, AuthorizedVerificationType, AutoSeat, BadGateway, BluetoothCloudUnavailableError, BluetoothCommandFailed, BluetoothTimeout, BluetoothTransportError, BluetoothUnconfirmedCommand, BluetoothVehicleData, CabinOverheatProtectionTemp, ClientClosedRequest, ClimateKeeperMode, ClimateModes, ClosureState, Commands, CouldNotHashMetadataFault, CouldNotRetrieveKeyFault, CouldNotRetrieveTokenFault, DeviceType, DeviceUnexpectedResponse, DistanceUnit, ECDSAInputFault, ECDSASignatureFault, Energy, EnergyDeviceIdentifierType, EnergyDisplayFormat, EnergyExportMode, EnergyGatewayUnreachable, EnergyIslandMode, EnergyOperationMode, EnergySpecific, Forbidden, GatewayTimeout, INFORMATION_FAULTS, IVIncorrectLengthFault, IVSmallerThanExpectedFault, IncorrectEpochFault, InsufficientCredits, InternalServerError, InvalidAuthCode, InvalidCommand, InvalidField, InvalidMethod, InvalidRedirectUrl, InvalidRegion, InvalidRequest, InvalidResource, InvalidResponse, InvalidScope, InvalidTeslemetryToken, InvalidTessieToken, InvalidToken, InvalidTokenFault, KEY_REJECTED_FAULTS, Level, LibraryError, LocalEntityResultFault, LocalEntityStartFault, Locked, LoginRequired, MESSAGE_FAULTS, Method, MissingTeslemetryToken, MissingTessieToken, MissingToken, MobileAccessDisabled, Models, NotAcceptable, NotAllowed, NotFound, NotOnWhitelistFault, NotProvisionedWithIdentityFault, OAuthExpired, OAuthInvalid, PaymentRequired, PhoneFontSize, PreconditionFailed, PrivateKeyError, RateLimited, ResourceUnavailableForLegalReasons, ResponseError, SERVERS, SIGNED_MESSAGE_INFORMATION_FAULTS, Scope, SeatHeaterLevel, Seats, ServiceUnavailable, Session, SessionInfoAuthenticationFault, SignatureTooShortFault, SignedCommandRequired, SignedCommandResponseReplayed, SignedMessageInformationFault, SignedMessageInformationFaultAESDecryptAuth, SignedMessageInformationFaultCouldNotHashMetadata, SignedMessageInformationFaultCouldNotRetrieveKey, SignedMessageInformationFaultCouldNotRetrieveToken, SignedMessageInformationFaultECDSAInput, SignedMessageInformationFaultECDSASignature, SignedMessageInformationFaultIVIncorrectLength, SignedMessageInformationFaultIVSmallerThanExpected, SignedMessageInformationFaultIncorrectEpoch, SignedMessageInformationFaultInvalidToken, SignedMessageInformationFaultLocalEntityResult, SignedMessageInformationFaultLocalEntityStart, SignedMessageInformationFaultNotOnWhitelist, SignedMessageInformationFaultNotProvisionedWithIdentity, SignedMessageInformationFaultSignatureTooShort, SignedMessageInformationFaultTimeExpired, SignedMessageInformationFaultTokenAndCounterInvalid, SignedMessageInformationFaultTokenIsIncorrectLength, SignedMessageInformationFaultUnknown, SigningDisabled, SubscriptionRequired, SunRoofCommand, TOKEN_URLS, TemperatureUnit, TeslaEnergyPeriod, TeslaFleetApi, TeslaFleetConnectionError, TeslaFleetError, TeslaFleetInformationFault, TeslaFleetMessageFault, TeslaFleetMessageFaultBadParameter, TeslaFleetMessageFaultBusy, TeslaFleetMessageFaultCommandRequiresAccountCredentials, TeslaFleetMessageFaultCouldNotHashMetadata, TeslaFleetMessageFaultDecoding, TeslaFleetMessageFaultFieldExceedsMTU, TeslaFleetMessageFaultIVIncorrectLength, TeslaFleetMessageFaultInactiveKey, TeslaFleetMessageFaultIncorrectEpoch, TeslaFleetMessageFaultInsufficientPrivileges, TeslaFleetMessageFaultInternal, TeslaFleetMessageFaultInvalidCommand, TeslaFleetMessageFaultInvalidDomains, TeslaFleetMessageFaultInvalidKeyHandle, TeslaFleetMessageFaultInvalidSignature, TeslaFleetMessageFaultInvalidTokenOrCounter, TeslaFleetMessageFaultKeychainIsFull, TeslaFleetMessageFaultNotProvisionedWithIdentity, TeslaFleetMessageFaultRemoteAccessDisabled, TeslaFleetMessageFaultRemoteServiceAccessDisabled, TeslaFleetMessageFaultRepeatedCounter, TeslaFleetMessageFaultRequiresResponseEncryption, TeslaFleetMessageFaultResponseSizeExceedsMTU, TeslaFleetMessageFaultTimeExpired, TeslaFleetMessageFaultTimeToLiveTooLong, TeslaFleetMessageFaultTimeout, TeslaFleetMessageFaultUnknownKeyId, TeslaFleetMessageFaultWrongPersonalization, Teslemetry, TeslemetryRegistrationError, Tessie, TimeDisplayFormat, TimeExpiredFault, TirePressureUnit, TokenAndCounterInvalidFault, TokenIsIncorrectLengthFault, Trunk, UnauthorizedClient, UnknownFault, UnsupportedVehicle, Vehicle, VehicleDataEndpoint, VehicleImageType, VehicleOffline, VehicleSigned, VehicleSpecific, VehicleSubscriptionRequired, WHITELIST_OPERATION_STATUS, WhitelistOperationAttemptingToAddExistingKey, WhitelistOperationAttemptingToAddKeyWithServiceRole, WhitelistOperationAttemptingToAddKeyWithoutRole, WhitelistOperationAttemptingToDemoteSuperiorToOneself, WhitelistOperationAttemptingToElevateOthersAboveOneself, WhitelistOperationAttemptingToRemoveOwnPermissions, WhitelistOperationCouldNotStartLocalEntityAuth, WhitelistOperationFMAttemptingToAddPermanentKey, WhitelistOperationFMAttemptingToRemovePermanentKey, WhitelistOperationFMModifyingOutsideOfFMode, WhitelistOperationInvalidPublicKey, WhitelistOperationKeychainWhileFSFull, WhitelistOperationKeyfobSlotsFull, WhitelistOperationLocalEntityAuthFailedCancelled, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForTap, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForUIAck, WhitelistOperationLocalEntityAuthFailedUIDenied, WhitelistOperationLocalEntityAuthFailedValetMode, WhitelistOperationNoPermissionToAdd, WhitelistOperationNoPermissionToChangePermissions, WhitelistOperationNoPermissionToRemove, WhitelistOperationNoPermissionToRemoveOneself, WhitelistOperationNonServiceKeyAttemptingToAddServiceTech, WhitelistOperationNotAllowedToAddUnlessOnReader, WhitelistOperationPublicKeyNotOnWhitelist, WhitelistOperationServiceKeyAttemptingToAddServiceTechOutsideServiceMode, WhitelistOperationStatus, WhitelistOperationUndocumentedError, WhitelistOperationWhitelistFull, WindowCommand, getTariffPeriods, is_key_rejected, is_valid_region, raise_for_status };
24
+ import { getTariffPeriods, unwrapTariffV2 } from "./tariff.mjs";
25
+ import { firmwareAtLeast, firmwareCompare } from "./util.mjs";
26
+ import { AuthorizationRole, AuthorizedClientKeyType, AuthorizedClientState, AuthorizedClientType, AuthorizedVerificationType, AutoSeat, BluetoothVehicleData, CabinOverheatProtectionTemp, ClimateKeeperMode, ClimateModes, ClosureState, DEFAULT_LOCALE, DeviceType, DistanceUnit, EnergyDeviceIdentifierType, EnergyDisplayFormat, EnergyExportMode, EnergyIslandMode, EnergyOperationMode, Level, Method, Models, PhoneFontSize, SERVERS, Scope, SeatHeaterLevel, Seats, SnapshotTopic, SunRoofCommand, TOKEN_URLS, TemperatureUnit, TeslaEnergyPeriod, TimeDisplayFormat, TirePressureUnit, Trunk, VehicleDataEndpoint, VehicleImageType, WaitTopic, WindowCommand, is_valid_region } from "./const.mjs";
27
+ export { AESDecryptAuthFault, AuthorizationRole, AuthorizedClientKeyType, AuthorizedClientPairingTimedOut, AuthorizedClientState, AuthorizedClientType, AuthorizedClientWaitExpired, AuthorizedVerificationType, AutoSeat, BadGateway, BluetoothCloudUnavailableError, BluetoothCommandFailed, BluetoothTimeout, BluetoothTransportError, BluetoothUnconfirmedCommand, BluetoothVehicleData, CLOSURE_STATES, CabinOverheatProtectionTemp, Charging, ClientClosedRequest, ClimateKeeperMode, ClimateModes, ClosureState, Commands, CouldNotHashMetadataFault, CouldNotRetrieveKeyFault, CouldNotRetrieveTokenFault, DAY_OF_WEEK_BITS, DEFAULT_LOCALE, DEFAULT_PAIRING_POLL_INTERVAL, DEFAULT_PAIRING_TIMEOUT, DeviceType, DeviceUnexpectedResponse, DistanceUnit, ECDSAInputFault, ECDSASignatureFault, ENERGY_SIGNATURE_TTL_SECONDS, Energy, EnergyDeviceIdentifierType, EnergyDeviceSigner, EnergyDisplayFormat, EnergyExportMode, EnergyGatewayUnreachable, EnergyIslandMode, EnergyOperationMode, EnergySpecific, FieldPath, Forbidden, GatewayTimeout, INFORMATION_FAULTS, IVIncorrectLengthFault, IVSmallerThanExpectedFault, IncorrectEpochFault, InsufficientCredits, InternalServerError, InvalidAuthCode, InvalidCommand, InvalidField, InvalidMethod, InvalidRedirectUrl, InvalidRegion, InvalidRequest, InvalidResource, InvalidResponse, InvalidScope, InvalidTeslemetryToken, InvalidTessieToken, InvalidToken, InvalidTokenFault, KEY_REJECTED_FAULTS, LOCK_STATES, Level, LibraryError, LocalEntityResultFault, LocalEntityStartFault, Locked, LoginRequired, MAX_DISPATCH_DURATION_SECONDS, MESSAGE_FAULTS, Method, MissingTeslemetryToken, MissingTessieToken, MissingToken, MobileAccessDisabled, Models, NotAcceptable, NotAllowed, NotFound, NotOnWhitelistFault, NotProvisionedWithIdentityFault, OAuthExpired, OAuthInvalid, ObservationFunnel, Partner, PaymentRequired, PhoneFontSize, PreconditionFailed, PrivateKeyError, REGISTER_URL, RateLimited, ResourceUnavailableForLegalReasons, ResponseError, SERVERS, SIGNED_MESSAGE_INFORMATION_FAULTS, Scope, SeatHeaterLevel, Seats, ServiceUnavailable, Session, SessionInfoAuthenticationFault, SignatureTooShortFault, SignedCommandRequired, SignedCommandResponseReplayed, SignedMessageInformationFault, SignedMessageInformationFaultAESDecryptAuth, SignedMessageInformationFaultCouldNotHashMetadata, SignedMessageInformationFaultCouldNotRetrieveKey, SignedMessageInformationFaultCouldNotRetrieveToken, SignedMessageInformationFaultECDSAInput, SignedMessageInformationFaultECDSASignature, SignedMessageInformationFaultIVIncorrectLength, SignedMessageInformationFaultIVSmallerThanExpected, SignedMessageInformationFaultIncorrectEpoch, SignedMessageInformationFaultInvalidToken, SignedMessageInformationFaultLocalEntityResult, SignedMessageInformationFaultLocalEntityStart, SignedMessageInformationFaultNotOnWhitelist, SignedMessageInformationFaultNotProvisionedWithIdentity, SignedMessageInformationFaultSignatureTooShort, SignedMessageInformationFaultTimeExpired, SignedMessageInformationFaultTokenAndCounterInvalid, SignedMessageInformationFaultTokenIsIncorrectLength, SignedMessageInformationFaultUnknown, SigningDisabled, SnapshotTopic, SubscriptionRequired, SunRoofCommand, TOKEN_URLS, TemperatureUnit, TeslaEnergyPeriod, TeslaFleetApi, TeslaFleetConnectionError, TeslaFleetError, TeslaFleetInformationFault, TeslaFleetMessageFault, TeslaFleetMessageFaultBadParameter, TeslaFleetMessageFaultBusy, TeslaFleetMessageFaultCommandRequiresAccountCredentials, TeslaFleetMessageFaultCouldNotHashMetadata, TeslaFleetMessageFaultDecoding, TeslaFleetMessageFaultFieldExceedsMTU, TeslaFleetMessageFaultIVIncorrectLength, TeslaFleetMessageFaultInactiveKey, TeslaFleetMessageFaultIncorrectEpoch, TeslaFleetMessageFaultInsufficientPrivileges, TeslaFleetMessageFaultInternal, TeslaFleetMessageFaultInvalidCommand, TeslaFleetMessageFaultInvalidDomains, TeslaFleetMessageFaultInvalidKeyHandle, TeslaFleetMessageFaultInvalidSignature, TeslaFleetMessageFaultInvalidTokenOrCounter, TeslaFleetMessageFaultKeychainIsFull, TeslaFleetMessageFaultNotProvisionedWithIdentity, TeslaFleetMessageFaultRemoteAccessDisabled, TeslaFleetMessageFaultRemoteServiceAccessDisabled, TeslaFleetMessageFaultRepeatedCounter, TeslaFleetMessageFaultRequiresResponseEncryption, TeslaFleetMessageFaultResponseSizeExceedsMTU, TeslaFleetMessageFaultTimeExpired, TeslaFleetMessageFaultTimeToLiveTooLong, TeslaFleetMessageFaultTimeout, TeslaFleetMessageFaultUnknownKeyId, TeslaFleetMessageFaultWrongPersonalization, TeslaFleetOAuth, Teslemetry, TeslemetryEnergySite, TeslemetryEnergySites, TeslemetryRegistrationError, TeslemetryStreamPublisher, TeslemetryVehicle, TeslemetryVehicleTelemetry, TeslemetryVehicles, Tessie, TessieVehicle, TessieVehicles, TimeDisplayFormat, TimeExpiredFault, TirePressureUnit, TokenAndCounterInvalidFault, TokenIsIncorrectLengthFault, Trunk, UNSIGNED_DEVICE_COMMANDS, UnauthorizedClient, UnknownFault, UnsupportedVehicle, User, Vehicle, VehicleDataEndpoint, VehicleDataResultPublisher, VehicleImageType, VehicleOffline, VehicleSigned, VehicleSpecific, VehicleSubscriptionRequired, WHITELIST_OPERATION_STATUS, WaitTopic, WhitelistOperationAttemptingToAddExistingKey, WhitelistOperationAttemptingToAddKeyWithServiceRole, WhitelistOperationAttemptingToAddKeyWithoutRole, WhitelistOperationAttemptingToDemoteSuperiorToOneself, WhitelistOperationAttemptingToElevateOthersAboveOneself, WhitelistOperationAttemptingToRemoveOwnPermissions, WhitelistOperationCouldNotStartLocalEntityAuth, WhitelistOperationFMAttemptingToAddPermanentKey, WhitelistOperationFMAttemptingToRemovePermanentKey, WhitelistOperationFMModifyingOutsideOfFMode, WhitelistOperationInvalidPublicKey, WhitelistOperationKeychainWhileFSFull, WhitelistOperationKeyfobSlotsFull, WhitelistOperationLocalEntityAuthFailedCancelled, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForTap, WhitelistOperationLocalEntityAuthFailedTimedOutWaitingForUIAck, WhitelistOperationLocalEntityAuthFailedUIDenied, WhitelistOperationLocalEntityAuthFailedValetMode, WhitelistOperationNoPermissionToAdd, WhitelistOperationNoPermissionToChangePermissions, WhitelistOperationNoPermissionToRemove, WhitelistOperationNoPermissionToRemoveOneself, WhitelistOperationNonServiceKeyAttemptingToAddServiceTech, WhitelistOperationNotAllowedToAddUnlessOnReader, WhitelistOperationPublicKeyNotOnWhitelist, WhitelistOperationServiceKeyAttemptingToAddServiceTechOutsideServiceMode, WhitelistOperationStatus, WhitelistOperationUndocumentedError, WhitelistOperationWhitelistFull, WindowCommand, days_of_week_mask, ec_private_key_from_pem, firmwareAtLeast, firmwareCompare, generate_ec_private_key_pem, generate_rsa_private_key_pem, getTariffPeriods, infotainment_command_name, is_key_rejected, is_valid_region, jws_exports as jws, parse_authorized_clients, parse_gateway_address, raise_for_status, register_client, rsa_public_der_pkcs1, unwrapTariffV2, validate_dispatch_battery_power, vcsec_command_name };
package/dist/log.cjs ADDED
@@ -0,0 +1,75 @@
1
+ const require_exceptions = require("./exceptions.cjs");
2
+ // src/log.ts
3
+ /**
4
+ * Parameter and body keys whose values never reach a log, lower-cased with
5
+ * separators removed. `migrate_to_oauth` sends and receives tokens through
6
+ * `_request`; PINs travel as `pin` or `password` body keys (Fleet, Teslemetry)
7
+ * or query parameters (Tessie); `trigger_homelink` and the telemetry JWS send
8
+ * `token`; a vehicle reply lists `tokens`; a signed `routable_message` executes
9
+ * again if replayed before it expires, up to 30 seconds later.
10
+ */
11
+ const SECRET_KEYS = /* @__PURE__ */ new Set([
12
+ "accesstoken",
13
+ "clientsecret",
14
+ "idtoken",
15
+ "password",
16
+ "pin",
17
+ "privatekey",
18
+ "refreshtoken",
19
+ "routablemessage",
20
+ "token",
21
+ "tokens"
22
+ ]);
23
+ const REDACTED = "[redacted]";
24
+ function isSecretKey(key) {
25
+ return SECRET_KEYS.has(key.replace(/[_-]/g, "").toLowerCase());
26
+ }
27
+ /** `JSON.stringify` for a log line, with secret values replaced at any depth. */
28
+ function redactedJson(value) {
29
+ return JSON.stringify(value, (key, val) => isSecretKey(key) ? REDACTED : val);
30
+ }
31
+ /** A query string for a log line, with secret values replaced. */
32
+ function redactedQuery(query) {
33
+ const redacted = new URLSearchParams(query);
34
+ for (const key of new Set(query.keys())) if (isSecretKey(key)) redacted.set(key, REDACTED);
35
+ return redacted.toString();
36
+ }
37
+ /**
38
+ * A `TeslaFleetError`'s message is its class's, with per-instance detail in
39
+ * `data`, so it is safe to log. Any other message, or thrown value, comes from
40
+ * outside (a token function, a transport) and may hold a secret, so only its
41
+ * type is logged.
42
+ */
43
+ function errorText(error) {
44
+ if (error instanceof require_exceptions.TeslaFleetError) return `${error.name}: ${error.message}`;
45
+ return error instanceof Error ? error.name : typeof error;
46
+ }
47
+ /**
48
+ * Log the outcome of a completed request: a command's `result` and `reason`
49
+ * (inside `response`, or at the top level as Tessie replies) when present,
50
+ * otherwise plain success. Mirrors Python's `_log_request_result`.
51
+ */
52
+ function logRequestResult(log, command, transport, data) {
53
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
54
+ log(`command=${command} transport=${transport} result=success`);
55
+ return;
56
+ }
57
+ const response = data.response;
58
+ let result;
59
+ if (response !== null && typeof response === "object" && "result" in response) result = response;
60
+ else if ("result" in data) result = data;
61
+ else {
62
+ log(`command=${command} transport=${transport} result=success`);
63
+ return;
64
+ }
65
+ log(`command=${command} transport=${transport} result=${result.result} reason=${result.reason ?? ""}`);
66
+ }
67
+ /** Log a failed request or command. The error class name carries the failure kind. */
68
+ function logRequestError(log, command, transport, error) {
69
+ log(`command=${command} transport=${transport} result=error error=${errorText(error)}`);
70
+ }
71
+ exports.errorText = errorText;
72
+ exports.logRequestError = logRequestError;
73
+ exports.logRequestResult = logRequestResult;
74
+ exports.redactedJson = redactedJson;
75
+ exports.redactedQuery = redactedQuery;
package/dist/log.d.cts ADDED
@@ -0,0 +1,4 @@
1
+ /** Receives the library's debug lines. Tokens, PINs and passcodes are never logged. */
2
+ export type Logger = {
3
+ debug(message: string): void;
4
+ };
package/dist/log.d.mts ADDED
@@ -0,0 +1,4 @@
1
+ /** Receives the library's debug lines. Tokens, PINs and passcodes are never logged. */
2
+ export type Logger = {
3
+ debug(message: string): void;
4
+ };
package/dist/log.mjs ADDED
@@ -0,0 +1,71 @@
1
+ import { TeslaFleetError } from "./exceptions.mjs";
2
+ // src/log.ts
3
+ /**
4
+ * Parameter and body keys whose values never reach a log, lower-cased with
5
+ * separators removed. `migrate_to_oauth` sends and receives tokens through
6
+ * `_request`; PINs travel as `pin` or `password` body keys (Fleet, Teslemetry)
7
+ * or query parameters (Tessie); `trigger_homelink` and the telemetry JWS send
8
+ * `token`; a vehicle reply lists `tokens`; a signed `routable_message` executes
9
+ * again if replayed before it expires, up to 30 seconds later.
10
+ */
11
+ const SECRET_KEYS = /* @__PURE__ */ new Set([
12
+ "accesstoken",
13
+ "clientsecret",
14
+ "idtoken",
15
+ "password",
16
+ "pin",
17
+ "privatekey",
18
+ "refreshtoken",
19
+ "routablemessage",
20
+ "token",
21
+ "tokens"
22
+ ]);
23
+ const REDACTED = "[redacted]";
24
+ function isSecretKey(key) {
25
+ return SECRET_KEYS.has(key.replace(/[_-]/g, "").toLowerCase());
26
+ }
27
+ /** `JSON.stringify` for a log line, with secret values replaced at any depth. */
28
+ function redactedJson(value) {
29
+ return JSON.stringify(value, (key, val) => isSecretKey(key) ? REDACTED : val);
30
+ }
31
+ /** A query string for a log line, with secret values replaced. */
32
+ function redactedQuery(query) {
33
+ const redacted = new URLSearchParams(query);
34
+ for (const key of new Set(query.keys())) if (isSecretKey(key)) redacted.set(key, REDACTED);
35
+ return redacted.toString();
36
+ }
37
+ /**
38
+ * A `TeslaFleetError`'s message is its class's, with per-instance detail in
39
+ * `data`, so it is safe to log. Any other message, or thrown value, comes from
40
+ * outside (a token function, a transport) and may hold a secret, so only its
41
+ * type is logged.
42
+ */
43
+ function errorText(error) {
44
+ if (error instanceof TeslaFleetError) return `${error.name}: ${error.message}`;
45
+ return error instanceof Error ? error.name : typeof error;
46
+ }
47
+ /**
48
+ * Log the outcome of a completed request: a command's `result` and `reason`
49
+ * (inside `response`, or at the top level as Tessie replies) when present,
50
+ * otherwise plain success. Mirrors Python's `_log_request_result`.
51
+ */
52
+ function logRequestResult(log, command, transport, data) {
53
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
54
+ log(`command=${command} transport=${transport} result=success`);
55
+ return;
56
+ }
57
+ const response = data.response;
58
+ let result;
59
+ if (response !== null && typeof response === "object" && "result" in response) result = response;
60
+ else if ("result" in data) result = data;
61
+ else {
62
+ log(`command=${command} transport=${transport} result=success`);
63
+ return;
64
+ }
65
+ log(`command=${command} transport=${transport} result=${result.result} reason=${result.reason ?? ""}`);
66
+ }
67
+ /** Log a failed request or command. The error class name carries the failure kind. */
68
+ function logRequestError(log, command, transport, error) {
69
+ log(`command=${command} transport=${transport} result=error error=${errorText(error)}`);
70
+ }
71
+ export { errorText, logRequestError, logRequestResult, redactedJson, redactedQuery };
package/dist/oauth.cjs ADDED
@@ -0,0 +1,143 @@
1
+ const require_const = require("./const.cjs");
2
+ const require_exceptions = require("./exceptions.cjs");
3
+ const require_teslafleetapi = require("./teslafleetapi.cjs");
4
+ // src/oauth.ts
5
+ function nowSeconds() {
6
+ return Date.now() / 1e3;
7
+ }
8
+ /** The fields every token reply needs; a reply without them must not replace a working token. */
9
+ function tokenFrom(data) {
10
+ if (typeof data?.access_token !== "string" || typeof data.expires_in !== "number") throw new require_exceptions.InvalidResponse();
11
+ return {
12
+ access_token: data.access_token,
13
+ expires: Math.floor(nowSeconds()) + data.expires_in
14
+ };
15
+ }
16
+ /**
17
+ * Tesla Fleet API client that signs a user in with OAuth and keeps its access
18
+ * token fresh. `expires` is in epoch seconds, as in Python, so a stored
19
+ * token works with either library.
20
+ */
21
+ var TeslaFleetOAuth = class extends require_teslafleetapi.default {
22
+ client_id;
23
+ redirect_uri;
24
+ refresh_token;
25
+ /** Expiry of the access token, in epoch seconds. */
26
+ expires;
27
+ #client_secret;
28
+ #refreshing = null;
29
+ constructor(options) {
30
+ if (!require_const.is_valid_region(options.region)) throw new Error("Invalid region");
31
+ const { region, client_id, client_secret, redirect_uri, access_token, refresh_token, expires, ...rest } = options;
32
+ super({
33
+ ...rest,
34
+ region,
35
+ accessToken: access_token
36
+ });
37
+ this.client_id = client_id;
38
+ this.#client_secret = client_secret ?? null;
39
+ this.redirect_uri = redirect_uri ?? null;
40
+ this.refresh_token = refresh_token ?? null;
41
+ this.expires = expires ?? 0;
42
+ }
43
+ /**
44
+ * The URL to send the user to, built exactly as Python builds it: values are
45
+ * not URL-encoded.
46
+ * @param scopes Scopes to request.
47
+ * @param state Value Tesla echoes back on the redirect.
48
+ */
49
+ get_login_url(scopes, state = "login") {
50
+ if (this.redirect_uri == null) throw new Error("Redirect URI is missing");
51
+ return `https://${this.region === "cn" ? "auth.tesla.cn" : "auth.tesla.com"}/oauth2/v3/authorize?response_type=code&client_id=${this.client_id}&redirect_uri=${this.redirect_uri}&scope=${scopes.join("+")}&state=${state}`;
52
+ }
53
+ /**
54
+ * Exchange an authorization code for tokens. A code prefixed with a region
55
+ * (e.g. `NA_...`) also selects that region's server.
56
+ * @param code The code from the login redirect.
57
+ */
58
+ async get_refresh_token(code) {
59
+ if (this.#client_secret == null) throw new Error("Client secret is missing");
60
+ if (this.redirect_uri == null) throw new Error("Redirect URI is missing");
61
+ this.server ??= require_const.SERVERS[this.region ?? "na"];
62
+ const data = await this.#token({
63
+ grant_type: "authorization_code",
64
+ client_id: this.client_id,
65
+ client_secret: this.#client_secret,
66
+ code,
67
+ audience: this.server,
68
+ redirect_uri: this.redirect_uri
69
+ });
70
+ const { access_token, expires } = tokenFrom(data);
71
+ this.refresh_token = data.refresh_token ?? null;
72
+ this.accessToken = access_token;
73
+ this.expires = expires;
74
+ const region = code.split("_")[0].toLowerCase();
75
+ if (require_const.is_valid_region(region)) this.server = require_const.SERVERS[region];
76
+ }
77
+ /**
78
+ * The current access token, refreshed first when `expires` has passed.
79
+ * @throws MissingToken when there is no token and none can be refreshed.
80
+ */
81
+ async access_token() {
82
+ if (this.expires < nowSeconds()) await this.refresh_access_token();
83
+ if (this.accessToken && typeof this.accessToken === "string") return this.accessToken;
84
+ throw new require_exceptions.MissingToken("Access token is missing");
85
+ }
86
+ /**
87
+ * Refresh the access token if it is missing or expired.
88
+ * @returns The refreshed token to persist, or `null` when none was needed.
89
+ */
90
+ async check_access_token() {
91
+ if (this.accessToken && this.expires > nowSeconds()) return null;
92
+ return this.refresh_access_token();
93
+ }
94
+ /**
95
+ * Refresh the access token. Tesla rotates the refresh token, so persist what
96
+ * this returns.
97
+ * @throws MissingToken without a refresh token, or the `TeslaFleetError`
98
+ * matching the failure, e.g. `LoginRequired`.
99
+ */
100
+ refresh_access_token() {
101
+ this.#refreshing ??= this.#refresh().finally(() => {
102
+ this.#refreshing = null;
103
+ });
104
+ return this.#refreshing;
105
+ }
106
+ async #refresh() {
107
+ if (!this.refresh_token) throw new require_exceptions.MissingToken("Refresh token is missing");
108
+ const data = await this.#token({
109
+ grant_type: "refresh_token",
110
+ client_id: this.client_id,
111
+ refresh_token: this.refresh_token
112
+ });
113
+ const { access_token, expires } = tokenFrom(data);
114
+ if (typeof data.refresh_token !== "string") throw new require_exceptions.InvalidResponse();
115
+ this.accessToken = access_token;
116
+ this.refresh_token = data.refresh_token;
117
+ this.expires = expires;
118
+ return {
119
+ refresh_token: data.refresh_token,
120
+ expires
121
+ };
122
+ }
123
+ /** POST a form to the region's token endpoint. Bypasses `_request`, which would log the reply. */
124
+ async #token(form) {
125
+ try {
126
+ const resp = await this._fetch(require_const.TOKEN_URLS[this.region ?? "na"], {
127
+ method: "POST",
128
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
129
+ body: new URLSearchParams(form),
130
+ signal: this.timeout != null ? AbortSignal.timeout(this.timeout) : null
131
+ });
132
+ return await this._response(resp);
133
+ } catch (error) {
134
+ if (error instanceof require_exceptions.TeslaFleetError) throw error;
135
+ throw new require_exceptions.TeslaFleetConnectionError(null, null, { cause: error });
136
+ }
137
+ }
138
+ async _request(method, path, params = null, json = null) {
139
+ await this.check_access_token();
140
+ return super._request(method, path, params, json);
141
+ }
142
+ };
143
+ exports.default = TeslaFleetOAuth;
@@ -0,0 +1,69 @@
1
+ import { Method, Region, Scope } from "./const.cjs";
2
+ import { Logger } from "./log.cjs";
3
+ import TeslaFleetApi from "./teslafleetapi.cjs";
4
+ export type TeslaFleetOAuthOptions = {
5
+ region: Region;
6
+ client_id: string;
7
+ /** Needed only to exchange an authorization code. */
8
+ client_secret?: string;
9
+ redirect_uri?: string;
10
+ access_token?: string;
11
+ refresh_token?: string;
12
+ /** Expiry of `access_token`, in epoch seconds as `TeslaFleetOAuth.expires`. */
13
+ expires?: number;
14
+ debug?: boolean;
15
+ logger?: Logger;
16
+ fetch?: typeof fetch;
17
+ timeout?: number;
18
+ };
19
+ /** What a refresh hands back for the caller to persist. */
20
+ export type RefreshedToken = {
21
+ refresh_token: string;
22
+ /** Epoch seconds. */
23
+ expires: number;
24
+ };
25
+ /**
26
+ * Tesla Fleet API client that signs a user in with OAuth and keeps its access
27
+ * token fresh. `expires` is in epoch seconds, as in Python, so a stored
28
+ * token works with either library.
29
+ */
30
+ export default class TeslaFleetOAuth extends TeslaFleetApi {
31
+ #private;
32
+ readonly client_id: string;
33
+ redirect_uri: string | null;
34
+ refresh_token: string | null;
35
+ /** Expiry of the access token, in epoch seconds. */
36
+ expires: number;
37
+ constructor(options: TeslaFleetOAuthOptions);
38
+ /**
39
+ * The URL to send the user to, built exactly as Python builds it: values are
40
+ * not URL-encoded.
41
+ * @param scopes Scopes to request.
42
+ * @param state Value Tesla echoes back on the redirect.
43
+ */
44
+ get_login_url(scopes: Scope[], state?: string): string;
45
+ /**
46
+ * Exchange an authorization code for tokens. A code prefixed with a region
47
+ * (e.g. `NA_...`) also selects that region's server.
48
+ * @param code The code from the login redirect.
49
+ */
50
+ get_refresh_token(code: string): Promise<void>;
51
+ /**
52
+ * The current access token, refreshed first when `expires` has passed.
53
+ * @throws MissingToken when there is no token and none can be refreshed.
54
+ */
55
+ access_token(): Promise<string>;
56
+ /**
57
+ * Refresh the access token if it is missing or expired.
58
+ * @returns The refreshed token to persist, or `null` when none was needed.
59
+ */
60
+ check_access_token(): Promise<RefreshedToken | null>;
61
+ /**
62
+ * Refresh the access token. Tesla rotates the refresh token, so persist what
63
+ * this returns.
64
+ * @throws MissingToken without a refresh token, or the `TeslaFleetError`
65
+ * matching the failure, e.g. `LoginRequired`.
66
+ */
67
+ refresh_access_token(): Promise<RefreshedToken>;
68
+ _request(method: Method, path: string, params?: Record<string, any> | null, json?: Record<string, any> | null): Promise<any>;
69
+ }
@@ -0,0 +1,69 @@
1
+ import { Method, Region, Scope } from "./const.mjs";
2
+ import { Logger } from "./log.mjs";
3
+ import TeslaFleetApi from "./teslafleetapi.mjs";
4
+ export type TeslaFleetOAuthOptions = {
5
+ region: Region;
6
+ client_id: string;
7
+ /** Needed only to exchange an authorization code. */
8
+ client_secret?: string;
9
+ redirect_uri?: string;
10
+ access_token?: string;
11
+ refresh_token?: string;
12
+ /** Expiry of `access_token`, in epoch seconds as `TeslaFleetOAuth.expires`. */
13
+ expires?: number;
14
+ debug?: boolean;
15
+ logger?: Logger;
16
+ fetch?: typeof fetch;
17
+ timeout?: number;
18
+ };
19
+ /** What a refresh hands back for the caller to persist. */
20
+ export type RefreshedToken = {
21
+ refresh_token: string;
22
+ /** Epoch seconds. */
23
+ expires: number;
24
+ };
25
+ /**
26
+ * Tesla Fleet API client that signs a user in with OAuth and keeps its access
27
+ * token fresh. `expires` is in epoch seconds, as in Python, so a stored
28
+ * token works with either library.
29
+ */
30
+ export default class TeslaFleetOAuth extends TeslaFleetApi {
31
+ #private;
32
+ readonly client_id: string;
33
+ redirect_uri: string | null;
34
+ refresh_token: string | null;
35
+ /** Expiry of the access token, in epoch seconds. */
36
+ expires: number;
37
+ constructor(options: TeslaFleetOAuthOptions);
38
+ /**
39
+ * The URL to send the user to, built exactly as Python builds it: values are
40
+ * not URL-encoded.
41
+ * @param scopes Scopes to request.
42
+ * @param state Value Tesla echoes back on the redirect.
43
+ */
44
+ get_login_url(scopes: Scope[], state?: string): string;
45
+ /**
46
+ * Exchange an authorization code for tokens. A code prefixed with a region
47
+ * (e.g. `NA_...`) also selects that region's server.
48
+ * @param code The code from the login redirect.
49
+ */
50
+ get_refresh_token(code: string): Promise<void>;
51
+ /**
52
+ * The current access token, refreshed first when `expires` has passed.
53
+ * @throws MissingToken when there is no token and none can be refreshed.
54
+ */
55
+ access_token(): Promise<string>;
56
+ /**
57
+ * Refresh the access token if it is missing or expired.
58
+ * @returns The refreshed token to persist, or `null` when none was needed.
59
+ */
60
+ check_access_token(): Promise<RefreshedToken | null>;
61
+ /**
62
+ * Refresh the access token. Tesla rotates the refresh token, so persist what
63
+ * this returns.
64
+ * @throws MissingToken without a refresh token, or the `TeslaFleetError`
65
+ * matching the failure, e.g. `LoginRequired`.
66
+ */
67
+ refresh_access_token(): Promise<RefreshedToken>;
68
+ _request(method: Method, path: string, params?: Record<string, any> | null, json?: Record<string, any> | null): Promise<any>;
69
+ }
package/dist/oauth.mjs ADDED
@@ -0,0 +1,143 @@
1
+ import { SERVERS, TOKEN_URLS, is_valid_region } from "./const.mjs";
2
+ import { InvalidResponse, MissingToken, TeslaFleetConnectionError, TeslaFleetError } from "./exceptions.mjs";
3
+ import TeslaFleetApi from "./teslafleetapi.mjs";
4
+ // src/oauth.ts
5
+ function nowSeconds() {
6
+ return Date.now() / 1e3;
7
+ }
8
+ /** The fields every token reply needs; a reply without them must not replace a working token. */
9
+ function tokenFrom(data) {
10
+ if (typeof data?.access_token !== "string" || typeof data.expires_in !== "number") throw new InvalidResponse();
11
+ return {
12
+ access_token: data.access_token,
13
+ expires: Math.floor(nowSeconds()) + data.expires_in
14
+ };
15
+ }
16
+ /**
17
+ * Tesla Fleet API client that signs a user in with OAuth and keeps its access
18
+ * token fresh. `expires` is in epoch seconds, as in Python, so a stored
19
+ * token works with either library.
20
+ */
21
+ var TeslaFleetOAuth = class extends TeslaFleetApi {
22
+ client_id;
23
+ redirect_uri;
24
+ refresh_token;
25
+ /** Expiry of the access token, in epoch seconds. */
26
+ expires;
27
+ #client_secret;
28
+ #refreshing = null;
29
+ constructor(options) {
30
+ if (!is_valid_region(options.region)) throw new Error("Invalid region");
31
+ const { region, client_id, client_secret, redirect_uri, access_token, refresh_token, expires, ...rest } = options;
32
+ super({
33
+ ...rest,
34
+ region,
35
+ accessToken: access_token
36
+ });
37
+ this.client_id = client_id;
38
+ this.#client_secret = client_secret ?? null;
39
+ this.redirect_uri = redirect_uri ?? null;
40
+ this.refresh_token = refresh_token ?? null;
41
+ this.expires = expires ?? 0;
42
+ }
43
+ /**
44
+ * The URL to send the user to, built exactly as Python builds it: values are
45
+ * not URL-encoded.
46
+ * @param scopes Scopes to request.
47
+ * @param state Value Tesla echoes back on the redirect.
48
+ */
49
+ get_login_url(scopes, state = "login") {
50
+ if (this.redirect_uri == null) throw new Error("Redirect URI is missing");
51
+ return `https://${this.region === "cn" ? "auth.tesla.cn" : "auth.tesla.com"}/oauth2/v3/authorize?response_type=code&client_id=${this.client_id}&redirect_uri=${this.redirect_uri}&scope=${scopes.join("+")}&state=${state}`;
52
+ }
53
+ /**
54
+ * Exchange an authorization code for tokens. A code prefixed with a region
55
+ * (e.g. `NA_...`) also selects that region's server.
56
+ * @param code The code from the login redirect.
57
+ */
58
+ async get_refresh_token(code) {
59
+ if (this.#client_secret == null) throw new Error("Client secret is missing");
60
+ if (this.redirect_uri == null) throw new Error("Redirect URI is missing");
61
+ this.server ??= SERVERS[this.region ?? "na"];
62
+ const data = await this.#token({
63
+ grant_type: "authorization_code",
64
+ client_id: this.client_id,
65
+ client_secret: this.#client_secret,
66
+ code,
67
+ audience: this.server,
68
+ redirect_uri: this.redirect_uri
69
+ });
70
+ const { access_token, expires } = tokenFrom(data);
71
+ this.refresh_token = data.refresh_token ?? null;
72
+ this.accessToken = access_token;
73
+ this.expires = expires;
74
+ const region = code.split("_")[0].toLowerCase();
75
+ if (is_valid_region(region)) this.server = SERVERS[region];
76
+ }
77
+ /**
78
+ * The current access token, refreshed first when `expires` has passed.
79
+ * @throws MissingToken when there is no token and none can be refreshed.
80
+ */
81
+ async access_token() {
82
+ if (this.expires < nowSeconds()) await this.refresh_access_token();
83
+ if (this.accessToken && typeof this.accessToken === "string") return this.accessToken;
84
+ throw new MissingToken("Access token is missing");
85
+ }
86
+ /**
87
+ * Refresh the access token if it is missing or expired.
88
+ * @returns The refreshed token to persist, or `null` when none was needed.
89
+ */
90
+ async check_access_token() {
91
+ if (this.accessToken && this.expires > nowSeconds()) return null;
92
+ return this.refresh_access_token();
93
+ }
94
+ /**
95
+ * Refresh the access token. Tesla rotates the refresh token, so persist what
96
+ * this returns.
97
+ * @throws MissingToken without a refresh token, or the `TeslaFleetError`
98
+ * matching the failure, e.g. `LoginRequired`.
99
+ */
100
+ refresh_access_token() {
101
+ this.#refreshing ??= this.#refresh().finally(() => {
102
+ this.#refreshing = null;
103
+ });
104
+ return this.#refreshing;
105
+ }
106
+ async #refresh() {
107
+ if (!this.refresh_token) throw new MissingToken("Refresh token is missing");
108
+ const data = await this.#token({
109
+ grant_type: "refresh_token",
110
+ client_id: this.client_id,
111
+ refresh_token: this.refresh_token
112
+ });
113
+ const { access_token, expires } = tokenFrom(data);
114
+ if (typeof data.refresh_token !== "string") throw new InvalidResponse();
115
+ this.accessToken = access_token;
116
+ this.refresh_token = data.refresh_token;
117
+ this.expires = expires;
118
+ return {
119
+ refresh_token: data.refresh_token,
120
+ expires
121
+ };
122
+ }
123
+ /** POST a form to the region's token endpoint. Bypasses `_request`, which would log the reply. */
124
+ async #token(form) {
125
+ try {
126
+ const resp = await this._fetch(TOKEN_URLS[this.region ?? "na"], {
127
+ method: "POST",
128
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
129
+ body: new URLSearchParams(form),
130
+ signal: this.timeout != null ? AbortSignal.timeout(this.timeout) : null
131
+ });
132
+ return await this._response(resp);
133
+ } catch (error) {
134
+ if (error instanceof TeslaFleetError) throw error;
135
+ throw new TeslaFleetConnectionError(null, null, { cause: error });
136
+ }
137
+ }
138
+ async _request(method, path, params = null, json = null) {
139
+ await this.check_access_token();
140
+ return super._request(method, path, params, json);
141
+ }
142
+ };
143
+ export { TeslaFleetOAuth as default };