mirror of
https://github.com/home-assistant/iOS.git
synced 2026-06-19 17:24:52 -05:00
604 lines
25 KiB
Swift
604 lines
25 KiB
Swift
//
|
|
// HAAPI.swift
|
|
// HomeAssistant
|
|
//
|
|
// Created by Robbie Trencheny on 3/25/16.
|
|
// Copyright © 2016 Robbie Trencheny. All rights reserved.
|
|
//
|
|
|
|
import Alamofire
|
|
import AlamofireObjectMapper
|
|
import PromiseKit
|
|
import Crashlytics
|
|
import CoreLocation
|
|
import CoreMotion
|
|
import DeviceKit
|
|
import Foundation
|
|
import KeychainAccess
|
|
import ObjectMapper
|
|
import RealmSwift
|
|
import UserNotifications
|
|
|
|
private let keychain = Keychain(service: "io.robbie.homeassistant")
|
|
|
|
// swiftlint:disable file_length
|
|
|
|
// swiftlint:disable:next type_body_length
|
|
public class HomeAssistantAPI {
|
|
public enum APIError: Error {
|
|
case managerNotAvailable
|
|
case invalidResponse
|
|
case cantBuildURL
|
|
case notConfigured
|
|
}
|
|
|
|
public enum AuthenticationMethod {
|
|
case legacy(apiPassword: String?)
|
|
case modern(tokenInfo: TokenInfo)
|
|
}
|
|
|
|
let prefs = UserDefaults(suiteName: Constants.AppGroupID)!
|
|
|
|
public var pushID: String?
|
|
|
|
public var loadedComponents = [String]()
|
|
|
|
var apiPassword: String?
|
|
|
|
private(set) var manager: Alamofire.SessionManager!
|
|
|
|
public var oneShotLocationManager: OneShotLocationManager?
|
|
|
|
public var cachedEntities: [Entity]?
|
|
|
|
public var notificationsEnabled: Bool {
|
|
return self.prefs.bool(forKey: "notificationsEnabled")
|
|
}
|
|
|
|
public var iosComponentLoaded: Bool {
|
|
return self.loadedComponents.contains("ios")
|
|
}
|
|
|
|
public var deviceTrackerComponentLoaded: Bool {
|
|
return self.loadedComponents.contains("device_tracker")
|
|
}
|
|
|
|
public var iosNotifyPlatformLoaded: Bool {
|
|
return self.loadedComponents.contains("notify.ios")
|
|
}
|
|
|
|
var enabledPermissions: [String] {
|
|
var permissionsContainer: [String] = []
|
|
if self.notificationsEnabled {
|
|
permissionsContainer.append("notifications")
|
|
}
|
|
if Current.settingsStore.locationEnabled {
|
|
permissionsContainer.append("location")
|
|
}
|
|
return permissionsContainer
|
|
}
|
|
|
|
var tokenManager: TokenManager?
|
|
public var connectionInfo: ConnectionInfo
|
|
|
|
/// Initialzie an API object with an authenticated tokenManager.
|
|
public init(connectionInfo: ConnectionInfo, authenticationMethod: AuthenticationMethod) {
|
|
self.connectionInfo = connectionInfo
|
|
|
|
switch authenticationMethod {
|
|
case .legacy(let apiPassword):
|
|
self.manager = self.configureSessionManager(withPassword: apiPassword)
|
|
case .modern(let tokenInfo):
|
|
// TODO: Take this into account when promoting to the main API. The one in Current is separate, which is bad.
|
|
self.tokenManager = TokenManager(connectionInfo: connectionInfo, tokenInfo: tokenInfo)
|
|
let manager = self.configureSessionManager()
|
|
manager.retrier = self.tokenManager
|
|
manager.adapter = self.tokenManager
|
|
self.manager = manager
|
|
}
|
|
|
|
let basicAuthKeychain = Keychain(server: self.connectionInfo.baseURL.absoluteString,
|
|
protocolType: .https,
|
|
authenticationType: .httpBasic)
|
|
self.configureBasicAuthWithKeychain(basicAuthKeychain)
|
|
|
|
self.pushID = self.prefs.string(forKey: "pushID")
|
|
|
|
if #available(iOS 10, *) {
|
|
UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { (settings) in
|
|
self.prefs.setValue((settings.authorizationStatus == UNAuthorizationStatus.authorized),
|
|
forKey: "notificationsEnabled")
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Configure global state of the app to use our newly validated credentials.
|
|
func confirmAPI() {
|
|
Current.tokenManager = self.tokenManager
|
|
}
|
|
|
|
public func Connect() -> Promise<ConfigResponse> {
|
|
return Promise { seal in
|
|
GetConfig().done { config in
|
|
if let components = config.Components {
|
|
self.loadedComponents = components
|
|
}
|
|
self.prefs.setValue(config.ConfigDirectory, forKey: "config_dir")
|
|
self.prefs.setValue(config.LocationName, forKey: "location_name")
|
|
self.prefs.setValue(config.Latitude, forKey: "latitude")
|
|
self.prefs.setValue(config.Longitude, forKey: "longitude")
|
|
self.prefs.setValue(config.TemperatureUnit, forKey: "temperature_unit")
|
|
self.prefs.setValue(config.LengthUnit, forKey: "length_unit")
|
|
self.prefs.setValue(config.MassUnit, forKey: "mass_unit")
|
|
self.prefs.setValue(config.VolumeUnit, forKey: "volume_unit")
|
|
self.prefs.setValue(config.Timezone, forKey: "time_zone")
|
|
self.prefs.setValue(config.Version, forKey: "version")
|
|
|
|
Crashlytics.sharedInstance().setObjectValue(config.Version, forKey: "hass_version")
|
|
Crashlytics.sharedInstance().setObjectValue(self.loadedComponents.joined(separator: ","),
|
|
forKey: "loadedComponents")
|
|
Crashlytics.sharedInstance().setObjectValue(self.enabledPermissions.joined(separator: ","),
|
|
forKey: "allowedPermissions")
|
|
|
|
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "connected"),
|
|
object: nil,
|
|
userInfo: nil)
|
|
|
|
_ = self.getManifestJSON().done { manifest in
|
|
if let themeColor = manifest.ThemeColor {
|
|
self.prefs.setValue(themeColor, forKey: "themeColor")
|
|
}
|
|
}
|
|
|
|
_ = self.GetStates().done { entities in
|
|
self.cachedEntities = entities
|
|
self.storeEntities(entities: entities)
|
|
if self.loadedComponents.contains("ios") {
|
|
CLSLogv("iOS component loaded, attempting identify", getVaList([]))
|
|
_ = self.identifyDevice()
|
|
}
|
|
|
|
seal.fulfill(config)
|
|
}
|
|
}.catch {error in
|
|
print("Error at launch!", error)
|
|
Crashlytics.sharedInstance().recordError(error)
|
|
seal.reject(error)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public enum HomeAssistantAPIError: Error {
|
|
case notAuthenticated
|
|
case unknown
|
|
}
|
|
|
|
private static var sharedAPI: HomeAssistantAPI?
|
|
public static func authenticatedAPI() -> HomeAssistantAPI? {
|
|
if let api = sharedAPI {
|
|
return api
|
|
}
|
|
|
|
guard let connectionInfo = Current.settingsStore.connectionInfo else {
|
|
return nil
|
|
}
|
|
|
|
if let tokenInfo = Current.settingsStore.tokenInfo {
|
|
let api = HomeAssistantAPI(connectionInfo: connectionInfo,
|
|
authenticationMethod: .modern(tokenInfo: tokenInfo))
|
|
self.sharedAPI = api
|
|
} else {
|
|
let api = HomeAssistantAPI(connectionInfo: connectionInfo,
|
|
authenticationMethod: .legacy(apiPassword: keychain["apiPassword"]))
|
|
self.sharedAPI = api
|
|
}
|
|
|
|
return self.sharedAPI
|
|
}
|
|
|
|
public static var authenticatedAPIPromise: Promise<HomeAssistantAPI> {
|
|
return Promise { seal in
|
|
if let api = self.authenticatedAPI() {
|
|
seal.fulfill(api)
|
|
} else {
|
|
seal.reject(APIError.notConfigured)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func getManifestJSON() -> Promise<ManifestJSON> {
|
|
return Promise { seal in
|
|
if let manager = self.manager {
|
|
let queryUrl = self.connectionInfo.activeURL.appendingPathComponent("manifest.json")
|
|
_ = manager.request(queryUrl, method: .get)
|
|
.validate()
|
|
.responseObject { (response: DataResponse<ManifestJSON>) in
|
|
switch response.result {
|
|
case .success:
|
|
if let resVal = response.result.value {
|
|
seal.fulfill(resVal)
|
|
} else {
|
|
seal.reject(APIError.invalidResponse)
|
|
}
|
|
case .failure(let error):
|
|
CLSLogv("Error on GetManifestJSON() request: %@",
|
|
getVaList([error.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(error)
|
|
seal.reject(error)
|
|
}
|
|
}
|
|
} else {
|
|
seal.reject(APIError.managerNotAvailable)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func GetStatus() -> Promise<StatusResponse> {
|
|
return self.request(path: "", callingFunctionName: "\(#function)", method: .get)
|
|
}
|
|
|
|
public func GetConfig() -> Promise<ConfigResponse> {
|
|
return self.request(path: "config", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
public func GetServices() -> Promise<[ServicesResponse]> {
|
|
return self.request(path: "services", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
public func GetStates() -> Promise<[Entity]> {
|
|
return self.request(path: "states", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
public func GetEntityState(entityId: String) -> Promise<Entity> {
|
|
return self.request(path: "states/\(entityId)", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
public func SetState(entityId: String, state: String) -> Promise<Entity> {
|
|
return self.request(path: "states/\(entityId)", callingFunctionName: "\(#function)", method: .post,
|
|
parameters: ["state": state], encoding: JSONEncoding.default)
|
|
}
|
|
|
|
public func createEvent(eventType: String, eventData: [String: Any]) -> Promise<String> {
|
|
return Promise { seal in
|
|
let queryUrl = self.connectionInfo.activeAPIURL.appendingPathComponent("events/\(eventType)")
|
|
_ = manager.request(queryUrl, method: .post,
|
|
parameters: eventData, encoding: JSONEncoding.default)
|
|
.validate()
|
|
.responseJSON { response in
|
|
switch response.result {
|
|
case .success:
|
|
if let jsonDict = response.result.value as? [String: String],
|
|
let msg = jsonDict["message"] {
|
|
seal.fulfill(msg)
|
|
}
|
|
case .failure(let error):
|
|
CLSLogv("Error when attemping to CreateEvent(): %@",
|
|
getVaList([error.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(error)
|
|
seal.reject(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func downloadDataAt(url: URL) -> Promise<Data> {
|
|
return Promise { seal in
|
|
self.manager.download(url).responseData { downloadResponse in
|
|
switch downloadResponse.result {
|
|
case .success(let data):
|
|
seal.fulfill(data)
|
|
case .failure(let error):
|
|
seal.reject(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func callService(domain: String, service: String, serviceData: [String: Any],
|
|
shouldLog: Bool = true)
|
|
-> Promise<[Entity]> {
|
|
return Promise { seal in
|
|
let queryUrl =
|
|
self.connectionInfo.activeAPIURL.appendingPathComponent("services/\(domain)/\(service)")
|
|
_ = manager.request(queryUrl, method: .post,
|
|
parameters: serviceData, encoding: JSONEncoding.default)
|
|
.validate()
|
|
.responseArray { (response: DataResponse<[Entity]>) in
|
|
switch response.result {
|
|
case .success:
|
|
if let resVal = response.result.value {
|
|
if shouldLog {
|
|
let event = ClientEvent(text: "Calling service: \(domain) - \(service)",
|
|
type: .serviceCall, payload: serviceData)
|
|
Current.clientEventStore.addEvent(event)
|
|
}
|
|
seal.fulfill(resVal)
|
|
} else {
|
|
seal.reject(APIError.invalidResponse)
|
|
}
|
|
case .failure(let error):
|
|
if let afError = error as? AFError {
|
|
var errorUserInfo: [String: Any] = [:]
|
|
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
|
|
if let errorJSON = utf8Text.dictionary(),
|
|
let errMessage = errorJSON["message"] as? String {
|
|
errorUserInfo["errorMessage"] = errMessage
|
|
}
|
|
}
|
|
CLSLogv("Error on CallService() request: %@", getVaList([afError.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(afError)
|
|
let customError = NSError(domain: "io.robbie.HomeAssistant",
|
|
code: afError.responseCode!,
|
|
userInfo: errorUserInfo)
|
|
seal.reject(customError)
|
|
} else {
|
|
CLSLogv("Error on CallService() request: %@", getVaList([error.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(error)
|
|
seal.reject(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func getDiscoveryInfo(baseUrl: URL) -> Promise<DiscoveryInfoResponse> {
|
|
return self.request(path: "discover_info", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
public func identifyDevice() -> Promise<String> {
|
|
return self.request(path: "ios/identify", callingFunctionName: "\(#function)", method: .post,
|
|
parameters: buildIdentifyDict(), encoding: JSONEncoding.default)
|
|
}
|
|
|
|
public func removeDevice() -> Promise<String> {
|
|
return self.request(path: "ios/identify", callingFunctionName: "\(#function)", method: .delete,
|
|
parameters: buildRemovalDict(), encoding: JSONEncoding.default)
|
|
}
|
|
|
|
public func registerDeviceForPush(deviceToken: String) -> Promise<PushRegistrationResponse> {
|
|
let queryUrl = "https://ios-push.home-assistant.io/registrations"
|
|
return Promise { seal in
|
|
Alamofire.request(queryUrl,
|
|
method: .post,
|
|
parameters: buildPushRegistrationDict(deviceToken: deviceToken),
|
|
encoding: JSONEncoding.default
|
|
).validate().responseObject {(response: DataResponse<PushRegistrationResponse>) in
|
|
switch response.result {
|
|
case .success:
|
|
if let json = response.result.value {
|
|
seal.fulfill(json)
|
|
} else {
|
|
let retErr = NSError(domain: "io.robbie.HomeAssistant",
|
|
code: 404,
|
|
userInfo: ["message": "json was nil!"])
|
|
CLSLogv("Error when attemping to registerDeviceForPush(), json was nil!: %@",
|
|
getVaList([retErr.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(retErr)
|
|
seal.reject(retErr)
|
|
}
|
|
case .failure(let error):
|
|
CLSLogv("Error when attemping to registerDeviceForPush(): %@",
|
|
getVaList([error.localizedDescription]))
|
|
Crashlytics.sharedInstance().recordError(error)
|
|
seal.reject(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func turnOn(entityId: String) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "turn_on", serviceData: ["entity_id": entityId])
|
|
}
|
|
|
|
public func turnOnEntity(entity: Entity) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "turn_on", serviceData: ["entity_id": entity.ID])
|
|
}
|
|
|
|
public func turnOff(entityId: String) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "turn_off", serviceData: ["entity_id": entityId])
|
|
}
|
|
|
|
public func turnOffEntity(entity: Entity) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "turn_off",
|
|
serviceData: ["entity_id": entity.ID])
|
|
}
|
|
|
|
public func toggle(entityId: String) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "toggle", serviceData: ["entity_id": entityId])
|
|
}
|
|
|
|
public func toggleEntity(entity: Entity) -> Promise<[Entity]> {
|
|
return callService(domain: "homeassistant", service: "toggle", serviceData: ["entity_id": entity.ID])
|
|
}
|
|
|
|
public func getPushSettings() -> Promise<PushConfiguration> {
|
|
return self.request(path: "ios/push", callingFunctionName: "\(#function)")
|
|
}
|
|
|
|
private func buildIdentifyDict() -> [String: Any] {
|
|
let deviceKitDevice = Device()
|
|
|
|
let ident = IdentifyRequest()
|
|
if let bundleVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") {
|
|
if let stringedBundleVersion = bundleVersion as? String {
|
|
ident.AppBuildNumber = Int(stringedBundleVersion)
|
|
}
|
|
}
|
|
if let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") {
|
|
if let stringedVersionNumber = versionNumber as? String {
|
|
ident.AppVersionNumber = stringedVersionNumber
|
|
}
|
|
}
|
|
ident.AppBundleIdentifer = Bundle.main.bundleIdentifier
|
|
ident.DeviceID = Current.settingsStore.deviceID
|
|
ident.DeviceLocalizedModel = deviceKitDevice.localizedModel
|
|
ident.DeviceModel = deviceKitDevice.model
|
|
ident.DeviceName = deviceKitDevice.name
|
|
ident.DevicePermanentID = Current.deviceIDProvider()
|
|
ident.DeviceSystemName = deviceKitDevice.systemName
|
|
ident.DeviceSystemVersion = deviceKitDevice.systemVersion
|
|
ident.DeviceType = deviceKitDevice.description
|
|
ident.Permissions = self.enabledPermissions
|
|
ident.PushID = pushID
|
|
ident.PushSounds = Notifications.installedPushNotificationSounds()
|
|
|
|
UIDevice.current.isBatteryMonitoringEnabled = true
|
|
|
|
switch UIDevice.current.batteryState {
|
|
case .unknown:
|
|
ident.BatteryState = "Unknown"
|
|
case .charging:
|
|
ident.BatteryState = "Charging"
|
|
case .unplugged:
|
|
ident.BatteryState = "Unplugged"
|
|
case .full:
|
|
ident.BatteryState = "Full"
|
|
}
|
|
|
|
ident.BatteryLevel = Int(UIDevice.current.batteryLevel*100)
|
|
if ident.BatteryLevel == -100 { // simulator fix
|
|
ident.BatteryLevel = 100
|
|
}
|
|
|
|
UIDevice.current.isBatteryMonitoringEnabled = false
|
|
|
|
return Mapper().toJSON(ident)
|
|
}
|
|
|
|
private func buildPushRegistrationDict(deviceToken: String) -> [String: Any] {
|
|
let deviceKitDevice = Device()
|
|
|
|
let ident = PushRegistrationRequest()
|
|
if let bundleVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") {
|
|
if let stringedBundleVersion = bundleVersion as? String {
|
|
ident.AppBuildNumber = Int(stringedBundleVersion)
|
|
}
|
|
}
|
|
if let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") {
|
|
if let stringedVersionNumber = versionNumber as? String {
|
|
ident.AppVersionNumber = stringedVersionNumber
|
|
}
|
|
}
|
|
if let isSandboxed = Bundle.main.object(forInfoDictionaryKey: "IS_SANDBOXED") {
|
|
if let stringedisSandboxed = isSandboxed as? String {
|
|
ident.APNSSandbox = (stringedisSandboxed == "true")
|
|
}
|
|
}
|
|
ident.AppBundleIdentifer = Bundle.main.bundleIdentifier
|
|
ident.DeviceID = Current.settingsStore.deviceID
|
|
ident.DeviceName = deviceKitDevice.name
|
|
ident.DevicePermanentID = Current.deviceIDProvider()
|
|
ident.DeviceSystemName = deviceKitDevice.systemName
|
|
ident.DeviceSystemVersion = deviceKitDevice.systemVersion
|
|
ident.DeviceType = deviceKitDevice.description
|
|
ident.DeviceTimezone = (NSTimeZone.local as NSTimeZone).name
|
|
ident.PushSounds = Notifications.installedPushNotificationSounds()
|
|
ident.PushToken = deviceToken
|
|
if let email = self.prefs.string(forKey: "userEmail") {
|
|
ident.UserEmail = email
|
|
}
|
|
if let version = self.prefs.string(forKey: "version") {
|
|
ident.HomeAssistantVersion = version
|
|
}
|
|
if let timeZone = self.prefs.string(forKey: "time_zone") {
|
|
ident.HomeAssistantTimezone = timeZone
|
|
}
|
|
|
|
return Mapper().toJSON(ident)
|
|
}
|
|
|
|
private func buildRemovalDict() -> [String: Any] {
|
|
let deviceKitDevice = Device()
|
|
|
|
let ident = IdentifyRequest()
|
|
if let bundleVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") {
|
|
if let stringedBundleVersion = bundleVersion as? String {
|
|
ident.AppBuildNumber = Int(stringedBundleVersion)
|
|
}
|
|
}
|
|
if let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") {
|
|
if let stringedVersionNumber = versionNumber as? String {
|
|
ident.AppVersionNumber = stringedVersionNumber
|
|
}
|
|
}
|
|
ident.AppBundleIdentifer = Bundle.main.bundleIdentifier
|
|
ident.DeviceID = Current.settingsStore.deviceID
|
|
ident.DeviceLocalizedModel = deviceKitDevice.localizedModel
|
|
ident.DeviceModel = deviceKitDevice.model
|
|
ident.DeviceName = deviceKitDevice.name
|
|
ident.DevicePermanentID = Current.deviceIDProvider()
|
|
ident.DeviceSystemName = deviceKitDevice.systemName
|
|
ident.DeviceSystemVersion = deviceKitDevice.systemVersion
|
|
ident.DeviceType = deviceKitDevice.description
|
|
ident.Permissions = self.enabledPermissions
|
|
ident.PushID = pushID
|
|
ident.PushSounds = Notifications.installedPushNotificationSounds()
|
|
|
|
return Mapper().toJSON(ident)
|
|
}
|
|
|
|
func storeEntities(entities: [Entity]) {
|
|
let storeableComponents = ["zone"]
|
|
let storeableEntities = entities.filter { (entity) -> Bool in
|
|
return storeableComponents.contains(entity.Domain)
|
|
}
|
|
|
|
for entity in storeableEntities {
|
|
// print("Storing \(entity.ID)")
|
|
|
|
if entity.Domain == "zone", let zone = entity as? Zone {
|
|
let storeableZone = RLMZone()
|
|
storeableZone.ID = zone.ID
|
|
storeableZone.Latitude = zone.Latitude
|
|
storeableZone.Longitude = zone.Longitude
|
|
storeableZone.Radius = zone.Radius
|
|
storeableZone.TrackingEnabled = zone.TrackingEnabled
|
|
storeableZone.BeaconUUID = zone.UUID
|
|
storeableZone.BeaconMajor.value = zone.Major
|
|
storeableZone.BeaconMinor.value = zone.Minor
|
|
|
|
let realm = Current.realm()
|
|
// swiftlint:disable:next force_try
|
|
try! realm.write {
|
|
realm.add(RLMZone(zone: zone), update: true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func configureSessionManager(withPassword password: String? = nil) -> SessionManager {
|
|
var headers = Alamofire.SessionManager.defaultHTTPHeaders
|
|
if let password = password {
|
|
headers["X-HA-Access"] = password
|
|
}
|
|
|
|
let configuration = URLSessionConfiguration.default
|
|
configuration.httpAdditionalHeaders = headers
|
|
configuration.timeoutIntervalForRequest = 10 // seconds
|
|
return Alamofire.SessionManager(configuration: configuration)
|
|
}
|
|
|
|
private func configureBasicAuthWithKeychain(_ basicAuthKeychain: Keychain) {
|
|
if let basicUsername = basicAuthKeychain["basicAuthUsername"],
|
|
let basicPassword = basicAuthKeychain["basicAuthPassword"] {
|
|
self.manager.delegate.sessionDidReceiveChallenge = { session, challenge in
|
|
print("Received basic auth challenge")
|
|
|
|
let authMethod = challenge.protectionSpace.authenticationMethod
|
|
|
|
guard authMethod == NSURLAuthenticationMethodDefault ||
|
|
authMethod == NSURLAuthenticationMethodHTTPBasic ||
|
|
authMethod == NSURLAuthenticationMethodHTTPDigest else {
|
|
print("Not handling auth method", authMethod)
|
|
return (.performDefaultHandling, nil)
|
|
}
|
|
|
|
return (.useCredential, URLCredential(user: basicUsername, password: basicPassword,
|
|
persistence: .synchronizable))
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|