diff --git a/Package.swift b/Package.swift index 9cc9743..b6db790 100644 --- a/Package.swift +++ b/Package.swift @@ -1,10 +1,10 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.5 import PackageDescription let package = Package( name: "TeslaSwift", platforms: [ - .macOS(.v11), .iOS(.v14), .watchOS(.v7), .tvOS(.v14) + .macOS(.v12), .iOS(.v15), .watchOS(.v8), .tvOS(.v15) ], products: [ .library(name: "TeslaSwift", targets: ["TeslaSwift"]), diff --git a/Sources/Extensions/Combine/TeslaSwift+Combine.swift b/Sources/Extensions/Combine/TeslaSwift+Combine.swift index ab0233b..a54c77d 100644 --- a/Sources/Extensions/Combine/TeslaSwift+Combine.swift +++ b/Sources/Extensions/Combine/TeslaSwift+Combine.swift @@ -137,7 +137,7 @@ extension TeslaSwift { return future } - public func getEnergySiteStatus(siteID: String) -> Future { + public func getEnergySiteStatus(siteID: SiteId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -151,7 +151,7 @@ extension TeslaSwift { return future } - public func getEnergySiteLiveStatus(siteID: String) -> Future { + public func getEnergySiteLiveStatus(siteID: SiteId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -165,7 +165,7 @@ extension TeslaSwift { return future } - public func getEnergySiteInfo(siteID: String) -> Future { + public func getEnergySiteInfo(siteID: SiteId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -179,7 +179,7 @@ extension TeslaSwift { return future } - public func getEnergySiteHistory(siteID: String, period: EnergySiteHistory.Period) -> Future { + public func getEnergySiteHistory(siteID: SiteId, period: EnergySiteHistory.Period) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -193,7 +193,7 @@ extension TeslaSwift { return future } - public func getBatteryStatus(batteryID: String) -> Future { + public func getBatteryStatus(batteryID: BatteryId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -207,7 +207,7 @@ extension TeslaSwift { return future } - public func getBatteryData(batteryID: String) -> Future { + public func getBatteryData(batteryID: BatteryId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { @@ -221,7 +221,7 @@ extension TeslaSwift { return future } - public func getBatteryPowerHistory(batteryID: String) -> Future { + public func getBatteryPowerHistory(batteryID: BatteryId) -> Future { let future = Future { (subscriber: @escaping (Result) -> Void) in Task { do { diff --git a/Sources/TeslaSwift/Model/EnergySite.swift b/Sources/TeslaSwift/Model/EnergySite.swift index 02d9d64..22b7bf9 100644 --- a/Sources/TeslaSwift/Model/EnergySite.swift +++ b/Sources/TeslaSwift/Model/EnergySite.swift @@ -13,7 +13,15 @@ open class EnergySite: Codable { // Unique to EnergySite open var id: String? + public var batteryId: BatteryId? { + guard let id else { return nil } + + return BatteryId(id: id) + } open var energySiteID: Decimal + public var siteId: SiteId { + SiteId(id: energySiteID) + } open var assetSiteID: String? open var components: Components? diff --git a/Sources/TeslaSwift/Model/Vehicle.swift b/Sources/TeslaSwift/Model/Vehicle.swift index 90ae999..944a54e 100644 --- a/Sources/TeslaSwift/Model/Vehicle.swift +++ b/Sources/TeslaSwift/Model/Vehicle.swift @@ -15,14 +15,14 @@ open class Vehicle: Codable { open var calendarEnabled: Bool? open var color: String? open var displayName: String? - open var id: String? { + open var id: VehicleId? { get { guard let value = idInt else { return nil } - return "\(value)" + return VehicleId(id: value) } set { - guard let newValue = newValue else { idInt = nil; return } - idInt = Int64(newValue) + guard let newValue = newValue?.id else { idInt = nil; return } + idInt = Int64(newValue) } } open var idInt: Int64? @@ -106,5 +106,28 @@ open class Vehicle: Codable { try container.encodeIfPresent(vin, forKey: .vin) } +} + +public class VehicleId { + public let id: Int64 + + init(id: Int64) { + self.id = id + } +} + +public class SiteId { + public let id: Decimal + + init(id: Decimal) { + self.id = id + } +} + +public class BatteryId { + public let id: String + init(id: String) { + self.id = id + } } diff --git a/Sources/TeslaSwift/TeslaEndpoint.swift b/Sources/TeslaSwift/TeslaEndpoint.swift index 6137f15..6b88780 100644 --- a/Sources/TeslaSwift/TeslaEndpoint.swift +++ b/Sources/TeslaSwift/TeslaEndpoint.swift @@ -22,27 +22,28 @@ enum Endpoint { case partnerAccounts case vehicles - case vehicleSummary(vehicleID: String) - case mobileAccess(vehicleID: String) - case allStates(vehicleID: String) - case chargeState(vehicleID: String) - case climateState(vehicleID: String) - case driveState(vehicleID: String) - case nearbyChargingSites(vehicleID: String) - case guiSettings(vehicleID: String) - case vehicleState(vehicleID: String) - case vehicleConfig(vehicleID: String) - case wakeUp(vehicleID: String) - case command(vehicleID: String, command: VehicleCommand) + case vehicleSummary(vehicleID: VehicleId) + case mobileAccess(vehicleID: VehicleId) + case allStates(vehicleID: VehicleId) + case chargeState(vehicleID: VehicleId) + case climateState(vehicleID: VehicleId) + case driveState(vehicleID: VehicleId) + case nearbyChargingSites(vehicleID: VehicleId) + case guiSettings(vehicleID: VehicleId) + case vehicleState(vehicleID: VehicleId) + case vehicleConfig(vehicleID: VehicleId) + case wakeUp(vehicleID: VehicleId) + case command(vehicleID: VehicleId, command: VehicleCommand) + case signedCommand(vehicleID: VehicleId) case products - case chargeHistory(vehicleID: String) - case getEnergySiteStatus(siteID: String) - case getEnergySiteLiveStatus(siteID: String) - case getEnergySiteInfo(siteID: String) - case getEnergySiteHistory(siteID: String, period: EnergySiteHistory.Period) - case getBatteryStatus(batteryID: String) - case getBatteryData(batteryID: String) - case getBatteryPowerHistory(batteryID: String) + case chargeHistory(vehicleID: VehicleId) + case getEnergySiteStatus(siteID: SiteId) + case getEnergySiteLiveStatus(siteID: SiteId) + case getEnergySiteInfo(siteID: SiteId) + case getEnergySiteHistory(siteID: SiteId, period: EnergySiteHistory.Period) + case getBatteryStatus(batteryID: BatteryId) + case getBatteryData(batteryID: BatteryId) + case getBatteryPowerHistory(batteryID: BatteryId) } extension Endpoint { @@ -69,55 +70,57 @@ extension Endpoint { case .vehicles: return "/api/1/vehicles" case .vehicleSummary(let vehicleID): - return "/api/1/vehicles/\(vehicleID)" + return "/api/1/vehicles/\(vehicleID.id)" case .mobileAccess(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/mobile_enabled" + return "/api/1/vehicles/\(vehicleID.id)/mobile_enabled" case .allStates(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/vehicle_data" + return "/api/1/vehicles/\(vehicleID.id)/vehicle_data" case .chargeState(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/charge_state" + return "/api/1/vehicles/\(vehicleID.id)/data_request/charge_state" case .climateState(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/climate_state" + return "/api/1/vehicles/\(vehicleID.id)/data_request/climate_state" case .driveState(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/drive_state" + return "/api/1/vehicles/\(vehicleID.id)/data_request/drive_state" case .guiSettings(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/gui_settings" + return "/api/1/vehicles/\(vehicleID.id)/data_request/gui_settings" case .nearbyChargingSites(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/nearby_charging_sites" + return "/api/1/vehicles/\(vehicleID.id)/nearby_charging_sites" case .vehicleState(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/vehicle_state" + return "/api/1/vehicles/\(vehicleID.id)/data_request/vehicle_state" case .vehicleConfig(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/data_request/vehicle_config" + return "/api/1/vehicles/\(vehicleID.id)/data_request/vehicle_config" case .wakeUp(let vehicleID): - return "/api/1/vehicles/\(vehicleID)/wake_up" + return "/api/1/vehicles/\(vehicleID.id)/wake_up" case let .command(vehicleID, command): - return "/api/1/vehicles/\(vehicleID)/\(command.path())" + return "/api/1/vehicles/\(vehicleID.id)/\(command.path())" + case let .signedCommand(vehicleID): + return "/api/1/vehicles/\(vehicleID.id)/signed_command" case .products: return "/api/1/products" case let .chargeHistory(vehicleID): - return "/api/1/vehicles/\(vehicleID)/charge_history" + return "/api/1/vehicles/\(vehicleID.id)/charge_history" // Energy Data case .getEnergySiteStatus(let siteID): - return "/api/1/energy_sites/\(siteID)/site_status" + return "/api/1/energy_sites/\(siteID.id)/site_status" case .getEnergySiteLiveStatus(let siteID): - return "/api/1/energy_sites/\(siteID)/live_status" + return "/api/1/energy_sites/\(siteID.id)/live_status" case .getEnergySiteInfo(let siteID): return "/api/1/energy_sites/\(siteID)/site_info" case .getEnergySiteHistory(let siteID, _): - return "/api/1/energy_sites/\(siteID)/history" + return "/api/1/energy_sites/\(siteID.id)/history" case .getBatteryStatus(let batteryID): - return "/api/1/powerwalls/\(batteryID)/status" + return "/api/1/powerwalls/\(batteryID.id)/status" case .getBatteryData(let batteryID): - return "/api/1/powerwalls/\(batteryID)/" + return "/api/1/powerwalls/\(batteryID.id)/" case .getBatteryPowerHistory(let batteryID): - return "/api/1/powerwalls/\(batteryID)/powerhistory" + return "/api/1/powerwalls/\(batteryID.id)/powerhistory" } } var method: String { switch self { - case .revoke, .oAuth2Token, .oAuth2TokenCN, .wakeUp, .partnerAccounts, .chargeHistory, .command: + case .revoke, .oAuth2Token, .oAuth2TokenCN, .wakeUp, .partnerAccounts, .chargeHistory, .command, .signedCommand: return "POST" 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: return "GET" diff --git a/Sources/TeslaSwift/TeslaSwift.swift b/Sources/TeslaSwift/TeslaSwift.swift index dca0cd9..f1e5141 100644 --- a/Sources/TeslaSwift/TeslaSwift.swift +++ b/Sources/TeslaSwift/TeslaSwift.swift @@ -27,6 +27,7 @@ public enum TeslaAPI { public enum Region: String, Codable { case northAmericaAsiaPacific = "https://fleet-api.prd.na.vn.cloud.tesla.com" case europeMiddleEastAfrica = "https://fleet-api.prd.eu.vn.cloud.tesla.com" + case china = "https://fleet-api.prd.cn.vn.cloud.tesla.cn" } case ownerAPI @@ -316,7 +317,7 @@ extension TeslaSwift { - returns: A Vehicle. */ - public func getVehicle(_ vehicleID: String) async throws -> Vehicle { + public func getVehicle(_ vehicleID: VehicleId) async throws -> Vehicle { _ = try await checkAuthentication() let response: Response = try await request(.vehicleSummary(vehicleID: vehicleID)) return response.response @@ -398,7 +399,7 @@ extension TeslaSwift { - parameter vehicle: the vehicle that will receive the command - parameter command: the command to send to the vehicle - - returns: A completion handler with the CommandResponse object containing the results of the command. + - returns: A CommandResponse object containing the results of the command. */ public func sendCommandToVehicle(_ vehicle: Vehicle, command: VehicleCommand) async throws -> CommandResponse { _ = try await checkAuthentication() @@ -468,12 +469,25 @@ extension TeslaSwift { } } + + /** + Sends a signed command to the vehicle + + - parameter vehicle: the vehicle that will receive the command + - parameter command: the command to send to the vehicle + - returns: A CommandResponse object containing the results of the command. + */ + public func sendSignedCommandToVehicle(_ vehicle: Vehicle, command: VehicleCommand) async throws -> CommandResponse { + _ = try await checkAuthentication() + let body = "" // TODO + return try await request(Endpoint.signedCommand(vehicleID: vehicle.id!), body: body) + } /** Fetchs the status of your energy site - returns: The EnergySiteStatus */ - public func getEnergySiteStatus(siteID: String) async throws -> EnergySiteStatus { + public func getEnergySiteStatus(siteID: SiteId) async throws -> EnergySiteStatus { _ = try await checkAuthentication() let response: Response = try await request(.getEnergySiteStatus(siteID: siteID)) return response.response @@ -484,7 +498,7 @@ extension TeslaSwift { - returns: A completion handler with an array of Products. */ - public func getEnergySiteLiveStatus(siteID: String) async throws -> EnergySiteLiveStatus { + public func getEnergySiteLiveStatus(siteID: SiteId) async throws -> EnergySiteLiveStatus { _ = try await checkAuthentication() let response: Response = try await request(.getEnergySiteLiveStatus(siteID: siteID)) return response.response @@ -495,7 +509,7 @@ extension TeslaSwift { - returns: The EnergySiteInfo. */ - public func getEnergySiteInfo(siteID: String) async throws -> EnergySiteInfo { + public func getEnergySiteInfo(siteID: SiteId) async throws -> EnergySiteInfo { _ = try await checkAuthentication() let response: Response = try await request(.getEnergySiteInfo(siteID: siteID)) return response.response @@ -506,7 +520,7 @@ extension TeslaSwift { - returns: The EnergySiteHistory */ - public func getEnergySiteHistory(siteID: String, period: EnergySiteHistory.Period) async throws -> EnergySiteHistory { + public func getEnergySiteHistory(siteID: SiteId, period: EnergySiteHistory.Period) async throws -> EnergySiteHistory { _ = try await checkAuthentication() let response: Response = try await request(.getEnergySiteHistory(siteID: siteID, period: period)) return response.response @@ -517,7 +531,7 @@ extension TeslaSwift { - returns: The BatteryStatus */ - public func getBatteryStatus(batteryID: String) async throws -> BatteryStatus { + public func getBatteryStatus(batteryID: BatteryId) async throws -> BatteryStatus { _ = try await checkAuthentication() let response: Response = try await request(.getBatteryStatus(batteryID: batteryID)) return response.response @@ -528,7 +542,7 @@ extension TeslaSwift { - returns: The BatteryData */ - public func getBatteryData(batteryID: String) async throws -> BatteryData { + public func getBatteryData(batteryID: BatteryId) async throws -> BatteryData { _ = try await checkAuthentication() let response: Response = try await request(.getBatteryData(batteryID: batteryID)) return response.response @@ -539,7 +553,7 @@ extension TeslaSwift { - returns: The BatteryPowerHistory */ - public func getBatteryPowerHistory(batteryID: String) async throws -> BatteryPowerHistory { + public func getBatteryPowerHistory(batteryID: BatteryId) async throws -> BatteryPowerHistory { _ = try await checkAuthentication() let response: Response = try await request(.getBatteryPowerHistory(batteryID: batteryID)) return response.response diff --git a/TeslaSwiftDemo/ProductViewController.swift b/TeslaSwiftDemo/ProductViewController.swift index e44d3b4..5a572fa 100644 --- a/TeslaSwiftDemo/ProductViewController.swift +++ b/TeslaSwiftDemo/ProductViewController.swift @@ -36,7 +36,7 @@ class ProductViewController: UIViewController { guard let energySite = energySite else { return } Task { @MainActor in do { - let response = try await api.getEnergySiteStatus(siteID: "\(energySite.energySiteID)") + let response = try await api.getEnergySiteStatus(siteID: energySite.siteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -48,7 +48,7 @@ class ProductViewController: UIViewController { guard let energySite = energySite else { return } Task { @MainActor in do { - let response = try await api.getEnergySiteLiveStatus(siteID: "\(energySite.energySiteID)") + let response = try await api.getEnergySiteLiveStatus(siteID: energySite.siteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -60,7 +60,7 @@ class ProductViewController: UIViewController { guard let energySite = energySite else { return } Task { @MainActor in do { - let response = try await api.getEnergySiteInfo(siteID: "\(energySite.energySiteID)") + let response = try await api.getEnergySiteInfo(siteID: energySite.siteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -72,7 +72,7 @@ class ProductViewController: UIViewController { guard let energySite = energySite else { return } Task { @MainActor in do { - let response = try await api.getEnergySiteHistory(siteID: "\(energySite.energySiteID)", period: EnergySiteHistory.Period.day) + let response = try await api.getEnergySiteHistory(siteID: energySite.siteId, period: EnergySiteHistory.Period.day) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -81,10 +81,10 @@ class ProductViewController: UIViewController { } @IBAction func getBatteryStatus(_ sender: Any) { - guard let energySiteId = energySite?.id else { return } + guard let energySiteId = energySite?.batteryId else { return } Task { @MainActor in do { - let response = try await api.getBatteryStatus(batteryID: "\(energySiteId)") + let response = try await api.getBatteryStatus(batteryID: energySiteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -93,10 +93,10 @@ class ProductViewController: UIViewController { } @IBAction func getBatteryData(_ sender: Any) { - guard let energySiteId = energySite?.id else { return } + guard let energySiteId = energySite?.batteryId else { return } Task { @MainActor in do { - let response = try await api.getBatteryData(batteryID: "\(energySiteId)") + let response = try await api.getBatteryData(batteryID: energySiteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription @@ -105,10 +105,10 @@ class ProductViewController: UIViewController { } @IBAction func getBatteryPowerHistory(_ sender: Any) { - guard let energySiteId = energySite?.id else { return } + guard let energySiteId = energySite?.batteryId else { return } Task { @MainActor in do { - let response = try await api.getBatteryPowerHistory(batteryID: "\(energySiteId)") + let response = try await api.getBatteryPowerHistory(batteryID: energySiteId) self.textView.text = response.jsonString } catch let error { self.textView.text = error.localizedDescription