├── .github
└── workflows
│ └── swift.yml
├── .gitignore
├── .hound.yml
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ └── xcschemes
│ ├── TeslaSwift-Combine.xcscheme
│ ├── TeslaSwift-Package.xcscheme
│ └── TeslaSwift.xcscheme
├── .travis.yml
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
├── Extensions
│ ├── Combine
│ │ └── TeslaSwift+Combine.swift
│ ├── Streaming
│ │ ├── StreamControl.swift
│ │ ├── StreamEvent.swift
│ │ └── TeslaStreaming.swift
│ └── StreamingCombine
│ │ └── TeslaSwift+StreamingCombine.swift
└── TeslaSwift
│ ├── Model
│ ├── Authentication.swift
│ ├── BatteryData.swift
│ ├── BatteryPowerHistory.swift
│ ├── BatteryStatus.swift
│ ├── ChargeAmpsCommandOptions.swift
│ ├── ChargeHistory.swift
│ ├── ChargeLimitPercentageCommandOptions.swift
│ ├── ChargeState.swift
│ ├── ClimateState.swift
│ ├── Codable+JSONString.swift
│ ├── CommandResponse.swift
│ ├── Distance.swift
│ ├── DriveState.swift
│ ├── EnergySite.swift
│ ├── EnergySiteHistory.swift
│ ├── EnergySiteInfo.swift
│ ├── EnergySiteLiveStatus.swift
│ ├── EnergySiteStatus.swift
│ ├── ErrorMessage.swift
│ ├── GenericResponse.swift
│ ├── GuiSettings.swift
│ ├── HomeLinkCommandOptions.swift
│ ├── MaxDefrostCommandOptions.swift
│ ├── Me.swift
│ ├── NearbyChargingSites.swift
│ ├── OpenTrunkOptions.swift
│ ├── Partner.swift
│ ├── Product.swift
│ ├── Region.swift
│ ├── RemoteSeatHeaterRequestOptions.swift
│ ├── RemoteStartDriveCommandOptions.swift
│ ├── RemoteSteeringWheelHeaterRequestOptions.swift
│ ├── ScheduledChargingCommandOptions.swift
│ ├── ScheduledDepartureCommandOptions.swift
│ ├── SentryModeCommandOptions.swift
│ ├── SetSunRoofCommandOptions.swift
│ ├── SetTemperatureCommandOptions.swift
│ ├── ShareToVehicleOptions.swift
│ ├── SoftwareUpdate.swift
│ ├── Speed.swift
│ ├── SpeedLimitOptions.swift
│ ├── ValetCommandOptions.swift
│ ├── Vehicle.swift
│ ├── VehicleConfig.swift
│ ├── VehicleExtended.swift
│ ├── VehicleState.swift
│ └── WindowControlCommandOptions.swift
│ ├── TeslaEndpoint.swift
│ ├── TeslaSwift.swift
│ └── VehicleCommands.swift
├── TeslaSwiftDemo
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── first.imageset
│ │ ├── Contents.json
│ │ └── first.pdf
│ └── second.imageset
│ │ ├── Contents.json
│ │ └── second.pdf
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── FirstViewController.swift
├── Info.plist
├── LoginViewController.swift
├── ProductViewController.swift
├── SecondViewController.swift
├── StreamViewController.swift
├── TabController.swift
├── TeslaSwift.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── TeslaSwiftDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── TeslaSwift.xcscheme
├── VehicleViewController.swift
└── ViewController+TeslaSwift.swift
└── TeslaSwiftTests
├── AllStates.json
├── Authentication.json
├── AuthenticationFailed.json
├── ChargeLimitMaxRange.json
├── ChargeLimitPercentage.json
├── ChargeLimitStandard.json
├── ChargeState.json
├── ClimateSettings.json
├── DriveState.json
├── FlashLights.json
├── GuiSettings.json
├── HonkHorn.json
├── Info.plist
├── LockDoors.json
├── MobileAccess.json
├── NearbyChargingSites.json
├── OpenChargeDoor.json
├── OpenTrunk.json
├── ResetValetPin.json
├── SetSpeedLimit.json
├── SetSunRoof.json
├── SetTemperature.json
├── SetValetMode.json
├── SpeedLimitPin.json
├── StartAutoConditioning.json
├── StartCharging.json
├── StartVehicle.json
├── StopAutoConditioning.json
├── StopCharging.json
├── StreamingData.txt
├── TeslaSwiftTests.swift
├── UnlockDoors.json
├── VehicleConfig.json
├── VehicleState.json
├── Vehicles.json
└── WakeUp.json
/.github/workflows/swift.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Swift project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
3 |
4 | name: Swift
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: macos-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Build
20 | run: swift build -v
21 | - name: Run tests
22 | run: swift test -v
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 | .DS_Store
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | Packages/
39 | .build/
40 |
41 | # CocoaPods
42 | #
43 | # We recommend against adding the Pods directory to your .gitignore. However
44 | # you should judge for yourself, the pros and cons are mentioned at:
45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
46 | #
47 | Pods/
48 |
49 | # Carthage
50 | #
51 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
52 | # Carthage/Checkouts
53 |
54 | Carthage/Build
55 |
56 | # fastlane
57 | #
58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
59 | # screenshots whenever they are needed.
60 | # For more information about the recommended setup visit:
61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
62 |
63 | fastlane/report.xml
64 | fastlane/screenshots
65 |
--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | swiftlint:
2 | config_file: .swiftlint.yml
3 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules: # rule identifiers to exclude from running
2 | - force_cast
3 | - operator_whitespace
4 | - cyclomatic_complexity
5 | - variable_name
6 | - switch_case_alignment
7 | - trailing_whitespace
8 | - no_space_in_method_call
9 | - nesting
10 | - function_body_length
11 | - type_body_length
12 | - file_length
13 | included: # paths to include during linting. `--path` is ignored if present.
14 | - Sources
15 | line_length: 600
16 | variable_name:
17 | min_length: # only min_length
18 | error: 3 # only error
19 | excluded: # excluded via string array
20 | - on
21 | - id
22 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/TeslaSwift-Combine.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/TeslaSwift-Package.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
57 |
63 |
64 |
65 |
71 |
77 |
78 |
79 |
85 |
91 |
92 |
93 |
99 |
105 |
106 |
107 |
113 |
119 |
120 |
121 |
127 |
133 |
134 |
135 |
141 |
147 |
148 |
149 |
155 |
161 |
162 |
163 |
164 |
165 |
170 |
171 |
172 |
173 |
183 |
184 |
190 |
191 |
197 |
198 |
199 |
200 |
202 |
203 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/TeslaSwift.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | osx_image: xcode12.2
3 | xcode_workspace: TeslaSwiftDemo/TeslaSwift.xcworkspace
4 | xcode_scheme: TeslaSwift
5 | podfile: TeslaSwiftDemo/Podfile
6 | notifications:
7 | email: false
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 João Nunes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "PromiseKit",
6 | "repositoryURL": "https://github.com/mxcl/PromiseKit",
7 | "state": {
8 | "branch": null,
9 | "revision": "2fdb8c64a85e2081388532dbc61eb348a0793b1c",
10 | "version": "6.10.0"
11 | }
12 | },
13 | {
14 | "package": "RxSwift",
15 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "97e14358a3e6564de8a6289362385a92d85936e1",
19 | "version": "6.0.0-rc.2"
20 | }
21 | },
22 | {
23 | "package": "Starscream",
24 | "repositoryURL": "https://github.com/daltoniam/Starscream.git",
25 | "state": {
26 | "branch": null,
27 | "revision": "df8d82047f6654d8e4b655d1b1525c64e1059d21",
28 | "version": "4.0.4"
29 | }
30 | }
31 | ]
32 | },
33 | "version": 1
34 | }
35 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.10
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "TeslaSwift",
6 | platforms: [
7 | .macOS(.v12), .iOS(.v15), .watchOS(.v8), .tvOS(.v15)
8 | ],
9 | products: [
10 | .library(name: "TeslaSwift", targets: ["TeslaSwift"]),
11 | .library(name: "TeslaSwiftStreaming", targets: ["TeslaSwiftStreaming"]),
12 | .library(name: "TeslaSwiftCombine", targets: ["TeslaSwiftCombine"]),
13 | .library(name: "TeslaSwiftStreamingCombine", targets: ["TeslaSwiftStreamingCombine"])
14 | ],
15 | dependencies: [
16 | .package(url: "https://github.com/daltoniam/Starscream.git", from: "4.0.6"),
17 | //.package(url: "https://github.com/AliSoftware/OHHTTPStubs.git", Package.Dependency.Requirement.branch("feature/spm-support"))
18 | ],
19 | targets: [
20 | .target(name: "TeslaSwift"),
21 | .target(name: "TeslaSwiftStreaming", dependencies: ["TeslaSwift", "Starscream"], path: "Sources/Extensions/Streaming"),
22 | .target(name: "TeslaSwiftCombine", dependencies: ["TeslaSwift"], path: "Sources/Extensions/Combine"),
23 | .target(name: "TeslaSwiftStreamingCombine", dependencies: ["TeslaSwiftStreaming", "TeslaSwiftCombine"], path: "Sources/Extensions/StreamingCombine"),
24 | //.testTarget(name: "TeslaSwiftTests", dependencies: ["TeslaSwiftPMK", "PromiseKit", "OHHTTPStubsSwift"], path: "TeslaSwiftTests")
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/Sources/Extensions/Combine/TeslaSwift+Combine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TeslaSwift+Combine.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 08/07/2019.
6 | // Copyright © 2019 Joao Nunes. All rights reserved.
7 | //
8 |
9 | #if swift(>=5.1)
10 | import Combine
11 | import TeslaSwift
12 |
13 | extension TeslaSwift {
14 | public func revokeToken() -> Future {
15 | Future { promise in
16 | Task {
17 | do {
18 | let result = try await self.revokeToken()
19 | promise(.success(result))
20 | } catch let error {
21 | promise(.failure(error))
22 | }
23 | }
24 | }
25 | }
26 |
27 | public func getVehicles() -> Future<[Vehicle], Error> {
28 | Future { promise in
29 | Task {
30 | do {
31 | let result = try await self.getVehicles()
32 | promise(.success(result))
33 | } catch let error {
34 | promise(.failure(error))
35 | }
36 | }
37 | }
38 | }
39 |
40 | public func getVehicle(_ vehicleID: VehicleId) -> Future {
41 | Future { promise in
42 | Task {
43 | do {
44 | let result = try await self.getVehicle(vehicleID)
45 | promise(.success(result))
46 | } catch let error {
47 | promise(.failure(error))
48 | }
49 | }
50 | }
51 | }
52 |
53 | public func getVehicle(_ vehicle: Vehicle) -> Future {
54 | Future { promise in
55 | Task {
56 | do {
57 | let result = try await self.getVehicle(vehicle)
58 | promise(.success(result))
59 | } catch let error {
60 | promise(.failure(error))
61 | }
62 | }
63 | }
64 | }
65 |
66 | public func getAllData(_ vehicle: Vehicle, endpoints: [AllStatesEndpoints] = AllStatesEndpoints.allWithLocation) -> Future {
67 | Future { promise in
68 | Task {
69 | do {
70 | let result = try await self.getAllData(vehicle, endpoints: endpoints)
71 | promise(.success(result))
72 | } catch let error {
73 | promise(.failure(error))
74 | }
75 | }
76 | }
77 | }
78 |
79 | public func getVehicleMobileAccessState(_ vehicle: Vehicle) -> Future {
80 | Future { promise in
81 | Task {
82 | do {
83 | let result = try await self.getVehicleMobileAccessState(vehicle)
84 | promise(.success(result))
85 | } catch let error {
86 | promise(.failure(error))
87 | }
88 | }
89 | }
90 | }
91 |
92 | public func wakeUp(_ vehicle: Vehicle) -> Future {
93 | Future { promise in
94 | Task {
95 | do {
96 | let result = try await self.wakeUp(vehicle)
97 | promise(.success(result))
98 | } catch let error {
99 | promise(.failure(error))
100 | }
101 | }
102 | }
103 | }
104 |
105 | public func sendCommandToVehicle(_ vehicle: Vehicle, command: VehicleCommand) -> Future {
106 | Future { promise in
107 | Task {
108 | do {
109 | let result = try await self.sendCommandToVehicle(vehicle, command: command)
110 | promise(.success(result))
111 | } catch let error {
112 | promise(.failure(error))
113 | }
114 | }
115 | }
116 | }
117 |
118 | public func getNearbyChargingSite(vehicle: Vehicle) -> Future {
119 | Future { promise in
120 | Task {
121 | do {
122 | let result = try await self.getNearbyChargingSites(vehicle)
123 | promise(.success(result))
124 | } catch let error {
125 | promise(.failure(error))
126 | }
127 | }
128 | }
129 | }
130 |
131 | public func getProducts() -> Future<[Product], Error> {
132 | Future { promise in
133 | Task {
134 | do {
135 | let result = try await self.getProducts()
136 | promise(.success(result))
137 | } catch let error {
138 | promise(.failure(error))
139 | }
140 | }
141 | }
142 | }
143 |
144 | public func getEnergySiteStatus(siteID: SiteId) -> Future {
145 | Future { promise in
146 | Task {
147 | do {
148 | let result = try await self.getEnergySiteStatus(siteID: siteID)
149 | promise(.success(result))
150 | } catch let error {
151 | promise(.failure(error))
152 | }
153 | }
154 | }
155 | }
156 |
157 | public func getEnergySiteLiveStatus(siteID: SiteId) -> Future {
158 | Future { promise in
159 | Task {
160 | do {
161 | let result = try await self.getEnergySiteLiveStatus(siteID: siteID)
162 | promise(.success(result))
163 | } catch let error {
164 | promise(.failure(error))
165 | }
166 | }
167 | }
168 | }
169 |
170 | public func getEnergySiteInfo(siteID: SiteId) -> Future {
171 | Future { promise in
172 | Task {
173 | do {
174 | let result = try await self.getEnergySiteInfo(siteID: siteID)
175 | promise(.success(result))
176 | } catch let error {
177 | promise(.failure(error))
178 | }
179 | }
180 | }
181 | }
182 | public func getEnergySiteHistory(siteID: SiteId, period: EnergySiteHistory.Period) -> Future {
183 | Future { promise in
184 | Task {
185 | do {
186 | let result = try await self.getEnergySiteHistory(siteID: siteID, period: period)
187 | promise(.success(result))
188 | } catch let error {
189 | promise(.failure(error))
190 | }
191 | }
192 | }
193 | }
194 |
195 |
196 | public func getBatteryStatus(batteryID: BatteryId) -> Future {
197 | Future { promise in
198 | Task {
199 | do {
200 | let result = try await self.getBatteryStatus(batteryID: batteryID)
201 | promise(.success(result))
202 | } catch let error {
203 | promise(.failure(error))
204 | }
205 | }
206 | }
207 | }
208 |
209 | public func getBatteryData(batteryID: BatteryId) -> Future {
210 | Future { promise in
211 | Task {
212 | do {
213 | let result = try await self.getBatteryData(batteryID: batteryID)
214 | promise(.success(result))
215 | } catch let error {
216 | promise(.failure(error))
217 | }
218 | }
219 | }
220 | }
221 |
222 | public func getBatteryPowerHistory(batteryID: BatteryId) -> Future {
223 | Future { promise in
224 | Task {
225 | do {
226 | let result = try await self.getBatteryPowerHistory(batteryID: batteryID)
227 | promise(.success(result))
228 | } catch let error {
229 | promise(.failure(error))
230 | }
231 | }
232 | }
233 | }
234 | }
235 |
236 | #endif
237 |
--------------------------------------------------------------------------------
/Sources/Extensions/Streaming/StreamControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StreamControl.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 20/10/2019.
6 | // Copyright © 2019 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class StreamAuthentication: Encodable {
12 | var messageType: String
13 | var token: String
14 | var value = "speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power,shift_state,range,est_range,heading"
15 | var tag: String
16 |
17 | init?(type: TeslaStreamAuthenticationType, vehicleId: String) {
18 | switch type {
19 | case let .bearer(email, vehicleToken):
20 | self.messageType = "data:subscribe"
21 | if let token = "\(email):\(vehicleToken)".data(using: String.Encoding.utf8)?.base64EncodedString() {
22 | self.token = token
23 | } else {
24 | return nil
25 | }
26 | case let .oAuth(oAuthToken):
27 | self.messageType = "data:subscribe_oauth"
28 | self.token = "\(oAuthToken)"
29 | }
30 | self.tag = vehicleId
31 | }
32 |
33 | enum CodingKeys: String, CodingKey {
34 | case messageType = "msg_type"
35 | case token
36 | case value
37 | case tag
38 | }
39 | }
40 |
41 | class StreamMessage: Decodable {
42 | var messageType: String
43 | var value: String?
44 | var tag: String?
45 | var errorType: String?
46 | var connectionTimeout: Int?
47 |
48 | enum CodingKeys: String, CodingKey {
49 | case messageType = "msg_type"
50 | case value
51 | case tag
52 | case errorType = "error_type"
53 | case connectionTimeout = "connection_timeout"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/Extensions/Streaming/StreamEvent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StreamEvent.swift
3 | // TeslaSwift
4 | //
5 | // Created by Jacob Holland on 4/11/17.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 | import TeslaSwift
12 |
13 | public enum TeslaStreamingEvent: Equatable {
14 | case open
15 | case event(StreamEvent)
16 | case error(Error)
17 | case disconnected
18 |
19 | public static func == (lhs: TeslaStreamingEvent, rhs: TeslaStreamingEvent) -> Bool {
20 | switch (lhs, rhs) {
21 | case (.open, .open): return true
22 | case (.event, .event): return true // ignore the event
23 | case (.error, .error): return true // ignore the error
24 | case (.disconnected, .disconnected): return true
25 | default: return false
26 | }
27 | }
28 | }
29 |
30 | open class StreamEvent: Codable {
31 | open var timestamp: Double?
32 | open var speed: CLLocationSpeed? // mph
33 | open var speedUnit: Speed? {
34 | guard let speed = speed else { return nil }
35 | return Speed(milesPerHour: speed)
36 | }
37 | open var odometer: Distance? // miles
38 | open var soc: Int?
39 | open var elevation: Int? // feet
40 | open var estLat: CLLocationDegrees?
41 | open var estLng: CLLocationDegrees?
42 | open var power: Int? // kW
43 | open var shiftState: String?
44 | open var range: Distance? // miles
45 | open var estRange: Distance? // miles
46 | open var estHeading: CLLocationDirection?
47 | open var heading: CLLocationDirection?
48 |
49 | open var position: CLLocation? {
50 | if let latitude = estLat,
51 | let longitude = estLng,
52 | let heading = heading,
53 | let timestamp = timestamp {
54 | let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
55 | return CLLocation(coordinate: coordinate,
56 | altitude: 0.0, horizontalAccuracy: 0.0, verticalAccuracy: 0.0,
57 | course: heading,
58 | speed: speed ?? 0,
59 | timestamp: Date(timeIntervalSince1970: timestamp/1000))
60 | }
61 | return nil
62 | }
63 |
64 | init(values: String) {
65 | // timeStamp,speed,odometer,soc,elevation,est_heading,est_lat,est_lng,power,shift_state,range,est_range,heading
66 | let separatedValues = values.components(separatedBy: ",")
67 |
68 | guard separatedValues.count > 11 else { return }
69 |
70 | if let timeValue = Double(separatedValues[0]) {
71 | timestamp = timeValue
72 | }
73 | speed = CLLocationSpeed(separatedValues[1])
74 | if let value = Double(separatedValues[2]) {
75 | odometer = Distance(miles: value)
76 | }
77 | soc = Int(separatedValues[3])
78 | elevation = Int(separatedValues[4])
79 | estHeading = CLLocationDirection(separatedValues[5])
80 | estLat = CLLocationDegrees(separatedValues[6])
81 | estLng = CLLocationDegrees(separatedValues[7])
82 | power = Int(separatedValues[8])
83 | shiftState = separatedValues[9]
84 | if let value = Double(separatedValues[10]) {
85 | range = Distance(miles: value)
86 | }
87 | if let value = Double(separatedValues[11]) {
88 | estRange = Distance(miles: value)
89 | }
90 | heading = CLLocationDirection(separatedValues[12])
91 | }
92 |
93 | open var originalValues: String {
94 | var resultString = ""
95 | if let timestamp = timestamp {
96 | resultString.append("\(timestamp)")
97 | }
98 | resultString.append(",")
99 | if let speed = speed {
100 | resultString.append("\(speed)")
101 | }
102 | resultString.append(",")
103 | if let odometer = odometer {
104 | resultString.append("\(odometer.miles)")
105 | }
106 | resultString.append(",")
107 | if let soc = soc {
108 | resultString.append("\(soc)")
109 | }
110 | resultString.append(",")
111 | if let elevation = elevation {
112 | resultString.append("\(elevation)")
113 | }
114 | resultString.append(",")
115 | if let estHeading = estHeading {
116 | resultString.append("\(estHeading)")
117 | }
118 | resultString.append(",")
119 | if let estLat = estLat {
120 | resultString.append("\(estLat)")
121 | }
122 | resultString.append(",")
123 | if let estLng = estLng {
124 | resultString.append("\(estLng)")
125 | }
126 | resultString.append(",")
127 | if let power = power {
128 | resultString.append("\(power)")
129 | }
130 | resultString.append(",")
131 | if let shiftState = shiftState {
132 | resultString.append("\(shiftState)")
133 | }
134 | resultString.append(",")
135 | if let range = range {
136 | resultString.append("\(range.miles)")
137 | }
138 | resultString.append(",")
139 | if let estRange = estRange {
140 | resultString.append("\(estRange.miles)")
141 | }
142 | resultString.append(",")
143 | if let heading = heading {
144 | resultString.append("\(heading)")
145 | }
146 |
147 | return resultString
148 | }
149 |
150 | enum CodingKeys: String, CodingKey {
151 | case timestamp
152 | case speed
153 | case odometer
154 | case soc
155 | case elevation
156 | case estLat
157 | case estLng
158 | case power
159 | case shiftState
160 | case range
161 | case estRange
162 | case estHeading
163 | case heading
164 | }
165 |
166 | }
167 |
168 | extension StreamEvent: CustomStringConvertible {
169 | public var description: String {
170 | return "speed: \(speed ?? -1), odo: \(odometer?.miles ?? -1.0), soc: \(soc ?? -1), elevation: \(elevation ?? -1), estLat: \(estLat ?? -1), estLng: \(estLng ?? -1), power: \(power ?? -1), shift: \(shiftState ?? ""), range: \(range?.miles ?? -1), estRange: \(estRange?.miles ?? -1) heading: \(heading ?? -1), estHeading: \(estHeading ?? -1), timestamp: \(timestamp ?? 0)"
171 | }
172 |
173 | public var descriptionKm: String {
174 | return "speed: \(speedUnit?.kilometersPerHour ?? -1), odo: \(odometer?.kms ?? -1.0), soc: \(soc ?? -1), elevation: \(elevation ?? -1), estLat: \(estLat ?? -1), estLng: \(estLng ?? -1), power: \(power ?? -1), shift: \(shiftState ?? ""), range: \(range?.kms ?? -1), estRange: \(estRange?.kms ?? -1) heading: \(heading ?? -1), estHeading: \(estHeading ?? -1), timestamp: \(timestamp ?? 0)"
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/Sources/Extensions/Streaming/TeslaStreaming.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TeslaStreaming.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 23/04/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Starscream
11 | import TeslaSwift
12 | import os
13 |
14 | enum TeslaStreamingError: Error {
15 | case streamingMissingVehicleTokenOrEmail
16 | case streamingMissingOAuthToken
17 | }
18 |
19 | enum TeslaStreamAuthenticationType {
20 | case bearer(String, String) // email, vehicleToken
21 | case oAuth(String) // oAuthToken
22 | }
23 |
24 | struct TeslaStreamAuthentication {
25 | let type: TeslaStreamAuthenticationType
26 | let vehicleId: String
27 |
28 | public init(type: TeslaStreamAuthenticationType, vehicleId: String) {
29 | self.type = type
30 | self.vehicleId = vehicleId
31 | }
32 | }
33 |
34 | /*
35 | * Streaming class takes care of the different types of data streaming from Tesla servers
36 | *
37 | */
38 | public class TeslaStreaming {
39 | var debuggingEnabled: Bool {
40 | teslaSwift.debuggingEnabled
41 | }
42 | private var httpStreaming: WebSocket
43 | private var webSocketTask: URLSessionWebSocketTask
44 | private var teslaSwift: TeslaSwift
45 |
46 | private static let logger = Logger(subsystem: "Tesla Swift", category: "Tesla Streaming")
47 |
48 | public init(teslaSwift: TeslaSwift) {
49 | webSocketTask = URLSession(configuration: .default).webSocketTask(with: URL(string: "wss://streaming.vn.teslamotors.com/streaming/")!)
50 |
51 | httpStreaming = WebSocket(request: URLRequest(url: URL(string: "wss://streaming.vn.teslamotors.com/streaming/")!))
52 | self.teslaSwift = teslaSwift
53 | }
54 |
55 | private static func logDebug(_ format: String, debuggingEnabled: Bool) {
56 | if debuggingEnabled {
57 | logger.debug("\(format)")
58 | }
59 | }
60 |
61 | /**
62 | Streams vehicle data
63 |
64 | - parameter vehicle: the vehicle that will receive the command
65 | - parameter reloadsVehicle: if you have a cached vehicle, the token might be expired, this forces a vehicle token reload
66 | - parameter dataReceived: callback to receive the websocket data
67 | */
68 | public func openStream(vehicle: Vehicle, reloadsVehicle: Bool = true) async throws -> AsyncThrowingStream {
69 | if reloadsVehicle {
70 | return AsyncThrowingStream { continuation in
71 | Task {
72 | do {
73 | let freshVehicle = try await reloadVehicle(vehicle: vehicle)
74 | self.startStream(vehicle: freshVehicle, dataReceived: { data in
75 | continuation.yield(data)
76 | if data == .disconnected {
77 | continuation.finish()
78 | }
79 | })
80 | } catch let error {
81 | continuation.finish(throwing: error)
82 | }
83 | }
84 | }
85 | } else {
86 | return AsyncThrowingStream { continuation in
87 | startStream(vehicle: vehicle, dataReceived: { data in
88 | continuation.yield(data)
89 | if data == .disconnected {
90 | continuation.finish()
91 | }
92 | })
93 | }
94 | }
95 | }
96 |
97 | /**
98 | Stops the stream
99 | */
100 | public func closeStream() {
101 | httpStreaming.disconnect()
102 | webSocketTask.cancel()
103 | Self.logDebug("Stream closed", debuggingEnabled: self.debuggingEnabled)
104 | }
105 |
106 | private func reloadVehicle(vehicle: Vehicle) async throws -> Vehicle {
107 | let vehicles = try await teslaSwift.getVehicles()
108 | for freshVehicle in vehicles where freshVehicle.vehicleID == vehicle.vehicleID {
109 | return freshVehicle
110 | }
111 | throw TeslaError.failedToReloadVehicle
112 | }
113 |
114 | private func startStream(vehicle: Vehicle, dataReceived: @escaping (TeslaStreamingEvent) -> Void) {
115 | guard let accessToken = teslaSwift.token?.accessToken else {
116 | dataReceived(TeslaStreamingEvent.error(TeslaStreamingError.streamingMissingOAuthToken))
117 | return
118 | }
119 | let type: TeslaStreamAuthenticationType = .oAuth(accessToken)
120 | let authentication = TeslaStreamAuthentication(type: type, vehicleId: "\(vehicle.vehicleID!)")
121 |
122 | openStream(authentication: authentication, dataReceived: dataReceived)
123 | }
124 |
125 | private func openStream(authentication: TeslaStreamAuthentication, dataReceived: @escaping (TeslaStreamingEvent) -> Void) {
126 | let url = httpStreaming.request.url?.absoluteString
127 |
128 | Self.logDebug("Opening Stream to: \(url ?? "")", debuggingEnabled: debuggingEnabled)
129 |
130 |
131 | webSocketTask.receive { result in
132 | switch result {
133 | case let .success(message):
134 | print(message)
135 | case let .failure(error):
136 | //logDebug("Stream disconnected \(code):\(error)", debuggingEnabled: self.debuggingEnabled)
137 | dataReceived(TeslaStreamingEvent.error(NSError(domain: "TeslaStreamingError", code: Int(404), userInfo: ["error": error])))
138 | }
139 | }
140 |
141 | httpStreaming.onEvent = {
142 | [weak self] event in
143 | guard let self = self else { return }
144 |
145 | switch event {
146 | case let .connected(headers):
147 | Self.logDebug("Stream open headers: \(headers)", debuggingEnabled: self.debuggingEnabled)
148 |
149 | if let authMessage = StreamAuthentication(type: authentication.type, vehicleId: authentication.vehicleId), let string = try? teslaJSONEncoder.encode(authMessage) {
150 |
151 | self.httpStreaming.write(data: string)
152 | dataReceived(TeslaStreamingEvent.open)
153 | } else {
154 | dataReceived(TeslaStreamingEvent.error(NSError(domain: "TeslaStreamingError", code: 0, userInfo: ["errorDescription": "Failed to parse authentication data"])))
155 | self.closeStream()
156 | }
157 | case let .binary(data):
158 | Self.logDebug("Stream data: \(String(data: data, encoding: .utf8) ?? "")", debuggingEnabled: self.debuggingEnabled)
159 |
160 | guard let message = try? teslaJSONDecoder.decode(StreamMessage.self, from: data) else { return }
161 |
162 | let type = message.messageType
163 | switch type {
164 | case "control:hello":
165 | Self.logDebug("Stream got hello", debuggingEnabled: self.debuggingEnabled)
166 | case "data:update":
167 | if let values = message.value {
168 | let event = StreamEvent(values: values)
169 | Self.logDebug("Stream got data: \(values)", debuggingEnabled: self.debuggingEnabled)
170 | dataReceived(TeslaStreamingEvent.event(event))
171 | }
172 | case "data:error":
173 | Self.logDebug("Stream got data error: \(message.value ?? ""), \(message.errorType ?? "")", debuggingEnabled: self.debuggingEnabled)
174 | dataReceived(TeslaStreamingEvent.error(NSError(domain: "TeslaStreamingError", code: 0, userInfo: [message.value ?? "error": message.errorType ?? ""])))
175 | default:
176 | break
177 | }
178 | case let .disconnected(error, code):
179 | Self.logDebug("Stream disconnected \(code):\(error)", debuggingEnabled: self.debuggingEnabled)
180 | dataReceived(TeslaStreamingEvent.error(NSError(domain: "TeslaStreamingError", code: Int(code), userInfo: ["error": error])))
181 | case let .pong(data):
182 | Self.logDebug("Stream Pong", debuggingEnabled: self.debuggingEnabled)
183 | self.httpStreaming.write(pong: data ?? Data())
184 | case let .text(text):
185 | Self.logDebug("Stream Text: \(text)", debuggingEnabled: self.debuggingEnabled)
186 | case let .ping(ping):
187 | Self.logDebug("Stream ping: \(String(describing: ping))", debuggingEnabled: self.debuggingEnabled)
188 | case let .error(error):
189 | DispatchQueue.main.async {
190 | Self.logDebug("Stream error:\(String(describing: error))", debuggingEnabled: self.debuggingEnabled)
191 | dataReceived(TeslaStreamingEvent.error(NSError(domain: "TeslaStreamingError", code: 0, userInfo: ["error": error ?? ""])))
192 | }
193 | case let .viabilityChanged(viability):
194 | Self.logDebug("Stream viabilityChanged: \(viability)", debuggingEnabled: self.debuggingEnabled)
195 | case let .reconnectSuggested(reconnect):
196 | Self.logDebug("Stream reconnectSuggested: \(reconnect)", debuggingEnabled: self.debuggingEnabled)
197 | case .cancelled:
198 | Self.logDebug("Stream cancelled", debuggingEnabled: self.debuggingEnabled)
199 | case .peerClosed:
200 | Self.logDebug("Peer Closed", debuggingEnabled: self.debuggingEnabled)
201 | }
202 | }
203 | httpStreaming.connect()
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/Sources/Extensions/StreamingCombine/TeslaSwift+StreamingCombine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TeslaSwift+StreamingCombine.swift
3 | // TeslaSwift
4 | //
5 | // Created by João Nunes on 19/12/2020.
6 | // Copyright © 2020 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Combine
10 | import TeslaSwift
11 |
12 | extension TeslaStreaming {
13 | public func streamPublisher(vehicle: Vehicle) -> TeslaStreamingPublisher {
14 | return TeslaStreamingPublisher(vehicle: vehicle, stream: self)
15 | }
16 |
17 | public struct TeslaStreamingPublisher: Publisher, Cancellable {
18 | public typealias Output = TeslaStreamingEvent
19 | public typealias Failure = Error
20 |
21 | let vehicle: Vehicle
22 | let stream: TeslaStreaming
23 |
24 | init(vehicle: Vehicle, stream: TeslaStreaming) {
25 | self.vehicle = vehicle
26 | self.stream = stream
27 | }
28 |
29 | public func receive(subscriber: S) where S: Subscriber, TeslaStreamingPublisher.Failure == S.Failure, TeslaStreamingPublisher.Output == S.Input {
30 |
31 | Task {
32 | do {
33 | for try await streamEvent in try await stream.openStream(vehicle: vehicle) {
34 | switch streamEvent {
35 | case .open:
36 | _ = subscriber.receive(TeslaStreamingEvent.open)
37 | case .event(let event):
38 | _ = subscriber.receive(TeslaStreamingEvent.event(event))
39 | case .error(let error):
40 | _ = subscriber.receive(TeslaStreamingEvent.error(error))
41 | case .disconnected:
42 | _ = subscriber.receive(TeslaStreamingEvent.disconnected)
43 | subscriber.receive(completion: Subscribers.Completion.finished)
44 | }
45 | }
46 | } catch let error {
47 | _ = subscriber.receive(TeslaStreamingEvent.error(error))
48 | subscriber.receive(completion: Subscribers.Completion.finished)
49 | }
50 | }
51 | }
52 |
53 | public func cancel() {
54 | stream.closeStream()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Authentication.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthToken.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 04/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CryptoKit
11 |
12 | private let oAuthCodeVerifier: String = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef21067963841234334232123232323232"
13 |
14 | open class AuthToken: Codable {
15 |
16 | open var accessToken: String?
17 | open var tokenType: String?
18 | open var createdAt: Date? = Date()
19 | open var expiresIn: TimeInterval?
20 | open var refreshToken: String?
21 | open var idToken: String?
22 |
23 | open var isValid: Bool {
24 | if let createdAt = createdAt, let expiresIn = expiresIn {
25 | return -createdAt.timeIntervalSinceNow < expiresIn
26 | } else {
27 | return false
28 | }
29 | }
30 |
31 | public init(accessToken: String) {
32 | self.accessToken = accessToken
33 | }
34 |
35 | public required init(from decoder: Decoder) throws {
36 | let container = try decoder.container(keyedBy: CodingKeys.self)
37 |
38 | accessToken = try? container.decode(String.self, forKey: .accessToken)
39 | tokenType = try? container.decode(String.self, forKey: .tokenType)
40 | createdAt = (try? container.decode(Date.self, forKey: .createdAt)) ?? Date()
41 | expiresIn = try? container.decode(TimeInterval.self, forKey: .expiresIn)
42 | refreshToken = try? container.decode(String.self, forKey: .refreshToken)
43 | idToken = try? container.decode(String.self, forKey: .idToken)
44 | }
45 |
46 | // MARK: Codable protocol
47 | enum CodingKeys: String, CodingKey {
48 | case accessToken = "access_token"
49 | case tokenType = "token_type"
50 | case createdAt = "created_at"
51 | case expiresIn = "expires_in"
52 | case refreshToken = "refresh_token"
53 | case idToken = "id_token"
54 | }
55 | }
56 |
57 | class AuthTokenRequestWeb: Encodable {
58 |
59 | enum GrantType: String, Encodable {
60 | case refreshToken = "refresh_token"
61 | case authorizationCode = "authorization_code"
62 | case clientCredentials = "client_credentials"
63 | }
64 |
65 | var grantType: GrantType
66 | var clientID: String
67 | var clientSecret: String
68 |
69 | var codeVerifier: String?
70 | var code: String?
71 | var redirectURI: String?
72 |
73 | var refreshToken: String?
74 | var scope: String?
75 | var audience: String?
76 |
77 | init(teslaAPI: TeslaAPI, grantType: GrantType = .authorizationCode, code: String? = nil, refreshToken: String? = nil) {
78 | switch grantType {
79 | case .authorizationCode:
80 | self.codeVerifier = oAuthCodeVerifier
81 | self.redirectURI = teslaAPI.redirectURI
82 | self.audience = teslaAPI.region?.rawValue
83 | self.code = code
84 | case .refreshToken:
85 | self.refreshToken = refreshToken
86 | self.scope = teslaAPI.scope
87 | case .clientCredentials:
88 | self.scope = teslaAPI.scope
89 | self.audience = teslaAPI.region?.rawValue
90 | }
91 | self.clientID = teslaAPI.clientID
92 | self.clientSecret = teslaAPI.clientSecret
93 | self.grantType = grantType
94 | }
95 |
96 | // MARK: Codable protocol
97 | enum CodingKeys: String, CodingKey {
98 | typealias RawValue = String
99 |
100 | case grantType = "grant_type"
101 | case clientID = "client_id"
102 | case clientSecret = "client_secret"
103 | case code = "code"
104 | case redirectURI = "redirect_uri"
105 | case refreshToken = "refresh_token"
106 | case codeVerifier = "code_verifier"
107 | case scope = "scope"
108 | }
109 | }
110 |
111 | class AuthCodeRequest: Encodable {
112 |
113 | var responseType: String = "code"
114 | var clientID: String
115 | var clientSecret: String
116 | var redirectURI: String
117 | var scope: String
118 | let codeChallenge: String
119 | var codeChallengeMethod = "S256"
120 | var state = "teslaSwift"
121 |
122 | init(teslaAPI: TeslaAPI) {
123 | self.clientID = teslaAPI.clientID
124 | self.clientSecret = teslaAPI.clientSecret
125 | self.redirectURI = teslaAPI.redirectURI
126 | self.scope = teslaAPI.scope
127 | self.codeChallenge = oAuthCodeVerifier.challenge
128 | }
129 |
130 | // MARK: Codable protocol
131 | enum CodingKeys: String, CodingKey {
132 | typealias RawValue = String
133 |
134 | case clientID = "client_id"
135 | case redirectURI = "redirect_uri"
136 | case responseType = "response_type"
137 | case scope = "scope"
138 | case codeChallenge = "code_challenge"
139 | case codeChallengeMethod = "code_challenge_method"
140 | case state = "state"
141 | }
142 |
143 | func parameters() -> [URLQueryItem] {
144 | return[
145 | URLQueryItem(name: CodingKeys.clientID.rawValue, value: clientID),
146 | URLQueryItem(name: CodingKeys.redirectURI.rawValue, value: redirectURI),
147 | URLQueryItem(name: CodingKeys.responseType.rawValue, value: responseType),
148 | URLQueryItem(name: CodingKeys.scope.rawValue, value: scope),
149 | URLQueryItem(name: CodingKeys.codeChallenge.rawValue, value: codeChallenge),
150 | URLQueryItem(name: CodingKeys.codeChallengeMethod.rawValue, value: codeChallengeMethod),
151 | URLQueryItem(name: CodingKeys.state.rawValue, value: state)
152 | ]
153 | }
154 | }
155 |
156 | extension String {
157 | var challenge: String {
158 | let hash = self.sha256
159 | let challenge = hash.base64EncodedString()
160 | .replacingOccurrences(of: "+", with: "-")
161 | .replacingOccurrences(of: "/", with: "_")
162 | .replacingOccurrences(of: "=", with: "")
163 | .trimmingCharacters(in: .whitespaces)
164 | return challenge
165 | }
166 |
167 | private var sha256: Data {
168 | let inputData = Data(self.utf8)
169 | let hashed = SHA256.hash(data: inputData)
170 | return Data(hashed)
171 | }
172 | }
173 |
174 | extension TeslaAPI {
175 | var region: Region? {
176 | switch self {
177 | case .ownerAPI: return nil
178 | case let .fleetAPI(region: region, clientID: _, clientSecret: _, redirectURI: _, scopes: _): return region
179 | }
180 | }
181 |
182 | var clientID: String {
183 | switch self {
184 | case .ownerAPI: return "ownerapi"
185 | case let .fleetAPI(region: _, clientID: clientID, clientSecret: _, redirectURI: _, scopes: _): return clientID
186 | }
187 | }
188 |
189 | var clientSecret: String {
190 | switch self {
191 | case .ownerAPI: return "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"
192 | case let .fleetAPI(region: _, clientID: _, clientSecret: clientSecret, redirectURI: _, scopes: _): return clientSecret
193 | }
194 | }
195 |
196 | var redirectURI: String {
197 | switch self {
198 | case .ownerAPI: return "https://auth.tesla.com/void/callback"
199 | case let .fleetAPI(region: _, clientID: _, clientSecret: _, redirectURI: redirectURI, scopes: _): return redirectURI
200 | }
201 | }
202 |
203 | var scope: String {
204 | switch self {
205 | case .ownerAPI: return "openid email offline_access"
206 | case let .fleetAPI(region: _, clientID: _, clientSecret: _, redirectURI: _, scopes: scopes):
207 | return scopes.map { $0.rawValue }.joined(separator: " ")
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/BatteryData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BatteryData.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Welcome
12 | open class BatteryData: Codable {
13 | open var energyLeft: Double
14 | open var totalPackEnergy: Double
15 | open var gridStatus: String
16 | open var defaultRealMode: String
17 | open var operation: String
18 | open var installationDate: Date
19 | open var batteryCount: Int
20 | open var backup: Backup
21 | open var userSettings: UserSettings
22 | open var components: Components
23 | open var powerReading: [PowerReading]
24 |
25 | enum CodingKeys: String, CodingKey {
26 | case energyLeft = "energy_left"
27 | case totalPackEnergy = "total_pack_energy"
28 | case gridStatus = "grid_status"
29 | case backup
30 | case userSettings = "user_settings"
31 | case components
32 | case defaultRealMode = "default_real_mode"
33 | case operation
34 | case installationDate = "installation_date"
35 | case powerReading = "power_reading"
36 | case batteryCount = "battery_count"
37 | }
38 |
39 | // MARK: - Backup
40 | open class Backup: Codable {
41 | open var backupReservePercent: Double
42 | open var events: [Event]
43 |
44 | enum CodingKeys: String, CodingKey {
45 | case backupReservePercent = "backup_reserve_percent"
46 | case events
47 | }
48 | }
49 |
50 | // MARK: - Event
51 | open class Event: Codable {
52 | let timestamp: Date
53 | let duration: Int
54 | }
55 |
56 | // MARK: - Components
57 | open class Components: Codable {
58 | open var solar: Bool
59 | open var solarType: String
60 | open var battery: Bool
61 | open var grid: Bool
62 | open var backup: Bool
63 | open var gateway: String
64 | open var loadMeter: Bool
65 | open var touCapable: Bool
66 | open var stormModeCapable: Bool
67 | open var flexEnergyRequestCapable: Bool
68 | open var carChargingDataSupported: Bool
69 | open var offGridVehicleChargingReserveSupported: Bool
70 | open var vehicleChargingPerformanceViewEnabled: Bool
71 | open var vehicleChargingSolarOffsetViewEnabled: Bool
72 | open var batterySolarOffsetViewEnabled: Bool
73 | open var setIslandingModeEnabled: Bool
74 | open var backupTimeRemainingEnabled: Bool
75 | open var batteryType: String
76 | open var configurable: Bool
77 | open var gridServicesEnabled: Bool
78 |
79 | enum CodingKeys: String, CodingKey {
80 | case solar
81 | case solarType = "solar_type"
82 | case battery, grid, backup, gateway
83 | case loadMeter = "load_meter"
84 | case touCapable = "tou_capable"
85 | case stormModeCapable = "storm_mode_capable"
86 | case flexEnergyRequestCapable = "flex_energy_request_capable"
87 | case carChargingDataSupported = "car_charging_data_supported"
88 | case offGridVehicleChargingReserveSupported = "off_grid_vehicle_charging_reserve_supported"
89 | case vehicleChargingPerformanceViewEnabled = "vehicle_charging_performance_view_enabled"
90 | case vehicleChargingSolarOffsetViewEnabled = "vehicle_charging_solar_offset_view_enabled"
91 | case batterySolarOffsetViewEnabled = "battery_solar_offset_view_enabled"
92 | case setIslandingModeEnabled = "set_islanding_mode_enabled"
93 | case backupTimeRemainingEnabled = "backup_time_remaining_enabled"
94 | case batteryType = "battery_type"
95 | case configurable
96 | case gridServicesEnabled = "grid_services_enabled"
97 | }
98 | }
99 |
100 | // MARK: - PowerReading
101 | open class PowerReading: Codable {
102 | open var timestamp: Date
103 | open var loadPower: Double
104 | open var solarPower: Double
105 | open var gridPower: Double
106 | open var batteryPower: Double
107 | open var generatorPower: Double
108 |
109 | enum CodingKeys: String, CodingKey {
110 | case timestamp
111 | case loadPower = "load_power"
112 | case solarPower = "solar_power"
113 | case gridPower = "grid_power"
114 | case batteryPower = "battery_power"
115 | case generatorPower = "generator_power"
116 | }
117 | }
118 |
119 | // MARK: - UserSettings
120 | open class UserSettings: Codable {
121 | open var stormModeEnabled: Bool
122 | open var syncGridAlertEnabled: Bool
123 | open var breakerAlertEnabled: Bool
124 |
125 | enum CodingKeys: String, CodingKey {
126 | case stormModeEnabled = "storm_mode_enabled"
127 | case syncGridAlertEnabled = "sync_grid_alert_enabled"
128 | case breakerAlertEnabled = "breaker_alert_enabled"
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/BatteryPowerHistory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BatteryPowerHistory.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - BatteryPowerHistory
12 | open class BatteryPowerHistory: Codable {
13 | open var serialNumber: String
14 | open var timeSeries: [TimeSeries]
15 | open var selfConsumptionData: [SelfConsumptionDatum]
16 |
17 | enum CodingKeys: String, CodingKey {
18 | case serialNumber = "serial_number"
19 | case timeSeries = "time_series"
20 | case selfConsumptionData = "self_consumption_data"
21 | }
22 |
23 | // MARK: - SelfConsumptionDatum
24 | open class SelfConsumptionDatum: Codable {
25 | open var timestamp: Date
26 | open var solar: Double
27 | open var battery: Double
28 | }
29 |
30 | // MARK: - TimeSeries
31 | open class TimeSeries: Codable {
32 | open var timestamp: Date
33 | open var solarPower: Double
34 | open var batteryPower: Double
35 | open var gridPower: Double
36 | open var gridServicesPower: Double
37 | open var generatorPower: Double
38 |
39 | enum CodingKeys: String, CodingKey {
40 | case timestamp
41 | case solarPower = "solar_power"
42 | case batteryPower = "battery_power"
43 | case gridPower = "grid_power"
44 | case gridServicesPower = "grid_services_power"
45 | case generatorPower = "generator_power"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/BatteryStatus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BatteryStatus.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - BatteryStatus
12 | open class BatteryStatus: Codable {
13 | open var siteName: String
14 | open var id: String
15 | open var energyLeft: Double
16 | open var totalPackEnergy: Double
17 | open var percentageCharged: Double
18 | open var batteryPower: Double
19 |
20 | enum CodingKeys: String, CodingKey {
21 | case siteName = "site_name"
22 | case id
23 | case energyLeft = "energy_left"
24 | case totalPackEnergy = "total_pack_energy"
25 | case percentageCharged = "percentage_charged"
26 | case batteryPower = "battery_power"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ChargeAmpsCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChargeAmpsCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by João Nunes on 30/10/2021.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ChargeAmpsCommandOptions: Encodable {
12 |
13 | open var amps: Int
14 |
15 | public init(amps: Int) {
16 | self.amps = amps
17 | }
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case amps = "charging_amps"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ChargeHistory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChargeHistory.swift
3 | // TeslaSwiftDemoTests
4 | //
5 | // Created by João Nunes on 29/10/2023.
6 | // Copyright © 2023 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct ChargeHistory: Codable {
12 | let screenTitle: String
13 | let screenSubtitle: String
14 | let totalCharged: Charged
15 | let totalChargedBreakdown: Breakdown
16 | let chargingHistoryGraph: Graph
17 | let setRatePlan: RatePlan
18 | let totalChargedEnergyBreakdown: Breakdown
19 | let chargingTips: Tips
20 | let timePeriodLimit: TimePeriodLimit
21 |
22 | enum CodingKeys: String, CodingKey {
23 | case screenTitle = "screen_title"
24 | case screenSubtitle = "screen_subtitle"
25 | case totalCharged = "total_charged"
26 | case totalChargedBreakdown = "total_charged_breakdown"
27 | case chargingHistoryGraph = "charging_history_graph"
28 | case setRatePlan = "set_rate_plan"
29 | case totalChargedEnergyBreakdown = "total_charged_energy_breakdown"
30 | case chargingTips = "charging_tips"
31 | case timePeriodLimit = "time_period_limit"
32 | }
33 |
34 | public struct Charged: Codable {
35 | let value: String
36 | let rawValue: Int
37 | let afterAdornment: String
38 | let title: String
39 |
40 | enum CodingKeys: String, CodingKey {
41 | case value
42 | case rawValue = "raw_value"
43 | case afterAdornment = "after_adornment"
44 | case title
45 | }
46 | }
47 |
48 | public struct Breakdown: Codable {
49 | let home, superCharger, other, work: BreakdownItem
50 |
51 | enum CodingKeys: String, CodingKey {
52 | case home
53 | case superCharger = "super_charger"
54 | case other
55 | case work
56 | }
57 | }
58 |
59 | public struct BreakdownItem: Codable {
60 | let value: String
61 | let rawValue: Int?
62 | let afterAdornment: String
63 | let subTitle: String?
64 |
65 | enum CodingKeys: String, CodingKey {
66 | case value
67 | case rawValue = "raw_value"
68 | case afterAdornment = "after_adornment"
69 | case subTitle = "sub_title"
70 | }
71 | }
72 |
73 | struct Graph: Codable {
74 | let dataPoints: [DataPoint]
75 | let period: Period
76 | let interval: Int
77 | let xLabels: [XLabel]
78 | let yLabels: [YLabel]
79 | let horizontalGridLines: [Double]
80 | let verticalGridLines: [Double]
81 | let discreteX: Bool
82 | let yRangeMax: Double
83 | let XDomainMin: String?
84 | let XDomainMax: String?
85 |
86 | enum CodingKeys: String, CodingKey {
87 | case dataPoints = "data_points"
88 | case period
89 | case interval
90 | case xLabels = "x_labels"
91 | case yLabels = "y_labels"
92 | case horizontalGridLines = "horizontal_grid_lines"
93 | case verticalGridLines = "vertical_grid_lines"
94 | case discreteX = "discrete_x"
95 | case yRangeMax = "y_range_max"
96 | case XDomainMin = "XDomainMin"
97 | case XDomainMax = "XDomainMax"
98 | }
99 | }
100 |
101 | struct Period: Codable {
102 | let startTimestamp: Timestamp
103 | let endTimestamp: Timestamp
104 |
105 | enum CodingKeys: String, CodingKey {
106 | case startTimestamp = "start_timestamp"
107 | case endTimestamp = "end_timestamp"
108 | }
109 | }
110 |
111 | struct XLabel: Codable {
112 | let value: String
113 | let rawValue: Int
114 |
115 | enum CodingKeys: String, CodingKey {
116 | case value
117 | case rawValue = "raw_value"
118 | }
119 | }
120 |
121 | struct YLabel: Codable {
122 | let value: String
123 | let rawValue: Double?
124 | let afterAdornment: String?
125 |
126 | enum CodingKeys: String, CodingKey {
127 | case value
128 | case rawValue = "raw_value"
129 | case afterAdornment = "after_adornment"
130 | }
131 | }
132 |
133 | public struct DataPoint: Codable {
134 | let timestamp: TimestampContainer
135 | let values: [Value]
136 | }
137 |
138 | public struct TimestampContainer: Codable {
139 | let timestamp: Timestamp
140 | let displayString: String
141 |
142 | enum CodingKeys: String, CodingKey {
143 | case timestamp
144 | case displayString = "display_string"
145 | }
146 | }
147 |
148 | public struct Timestamp: Codable {
149 | let seconds: Int
150 | }
151 |
152 | public struct Value: Codable {
153 | let value: String
154 | let rawValue: Double?
155 | let afterAdornment: String
156 | let title: String?
157 | let subTitle: String?
158 |
159 | enum CodingKeys: String, CodingKey {
160 | case value
161 | case rawValue = "raw_value"
162 | case afterAdornment = "after_adornment"
163 | case title
164 | case subTitle = "sub_title"
165 | }
166 | }
167 |
168 | struct RatePlan: Codable {
169 | let messageCard: MessageCard
170 | let primaryLink: PrimaryLink
171 |
172 | enum CodingKeys: String, CodingKey {
173 | case messageCard = "message_card"
174 | case primaryLink = "primary_link"
175 | }
176 | }
177 |
178 | struct Card: Codable {
179 | let id: String
180 | let title: String
181 | }
182 |
183 | struct PrimaryLink: Codable {
184 | let type: Int
185 | let destination: String
186 | let label: String
187 | }
188 |
189 | struct Tips: Codable {
190 | let title: String
191 | let imageUrl: String
192 | let textSections: [TextSection]
193 | let tips: [TipLink]
194 |
195 | enum CodingKeys: String, CodingKey {
196 | case title
197 | case imageUrl = "image_url"
198 | case textSections = "text_sections"
199 | case tips
200 | }
201 | }
202 |
203 | struct TextSection: Codable {
204 | let paragraphs: [String]
205 | }
206 |
207 | struct TipLink: Codable {
208 | let link: LinkIcon
209 | let section: Section
210 | }
211 |
212 | struct LinkIcon: Codable {
213 | let link: Link
214 | let leftIcon: String
215 | let rightIcon: String
216 |
217 | enum CodingKeys: String, CodingKey {
218 | case link
219 | case leftIcon = "left_icon"
220 | case rightIcon = "right_icon"
221 | }
222 | }
223 |
224 | struct Link: Codable {
225 | let type: Int
226 | let destination: String
227 | let label: String
228 | }
229 |
230 | struct Section: Codable {
231 | let title: String
232 | let tips: [Tip]
233 | }
234 |
235 | struct Tip: Codable {
236 | let title: String
237 | let description: String
238 | let media: Media
239 | }
240 |
241 | struct Media: Codable {
242 | let type: Int
243 | let source: String
244 | let resizeMode: Int
245 |
246 | enum CodingKeys: String, CodingKey {
247 | case type
248 | case source
249 | case resizeMode = "resize_mode"
250 | }
251 | }
252 |
253 | public struct SetRatePlan: Codable {
254 | let messageCard: MessageCard
255 | let primaryLink: PrimaryLink
256 | }
257 |
258 | public struct MessageCard: Codable {
259 | let card: Card
260 | let imageID: String
261 |
262 | enum CodingKeys: String, CodingKey {
263 | case card
264 | case imageID = "image_id"
265 | }
266 | }
267 |
268 | public struct TimePeriodLimit: Codable {
269 | let ownershipStart: TimestampContainer
270 | let featureStart: TimestampContainer
271 |
272 | enum CodingKeys: String, CodingKey {
273 | case ownershipStart = "ownership_start"
274 | case featureStart = "feature_start"
275 | }
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ChargeLimitPercentageCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChargeLimitPercentageCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 10/11/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ChargeLimitPercentageCommandOptions: Encodable {
12 | open var percent: Int?
13 |
14 | public init(limit: Int) {
15 | percent = limit
16 | }
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case percent
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Codable+JSONString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Codable+JSONString.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 23/09/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension Encodable {
12 |
13 | var jsonString: String? {
14 | let encoder = teslaJSONEncoder
15 | guard let data = try? encoder.encode(self) else { return nil }
16 | return String(data: data, encoding: String.Encoding.utf8)
17 | }
18 |
19 | }
20 |
21 | public extension String {
22 | func decodeJSON() -> T? {
23 | guard let data = self.data(using: String.Encoding.utf8) else { return nil }
24 | return try? teslaJSONDecoder.decode(T.self, from: data)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/CommandResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommandResponse.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 05/04/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class CommandResponse: Decodable {
12 |
13 | private struct Response: Decodable {
14 | var result: Bool?
15 | var reason: String?
16 | }
17 | private var response: Response
18 |
19 | open var result: Bool? { return response.result }
20 | open var reason: String? { return response.reason }
21 |
22 | init() {
23 | response = Response()
24 | }
25 |
26 | enum CodingKeys: String, CodingKey {
27 | case response
28 | }
29 |
30 | required public init(from decoder: Decoder) throws {
31 |
32 | let container = try decoder.container(keyedBy: CodingKeys.self)
33 | response = (try? container.decode(Response.self, forKey: .response)) ?? Response()
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Distance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Distance.swift
3 | // Pods
4 | //
5 | // Created by Jacob Holland on 7/20/17.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Distance: Codable {
12 | public var value: Measurement
13 |
14 | public init(miles: Double?) {
15 | let tempValue = miles ?? 0.0
16 | value = Measurement(value: tempValue, unit: UnitLength.miles)
17 | }
18 | public init(kms: Double) {
19 | value = Measurement(value: kms, unit: UnitLength.kilometers)
20 | }
21 |
22 | public init(from decoder: Decoder) throws {
23 | let container = try decoder.singleValueContainer()
24 | if let tempValue = try? container.decode(Double.self) {
25 | value = Measurement(value: tempValue, unit: UnitLength.miles)
26 | } else {
27 | value = Measurement(value: 0, unit: UnitLength.miles)
28 | }
29 | }
30 |
31 | public func encode(to encoder: Encoder) throws {
32 |
33 | var container = encoder.singleValueContainer()
34 | try container.encode(value.converted(to: .miles).value)
35 |
36 | }
37 |
38 | public var miles: Double { return value.converted(to: .miles).value }
39 | public var kms: Double { return value.converted(to: .kilometers).value }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/DriveState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrivingPosition.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 14/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | open class DriveState: Codable {
13 | public enum ShiftState: String, Codable {
14 | case drive = "D"
15 | case park = "P"
16 | case reverse = "R"
17 | case neutral = "N"
18 | }
19 |
20 | open var shiftState: ShiftState?
21 |
22 | open var speed: CLLocationSpeed?
23 | open var latitude: CLLocationDegrees?
24 | open var longitude: CLLocationDegrees?
25 | open var heading: CLLocationDirection?
26 | open var nativeLatitude: CLLocationDegrees?
27 | open var nativeLongitude: CLLocationDegrees?
28 | private var nativeLocationSupportedBool: Int?
29 | open var nativeLocationSupported: Bool { return nativeLocationSupportedBool == 1 }
30 | open var nativeType: String?
31 |
32 | open var date: Date?
33 | open var timeStamp: Double?
34 | open var power: Int?
35 |
36 | open var activeRouteLatitude: CLLocationDegrees?
37 | open var activeRouteLongitude: CLLocationDegrees?
38 | open var activeRouteTrafficMinutesDelay: Double?
39 | open var activeRouteDestination: String?
40 | open var activeRouteEnergyAtArrival: Int?
41 | open var activeRouteMilesToArrival: Double?
42 | open var activeRouteMinutesToArrival: Double?
43 |
44 | open var position: CLLocation? {
45 | if let latitude = latitude,
46 | let longitude = longitude,
47 | let heading = heading,
48 | let date = date {
49 | let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
50 | return CLLocation(coordinate: coordinate,
51 | altitude: 0.0, horizontalAccuracy: 0.0, verticalAccuracy: 0.0,
52 | course: heading,
53 | speed: speed ?? 0,
54 | timestamp: date)
55 |
56 | }
57 | return nil
58 | }
59 |
60 | enum CodingKeys: String, CodingKey {
61 | case shiftState = "shift_state"
62 | case speed = "speed"
63 | case latitude = "latitude"
64 | case longitude = "longitude"
65 | case power
66 | case heading = "heading"
67 | case date = "gps_as_of"
68 | case timeStamp = "timestamp"
69 | case nativeLatitude = "native_latitude"
70 | case nativeLongitude = "native_longitude"
71 | case nativeLocationSupportedBool = "native_location_supported"
72 | case nativeType = "native_type"
73 | case activeRouteLatitude = "active_route_latitude"
74 | case activeRouteLongitude = "active_route_longitude"
75 | case activeRouteTrafficMinutesDelay = "active_route_traffic_minutes_delay"
76 | case activeRouteDestination = "active_route_destination"
77 | case activeRouteEnergyAtArrival = "active_route_energy_at_arrival"
78 | case activeRouteMilesToArrival = "active_route_miles_to_arrival"
79 | case activeRouteMinutesToArrival = "active_route_minutes_to_arrival"
80 | }
81 |
82 | required public init(from decoder: Decoder) throws {
83 | let container = try decoder.container(keyedBy: CodingKeys.self)
84 |
85 | shiftState = try? container.decode(ShiftState.self, forKey: .shiftState)
86 |
87 | speed = try? container.decode(CLLocationSpeed.self, forKey: .speed)
88 | latitude = try? container.decode(CLLocationDegrees.self, forKey: .latitude)
89 | longitude = try? container.decode(CLLocationDegrees.self, forKey: .longitude)
90 | heading = try? container.decode(CLLocationDirection.self, forKey: .heading)
91 | nativeLatitude = try? container.decode(CLLocationDegrees.self, forKey: .nativeLatitude)
92 | nativeLongitude = try? container.decode(CLLocationDegrees.self, forKey: .nativeLongitude)
93 | nativeLocationSupportedBool = try? container.decode(Int.self, forKey: .nativeLocationSupportedBool)
94 |
95 | nativeType = try? container.decode(String.self, forKey: .nativeType)
96 |
97 | date = try? container.decode(Date.self, forKey: .date)
98 | timeStamp = try? container.decode(Double.self, forKey: .timeStamp)
99 | power = try? container.decode(Int.self, forKey: .power)
100 |
101 | activeRouteLatitude = try? container.decode(CLLocationDegrees.self, forKey: .activeRouteLatitude)
102 | activeRouteLongitude = try? container.decode(CLLocationDegrees.self, forKey: .activeRouteLongitude)
103 | activeRouteTrafficMinutesDelay = try? container.decode(Double.self, forKey: .activeRouteTrafficMinutesDelay)
104 | activeRouteDestination = try? container.decode(String.self, forKey: .activeRouteDestination)
105 | activeRouteEnergyAtArrival = try? container.decode(Int.self, forKey: .activeRouteEnergyAtArrival)
106 | activeRouteMilesToArrival = try? container.decode(Double.self, forKey: .activeRouteMilesToArrival)
107 | activeRouteMinutesToArrival = try? container.decode(Double.self, forKey: .activeRouteMinutesToArrival)
108 | }
109 |
110 | public func encode(to encoder: Encoder) throws {
111 | var container = encoder.container(keyedBy: CodingKeys.self)
112 |
113 | try container.encodeIfPresent(shiftState, forKey: .shiftState)
114 |
115 | try container.encodeIfPresent(speed, forKey: .speed)
116 | try container.encodeIfPresent(latitude, forKey: .latitude)
117 | try container.encodeIfPresent(longitude, forKey: .longitude)
118 | try container.encodeIfPresent(heading, forKey: .heading)
119 | try container.encodeIfPresent(nativeLatitude, forKey: .nativeLatitude)
120 | try container.encodeIfPresent(nativeLongitude, forKey: .nativeLongitude)
121 | try container.encodeIfPresent(nativeLocationSupportedBool, forKey: .nativeLocationSupportedBool)
122 |
123 | try container.encodeIfPresent(nativeType, forKey: .nativeType)
124 |
125 | try container.encodeIfPresent(date, forKey: .date)
126 | try container.encodeIfPresent(timeStamp, forKey: .timeStamp)
127 | try container.encodeIfPresent(power, forKey: .power)
128 |
129 | try container.encodeIfPresent(activeRouteLatitude, forKey: .activeRouteLatitude)
130 | try container.encodeIfPresent(activeRouteLongitude, forKey: .activeRouteLongitude)
131 | try container.encodeIfPresent(activeRouteTrafficMinutesDelay, forKey: .activeRouteTrafficMinutesDelay)
132 | try container.encodeIfPresent(activeRouteDestination, forKey: .activeRouteDestination)
133 | try container.encodeIfPresent(activeRouteEnergyAtArrival, forKey: .activeRouteEnergyAtArrival)
134 | try container.encodeIfPresent(activeRouteMilesToArrival, forKey: .activeRouteMilesToArrival)
135 | try container.encodeIfPresent(activeRouteMinutesToArrival, forKey: .activeRouteMinutesToArrival)
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/EnergySite.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnergySite.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - EnergySite
12 | open class EnergySite: Codable {
13 |
14 | // Unique to EnergySite
15 | open var id: String?
16 | public var batteryId: BatteryId? {
17 | guard let id else { return nil }
18 |
19 | return BatteryId(id: id)
20 | }
21 | open var energySiteID: Decimal
22 | public var siteId: SiteId {
23 | SiteId(id: energySiteID)
24 | }
25 | open var siteName: String?
26 | open var assetSiteID: String?
27 | open var components: Components?
28 | open var goOffGridTestBannerEnabled: Bool?
29 | open var stormModeEnabled: Bool?
30 | open var powerwallOnboardingSettingsSet: Bool?
31 | open var powerwallTeslaElectricInterestedIn: String?
32 | open var vppTourEnabled: Bool?
33 |
34 | // Also available in EnergySiteStatus
35 | open var resourceType: String
36 | open var warpSiteNumber: String?
37 | open var gatewayID: String?
38 | open var energyLeft: Double?
39 | open var totalPackEnergy: Double?
40 | open var percentageCharged: Double?
41 | open var batteryType: String?
42 | open var backupCapable: Bool?
43 | open var batteryPower: Double?
44 | open var syncGridAlertEnabled: Bool?
45 | open var breakerAlertEnabled: Bool?
46 |
47 | enum CodingKeys: String, CodingKey {
48 | case siteName = "site_name"
49 | case energySiteID = "energy_site_id"
50 | case resourceType = "resource_type"
51 | case warpSiteNumber = "warp_site_number"
52 | case id
53 | case gatewayID = "gateway_id"
54 | case assetSiteID = "asset_site_id"
55 | case energyLeft = "energy_left"
56 | case totalPackEnergy = "total_pack_energy"
57 | case percentageCharged = "percentage_charged"
58 | case batteryType = "battery_type"
59 | case backupCapable = "backup_capable"
60 | case batteryPower = "battery_power"
61 | case syncGridAlertEnabled = "sync_grid_alert_enabled"
62 | case breakerAlertEnabled = "breaker_alert_enabled"
63 | case components
64 | case goOffGridTestBannerEnabled = "go_off_grid_test_banner_enabled"
65 | case stormModeEnabled = "storm_mode_enabled"
66 | case powerwallOnboardingSettingsSet = "powerwall_onboarding_settings_set"
67 | case powerwallTeslaElectricInterestedIn = "powerwall_tesla_electric_interested_in"
68 | case vppTourEnabled = "vpp_tour_enabled"
69 | }
70 |
71 | // MARK: - Components
72 | open class Components: Codable {
73 | open var battery: Bool
74 | open var batteryType: String?
75 | open var solar: Bool
76 | open var solarType: String?
77 | open var grid: Bool
78 | open var loadMeter: Bool
79 | open var marketType: String
80 | open var wallConnectors: [WallConnectors]?
81 |
82 | enum CodingKeys: String, CodingKey {
83 | case battery
84 | case batteryType = "battery_type"
85 | case solar
86 | case solarType = "solar_type"
87 | case grid
88 | case loadMeter = "load_meter"
89 | case marketType = "market_type"
90 | case wallConnectors = "wall_connectors"
91 | }
92 | }
93 | }
94 |
95 | open class WallConnectors: Codable {
96 | open var deviceId: String?
97 | open var din: String?
98 | open var serialNumber: String?
99 | open var partNumber: String?
100 | open var partType: Int?
101 | open var partName: String?
102 | open var isActive: Bool?
103 |
104 | enum CodingKeys: String, CodingKey {
105 | case deviceId = "device_id"
106 | case din
107 | case serialNumber = "serial_number"
108 | case partNumber = "part_number"
109 | case partType = "part_type"
110 | case partName = "part_name"
111 | case isActive = "is_active"
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/EnergySiteHistory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnergySiteHistory.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - EnergySiteHistory
12 | open class EnergySiteHistory: Codable {
13 | open var serialNumber: String
14 | open var period: Period
15 | open var timeSeries: [TimeSeries]
16 |
17 | enum CodingKeys: String, CodingKey {
18 | case serialNumber = "serial_number"
19 | case period
20 | case timeSeries = "time_series"
21 | }
22 |
23 | public enum Period: String, Codable {
24 | case day, week, month, year
25 | }
26 |
27 | // MARK: - TimeSeries
28 | open class TimeSeries: Codable {
29 | open var timestamp: Date
30 | open var solarEnergyExported: Double
31 | open var generatorEnergyExported: Double
32 | open var gridEnergyImported: Double
33 | open var gridServicesEnergyImported: Double
34 | open var gridServicesEnergyExported: Double
35 | open var gridEnergyExportedFromSolar: Double
36 | open var gridEnergyExportedFromGenerator: Double
37 | open var gridEnergyExportedFromBattery: Double
38 | open var batteryEnergyExported: Double
39 | open var batteryEnergyImportedFromGrid: Double
40 | open var batteryEnergyImportedFromSolar: Double
41 | open var batteryEnergyImportedFromGenerator: Double
42 | open var consumerEnergyImportedFromGrid: Double
43 | open var consumerEnergyImportedFromSolar: Double
44 | open var consumerEnergyImportedFromBattery: Double
45 | open var consumerEnergyImportedFromGenerator: Double
46 |
47 | enum CodingKeys: String, CodingKey {
48 | case timestamp
49 | case solarEnergyExported = "solar_energy_exported"
50 | case generatorEnergyExported = "generator_energy_exported"
51 | case gridEnergyImported = "grid_energy_imported"
52 | case gridServicesEnergyImported = "grid_services_energy_imported"
53 | case gridServicesEnergyExported = "grid_services_energy_exported"
54 | case gridEnergyExportedFromSolar = "grid_energy_exported_from_solar"
55 | case gridEnergyExportedFromGenerator = "grid_energy_exported_from_generator"
56 | case gridEnergyExportedFromBattery = "grid_energy_exported_from_battery"
57 | case batteryEnergyExported = "battery_energy_exported"
58 | case batteryEnergyImportedFromGrid = "battery_energy_imported_from_grid"
59 | case batteryEnergyImportedFromSolar = "battery_energy_imported_from_solar"
60 | case batteryEnergyImportedFromGenerator = "battery_energy_imported_from_generator"
61 | case consumerEnergyImportedFromGrid = "consumer_energy_imported_from_grid"
62 | case consumerEnergyImportedFromSolar = "consumer_energy_imported_from_solar"
63 | case consumerEnergyImportedFromBattery = "consumer_energy_imported_from_battery"
64 | case consumerEnergyImportedFromGenerator = "consumer_energy_imported_from_generator"
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/EnergySiteInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnergySiteInfo.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - EnergySiteInfo
12 | open class EnergySiteInfo: Codable {
13 | open var id: String
14 | open var siteName: String?
15 | open var siteNumber: String?
16 | open var backupReservePercent: Double?
17 | open var defaultRealMode: String?
18 | open var installationDate: Date
19 | open var version: String?
20 | open var batteryCount: Int?
21 | open var nameplatePower: Double?
22 | open var nameplateEnergy: Double?
23 | open var installationTimeZone: String
24 | open var offGridVehicleChargingReservePercent: Double?
25 |
26 | open var userSettings: UserSettings
27 | open var touSettings: TOUSettings?
28 | open var components: Components
29 | // tariff_content_v2
30 |
31 | enum CodingKeys: String, CodingKey {
32 | case id
33 | case siteName = "site_name"
34 | case siteNumber = "site_number"
35 | case backupReservePercent = "backup_reserve_percent"
36 | case defaultRealMode = "default_real_mode"
37 | case installationDate = "installation_date"
38 | case userSettings = "user_settings"
39 | case components
40 | case version
41 | case batteryCount = "battery_count"
42 | case touSettings = "tou_settings"
43 | case nameplatePower = "nameplate_power"
44 | case nameplateEnergy = "nameplate_energy"
45 | case installationTimeZone = "installation_time_zone"
46 | case offGridVehicleChargingReservePercent = "off_grid_vehicle_charging_reserve_percent"
47 | }
48 |
49 | // MARK: - Components
50 | open class Components: Codable {
51 | open var solar: Bool
52 | open var solarType: String?
53 | open var battery: Bool
54 | open var grid: Bool
55 | open var backup: Bool
56 | open var gateway: String
57 | open var loadMeter: Bool
58 | open var touCapable: Bool?
59 | open var stormModeCapable: Bool?
60 | open var flexEnergyRequestCapable: Bool?
61 | open var carChargingDataSupported: Bool?
62 | open var offGridVehicleChargingReserveSupported: Bool
63 | open var vehicleChargingPerformanceViewEnabled: Bool
64 | open var vehicleChargingSolarOffsetViewEnabled: Bool
65 | open var batterySolarOffsetViewEnabled: Bool
66 | open var setIslandingModeEnabled: Bool?
67 | open var backupTimeRemainingEnabled: Bool?
68 | open var batteryType: String?
69 | open var configurable: Bool?
70 | open var gridServicesEnabled: Bool?
71 | open var energyServiceSelfSchedulingEnabled: Bool?
72 | open var wallConnectors: [WallConnectors]?
73 | open var nbtSupported: Bool?
74 | open var systemAlertsEnabled: Bool?
75 |
76 | enum CodingKeys: String, CodingKey {
77 | case solar
78 | case solarType = "solar_type"
79 | case battery, grid, backup, gateway
80 | case loadMeter = "load_meter"
81 | case touCapable = "tou_capable"
82 | case stormModeCapable = "storm_mode_capable"
83 | case flexEnergyRequestCapable = "flex_energy_request_capable"
84 | case carChargingDataSupported = "car_charging_data_supported"
85 | case offGridVehicleChargingReserveSupported = "off_grid_vehicle_charging_reserve_supported"
86 | case vehicleChargingPerformanceViewEnabled = "vehicle_charging_performance_view_enabled"
87 | case vehicleChargingSolarOffsetViewEnabled = "vehicle_charging_solar_offset_view_enabled"
88 | case batterySolarOffsetViewEnabled = "battery_solar_offset_view_enabled"
89 | case setIslandingModeEnabled = "set_islanding_mode_enabled"
90 | case backupTimeRemainingEnabled = "backup_time_remaining_enabled"
91 | case batteryType = "battery_type"
92 | case configurable
93 | case gridServicesEnabled = "grid_services_enabled"
94 | case energyServiceSelfSchedulingEnabled = "energy_service_self_scheduling_enabled"
95 | case wallConnectors = "wall_connectors"
96 | case nbtSupported = "nbt_supported"
97 | case systemAlertsEnabled = "system_alerts_enabled"
98 | }
99 | }
100 |
101 | // MARK: - TouSettings
102 | open class TOUSettings: Codable {
103 | open var optimizationStrategy: String
104 | open var schedule: [Schedule]
105 |
106 | enum CodingKeys: String, CodingKey {
107 | case optimizationStrategy = "optimization_strategy"
108 | case schedule
109 | }
110 | }
111 |
112 | // MARK: - Schedule
113 | open class Schedule: Codable {
114 | open var target: String
115 | open var weekDays: [Int]
116 | open var startSeconds: Int
117 | open var endSeconds: Int
118 |
119 | enum CodingKeys: String, CodingKey {
120 | case target
121 | case weekDays = "week_days"
122 | case startSeconds = "start_seconds"
123 | case endSeconds = "end_seconds"
124 | }
125 | }
126 |
127 | // MARK: - UserSettings
128 | open class UserSettings: Codable {
129 | open var stormModeEnabled: Bool?
130 | open var syncGridAlertEnabled: Bool?
131 | open var breakerAlertEnabled: Bool?
132 | open var goOffGridTestBannerEnabled: Bool?
133 | open var powerwallOnboardingSettingsSet: Bool?
134 | open var powerwallTeslaElectricInterestedIn: Bool?
135 | open var vppTourEnabled: Bool?
136 | open var offGridVehicleChargingEnabled: Bool?
137 |
138 | enum CodingKeys: String, CodingKey {
139 | case stormModeEnabled = "storm_mode_enabled"
140 | case syncGridAlertEnabled = "sync_grid_alert_enabled"
141 | case breakerAlertEnabled = "breaker_alert_enabled"
142 | case goOffGridTestBannerEnabled = "go_off_grid_test_banner_enabled"
143 | case powerwallOnboardingSettingsSet = "powerwall_onboarding_settings_set"
144 | case powerwallTeslaElectricInterestedIn = "powerwall_tesla_electric_interested_in"
145 | case vppTourEnabled = "vpp_tour_enabled"
146 | case offGridVehicleChargingEnabled = "off_grid_vehicle_charging_enabled"
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/EnergySiteLiveStatus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnergySiteLiveStatus.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - EnergySiteLiveStatus
12 | open class EnergySiteLiveStatus: Codable {
13 | open var solarPower: Double?
14 | open var energyLeft: Double?
15 | open var totalPackEnergy: Double?
16 | open var percentageCharged: Double?
17 | open var backupCapable: Bool?
18 | open var batteryPower: Double?
19 | open var loadPower: Double?
20 | open var gridStatus: String?
21 | open var gridServicesActive: Bool?
22 | open var gridPower: Double?
23 | open var gridServicesPower: Double?
24 | open var generatorPower: Double?
25 | open var islandStatus: String?
26 | open var stormModeActive: Bool?
27 | open var wallConnectors: [WallConnector]
28 | open var timestamp: Date
29 |
30 | enum CodingKeys: String, CodingKey {
31 | case solarPower = "solar_power"
32 | case energyLeft = "energy_left"
33 | case totalPackEnergy = "total_pack_energy"
34 | case percentageCharged = "percentage_charged"
35 | case backupCapable = "backup_capable"
36 | case batteryPower = "battery_power"
37 | case loadPower = "load_power"
38 | case gridStatus = "grid_status"
39 | case gridServicesActive = "grid_services_active"
40 | case gridPower = "grid_power"
41 | case gridServicesPower = "grid_services_power"
42 | case generatorPower = "generator_power"
43 | case islandStatus = "island_status"
44 | case stormModeActive = "storm_mode_active"
45 | case wallConnectors = "wall_connectors"
46 | case timestamp
47 | }
48 |
49 | open class WallConnector: Codable {
50 | open var din: String?
51 | open var vin: String?
52 | open var wallConnectorState: Int?
53 | open var wallConnectorFaultState: Int?
54 | open var wallConnectorPower: Int?
55 | open var ocppStatus: Int?
56 | open var powershareSessionState: Int?
57 |
58 | enum CodingKeys: String, CodingKey {
59 | case din
60 | case vin
61 | case wallConnectorState = "wall_connector_state"
62 | case wallConnectorFaultState = "wall_connector_fault_state"
63 | case wallConnectorPower = "wall_connector_power"
64 | case ocppStatus = "ocpp_status"
65 | case powershareSessionState = "powershare_session_state"
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/EnergySiteStatus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnergySiteStatus.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - EnergySiteStatus
12 | open class EnergySiteStatus: Codable {
13 | open var resourceType: String
14 | open var siteName: String
15 | open var assetSiteId: String?
16 | open var goOffGridTestBannerEnabled: Bool?
17 | open var stormModeEnabled: Bool?
18 | open var powerwallOnboardingSettingsSet: Bool?
19 | open var powerwallTeslaElectricInterestedIn: Bool?
20 | open var vppTourEnabled: Bool?
21 | open var solarPower: Int?
22 | open var gatewayID: String?
23 | open var energyLeft: Double?
24 | open var totalPackEnergy: Double?
25 | open var percentageCharged: Double?
26 | open var batteryType: String?
27 | open var backupCapable: Bool?
28 | open var batteryPower: Double?
29 | open var syncGridAlertEnabled: Bool?
30 | open var breakerAlertEnabled: Bool?
31 |
32 | enum CodingKeys: String, CodingKey {
33 | case resourceType = "resource_type"
34 | case siteName = "site_name"
35 | case solarPower = "solar_power"
36 | case assetSiteId = "asset_site_id"
37 | case goOffGridTestBannerEnabled = "go_off_grid_test_banner_enabled"
38 | case stormModeEnabled = "storm_mode_enabled"
39 | case powerwallOnboardingSettingsSet = "powerwall_onboarding_settings_set"
40 | case powerwallTeslaElectricInterestedIn = "powerwall_tesla_electric_interested_in"
41 | case vppTourEnabled = "vpp_tour_enabled"
42 | case gatewayID = "gateway_id"
43 | case energyLeft = "energy_left"
44 | case totalPackEnergy = "total_pack_energy"
45 | case percentageCharged = "percentage_charged"
46 | case batteryType = "battery_type"
47 | case backupCapable = "backup_capable"
48 | case batteryPower = "battery_power"
49 | case syncGridAlertEnabled = "sync_grid_alert_enabled"
50 | case breakerAlertEnabled = "breaker_alert_enabled"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ErrorMessage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorMessage.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 28/02/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ErrorMessage: Codable {
12 |
13 | open var error: String?
14 | open var description: String?
15 |
16 | enum CodingKeys: String, CodingKey {
17 | case error = "error"
18 | case description = "error_description"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/GenericResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GenericResponse.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 24/06/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class Response: Decodable {
12 | open var response: T
13 |
14 | public init(response: T) {
15 | self.response = response
16 | }
17 |
18 | // MARK: Codable protocol
19 |
20 | enum CodingKeys: String, CodingKey {
21 | case response
22 | }
23 | }
24 |
25 | open class ArrayResponse: Decodable {
26 | open var response: [T] = []
27 |
28 | // MARK: Codable protocol
29 |
30 | enum CodingKeys: String, CodingKey {
31 | case response
32 | }
33 | }
34 |
35 | open class BoolResponse: Decodable {
36 | open var response: Bool
37 |
38 | public init(response: Bool) {
39 | self.response = response
40 | }
41 |
42 | // MARK: Codable protocol
43 |
44 | enum CodingKeys: String, CodingKey {
45 | case response
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/GuiSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GuiSettings.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 17/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class GuiSettings: Codable {
12 | open var distanceUnits: String?
13 | open var temperatureUnits: String?
14 | open var chargeRateUnits: String?
15 | open var time24Hours: Bool?
16 | open var rangeDisplay: String?
17 | open var timeStamp: Double?
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case distanceUnits = "gui_distance_units"
21 | case temperatureUnits = "gui_temperature_units"
22 | case chargeRateUnits = "gui_charge_rate_units"
23 | case time24Hours = "gui_24_hour_time"
24 | case rangeDisplay = "gui_range_display"
25 | case timeStamp = "timestamp"
26 | }
27 |
28 | required public init(from decoder: Decoder) throws {
29 | let container = try decoder.container(keyedBy: CodingKeys.self)
30 | distanceUnits = try? container.decode(String.self, forKey: .distanceUnits)
31 | temperatureUnits = try? container.decode(String.self, forKey: .temperatureUnits)
32 | chargeRateUnits = try? container.decode(String.self, forKey: .chargeRateUnits)
33 | time24Hours = try? container.decode(Bool.self, forKey: .time24Hours)
34 | rangeDisplay = try? container.decode(String.self, forKey: .rangeDisplay)
35 | timeStamp = try? container.decode(Double.self, forKey: .timeStamp)
36 | }
37 |
38 | public func encode(to encoder: Encoder) throws {
39 | var container = encoder.container(keyedBy: CodingKeys.self)
40 | try container.encodeIfPresent(distanceUnits, forKey: .distanceUnits)
41 | try container.encodeIfPresent(temperatureUnits, forKey: .temperatureUnits)
42 | try container.encodeIfPresent(chargeRateUnits, forKey: .chargeRateUnits)
43 | try container.encodeIfPresent(time24Hours, forKey: .time24Hours)
44 | try container.encodeIfPresent(rangeDisplay, forKey: .rangeDisplay)
45 | try container.encodeIfPresent(timeStamp, forKey: .timeStamp)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/HomeLinkCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeLinkCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 19/10/2019.
6 | // Copyright © 2019 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | open class HomeLinkCommandOptions: Encodable {
13 |
14 | open var latitude: CLLocationDegrees
15 | open var longitude: CLLocationDegrees
16 |
17 | public init(coordinates: CLLocation) {
18 | self.latitude = coordinates.coordinate.latitude
19 | self.longitude = coordinates.coordinate.longitude
20 | }
21 |
22 | enum CodingKeys: String, CodingKey {
23 | case latitude = "lat"
24 | case longitude = "lon"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/MaxDefrostCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MaxDefrostCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 19/10/2019.
6 | // Copyright © 2019 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class MaxDefrostCommandOptions: Encodable {
12 | open var on: Bool
13 |
14 | public init(state: Bool) {
15 | on = state
16 | }
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case on
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Me.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Me.swift
3 | // TeslaSwiftDemoTests
4 | //
5 | // Created by João Nunes on 29/10/2023.
6 | // Copyright © 2023 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Me: Codable {
12 | public var email: String
13 | public var fullName: String
14 | public var profileImageUrl: String
15 |
16 | enum CodingKeys: String, CodingKey {
17 | case email
18 | case fullName = "full_name"
19 | case profileImageUrl = "profile_image_url"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/NearbyChargingSites.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NearbyChargingSites.swift
3 | // TeslaSwift
4 | //
5 | // Created by Jordan Owens on 7/4/19.
6 | // Copyright © 2019 Jordan Owens. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | public protocol Charger {
13 | var name: String? { get }
14 | var type: String? { get }
15 | var distance: Distance? { get }
16 | var location: NearbyChargingSites.ChargerLocation? { get }
17 | }
18 |
19 | open class NearbyChargingSites: Codable {
20 |
21 | open var congestionSyncTimeUTCSecs: Int?
22 | open var destinationChargers: [DestinationCharger]?
23 | open var superchargers: [Supercharger]?
24 | open var timestamp: Double?
25 |
26 | enum CodingKeys: String, CodingKey {
27 | case congestionSyncTimeUTCSecs = "congestion_sync_time_utc_secs"
28 | case destinationChargers = "destination_charging"
29 | case superchargers = "superchargers"
30 | case timestamp = "timestamp"
31 | }
32 |
33 | required public init(from decoder: Decoder) throws {
34 | let container = try decoder.container(keyedBy: CodingKeys.self)
35 | congestionSyncTimeUTCSecs = try? container.decode(Int.self, forKey: .congestionSyncTimeUTCSecs)
36 | destinationChargers = try? container.decode([DestinationCharger].self, forKey: .destinationChargers)
37 | superchargers = try? container.decode([Supercharger].self, forKey: .superchargers)
38 | timestamp = try? container.decode(Double.self, forKey: .timestamp)
39 | }
40 |
41 | public func encode(to encoder: Encoder) throws {
42 | var container = encoder.container(keyedBy: CodingKeys.self)
43 | try container.encodeIfPresent(congestionSyncTimeUTCSecs, forKey: .congestionSyncTimeUTCSecs)
44 | try container.encodeIfPresent(destinationChargers, forKey: .destinationChargers)
45 | try container.encodeIfPresent(superchargers, forKey: .superchargers)
46 | try container.encodeIfPresent(timestamp, forKey: .timestamp)
47 | }
48 |
49 | public struct DestinationCharger: Codable, Charger {
50 | public var distance: Distance?
51 | public var location: ChargerLocation?
52 | public var name: String?
53 | public var type: String?
54 |
55 | enum CodingKeys: String, CodingKey {
56 | case distance = "distance_miles"
57 | case name = "name"
58 | case location = "location"
59 | case type = "type"
60 | }
61 | }
62 |
63 | public struct Supercharger: Codable, Charger {
64 | public var availableStalls: Int?
65 | public var distance: Distance?
66 | public var location: ChargerLocation?
67 | public var name: String?
68 | public var siteClosed: Bool?
69 | public var totalStalls: Int?
70 | public var type: String?
71 |
72 | enum CodingKeys: String, CodingKey {
73 | case availableStalls = "available_stalls"
74 | case distance = "distance_miles"
75 | case location = "location"
76 | case name = "name"
77 | case siteClosed = "site_closed"
78 | case totalStalls = "total_stalls"
79 | case type = "type"
80 | }
81 | }
82 |
83 | public struct ChargerLocation: Codable {
84 | public var latitude: CLLocationDegrees?
85 | public var longitude: CLLocationDegrees?
86 |
87 | enum CodingKeys: String, CodingKey {
88 | case latitude = "lat"
89 | case longitude = "long"
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/OpenTrunkOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OpenTrunkOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 16/04/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum OpenTrunkOptions: String, Codable {
12 |
13 | case rear
14 | case front
15 |
16 | enum CodingKeys: String, CodingKey {
17 | typealias RawValue = String
18 |
19 | case whichTrunk = "which_trunk"
20 | }
21 |
22 | public init(from decoder: Decoder) throws {
23 | let values = try decoder.container(keyedBy: CodingKeys.self)
24 |
25 | if let trunk = try? values.decode(String.self, forKey: .whichTrunk),
26 | trunk == "front" {
27 | self = .front
28 | } else {
29 | self = .rear
30 | }
31 | }
32 |
33 | public func encode(to encoder: Encoder) throws {
34 | var container = encoder.container(keyedBy: CodingKeys.self)
35 |
36 | switch self {
37 | case .rear:
38 | try container.encode("rear", forKey: .whichTrunk)
39 | case .front:
40 | try container.encode("front", forKey: .whichTrunk)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Partner.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Partner.swift
3 | // TeslaSwiftDemoTests
4 | //
5 | // Created by João Nunes on 29/10/2023.
6 | // Copyright © 2023 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct PartnerBody: Codable {
12 | let domain: String
13 | }
14 |
15 | public struct PartnerResponse: Codable {
16 | let response: PartnerResponseBody
17 | }
18 |
19 | public struct PartnerResponseBody: Codable {
20 | let domain: String
21 | let name: String
22 | let description: String
23 | let clientId: String
24 | let ca: String?
25 | let createdAt: Date
26 | let updatedAt: Date
27 | let enterpriseTier: String
28 | let publicKey: String
29 |
30 | enum CodingKeys: String, CodingKey {
31 | case domain
32 | case name
33 | case description
34 | case clientId = "client_id"
35 | case ca
36 | case createdAt = "created_at"
37 | case updatedAt = "updated_at"
38 | case enterpriseTier = "enterprise_tier"
39 | case publicKey = "public_key"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Product.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Product.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/23/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class Product: Codable {
12 | open var vehicle: Vehicle?
13 | open var energySite: EnergySite?
14 |
15 | open var isValidProduct: Bool { vehicle != nil || energySite != nil}
16 |
17 | required public init(from decoder: Decoder) throws {
18 | let vehicleContainer = try decoder.container(keyedBy: Vehicle.CodingKeys.self)
19 | let energySiteContainer = try decoder.container(keyedBy: EnergySite.CodingKeys.self)
20 |
21 | if vehicleContainer.contains(Vehicle.CodingKeys.vehicleID) {
22 | self.vehicle = try Vehicle(from: decoder)
23 | self.energySite = nil
24 | } else if energySiteContainer.contains(EnergySite.CodingKeys.energySiteID) {
25 | self.vehicle = nil
26 | self.energySite = try EnergySite(from: decoder)
27 | } else {
28 | self.vehicle = nil
29 | self.energySite = nil
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Region.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Region.swift
3 | // TeslaSwiftDemoTests
4 | //
5 | // Created by João Nunes on 29/10/2023.
6 | // Copyright © 2023 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Region: Codable {
12 | public var region: String
13 | public var fleetApiBaseUrl: String
14 |
15 | enum CodingKeys: String, CodingKey {
16 | case region
17 | case fleetApiBaseUrl = "fleet_api_base_url"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/RemoteSeatHeaterRequestOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteSeatHeaterRequestOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Jordan Owens on 2/17/19.
6 | // Copyright © 2019 Jordan Owens. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class RemoteSeatHeaterRequestOptions: Encodable {
12 |
13 | open var seat: HeatedSeat
14 | open var level: HeatLevel
15 |
16 | public init(seat: HeatedSeat, level: HeatLevel) {
17 | self.seat = seat
18 | self.level = level
19 | }
20 |
21 | enum CodingKeys: String, CodingKey {
22 | case seat = "heater"
23 | case level = "level"
24 | }
25 | }
26 |
27 | public enum HeatedSeat: Int, Encodable {
28 | case driver = 0
29 | case passenger = 1
30 | case rearLeft = 2
31 | case rearLeftBack = 3
32 | case rearCenter = 4
33 | case rearRight = 5
34 | case rearRightBack = 6
35 | case thirdRowLeft = 7
36 | case thirdRowRight = 8
37 | }
38 |
39 | public enum HeatLevel: Int, Encodable {
40 | case off = 0
41 | case low = 1
42 | case mid = 2
43 | case high = 3
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/RemoteStartDriveCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteStartDriveCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 10/11/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class RemoteStartDriveCommandOptions: Encodable {
12 |
13 | open var password: String?
14 | public init(password: String) {
15 | self.password = password
16 | }
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case password
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/RemoteSteeringWheelHeaterRequestOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteSteeringWheelHeaterRequestOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Jordan Owens on 2/17/19.
6 | // Copyright © 2019 Jordan Owens. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class RemoteSteeringWheelHeaterRequestOptions: Encodable {
12 | open var on: Bool
13 |
14 | public init(on: Bool) {
15 | self.on = on
16 | }
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case on
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ScheduledChargingCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScheduledChargingCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Philip Engberg on 07/11/2021.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ScheduledChargingCommandOptions: Encodable {
12 |
13 | open var enable: Bool
14 | open var time: Int
15 |
16 | public init(enable: Bool, time: Int) {
17 | self.enable = enable
18 | self.time = time
19 | }
20 |
21 | enum CodingKeys: String, CodingKey {
22 | case enable
23 | case time
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ScheduledDepartureCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScheduledDepartureCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Philip Engberg on 07/11/2021.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ScheduledDepartureCommandOptions: Encodable {
12 |
13 | open var enable: Bool
14 | open var departureTime: Int
15 | open var preconditioningEnabled: Bool
16 | open var preconditioningWeekdaysOnly: Bool
17 | open var offPeakChargingEnabled: Bool
18 | open var endOffPeakTime: Int
19 | open var offPeakChargingWeekdaysOnly: Bool
20 |
21 | public init(enable: Bool, departureTime: Int, preconditioningEnabled: Bool, preconditioningWeekdaysOnly: Bool, offPeakChargingEnabled: Bool, endOffPeakTime: Int, offPeakChargingWeekdaysOnly: Bool) {
22 | self.enable = enable
23 | self.departureTime = departureTime
24 | self.preconditioningEnabled = preconditioningEnabled
25 | self.preconditioningWeekdaysOnly = preconditioningWeekdaysOnly
26 | self.offPeakChargingEnabled = offPeakChargingEnabled
27 | self.endOffPeakTime = endOffPeakTime
28 | self.offPeakChargingWeekdaysOnly = offPeakChargingWeekdaysOnly
29 | }
30 |
31 | enum CodingKeys: String, CodingKey {
32 | case enable
33 | case departureTime = "departure_time"
34 | case preconditioningEnabled = "preconditioning_enabled"
35 | case preconditioningWeekdaysOnly = "preconditioning_weekdays_only"
36 | case offPeakChargingEnabled = "off_peak_charging_enabled"
37 | case endOffPeakTime = "end_off_peak_time"
38 | case offPeakChargingWeekdaysOnly = "off_peak_charging_weekdays_only"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/SentryModeCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SentryModeCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Jordan Owens on 3/18/19.
6 | // Copyright © 2019 Jordan Owens. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class SentryModeCommandOptions: Encodable {
12 | open var on: Bool
13 |
14 | public init(activated: Bool) {
15 | self.on = activated
16 | }
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case on
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/SetSunRoofCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetSunRoofCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 10/11/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum RoofState: String, Codable {
12 | case close
13 | case vent
14 | }
15 |
16 | open class SetSunRoofCommandOptions: Encodable {
17 |
18 | open var state: RoofState
19 | open var percent: Int?
20 | public init(state: RoofState, percent: Int?) {
21 | self.state = state
22 | self.percent = percent
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/SetTemperatureCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetTemperatureCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 10/11/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class SetTemperatureCommandOptions: Encodable {
12 |
13 | open var driverTemp: Double?
14 | open var passengerTemp: Double?
15 | public init(driverTemperature: Double, passengerTemperature: Double) {
16 | driverTemp = driverTemperature
17 | passengerTemp = passengerTemperature
18 | }
19 |
20 | enum CodingKeys: String, CodingKey {
21 | case driverTemp = "driver_temp"
22 | case passengerTemp = "passenger_temp"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ShareToVehicleOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShareToVehicleOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Derek Johnson on 10/14/18.
6 | //
7 |
8 | import Foundation
9 | import CoreLocation
10 |
11 | open class ShareToVehicleOptions: Codable {
12 |
13 | public let type: String
14 | public let value: ShareToVehicleValue
15 | public let locale: String
16 | public let timestamp_ms: String
17 |
18 | public init(address: String) {
19 | self.value = ShareToVehicleValue(address: address)
20 | self.type = "share_ext_content_raw"
21 | self.locale = "en-US"
22 | self.timestamp_ms = "12345"
23 | }
24 |
25 | public init(coordinate: CLLocationCoordinate2D) {
26 | self.value = ShareToVehicleValue(coordinate: coordinate)
27 | self.type = "share_ext_content_raw"
28 | self.locale = "en-US"
29 | self.timestamp_ms = "12345"
30 | }
31 |
32 | public class ShareToVehicleValue: Codable {
33 | public let intentAction: String
34 | public let intentType: String
35 | public let intentText: String
36 |
37 | init(address: String) {
38 | self.intentText = "Place Name\n\(address)\n(123) 123-1234\nhttps://maps.google.com/?cid=12345"
39 | self.intentAction = "android.intent.action.SEND"
40 | self.intentType = "text%2F%0Aplain"
41 | }
42 |
43 | init(coordinate: CLLocationCoordinate2D) {
44 | self.intentText = "\(coordinate.latitude),\(coordinate.longitude)"
45 | self.intentAction = "android.intent.action.SEND"
46 | self.intentType = "text%2F%0Aplain"
47 | }
48 |
49 | enum CodingKeys: String, CodingKey {
50 | case intentAction = "android.intent.ACTION"
51 | case intentType = "android.intent.TYPE"
52 | case intentText = "android.intent.extra.TEXT"
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/SoftwareUpdate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SoftwareUpdate.swift
3 | // TeslaSwift
4 | //
5 | // Created by Derek Johnson on 10/31/18.
6 | // Copyright © 2018 Derek Johnson. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class SoftwareUpdate: Codable {
12 |
13 | open var status: String?
14 | open var expectedDuration: Int?
15 | open var scheduledTime: Double?
16 | open var warningTimeRemaining: Double?
17 | open var downloadPercentage: Int?
18 | open var installPercentage: Int?
19 | open var version: String?
20 |
21 | enum CodingKeys: String, CodingKey {
22 | case status = "status"
23 | case expectedDuration = "expected_duration_sec"
24 | case scheduledTime = "scheduled_time_ms"
25 | case warningTimeRemaining = "warning_time_remaining_ms"
26 | case downloadPercentage = "download_perc"
27 | case installPercentage = "install_perc"
28 | case version = "version"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Speed.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Speed.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 21/03/2020.
6 | // Copyright © 2020 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Speed: Codable {
12 | public var value: Measurement
13 |
14 | public init(milesPerHour: Double?) {
15 | let tempValue = milesPerHour ?? 0.0
16 | value = Measurement(value: tempValue, unit: UnitSpeed.milesPerHour)
17 | }
18 | public init(kilometersPerHour: Double) {
19 | value = Measurement(value: kilometersPerHour, unit: UnitSpeed.kilometersPerHour)
20 | }
21 |
22 | public init(from decoder: Decoder) throws {
23 | let container = try decoder.singleValueContainer()
24 | if let tempValue = try? container.decode(Double.self) {
25 | value = Measurement(value: tempValue, unit: UnitSpeed.milesPerHour)
26 | } else {
27 | value = Measurement(value: 0, unit: UnitSpeed.milesPerHour)
28 | }
29 | }
30 |
31 | public func encode(to encoder: Encoder) throws {
32 |
33 | var container = encoder.singleValueContainer()
34 | try container.encode(value.converted(to: .milesPerHour).value)
35 |
36 | }
37 |
38 | public var milesPerHour: Double { return value.converted(to: .milesPerHour).value }
39 | public var kilometersPerHour: Double { return value.converted(to: .kilometersPerHour).value }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/SpeedLimitOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SpeedLimitOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 15/12/2018.
6 | // Copyright © 2018 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class SetSpeedLimitOptions: Codable {
12 |
13 | open var limit: Measurement
14 |
15 | enum CodingKeys: String, CodingKey {
16 | case limit = "limit_mph"
17 | }
18 |
19 | public init(limit: Measurement) {
20 | self.limit = limit.converted(to: UnitSpeed.milesPerHour)
21 | }
22 |
23 | required public init(from decoder: Decoder) throws {
24 |
25 | let container = try decoder.container(keyedBy: CodingKeys.self)
26 |
27 | if let value = try? container.decode(Int.self, forKey: .limit) {
28 | limit = Measurement(value: Double(value), unit: UnitSpeed.milesPerHour)
29 | } else {
30 | limit = Measurement(value: 0, unit: UnitSpeed.milesPerHour)
31 | }
32 | }
33 |
34 | public func encode(to encoder: Encoder) throws {
35 | var container = encoder.container(keyedBy: CodingKeys.self)
36 |
37 | let milesUnit = limit.converted(to: UnitSpeed.milesPerHour)
38 | try container.encode(Int(milesUnit.value), forKey: .limit)
39 | }
40 |
41 | }
42 |
43 | open class SpeedLimitPinOptions: Codable {
44 |
45 | open var pin: String // 4 digits pin
46 |
47 | public init(pin: String) {
48 | self.pin = pin
49 | }
50 |
51 | enum CodingKeys: String, CodingKey {
52 | case pin
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/ValetCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ValetCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 12/04/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class ValetCommandOptions: Codable {
12 | open var on: Bool = false
13 | open var password: String?
14 |
15 | public init(valetActivated: Bool, pin: String?) {
16 | on = valetActivated
17 | password = pin
18 | }
19 |
20 | enum CodingKeys: String, CodingKey {
21 | case on
22 | case password
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/Vehicle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Vehicle.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 05/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class Vehicle: Codable {
12 |
13 | open var backseatToken: String?
14 | open var backseatTokenUpdatedAt: Date?
15 | open var calendarEnabled: Bool?
16 | open var color: String?
17 | open var displayName: String?
18 | open var id: VehicleId? {
19 | get {
20 | guard let value = idInt else { return nil }
21 | return VehicleId(id: value)
22 | }
23 | set {
24 | guard let newValue = newValue?.id else { idInt = nil; return }
25 | idInt = newValue
26 | }
27 | }
28 | open var idInt: Int64?
29 | open var idS: String?
30 | open var inService: Bool?
31 | open var optionCodes: String?
32 | open var state: String?
33 | open var tokens: [String]?
34 | open var vehicleID: Int64?
35 | open var vin: String?
36 |
37 | // fields via /api/1/products
38 | open var userId: Int64?
39 | open var accessType: String?
40 | open var cachedData: String?
41 | open var apiVersion: Int?
42 | open var bleAutopairEnrolled: Bool?
43 | open var commandSigning: Bool?
44 | open var releaseNotesSupported: Bool?
45 |
46 | // MARK: Codable protocol
47 |
48 | enum CodingKeys: String, CodingKey {
49 |
50 | case backseatToken = "backseat_token"
51 | case backseatTokenUpdatedAt = "backseat_token_updated_at"
52 | case calendarEnabled = "calendar_enabled"
53 | case color = "color"
54 | case displayName = "display_name"
55 | case idInt = "id"
56 | case idS = "id_s"
57 | case inService = "in_service"
58 | case optionCodes = "option_codes"
59 | case state = "state"
60 | case tokens = "tokens"
61 | case vehicleID = "vehicle_id"
62 | case vin = "vin"
63 | case userId = "user_id"
64 | case accessType = "access_type"
65 | case cachedData = "cached_data"
66 | case apiVersion = "api_version"
67 | case bleAutopairEnrolled = "ble_autopair_enrolled"
68 | case commandSigning = "command_signing"
69 | case releaseNotesSupported = "release_notes_supported"
70 |
71 | }
72 |
73 | required public init(from decoder: Decoder) throws {
74 |
75 | let container = try decoder.container(keyedBy: CodingKeys.self)
76 | backseatToken = try? container.decode(String.self, forKey: .backseatToken)
77 | backseatTokenUpdatedAt = try? container.decode(Date.self, forKey: .backseatTokenUpdatedAt)
78 | calendarEnabled = {
79 | if let boolValue = try? container.decode(Bool.self, forKey: .calendarEnabled) {
80 | return boolValue
81 | } else if let intValue = try? container.decode(Int.self, forKey: .calendarEnabled) {
82 | return intValue > 0
83 | } else {
84 | return nil
85 | }
86 | }()
87 | color = try? container.decode(String.self, forKey: .color)
88 | displayName = try? container.decode(String.self, forKey: .displayName)
89 | idInt = try? container.decode(Int64.self, forKey: .idInt)
90 | idS = try? container.decode(String.self, forKey: .idS)
91 | inService = {
92 | if let boolValue = try? container.decode(Bool.self, forKey: .inService) {
93 | return boolValue
94 | } else if let intValue = try? container.decode(Int.self, forKey: .inService) {
95 | return intValue > 0
96 | } else {
97 | return nil
98 | }
99 | }()
100 | optionCodes = try? container.decode(String.self, forKey: .optionCodes)
101 | state = try? container.decode(String.self, forKey: .state)
102 | tokens = try? container.decode([String].self, forKey: .tokens)
103 | vehicleID = try? container.decode(Int64.self, forKey: .vehicleID)
104 | vin = try? container.decode(String.self, forKey: .vin)
105 | userId = try? container.decode(Int64.self, forKey: .userId)
106 | accessType = try? container.decode(String.self, forKey: .accessType)
107 | cachedData = try? container.decode(String.self, forKey: .cachedData)
108 | apiVersion = try? container.decode(Int.self, forKey: .apiVersion)
109 | bleAutopairEnrolled = try? container.decode(Bool.self, forKey: .bleAutopairEnrolled)
110 | commandSigning = try? container.decode(Bool.self, forKey: .commandSigning)
111 | releaseNotesSupported = try? container.decode(Bool.self, forKey: .releaseNotesSupported)
112 | }
113 |
114 | public func encode(to encoder: Encoder) throws {
115 |
116 | var container = encoder.container(keyedBy: CodingKeys.self)
117 | try container.encodeIfPresent(backseatToken, forKey: .backseatToken)
118 | try container.encodeIfPresent(backseatTokenUpdatedAt, forKey: .backseatTokenUpdatedAt)
119 | try container.encodeIfPresent(calendarEnabled, forKey: .calendarEnabled)
120 | try container.encodeIfPresent(color, forKey: .color)
121 | try container.encodeIfPresent(displayName, forKey: .displayName)
122 | try container.encodeIfPresent(idInt, forKey: .idInt)
123 | try container.encodeIfPresent(idS, forKey: .idS)
124 | try container.encodeIfPresent(inService, forKey: .inService)
125 | try container.encodeIfPresent(optionCodes, forKey: .optionCodes)
126 | try container.encodeIfPresent(state, forKey: .state)
127 | try container.encodeIfPresent(tokens, forKey: .tokens)
128 | try container.encodeIfPresent(vehicleID, forKey: .vehicleID)
129 | try container.encodeIfPresent(vin, forKey: .vin)
130 | try container.encodeIfPresent(userId, forKey: .userId)
131 | try container.encodeIfPresent(accessType, forKey: .accessType)
132 | try container.encodeIfPresent(cachedData, forKey: .cachedData)
133 | try container.encodeIfPresent(apiVersion, forKey: .apiVersion)
134 | try container.encodeIfPresent(bleAutopairEnrolled, forKey: .bleAutopairEnrolled)
135 | try container.encodeIfPresent(commandSigning, forKey: .commandSigning)
136 | try container.encodeIfPresent(releaseNotesSupported, forKey: .releaseNotesSupported)
137 | }
138 | }
139 |
140 | public class VehicleId {
141 | public let id: Int64
142 |
143 | init(id: Int64) {
144 | self.id = id
145 | }
146 | }
147 |
148 | public class SiteId {
149 | public let id: Decimal
150 |
151 | init(id: Decimal) {
152 | self.id = id
153 | }
154 | }
155 |
156 | public class BatteryId {
157 | public let id: String
158 |
159 | init(id: String) {
160 | self.id = id
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/VehicleConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VehicleConfig.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 12/03/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class VehicleConfig: Codable {
12 | open var canAcceptNavigationRequests: Bool?
13 | open var canActuateTrunks: Bool?
14 | open var carSpecialType: String?
15 | open var carType: String?
16 | open var chargePortType: String?
17 | open var euVehicle: Bool?
18 | open var exteriorColor: String?
19 | open var hasAirSuspension: Bool?
20 | open var hasLudicrousMode: Bool?
21 | open var motorizedChargePort: Bool?
22 | open var perfConfig: String?
23 | open var plg: Bool?
24 | open var rearSeatHeaters: Int?
25 | open var rearSeatType: Int?
26 | open var rhd: Bool?
27 | open var roofColor: String? // "None" for panoramic roof
28 | open var seatType: Int?
29 | open var spoilerType: String?
30 | open var sunRoofInstalled: Int?
31 | open var thirdRowSeats: String?
32 | open var timeStamp: Double?
33 | open var trimBadging: String?
34 | open var wheelType: String?
35 |
36 | enum CodingKeys: String, CodingKey {
37 | case canAcceptNavigationRequests = "can_accept_navigation_requests"
38 | case canActuateTrunks = "can_actuate_trunks"
39 | case carSpecialType = "car_special_type"
40 | case carType = "car_type"
41 | case chargePortType = "charge_port_type"
42 | case euVehicle = "eu_vehicle"
43 | case exteriorColor = "exterior_color"
44 | case hasAirSuspension = "has_air_suspension"
45 | case hasLudicrousMode = "has_ludicrous_mode"
46 | case motorizedChargePort = "motorized_charge_port"
47 | case perfConfig = "perf_config"
48 | case plg = "plg"
49 | case rearSeatHeaters = "rear_seat_heaters"
50 | case rearSeatType = "rear_seat_type"
51 | case rhd = "rhd"
52 | case roofColor = "roof_color"
53 | case seatType = "seat_type"
54 | case spoilerType = "spoiler_type"
55 | case sunRoofInstalled = "sun_roof_installed"
56 | case thirdRowSeats = "third_row_seats"
57 | case timeStamp = "timestamp"
58 | case trimBadging = "trim_badging"
59 | case wheelType = "wheel_type"
60 | }
61 |
62 | required public init(from decoder: Decoder) throws {
63 | let container = try decoder.container(keyedBy: CodingKeys.self)
64 |
65 | canAcceptNavigationRequests = try? container.decode(Bool.self, forKey: .canAcceptNavigationRequests)
66 | canActuateTrunks = try? container.decode(Bool.self, forKey: .canActuateTrunks)
67 | carSpecialType = try? container.decode(String.self, forKey: .carSpecialType)
68 | carType = try? container.decode(String.self, forKey: .carType)
69 | chargePortType = try? container.decode(String.self, forKey: .chargePortType)
70 | euVehicle = try? container.decode(Bool.self, forKey: .euVehicle)
71 | hasAirSuspension = try? container.decode(Bool.self, forKey: .hasAirSuspension)
72 | exteriorColor = try? container.decode(String.self, forKey: .exteriorColor)
73 | hasLudicrousMode = try? container.decode(Bool.self, forKey: .hasLudicrousMode)
74 | motorizedChargePort = try? container.decode(Bool.self, forKey: .motorizedChargePort)
75 | perfConfig = try? container.decode(String.self, forKey: .perfConfig)
76 | plg = try? container.decode(Bool.self, forKey: .plg)
77 |
78 | rearSeatHeaters = try? container.decode(Int.self, forKey: .rearSeatHeaters)
79 |
80 | rearSeatType = try? container.decode(Int.self, forKey: .rearSeatType)
81 | rhd = try? container.decode(Bool.self, forKey: .rhd)
82 | roofColor = try? container.decode(String.self, forKey: .roofColor) // "None" for panoramic roof
83 | seatType = try? container.decode(Int.self, forKey: .seatType)
84 | spoilerType = try? container.decode(String.self, forKey: .spoilerType)
85 | sunRoofInstalled = try? container.decode(Int.self, forKey: .sunRoofInstalled)
86 | thirdRowSeats = try? container.decode(String.self, forKey: .thirdRowSeats)
87 | timeStamp = try? container.decode(Double.self, forKey: .timeStamp)
88 | trimBadging = try? container.decode(String.self, forKey: .trimBadging)
89 | wheelType = try? container.decode(String.self, forKey: .wheelType)
90 | }
91 |
92 | public func encode(to encoder: Encoder) throws {
93 | var container = encoder.container(keyedBy: CodingKeys.self)
94 |
95 | try container.encodeIfPresent(canAcceptNavigationRequests, forKey: .canAcceptNavigationRequests)
96 | try container.encodeIfPresent(canActuateTrunks, forKey: .canActuateTrunks)
97 | try container.encodeIfPresent(carSpecialType, forKey: .carSpecialType)
98 | try container.encodeIfPresent(carType, forKey: .carType)
99 | try container.encodeIfPresent(chargePortType, forKey: .chargePortType)
100 | try container.encodeIfPresent(euVehicle, forKey: .euVehicle)
101 | try container.encodeIfPresent(exteriorColor, forKey: .exteriorColor)
102 | try container.encodeIfPresent(hasAirSuspension, forKey: .hasAirSuspension)
103 | try container.encodeIfPresent(hasLudicrousMode, forKey: .hasLudicrousMode)
104 | try container.encodeIfPresent(motorizedChargePort, forKey: .motorizedChargePort)
105 | try container.encodeIfPresent(perfConfig, forKey: .perfConfig)
106 | try container.encodeIfPresent(plg, forKey: .plg)
107 | try container.encodeIfPresent(rearSeatHeaters, forKey: .rearSeatHeaters)
108 |
109 | try container.encodeIfPresent(rearSeatType, forKey: .rearSeatType)
110 | try container.encodeIfPresent(rhd, forKey: .rhd)
111 | try container.encodeIfPresent(roofColor, forKey: .roofColor)
112 | try container.encodeIfPresent(seatType, forKey: .seatType)
113 | try container.encodeIfPresent(spoilerType, forKey: .spoilerType)
114 | try container.encodeIfPresent(sunRoofInstalled, forKey: .sunRoofInstalled)
115 | try container.encodeIfPresent(thirdRowSeats, forKey: .thirdRowSeats)
116 | try container.encodeIfPresent(timeStamp, forKey: .timeStamp)
117 | try container.encodeIfPresent(trimBadging, forKey: .trimBadging)
118 | try container.encodeIfPresent(wheelType, forKey: .wheelType)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/VehicleExtended.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VehicleExtended.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 12/03/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class VehicleExtended: Vehicle {
12 | open var chargeState: ChargeState?
13 | open var climateState: ClimateState?
14 | open var driveState: DriveState?
15 | open var guiSettings: GuiSettings?
16 | open var vehicleConfig: VehicleConfig?
17 | open var vehicleState: VehicleState?
18 |
19 | private enum CodingKeys: String, CodingKey {
20 | case userId = "user_id"
21 | case chargeState = "charge_state"
22 | case climateState = "climate_state"
23 | case driveState = "drive_state"
24 | case guiSettings = "gui_settings"
25 | case vehicleConfig = "vehicle_config"
26 | case vehicleState = "vehicle_state"
27 |
28 | case superWorkaround = "super" // We need this to be able to decode from the Tesla API and from an encoded string
29 | }
30 |
31 | required public init(from decoder: Decoder) throws {
32 | let container = try decoder.container(keyedBy: CodingKeys.self)
33 | chargeState = try container.decodeIfPresent(ChargeState.self, forKey: .chargeState)
34 | climateState = try container.decodeIfPresent(ClimateState.self, forKey: .climateState)
35 | driveState = try container.decodeIfPresent(DriveState.self, forKey: .driveState)
36 | guiSettings = try container.decodeIfPresent(GuiSettings.self, forKey: .guiSettings)
37 | vehicleConfig = try container.decodeIfPresent(VehicleConfig.self, forKey: .vehicleConfig)
38 | vehicleState = try container.decodeIfPresent(VehicleState.self, forKey: .vehicleState)
39 | if container.contains(.superWorkaround) {
40 | try super.init(from: container.superDecoder() )
41 | } else {
42 | try super.init(from: decoder)
43 | }
44 | }
45 |
46 | override open func encode(to encoder: Encoder) throws {
47 | var container = encoder.container(keyedBy: CodingKeys.self)
48 | try container.encodeIfPresent(chargeState, forKey: .chargeState)
49 | try container.encodeIfPresent(climateState, forKey: .climateState)
50 | try container.encodeIfPresent(driveState, forKey: .driveState)
51 | try container.encodeIfPresent(guiSettings, forKey: .guiSettings)
52 | try container.encodeIfPresent(vehicleConfig, forKey: .vehicleConfig)
53 | try container.encodeIfPresent(vehicleState, forKey: .vehicleState)
54 |
55 | let superEncoder = container.superEncoder()
56 | try super.encode(to: superEncoder)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/Model/WindowControlCommandOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WindowControlCommandOptions.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 19/10/2019.
6 | // Copyright © 2019 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | public enum WindowState: String, Codable {
13 | case close
14 | case vent
15 | }
16 |
17 | open class WindowControlCommandOptions: Encodable {
18 |
19 | open var latitude: CLLocationDegrees = 0
20 | open var longitude: CLLocationDegrees = 0
21 | open var command: WindowState
22 |
23 | public init(command: WindowState) {
24 | self.command = command
25 | }
26 |
27 | enum CodingKeys: String, CodingKey {
28 | case latitude = "lat"
29 | case longitude = "lon"
30 | case command = "command"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/TeslaEndpoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TeslaEndpoint.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 16/04/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum Endpoint {
12 | case revoke
13 | case oAuth2Authorization(auth: AuthCodeRequest)
14 | case oAuth2AuthorizationCN(auth: AuthCodeRequest)
15 | case oAuth2Token
16 | case oAuth2TokenCN
17 | case oAuth2revoke(token: String)
18 | case oAuth2revokeCN(token: String)
19 |
20 | case me
21 | case region
22 |
23 | case partnerAccounts
24 | case vehicles
25 | case vehicleSummary(vehicleID: VehicleId)
26 | case mobileAccess(vehicleID: VehicleId)
27 | case allStates(vehicleID: VehicleId, endpoints: [AllStatesEndpoints])
28 | case chargeState(vehicleID: VehicleId)
29 | case climateState(vehicleID: VehicleId)
30 | case driveState(vehicleID: VehicleId)
31 | case nearbyChargingSites(vehicleID: VehicleId)
32 | case guiSettings(vehicleID: VehicleId)
33 | case vehicleState(vehicleID: VehicleId)
34 | case vehicleConfig(vehicleID: VehicleId)
35 | case wakeUp(vehicleID: VehicleId)
36 | case command(vehicleID: VehicleId, command: VehicleCommand)
37 | case signedCommand(vehicleID: VehicleId)
38 | case products
39 | case chargeHistory(vehicleID: VehicleId)
40 | case getEnergySiteStatus(siteID: SiteId)
41 | case getEnergySiteLiveStatus(siteID: SiteId)
42 | case getEnergySiteInfo(siteID: SiteId)
43 | case getEnergySiteHistory(siteID: SiteId, period: EnergySiteHistory.Period)
44 | case getBatteryStatus(batteryID: BatteryId)
45 | case getBatteryData(batteryID: BatteryId)
46 | case getBatteryPowerHistory(batteryID: BatteryId)
47 | }
48 |
49 | public enum AllStatesEndpoints: String {
50 | case chargeState = "charge_state"
51 | case climateState = "climate_state"
52 | case closuresState = "closures_state"
53 | case driveState = "drive_state"
54 | case guiSettings = "gui_settings"
55 | case locationData = "location_data" // Same as driveState but with location
56 | case vehicleConfig = "vehicle_config"
57 | case vehicleState = "vehicle_state"
58 | case vehicleDataCombo = "vehicle_data_combo"
59 |
60 | public static var all: [AllStatesEndpoints] = [.chargeState, .climateState, .closuresState, .driveState, .guiSettings, .vehicleConfig, .vehicleState]
61 | public static var allWithLocation: [AllStatesEndpoints] = [.chargeState, .climateState, .closuresState, .locationData, .guiSettings, .vehicleConfig, .vehicleState]
62 | }
63 |
64 | extension Endpoint {
65 |
66 | var path: String {
67 | switch self {
68 | // Auth
69 | case .revoke:
70 | return "/oauth/revoke"
71 | case .oAuth2Authorization, .oAuth2AuthorizationCN:
72 | return "/oauth2/v3/authorize"
73 | case .oAuth2Token, .oAuth2TokenCN:
74 | return "/oauth2/v3/token"
75 | case .oAuth2revoke, .oAuth2revokeCN:
76 | return "/oauth2/v3/revoke"
77 | case .partnerAccounts:
78 | return "/api/1/partner_accounts"
79 |
80 | case .me:
81 | return "/api/1/users/me"
82 | case .region:
83 | return "/api/1/users/region"
84 | // Vehicle Data and Commands
85 | case .vehicles:
86 | return "/api/1/vehicles"
87 | case .vehicleSummary(let vehicleID):
88 | return "/api/1/vehicles/\(vehicleID.id)"
89 | case .mobileAccess(let vehicleID):
90 | return "/api/1/vehicles/\(vehicleID.id)/mobile_enabled"
91 | case .allStates(let vehicleID, _):
92 | return "/api/1/vehicles/\(vehicleID.id)/vehicle_data"
93 | case .chargeState(let vehicleID):
94 | return "/api/1/vehicles/\(vehicleID.id)/data_request/charge_state"
95 | case .climateState(let vehicleID):
96 | return "/api/1/vehicles/\(vehicleID.id)/data_request/climate_state"
97 | case .driveState(let vehicleID):
98 | return "/api/1/vehicles/\(vehicleID.id)/data_request/drive_state"
99 | case .guiSettings(let vehicleID):
100 | return "/api/1/vehicles/\(vehicleID.id)/data_request/gui_settings"
101 | case .nearbyChargingSites(let vehicleID):
102 | return "/api/1/vehicles/\(vehicleID.id)/nearby_charging_sites"
103 | case .vehicleState(let vehicleID):
104 | return "/api/1/vehicles/\(vehicleID.id)/data_request/vehicle_state"
105 | case .vehicleConfig(let vehicleID):
106 | return "/api/1/vehicles/\(vehicleID.id)/data_request/vehicle_config"
107 | case .wakeUp(let vehicleID):
108 | return "/api/1/vehicles/\(vehicleID.id)/wake_up"
109 | case let .command(vehicleID, command):
110 | return "/api/1/vehicles/\(vehicleID.id)/\(command.path())"
111 | case let .signedCommand(vehicleID):
112 | return "/api/1/vehicles/\(vehicleID.id)/signed_command"
113 | case .products:
114 | return "/api/1/products"
115 | case let .chargeHistory(vehicleID):
116 | return "/api/1/vehicles/\(vehicleID.id)/charge_history"
117 |
118 | // Energy Data
119 | case .getEnergySiteStatus(let siteID):
120 | return "/api/1/energy_sites/\(siteID.id)/site_status"
121 | case .getEnergySiteLiveStatus(let siteID):
122 | return "/api/1/energy_sites/\(siteID.id)/live_status"
123 | case .getEnergySiteInfo(let siteID):
124 | return "/api/1/energy_sites/\(siteID.id)/site_info"
125 | case .getEnergySiteHistory(let siteID, _):
126 | return "/api/1/energy_sites/\(siteID.id)/history"
127 | case .getBatteryStatus(let batteryID):
128 | return "/api/1/powerwalls/\(batteryID.id)/status"
129 | case .getBatteryData(let batteryID):
130 | return "/api/1/powerwalls/\(batteryID.id)/"
131 | case .getBatteryPowerHistory(let batteryID):
132 | return "/api/1/powerwalls/\(batteryID.id)/powerhistory"
133 | }
134 | }
135 |
136 | var method: String {
137 | switch self {
138 | case .revoke, .oAuth2Token, .oAuth2TokenCN, .wakeUp, .partnerAccounts, .chargeHistory, .command, .signedCommand:
139 | return "POST"
140 | case .me, .region, .vehicles, .vehicleSummary, .mobileAccess, .allStates, .chargeState, .climateState, .driveState, .guiSettings, .vehicleState, .vehicleConfig, .nearbyChargingSites, .oAuth2Authorization, .oAuth2revoke, .oAuth2AuthorizationCN, .oAuth2revokeCN, .products, .getEnergySiteStatus, .getEnergySiteLiveStatus, .getEnergySiteInfo, .getEnergySiteHistory, .getBatteryStatus, .getBatteryData, .getBatteryPowerHistory:
141 | return "GET"
142 | }
143 | }
144 |
145 | var queryParameters: [URLQueryItem] {
146 | switch self {
147 | case let .oAuth2Authorization(auth):
148 | return auth.parameters()
149 | case let .oAuth2revoke(token):
150 | return [URLQueryItem(name: "token", value: token)]
151 | case let .getEnergySiteHistory(_, period):
152 | return [URLQueryItem(name: "period", value: period.rawValue), URLQueryItem(name: "kind", value: "energy")]
153 | case let .allStates(_, endpoints):
154 | return [URLQueryItem(name: "endpoints", value: endpoints.map({ $0.rawValue }).joined(separator: ";"))]
155 | default:
156 | return []
157 | }
158 | }
159 |
160 | func baseURL(teslaAPI: TeslaAPI) -> String {
161 | switch self {
162 | case .oAuth2Authorization, .oAuth2Token, .oAuth2revoke:
163 | return "https://auth.tesla.com"
164 | case .oAuth2AuthorizationCN, .oAuth2TokenCN, .oAuth2revokeCN:
165 | return "https://auth.tesla.cn"
166 | default:
167 | return teslaAPI.url
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/Sources/TeslaSwift/VehicleCommands.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VehicleCommands.swift
3 | // TeslaSwift
4 | //
5 | // Created by João Nunes on 02/04/2022.
6 | // Copyright © 2022 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation.CLLocation
11 |
12 | public enum VehicleCommand {
13 | case valetMode(valetActivated: Bool, pin: String?)
14 | case resetValetPin
15 | case openChargeDoor
16 | case closeChargeDoor
17 | case chargeLimitStandard
18 | case chargeLimitMaxRange
19 | case chargeLimitPercentage(limit: Int)
20 | case startCharging
21 | case stopCharging
22 | case scheduledCharging(enable: Bool, time: Int)
23 | case scheduledDeparture(options: ScheduledDepartureCommandOptions)
24 | case flashLights
25 | case triggerHomeLink(location: CLLocation)
26 | case honkHorn
27 | case unlockDoors
28 | case lockDoors
29 | case remoteBoombox
30 | case setTemperature(driverTemperature: Double, passengerTemperature: Double)
31 | case setMaxDefrost(on: Bool)
32 | case startAutoConditioning
33 | case stopAutoConditioning
34 | case setSunRoof(state: RoofState, percentage: Int?)
35 | case startVehicle(password: String)
36 | case openTrunk(options: OpenTrunkOptions)
37 | case togglePlayback
38 | case nextTrack
39 | case previousTrack
40 | case nextFavorite
41 | case previousFavorite
42 | case volumeUp
43 | case volumeDown
44 | case shareToVehicle(options: ShareToVehicleOptions)
45 | case cancelSoftwareUpdate
46 | case scheduleSoftwareUpdate
47 | case speedLimitSetLimit(speed: Measurement)
48 | case speedLimitActivate(pin: String)
49 | case speedLimitDeactivate(pin: String)
50 | case speedLimitClearPin(pin: String)
51 | case setSeatHeater(seat: HeatedSeat, level: HeatLevel)
52 | case setSteeringWheelHeater(on: Bool)
53 | case sentryMode(activated: Bool)
54 | case windowControl(state: WindowState)
55 | case setCharging(amps: Int)
56 |
57 | func path() -> String {
58 | switch self {
59 | case .valetMode:
60 | return "command/set_valet_mode"
61 | case .resetValetPin:
62 | return "command/reset_valet_pin"
63 | case .openChargeDoor:
64 | return "command/charge_port_door_open"
65 | case .closeChargeDoor:
66 | return "command/charge_port_door_close"
67 | case .chargeLimitStandard:
68 | return "command/charge_standard"
69 | case .chargeLimitMaxRange:
70 | return "command/charge_max_range"
71 | case .chargeLimitPercentage:
72 | return "command/set_charge_limit"
73 | case .startCharging:
74 | return "command/charge_start"
75 | case .stopCharging:
76 | return "command/charge_stop"
77 | case .scheduledCharging:
78 | return "command/set_scheduled_charging"
79 | case .scheduledDeparture:
80 | return "command/set_scheduled_departure"
81 | case .flashLights:
82 | return "command/flash_lights"
83 | case .triggerHomeLink:
84 | return "command/trigger_homelink"
85 | case .honkHorn:
86 | return "command/honk_horn"
87 | case .unlockDoors:
88 | return "command/door_unlock"
89 | case .lockDoors:
90 | return "command/door_lock"
91 | case .remoteBoombox:
92 | return "command/remote_boombox"
93 | case .setTemperature:
94 | return "command/set_temps"
95 | case .setMaxDefrost:
96 | return "command/set_preconditioning_max"
97 | case .startAutoConditioning:
98 | return "command/auto_conditioning_start"
99 | case .stopAutoConditioning:
100 | return "command/auto_conditioning_stop"
101 | case .setSunRoof:
102 | return "command/sun_roof_control"
103 | case .startVehicle:
104 | return "command/remote_start_drive"
105 | case .openTrunk:
106 | return "command/actuate_trunk"
107 | case .togglePlayback:
108 | return "command/media_toggle_playback"
109 | case .nextTrack:
110 | return "command/media_next_track"
111 | case .previousTrack:
112 | return "command/media_prev_track"
113 | case .nextFavorite:
114 | return "command/media_next_fav"
115 | case .previousFavorite:
116 | return "command/media_prev_fav"
117 | case .volumeUp:
118 | return "command/media_volume_up"
119 | case .volumeDown:
120 | return "command/media_volume_down"
121 | case .shareToVehicle:
122 | return "command/share"
123 | case .scheduleSoftwareUpdate:
124 | return "command/schedule_software_update"
125 | case .cancelSoftwareUpdate:
126 | return "command/cancel_software_update"
127 | case .speedLimitSetLimit:
128 | return "command/speed_limit_set_limit"
129 | case .speedLimitActivate:
130 | return "command/speed_limit_activate"
131 | case .speedLimitDeactivate:
132 | return "command/speed_limit_deactivate"
133 | case .speedLimitClearPin:
134 | return "command/speed_limit_clear_pin"
135 | case .setSeatHeater:
136 | return "command/remote_seat_heater_request"
137 | case .setSteeringWheelHeater:
138 | return "command/remote_steering_wheel_heater_request"
139 | case .sentryMode:
140 | return "command/set_sentry_mode"
141 | case .windowControl:
142 | return "command/window_control"
143 | case .setCharging:
144 | return "command/set_charging_amps"
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 04/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import TeslaSwift
11 |
12 | // Change this!
13 | let clientID = "ABC"
14 | let clientSecret = "DEF"
15 | let redirectURI = "teslaswift://teslaswift"
16 |
17 | @UIApplicationMain
18 | class AppDelegate: UIResponder, UIApplicationDelegate {
19 |
20 | var window: UIWindow?
21 | var api: TeslaSwift!
22 |
23 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
24 | // Override point for customization after application launch.
25 |
26 | let teslaAPI = TeslaAPI.fleetAPI(region: .europeMiddleEastAfrica, clientID: clientID, clientSecret: clientSecret, redirectURI: redirectURI)
27 | api = TeslaSwift(teslaAPI: teslaAPI)
28 | api.debuggingEnabled = true
29 |
30 | if let jsonString = UserDefaults.standard.object(forKey: "tesla.token") as? String,
31 | let token: AuthToken = jsonString.decodeJSON(),
32 | let email = UserDefaults.standard.object(forKey: "tesla.email") as? String {
33 | api.reuse(token: token, email: email)
34 | }
35 |
36 | return true
37 | }
38 |
39 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
40 | print(url)
41 | Task { @MainActor in
42 | do {
43 | _ = try await api.authenticateWebNative(url: url)
44 | NotificationCenter.default.post(name: Notification.Name.nativeLoginDone, object: nil)
45 | } catch {
46 | print("Error")
47 | }
48 | }
49 |
50 | return true
51 | }
52 |
53 | func applicationWillResignActive(_ application: UIApplication) {
54 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
55 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
56 | }
57 |
58 | func applicationDidEnterBackground(_ application: UIApplication) {
59 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
60 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
61 |
62 | UserDefaults.standard.set(api.token?.jsonString, forKey: "tesla.token")
63 | UserDefaults.standard.set(api.email, forKey: "tesla.email")
64 | UserDefaults.standard.synchronize()
65 | }
66 |
67 | func applicationWillEnterForeground(_ application: UIApplication) {
68 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
69 | }
70 |
71 | func applicationDidBecomeActive(_ application: UIApplication) {
72 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
73 | }
74 |
75 | func applicationWillTerminate(_ application: UIApplication) {
76 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Assets.xcassets/first.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "first.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Assets.xcassets/first.imageset/first.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonasman/TeslaSwift/6fa8ce145451f63a6399fe153e439bfb1b20b500/TeslaSwiftDemo/Assets.xcassets/first.imageset/first.pdf
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Assets.xcassets/second.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "second.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Assets.xcassets/second.imageset/second.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonasman/TeslaSwift/6fa8ce145451f63a6399fe153e439bfb1b20b500/TeslaSwiftDemo/Assets.xcassets/second.imageset/second.pdf
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/FirstViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirstViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 04/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | #if canImport(Combine)
11 | import Combine
12 | #endif
13 | import TeslaSwift
14 |
15 | class FirstViewController: UIViewController, UITableViewDataSource {
16 |
17 | @IBOutlet weak var tableView: UITableView!
18 |
19 | var data: [Product]?
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | getVehicles()
25 |
26 | NotificationCenter.default.addObserver(forName: Notification.Name.loginDone, object: nil, queue: nil) { [weak self] (notification: Notification) in
27 |
28 | self?.getVehicles()
29 | }
30 | }
31 |
32 | override func viewDidAppear(_ animated: Bool) {
33 | super.viewDidAppear(animated)
34 |
35 | tableView.estimatedRowHeight = 50.0
36 |
37 | }
38 |
39 | func getVehicles() {
40 | Task { @MainActor in
41 | let products = try await api.getProducts()
42 | self.data = products
43 | self.tableView.reloadData()
44 | }
45 | }
46 |
47 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
48 | return data?.count ?? 0
49 | }
50 |
51 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
52 |
53 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
54 |
55 | let product = data![(indexPath as NSIndexPath).row]
56 |
57 | cell.textLabel?.text = product.vehicle?.displayName ?? ""
58 | cell.detailTextLabel?.text = product.vehicle?.vin ?? product.energySite?.id
59 |
60 |
61 | return cell
62 | }
63 |
64 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
65 | super.prepare(for: segue, sender: sender)
66 |
67 | if segue.identifier == "toDetail" {
68 |
69 | if let indexPath = tableView.indexPathForSelectedRow {
70 | let vc = segue.destination as! VehicleViewController
71 | if let vehicle = data![indexPath.row].vehicle {
72 | vc.vehicle = vehicle
73 | }
74 | }
75 |
76 | }
77 | }
78 |
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleURLTypes
22 |
23 |
24 | CFBundleTypeRole
25 | Editor
26 | CFBundleURLName
27 |
28 | CFBundleURLSchemes
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | NSAppTransportSecurity
37 |
38 | NSAllowsArbitraryLoads
39 |
40 |
41 | UILaunchStoryboardName
42 | LaunchScreen.storyboard
43 | UIMainStoryboardFile
44 | Main
45 | UIRequiredDeviceCapabilities
46 |
47 | armv7
48 |
49 | UIStatusBarTintParameters
50 |
51 | UINavigationBar
52 |
53 | Style
54 | UIBarStyleDefault
55 | Translucent
56 |
57 |
58 |
59 | UISupportedInterfaceOrientations
60 |
61 | UIInterfaceOrientationPortrait
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 05/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SafariServices
11 |
12 | extension Notification.Name {
13 | static let loginDone = Notification.Name("loginDone")
14 | static let nativeLoginDone = Notification.Name("nativeLoginDone")
15 | }
16 |
17 | class LoginViewController: UIViewController {
18 | @IBOutlet weak var messageLabel: UILabel!
19 |
20 | @IBAction func webLoginAction(_ sender: AnyObject) {
21 | let webloginViewController = api.authenticateWeb(delegate: self)
22 |
23 | guard let webloginViewController else { return }
24 |
25 | self.present(webloginViewController, animated: true, completion: nil)
26 |
27 | NotificationCenter.default.addObserver(forName: Notification.Name.nativeLoginDone, object: nil, queue: nil) { [weak self] (notification: Notification) in
28 | self?.dismiss(animated: false) {
29 | self?.dismiss(animated: false)
30 | }
31 |
32 | NotificationCenter.default.post(name: Notification.Name.loginDone, object: nil)
33 | }
34 | }
35 |
36 | @IBAction func nativeLoginAction(_ sender: AnyObject) {
37 | if let url = api.authenticateWebNativeURL() {
38 | UIApplication.shared.open(url)
39 | }
40 | NotificationCenter.default.addObserver(forName: Notification.Name.nativeLoginDone, object: nil, queue: nil) { [weak self] (notification: Notification) in
41 | NotificationCenter.default.post(name: Notification.Name.loginDone, object: nil)
42 |
43 | self?.dismiss(animated: true, completion: nil)
44 | }
45 | }
46 | }
47 |
48 | extension LoginViewController: SFSafariViewControllerDelegate {
49 | public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
50 | self.dismiss(animated: false)
51 | print("cancelled")
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/ProductViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProductViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Alec on 11/24/21.
6 | // Copyright © 2021 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import TeslaSwift
12 |
13 | class ProductViewController: UIViewController {
14 | @IBOutlet private weak var textView: UITextView!
15 |
16 | var product: Product?
17 |
18 | var energySite: EnergySite? {
19 | return product?.energySite
20 | }
21 |
22 | override func viewWillAppear(_ animated: Bool) {
23 | super.viewWillAppear(animated)
24 | // This page is best suited for Energy Sites, but Vehicles are also returned in the Product API
25 | guard energySite != nil else {
26 | textView.text = "Select the Vehicle tab to interact with a vehicle"
27 | textView.isEditable = false
28 | return
29 | }
30 | textView.isEditable = true
31 | }
32 |
33 |
34 |
35 | @IBAction func getEnergySiteStatus(_ sender: Any) {
36 | guard let energySite = energySite else { return }
37 | Task { @MainActor in
38 | do {
39 | let response = try await api.getEnergySiteStatus(siteID: energySite.siteId)
40 | self.textView.text = response.jsonString
41 | } catch let error {
42 | self.textView.text = error.localizedDescription
43 | }
44 | }
45 | }
46 |
47 | @IBAction func getEnergySiteLiveStatus(_ sender: Any) {
48 | guard let energySite = energySite else { return }
49 | Task { @MainActor in
50 | do {
51 | let response = try await api.getEnergySiteLiveStatus(siteID: energySite.siteId)
52 | self.textView.text = response.jsonString
53 | } catch let error {
54 | self.textView.text = error.localizedDescription
55 | }
56 | }
57 | }
58 |
59 | @IBAction func getEnergySiteInfo(_ sender: Any) {
60 | guard let energySite = energySite else { return }
61 | Task { @MainActor in
62 | do {
63 | let response = try await api.getEnergySiteInfo(siteID: energySite.siteId)
64 | self.textView.text = response.jsonString
65 | } catch let error {
66 | self.textView.text = error.localizedDescription
67 | }
68 | }
69 | }
70 |
71 | @IBAction func getEnergySiteHistory(_ sender: Any) {
72 | guard let energySite = energySite else { return }
73 | Task { @MainActor in
74 | do {
75 | let response = try await api.getEnergySiteHistory(siteID: energySite.siteId, period: EnergySiteHistory.Period.day)
76 | self.textView.text = response.jsonString
77 | } catch let error {
78 | self.textView.text = error.localizedDescription
79 | }
80 | }
81 | }
82 |
83 | @IBAction func getBatteryStatus(_ sender: Any) {
84 | guard let energySiteId = energySite?.batteryId else { return }
85 | Task { @MainActor in
86 | do {
87 | let response = try await api.getBatteryStatus(batteryID: energySiteId)
88 | self.textView.text = response.jsonString
89 | } catch let error {
90 | self.textView.text = error.localizedDescription
91 | }
92 | }
93 | }
94 |
95 | @IBAction func getBatteryData(_ sender: Any) {
96 | guard let energySiteId = energySite?.batteryId else { return }
97 | Task { @MainActor in
98 | do {
99 | let response = try await api.getBatteryData(batteryID: energySiteId)
100 | self.textView.text = response.jsonString
101 | } catch let error {
102 | self.textView.text = error.localizedDescription
103 | }
104 | }
105 | }
106 |
107 | @IBAction func getBatteryPowerHistory(_ sender: Any) {
108 | guard let energySiteId = energySite?.batteryId else { return }
109 | Task { @MainActor in
110 | do {
111 | let response = try await api.getBatteryPowerHistory(batteryID: energySiteId)
112 | self.textView.text = response.jsonString
113 | } catch let error {
114 | self.textView.text = error.localizedDescription
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 04/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | #if canImport(Combine)
11 | import Combine
12 | #endif
13 | import TeslaSwift
14 |
15 | class SecondViewController: UIViewController, UITableViewDataSource {
16 | @IBOutlet weak var tableView: UITableView!
17 |
18 | var data:[Product]?
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | getProducts()
24 |
25 | NotificationCenter.default.addObserver(forName: Notification.Name.loginDone, object: nil, queue: nil) { [weak self] (notification: Notification) in
26 |
27 | self?.getProducts()
28 | }
29 | }
30 |
31 | override func viewDidAppear(_ animated: Bool) {
32 | super.viewDidAppear(animated)
33 | tableView.estimatedRowHeight = 50.0
34 | }
35 |
36 | func getProducts() {
37 | Task { @MainActor in
38 | self.data = try await api.getProducts()
39 | self.tableView.reloadData()
40 | }
41 | }
42 |
43 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
44 | return data?.count ?? 0
45 | }
46 |
47 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
48 | let cell = tableView.dequeueReusableCell(withIdentifier: "product-cell", for: indexPath)
49 |
50 | let product = data![(indexPath as NSIndexPath).row]
51 |
52 | if let vehicle = product.vehicle {
53 | cell.textLabel?.text = vehicle.displayName
54 | cell.detailTextLabel?.text = vehicle.vin
55 | } else if let energySite = product.energySite {
56 | cell.textLabel?.text = energySite.id
57 | cell.detailTextLabel?.text = energySite.resourceType
58 | } else {
59 | cell.textLabel?.text = "Unknown"
60 | cell.detailTextLabel?.text = "Unknown"
61 | }
62 |
63 | return cell
64 | }
65 |
66 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
67 | super.prepare(for: segue, sender: sender)
68 | if segue.identifier == "toProductDetail" {
69 | if let indexPath = tableView.indexPathForSelectedRow {
70 | let vc = segue.destination as! ProductViewController
71 | vc.product = data![indexPath.row]
72 | }
73 | }
74 | }
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/StreamViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StreamViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 10/11/2017.
6 | // Copyright © 2017 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import TeslaSwift
11 | import TeslaSwiftStreaming
12 |
13 | class StreamViewController: UIViewController {
14 | @IBOutlet weak var textView: UITextView!
15 |
16 | var streaming = false
17 | var vehicle: Vehicle?
18 | var stream: TeslaStreaming!
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | stream = TeslaStreaming(teslaSwift: api)
23 | }
24 |
25 | override func viewWillDisappear(_ animated: Bool) {
26 | super.viewWillDisappear(animated)
27 | stopStream(self)
28 | }
29 |
30 | @IBAction func stream(_ sender: Any) {
31 | if !streaming {
32 | guard let vehicle = vehicle else { return }
33 | self.textView.text = ""
34 | Task { @MainActor in
35 | for try await event in try await stream.openStream(vehicle: vehicle) {
36 | self.processEvent(event: event)
37 | }
38 | self.streaming = true
39 | }
40 | }
41 | }
42 |
43 | func processEvent(event: TeslaStreamingEvent) {
44 | switch event {
45 | case .error(let error):
46 | textView.text = error.localizedDescription
47 | case .event(let event):
48 | textView.text = "\(self.textView.text ?? "")\nevent:\n \(event.descriptionKm)"
49 | case .disconnected:
50 | break
51 | case .open:
52 | textView.text = "open"
53 | }
54 | }
55 |
56 | @IBAction func stopStream(_ sender: Any) {
57 | stream.closeStream()
58 | streaming = false
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TabController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 05/03/16.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TabController: UITabBarController {
12 |
13 | override func viewDidAppear(_ animated: Bool) {
14 | super.viewDidAppear(animated)
15 |
16 | if (!api.isAuthenticated) {
17 |
18 | performSegue(withIdentifier: "loginSegue", sender: self)
19 |
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwift.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "Starscream",
6 | "repositoryURL": "https://github.com/daltoniam/Starscream.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "ac6c0fc9da221873e01bd1a0d4818498a71eef33",
10 | "version": "4.0.6"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/TeslaSwiftDemo.xcodeproj/xcshareddata/xcschemes/TeslaSwift.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
52 |
53 |
54 |
55 |
57 |
63 |
64 |
65 |
66 |
67 |
77 |
79 |
85 |
86 |
87 |
88 |
94 |
96 |
102 |
103 |
104 |
105 |
107 |
108 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/VehicleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VehicleViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 22/10/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import TeslaSwift
12 |
13 | class VehicleViewController: UIViewController {
14 | @IBOutlet weak var textView: UITextView!
15 |
16 | var vehicle: Vehicle?
17 |
18 | @IBAction func getVehicle(_ sender: Any) {
19 | guard let vehicle = vehicle else { return }
20 | Task { @MainActor in
21 | let vehicle2 = try await api.getVehicle(vehicle)
22 | self.textView.text = "Vehicle: \(vehicle2.jsonString!)"
23 | }
24 | }
25 |
26 | @IBAction func gettAll(_ sender: Any) {
27 | guard let vehicle = vehicle else { return }
28 | Task { @MainActor in
29 | let extendedVehicle = try await api.getAllData(vehicle, endpoints: AllStatesEndpoints.allWithLocation)
30 | self.textView.text = "All data:\n" +
31 | extendedVehicle.jsonString!
32 | }
33 | }
34 |
35 | @IBAction func command(_ sender: AnyObject) {
36 | guard let vehicle = vehicle else { return }
37 | Task { @MainActor in
38 | let response = try await api.sendCommandToVehicle(vehicle, command: VehicleCommand.setMaxDefrost(on: false))
39 | self.textView.text = (response.result! ? "true" : "false")
40 | if let reason = response.reason {
41 | self.textView.text.append(reason)
42 | }
43 | }
44 | }
45 |
46 | @IBAction func wakeup(_ sender: Any) {
47 | guard let vehicle = vehicle else { return }
48 | Task { @MainActor in
49 | let response = try await api.wakeUp(vehicle)
50 | self.textView.text = response.state
51 | }
52 | }
53 |
54 |
55 | @IBAction func speedLimit(_ sender: Any) {
56 | guard let vehicle = vehicle else { return }
57 | Task { @MainActor in
58 | let response = try await api.sendCommandToVehicle(vehicle, command: VehicleCommand.speedLimitClearPin(pin: "1234"))
59 | self.textView.text = (response.result! ? "true" : "false")
60 | if let reason = response.reason {
61 | self.textView.text.append(reason)
62 | }
63 | }
64 | }
65 |
66 | @IBAction func getNearbyChargingSites(_ sender: Any) {
67 | guard let vehicle = vehicle else { return }
68 | Task { @MainActor in
69 | let nearbyChargingSites = try await api.getNearbyChargingSites(vehicle)
70 | self.textView.text = "NearbyChargingSites:\n" +
71 | nearbyChargingSites.jsonString!
72 | }
73 | }
74 |
75 | @IBAction func refreshToken(_ sender: Any) {
76 | Task { @MainActor in
77 | do {
78 | let token = try await api.refreshToken()
79 | self.textView.text = "New access Token:\n \(token)"
80 | } catch {
81 | self.textView.text = "Refresh Token:\n CATCH"
82 | }
83 | }
84 | }
85 |
86 | @IBAction func ampsTo16(_ sender: Any) {
87 | guard let vehicle = vehicle else { return }
88 | Task { @MainActor in
89 | let response = try await api.sendCommandToVehicle(vehicle, command: VehicleCommand.setCharging(amps: 16))
90 | self.textView.text = (response.result! ? "true" : "false")
91 | if let reason = response.reason {
92 | self.textView.text.append(reason)
93 | }
94 | }
95 | }
96 |
97 | @IBAction func revokeToken(_ sender: Any) {
98 | Task { @MainActor in
99 | do {
100 | let status = try await api.revokeToken()
101 | self.textView.text = "Revoked: \(status)"
102 | } catch {
103 | self.textView.text = "Revoke Token:\n CATCH"
104 | }
105 | }
106 | }
107 |
108 | @IBAction func logout(_ sender: Any) {
109 | api.logout()
110 | }
111 | @IBAction func sendKeyToVehicle(_ sender: Any) {
112 | let yourDomain = "orange-dune-0e6c58803.5.azurestaticapps.net"
113 | if let url = api.urlToSendPublicKeyToVehicle(domain: yourDomain) {
114 | UIApplication.shared.open(url)
115 | }
116 | }
117 |
118 |
119 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
120 | super.prepare(for: segue, sender: sender)
121 | if segue.identifier == "toStream" {
122 | let vc = segue.destination as! StreamViewController
123 | vc.vehicle = self.vehicle
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/TeslaSwiftDemo/ViewController+TeslaSwift.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIViewController.swift
3 | // TeslaSwift
4 | //
5 | // Created by Joao Nunes on 03/12/2016.
6 | // Copyright © 2016 Joao Nunes. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import TeslaSwift
11 |
12 | extension UIViewController {
13 | public var api: TeslaSwift {
14 | return (UIApplication.shared.delegate as! AppDelegate).api
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/AllStates.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "id": 1234,
4 | "user_id": 1234,
5 | "vehicle_id": 123,
6 | "vin": "12345",
7 | "display_name": "Tesla",
8 | "option_codes": "MS02,REEU,AU01,BS00,BT85,CH01,PMMB,CW01,DRLH,HP00,IDPB,IPMG,LP01,PA00,PF00,PK01,PS01,RFPO,SC01,SP01,SU01,TM00,TP01,TR00,WT19,X001,X003,X007,X009,X011,X013,X020,X025,X027,X031,YF00,CONO",
9 | "color": null,
10 | "tokens": [
11 | "gfdsgfdsgf",
12 | "gfdsgfdsgfsdgfds"
13 | ],
14 | "state": "online",
15 | "in_service": false,
16 | "id_s": "604565436545487257",
17 | "remote_start_enabled": true,
18 | "calendar_enabled": true,
19 | "notifications_enabled": true,
20 | "backseat_token": null,
21 | "backseat_token_updated_at": null,
22 | "climate_state": {
23 | "inside_temp": 12.8,
24 | "outside_temp": 7.5,
25 | "driver_temp_setting": 17,
26 | "passenger_temp_setting": 17,
27 | "left_temp_direction": 118,
28 | "right_temp_direction": 118,
29 | "is_auto_conditioning_on": true,
30 | "is_front_defroster_on": 3,
31 | "is_rear_defroster_on": false,
32 | "fan_status": 0,
33 | "is_climate_on": false,
34 | "min_avail_temp": 15,
35 | "max_avail_temp": 28,
36 | "seat_heater_left": 0,
37 | "seat_heater_right": 0,
38 | "seat_heater_rear_left": 0,
39 | "seat_heater_rear_right": 0,
40 | "seat_heater_rear_center": 0,
41 | "seat_heater_rear_right_back": 0,
42 | "seat_heater_rear_left_back": 0,
43 | "smart_preconditioning": false,
44 | "timestamp": 1488311473437
45 | },
46 | "drive_state": {
47 | "shift_state": null,
48 | "speed": null,
49 | "power": 0,
50 | "latitude": 59.957517,
51 | "longitude": 10.695389,
52 | "heading": 191,
53 | "gps_as_of": 1488311472,
54 | "timestamp": 1488311473445
55 | },
56 | "gui_settings": {
57 | "gui_distance_units": "km/hr",
58 | "gui_temperature_units": "C",
59 | "gui_charge_rate_units": "km/hr",
60 | "gui_24_hour_time": true,
61 | "gui_range_display": "Ideal",
62 | "timestamp": 1488311473450
63 | },
64 | "vehicle_state": {
65 | "api_version": 3,
66 | "autopark_state": "unavailable",
67 | "autopark_state_v2": "unavailable",
68 | "calendar_supported": true,
69 | "car_type": "s",
70 | "car_version": "2.52.22",
71 | "center_display_state": 0,
72 | "dark_rims": false,
73 | "df": 0,
74 | "dr": 0,
75 | "exterior_color": "Blue",
76 | "ft": 0,
77 | "has_spoiler": false,
78 | "locked": true,
79 | "notifications_supported": true,
80 | "odometer": 66347.902989,
81 | "parsed_calendar_supported": true,
82 | "perf_config": "P1",
83 | "pf": 0,
84 | "pr": 0,
85 | "rear_seat_heaters": 1,
86 | "rear_seat_type": 0,
87 | "remote_start": false,
88 | "remote_start_supported": true,
89 | "rhd": false,
90 | "roof_color": "None",
91 | "rt": 0,
92 | "seat_type": 0,
93 | "spoiler_type": "None",
94 | "sun_roof_installed": 1,
95 | "sun_roof_percent_open": 0,
96 | "sun_roof_state": "unknown",
97 | "third_row_seats": "None",
98 | "timestamp": 1488311473456,
99 | "valet_mode": false,
100 | "valet_pin_needed": true,
101 | "vehicle_name": "Elsa",
102 | "wheel_type": "Base19"
103 | },
104 | "charge_state": {
105 | "charging_state": "Disconnected",
106 | "charge_limit_soc": 90,
107 | "charge_limit_soc_std": 90,
108 | "charge_limit_soc_min": 50,
109 | "charge_limit_soc_max": 100,
110 | "charge_to_max_range": false,
111 | "battery_heater_on": false,
112 | "not_enough_power_to_heat": false,
113 | "max_range_charge_counter": 2,
114 | "fast_charger_present": false,
115 | "fast_charger_type": "",
116 | "battery_range": 280.0,
117 | "est_battery_range": 186.31,
118 | "ideal_battery_range": 225.98,
119 | "battery_level": 96,
120 | "usable_battery_level": 96,
121 | "battery_current": -0.6,
122 | "charge_energy_added": 12.63,
123 | "charge_miles_added_rated": 49,
124 | "charge_miles_added_ideal": 39,
125 | "charger_voltage": 0,
126 | "charger_pilot_current": 32,
127 | "charger_actual_current": 0,
128 | "charger_power": 0,
129 | "time_to_full_charge": 0,
130 | "trip_charging": false,
131 | "charge_rate": 0,
132 | "charge_port_door_open": false,
133 | "scheduled_charging_start_time": null,
134 | "scheduled_charging_pending": false,
135 | "user_charge_enable_request": null,
136 | "charge_enable_request": true,
137 | "charger_phases": null,
138 | "charge_port_latch": "Blocking",
139 | "charge_current_request": 32,
140 | "charge_current_request_max": 32,
141 | "charge_port_led_color": "Off",
142 | "managed_charging_active": false,
143 | "managed_charging_user_canceled": false,
144 | "managed_charging_start_time": null,
145 | "motorized_charge_port": false,
146 | "eu_vehicle": true,
147 | "timestamp": 1488311473464
148 | },
149 | "vehicle_config": {
150 | "car_special_type": "base",
151 | "car_type": "s",
152 | "eu_vehicle": true,
153 | "exterior_color": "Blue",
154 | "has_ludicrous_mode": false,
155 | "motorized_charge_port": false,
156 | "perf_config": "P1",
157 | "rear_seat_heaters": 1,
158 | "rear_seat_type": 0,
159 | "rhd": false,
160 | "roof_color": "None",
161 | "seat_type": 0,
162 | "spoiler_type": "None",
163 | "sun_roof_installed": 1,
164 | "third_row_seats": "None",
165 | "timestamp": 1488311473475,
166 | "trim_badging": "85",
167 | "wheel_type": "Base19"
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/Authentication.json:
--------------------------------------------------------------------------------
1 | {
2 | "access_token": "abc123-mock",
3 | "token_type": "bearer",
4 | "expires_in": 7776000,
5 | "created_at": 1457385291
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/AuthenticationFailed.json:
--------------------------------------------------------------------------------
1 | {
2 | "error" : "invalid_grant",
3 | "error_description" : "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
4 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/ChargeLimitMaxRange.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test charge max range"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/ChargeLimitPercentage.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test charge percentage"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/ChargeLimitStandard.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test charge standard"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/ChargeState.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "charging_state": "Complete",
4 | "charge_to_max_range": false,
5 | "max_range_charge_counter": 0,
6 | "fast_charger_present": false,
7 | "battery_range": 200.00,
8 | "est_battery_range": 155.79,
9 | "ideal_battery_range": 275.09,
10 | "battery_level": 91,
11 | "battery_current": -0.6,
12 | "charge_starting_range": null,
13 | "charge_starting_soc": null,
14 | "charger_voltage": 0,
15 | "charger_pilot_current": 40,
16 | "charger_actual_current": 0,
17 | "charger_power": 0,
18 | "time_to_full_charge": null,
19 | "charge_rate": -1,
20 | "charge_port_door_open": true,
21 | "battery_heater_on": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/ClimateSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "driver_temp_setting" : 24,
4 | "fan_status" : 0,
5 | "inside_temp" : 18.0,
6 | "is_auto_conditioning_on" : 0,
7 | "is_climate_on" : 0,
8 | "is_front_defroster_on" : 3,
9 | "is_rear_defroster_on" : 0,
10 | "left_temp_direction" : 583,
11 | "max_avail_temp" : 28,
12 | "min_avail_temp" : 15,
13 | "outside_temp" : -1.5,
14 | "passenger_temp_setting" : 24,
15 | "right_temp_direction" : 583,
16 | "seat_heater_left" : 0,
17 | "seat_heater_rear_center" : 0,
18 | "seat_heater_rear_left" : 0,
19 | "seat_heater_rear_left_back" : 0,
20 | "seat_heater_rear_right" : 0,
21 | "seat_heater_rear_right_back" : 0,
22 | "seat_heater_right" : 0,
23 | "smart_preconditioning" : 0
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/DriveState.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "shift_state": null,
4 | "speed": null,
5 | "latitude": 33.794839,
6 | "longitude": -84.401593,
7 | "heading": 10,
8 | "gps_as_of": 1359863204
9 | }
10 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/FlashLights.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test FlashLights"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/GuiSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "gui_distance_units": "km/hr",
4 | "gui_temperature_units": "C",
5 | "gui_charge_rate_units": "km/hr",
6 | "gui_24_hour_time": false,
7 | "gui_range_display": "Rated"
8 | }
9 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/HonkHorn.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test HonkHorn"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | NSAppTransportSecurity
24 |
25 | NSAllowsArbitraryLoads
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/LockDoors.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test LockDoors"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/MobileAccess.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": false
3 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/NearbyChargingSites.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "congestion_sync_time_utc_secs": 1545091987,
4 | "destination_charging": [
5 | {
6 | "location": {
7 | "lat": 33.811484,
8 | "long": -118.138451
9 | },
10 | "name": "Long Beach Marriott",
11 | "type": "destination",
12 | "distance_miles": 2.201606
13 | },
14 | {
15 | "location": {
16 | "lat": 33.767198,
17 | "long": -118.191987
18 | },
19 | "name": "Renaissance Long Beach Hotel",
20 | "type": "destination",
21 | "distance_miles": 4.071068
22 | },
23 | {
24 | "location": {
25 | "lat": 33.757146,
26 | "long": -118.19861
27 | },
28 | "name": "Hotel Maya, a Doubletree by Hilton",
29 | "type": "destination",
30 | "distance_miles": 4.843953
31 | },
32 | {
33 | "location": {
34 | "lat": 33.832254,
35 | "long": -118.079218
36 | },
37 | "name": "The Gardens Casino",
38 | "type": "destination",
39 | "distance_miles": 6.449794
40 | }
41 | ],
42 | "superchargers": [
43 | {
44 | "location": {
45 | "lat": 33.934471,
46 | "long": -118.121217
47 | },
48 | "name": "Downey, CA - Stonewood Street",
49 | "type": "supercharger",
50 | "distance_miles": 2.196721,
51 | "available_stalls": 5,
52 | "total_stalls": 12,
53 | "site_closed": false
54 | },
55 | {
56 | "location": {
57 | "lat": 33.953385,
58 | "long": -118.112905
59 | },
60 | "name": "Downey, CA - Lakewood Boulevard",
61 | "type": "supercharger",
62 | "distance_miles": 9.587273,
63 | "available_stalls": 6,
64 | "total_stalls": 12,
65 | "site_closed": false
66 | },
67 | {
68 | "location": {
69 | "lat": 33.921063,
70 | "long": -118.330074
71 | },
72 | "name": "Hawthorne, CA",
73 | "type": "supercharger",
74 | "distance_miles": 12.197322,
75 | "available_stalls": 3,
76 | "total_stalls": 6,
77 | "site_closed": false
78 | },
79 | {
80 | "location": {
81 | "lat": 33.894227,
82 | "long": -118.367407
83 | },
84 | "name": "Redondo Beach, CA",
85 | "type": "supercharger",
86 | "distance_miles": 13.125912,
87 | "available_stalls": 3,
88 | "total_stalls": 8,
89 | "site_closed": false
90 | }
91 | ],
92 | "timestamp": 1545092157769
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/OpenChargeDoor.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test open charge door"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/OpenTrunk.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test OpenTrunk"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/ResetValetPin.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test resetValet"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/SetSpeedLimit.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": true,
4 | "reason": "Test Set SpeedLimit"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/SetSunRoof.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test SetSunRoof"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/SetTemperature.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test SetTemperature"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/SetValetMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test valet"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/SpeedLimitPin.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": true,
4 | "reason": "Test Set SpeedLimit Pin"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/StartAutoConditioning.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test StartAutoConditioning"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/StartCharging.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test start charging"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/StartVehicle.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test StartVehicle"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/StopAutoConditioning.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test StopAutoConditioning"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/StopCharging.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test stop charging"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/StreamingData.txt:
--------------------------------------------------------------------------------
1 | 1493496113857,,5241.9,84,17,142,60.164031,24.925366,-3,,289,150,142
2 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/UnlockDoors.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "result": false,
4 | "reason": "Test UnlockDoors"
5 | }
6 | }
--------------------------------------------------------------------------------
/TeslaSwiftTests/VehicleConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "can_accept_navigation_requests": true,
4 | "car_special_type": "base",
5 | "car_type": "s",
6 | "eu_vehicle": true,
7 | "exterior_color": "Blue",
8 | "has_ludicrous_mode": false,
9 | "motorized_charge_port": false,
10 | "perf_config": "P1",
11 | "rear_seat_heaters": 1,
12 | "rear_seat_type": 0,
13 | "rhd": false,
14 | "roof_color": "None",
15 | "seat_type": 0,
16 | "spoiler_type": "None",
17 | "sun_roof_installed": 1,
18 | "third_row_seats": "None",
19 | "timestamp": 1488311473475,
20 | "trim_badging": "85",
21 | "wheel_type": "Base19"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/VehicleState.json:
--------------------------------------------------------------------------------
1 | {
2 | "response": {
3 | "df": 0,
4 | "dr": 0,
5 | "pf": 0,
6 | "pr": 0,
7 | "ft": 0,
8 | "rt": 0,
9 | "car_verson": "1.19.42",
10 | "locked": true,
11 | "sun_roof_installed": false,
12 | "sun_roof_state": "unknown",
13 | "sun_roof_percent_open": 0,
14 | "dark_rims": true,
15 | "wheel_type": "Base19",
16 | "has_spoiler": false,
17 | "roof_color": "Colored",
18 | "perf_config": "Base"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/Vehicles.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "response": [
4 | {
5 | "color": null,
6 | "display_name": "mockCar",
7 | "id": 321,
8 | "option_codes": "MS01,RENA,TM00,DRLH,PF00,BT85,PBCW,RFPO,WT19,IBMB,IDPB,TR00,SU01,SC01,TP01,AU01,CH00,HP00,PA00,PS00,AD02,X020,X025,X001,X003,X007,X011,X013",
9 | "user_id": 123,
10 | "vehicle_id": 1234567890,
11 | "vin": "5YJSA1CN5CFP01657",
12 | "tokens": [
13 | "x",
14 | "x"
15 | ],
16 | "state": "online",
17 | "backseat_token": "111",
18 | "backseat_token_updated_at": 1488311472,
19 | "calendar_enabled": true,
20 | "id_s": "321",
21 | "in_service": true
22 |
23 | }
24 | ],
25 | "count": 1
26 | }
27 |
--------------------------------------------------------------------------------
/TeslaSwiftTests/WakeUp.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "response":
4 | {
5 | "color": null,
6 | "display_name": "mockCar",
7 | "id": 321,
8 | "option_codes": "MS01,RENA,TM00,DRLH,PF00,BT85,PBCW,RFPO,WT19,IBMB,IDPB,TR00,SU01,SC01,TP01,AU01,CH00,HP00,PA00,PS00,AD02,X020,X025,X001,X003,X007,X011,X013",
9 | "user_id": 123,
10 | "vehicle_id": 1234567890,
11 | "vin": "5YJSA1CN5CFP01657",
12 | "tokens": [
13 | "x",
14 | "x"
15 | ],
16 | "state": "online",
17 | "backseat_token": "111",
18 | "backseat_token_updated_at": 1488311472,
19 | "calendar_enabled": true,
20 | "id_s": "321",
21 | "in_service": true
22 |
23 | }
24 | }
25 |
--------------------------------------------------------------------------------