summaryrefslogtreecommitdiff
path: root/Example Apps/Example Swift/RemoteControlManager.swift
blob: fc73434b42cae58a70451f9c0296b3debc4d6712 (plain)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//
//  RemoteControlManager.swift
//  SmartDeviceLink-Example-Swift
//
//  Created by Beharry, Justin (J.S.) on 7/28/22.
//  Copyright © 2022 smartdevicelink. All rights reserved.
//

import Foundation
import SmartDeviceLink
import SmartDeviceLinkSwift

class RemoteControlManager {
    private let sdlManager: SDLManager
    private let homeButtons: [SDLSoftButtonObject]
    private var remoteControlCapabilities: SDLRemoteControlCapabilities?
    private var climateModuleId: String?
    private var hasConsent: Bool?
    private var climateData: SDLClimateControlData?

    let isEnabled: Bool
    var climateDataString: String {
            """
            AC: \(optionalNumberBoolToString(climateData?.acEnable))
            AC Max: \(optionalNumberBoolToString(climateData?.acMaxEnable))
            Auto Mode: \(optionalNumberBoolToString(climateData?.autoModeEnable))
            Circulate Air: \(optionalNumberBoolToString(climateData?.circulateAirEnable))
            Climate: \(optionalNumberBoolToString(climateData?.climateEnable))
            Current Temperature: \(optionalTemperatureToString(climateData?.currentTemperature))
            Defrost Zone: \(optionalSDLEnumToString(climateData?.defrostZone?.rawValue))
            Desired Temperature: \(optionalTemperatureToString(climateData?.desiredTemperature))
            Dual Mode: \(optionalNumberBoolToString(climateData?.dualModeEnable))
            Fan Speed: \(optionalNumberToString(climateData?.fanSpeed))
            Heated Mirrors: \(optionalNumberBoolToString(climateData?.heatedMirrorsEnable))
            Heated Rears Window: \(optionalNumberBoolToString(climateData?.heatedRearWindowEnable))
            Heated Steering: \(optionalNumberBoolToString(climateData?.heatedSteeringWheelEnable))
            Heated Windshield: \(optionalNumberBoolToString(climateData?.heatedWindshieldEnable))
            Ventilation: \(optionalSDLEnumToString(climateData?.ventilationMode?.rawValue))
            """
    }

    /// Initialize the RemoteControlManager Object
    ///
    /// - Parameters:
    ///     - sdlManager: The SDL Manager.
    ///     - enabled: Permission from the proxy manager to access remote control data
    ///     - homeButton: An array of SDLSoftButtonObjects that remote control manager can reset to.
    init(sdlManager: SDLManager, enabled: Bool, homeButtons: [SDLSoftButtonObject]) {
        self.sdlManager = sdlManager
        self.isEnabled = enabled
        self.homeButtons = homeButtons
    }

    func start() {
        if !self.isEnabled {
            return SDLLog.w("Missing permissions for Remote Control Manager. Example remote control works only on TCP.")
        }

        // Retrieve remote control information and store module ids
        self.sdlManager.systemCapabilityManager.subscribe(capabilityType: .remoteControl) { (capability, subscribed, error) in
            guard capability?.remoteControlCapability != nil else {
                return SDLLog.e("SDL errored getting remote control module information: \(String(describing: error))")
            }
            self.remoteControlCapabilities = capability?.remoteControlCapability

            let firstClimateModule = self.remoteControlCapabilities?.climateControlCapabilities?.first
            let moduleId = firstClimateModule?.moduleInfo?.moduleId
            self.climateModuleId = moduleId

            // Get Consent to control module
            let getInteriorVehicleDataConsent = SDLGetInteriorVehicleDataConsent(moduleType: .climate, moduleIds: [self.climateModuleId!])
            self.sdlManager.send(request: getInteriorVehicleDataConsent, responseHandler: { (request, response, error) in
                guard let response = response as? SDLGetInteriorVehicleDataConsentResponse else {
                    return SDLLog.e("SDL errored getting remote control consent: \(String(describing: error))");
                }
                guard let allowed = response.allowed?.first?.boolValue else { return }

                self.hasConsent = allowed

                // initialize climate data and setup subscription
                if self.hasConsent == true {
                    self.initializeClimateData()
                    self.subscribeClimateControlData()
                }
            })
        }
    }

    /// Displays Buttons for the user to control the climate
    func showClimateControl() {
        // Check that the climate module id has been set and consent has been given
        guard climateModuleId != nil && hasConsent == true else {
            return AlertManager.sendAlert(textField1: "The climate module id was not set or consent was not given", sdlManager: self.sdlManager)
        }
        self.sdlManager.screenManager.softButtonObjects = climateButtons
    }

    private func optionalNumberBoolToString(_ number: NSNumber?) -> String {
        guard let number = number else { return "Unknown" }
        return number.boolValue ? "On" : "Off"
    }

    private func optionalNumberToString(_ number: NSNumber?) -> String {
        guard let number = number else { return "Unknown" }
        return number.stringValue
    }

    private func optionalTemperatureToString(_ temperature: SDLTemperature?) -> String {
        guard let temperature = temperature else { return "Unknown" }
        return temperature.description
    }

    private func optionalSDLEnumToString(_ sdlEnum: SDLEnum?) -> String {
        guard let sdlEnum = sdlEnum else { return "Unknown" }
        return sdlEnum.rawValue
    }

    private func initializeClimateData() {
        // Check that the climate module id has been set and consent has been given
        guard climateModuleId != nil && hasConsent == true else {
            return AlertManager.sendAlert(textField1: "The climate module id was not set or consent was not given", sdlManager: self.sdlManager)
        }

        let getInteriorVehicleData =  SDLGetInteriorVehicleData(moduleType: .climate, moduleId: self.climateModuleId!)
        self.sdlManager.send(request: getInteriorVehicleData) { (req, res, err) in
            guard let response = res as? SDLGetInteriorVehicleDataResponse else { return }
            self.climateData = response.moduleData?.climateControlData
        }
    }

    private func subscribeClimateControlData() {
        // Start the subscription to remote control data
        sdlManager.subscribe(to: .SDLDidReceiveInteriorVehicleData) { (message) in
            guard let onInteriorVehicleData = message as? SDLOnInteriorVehicleData else { return }
            self.climateData = onInteriorVehicleData.moduleData.climateControlData
        }

        // Start the subscription to climate data
        let getInteriorVehicleData = SDLGetInteriorVehicleData(andSubscribeToModuleType: .climate, moduleId: self.climateModuleId!)
        sdlManager.send(request: getInteriorVehicleData) { (req, res, err) in
            guard let response = res as? SDLGetInteriorVehicleDataResponse, response.success.boolValue == true else {
                return SDLLog.e("SDL errored trying to subscribe to climate data: \(String(describing: err))")
            }
            SDLLog.d("Subscribed to climate control data");
        }
    }

    private func turnOnAC() {
        let climateControlData = SDLClimateControlData(dictionary: [
            "acEnable": true
        ])
        let moduleData = SDLModuleData(climateControlData: climateControlData)
        let setInteriorVehicleData = SDLSetInteriorVehicleData(moduleData: moduleData)

        self.sdlManager.send(request: setInteriorVehicleData) { (request, response, error) in
            guard response?.success.boolValue == true else {
                return SDLLog.e("SDL errored trying to turn on climate AC: \(String(describing: error))")
            }
        }
    }

    private func turnOffAC() {
        let climateControlData = SDLClimateControlData(dictionary: [
            "acEnable": false
        ])
        let moduleData = SDLModuleData(climateControlData: climateControlData)
        let setInteriorVehicleData = SDLSetInteriorVehicleData(moduleData: moduleData)

        self.sdlManager.send(request: setInteriorVehicleData) { (request, response, error) in
            guard response?.success.boolValue == true else {
                return SDLLog.e("SDL errored trying to turn off climate AC: \(String(describing: error))")
            }
        }
    }

    // An array of button objects to control the climate
    private var climateButtons : [SDLSoftButtonObject] {
        let acOnButton = SDLSoftButtonObject(name: "AC On", text: "AC On", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            self.turnOnAC()
        }

        let acOffButton = SDLSoftButtonObject(name: "AC Off", text: "AC Off", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            self.turnOffAC()
        }

        let acMaxToggle = SDLSoftButtonObject(name: "AC Max", text: "AC Max", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            let remoteButtonPress = SDLButtonPress(buttonName: .acMax, moduleType: .climate, moduleId: self.climateModuleId, buttonPressMode: .short)
            self.sdlManager.send(request: remoteButtonPress) { (request, response, error) in
                guard response?.success.boolValue == true else {
                    return SDLLog.e("SDL errored toggling AC Max with remote button press: \(String(describing: error))")
                }
            }
        }

        let temperatureDecreaseButton = SDLSoftButtonObject(name: "Temperature Decrease", text: "Temperature -", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            let remoteButtonPress = SDLButtonPress(buttonName: .tempDown, moduleType: .climate, moduleId: self.climateModuleId, buttonPressMode: .short)
            self.sdlManager.send(request: remoteButtonPress) { (request, response, error) in
                guard response?.success.boolValue == true else {
                    return SDLLog.e("SDL errored decreasing target climate temperature with remote button: \(String(describing: error))")
                }
            }
        }

        let temperatureIncreaseButton = SDLSoftButtonObject(name: "Temperature Increase", text: "Temperature +", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            let remoteButtonPress = SDLButtonPress(buttonName: .tempUp, moduleType: .climate, moduleId: self.climateModuleId, buttonPressMode: .short)
            self.sdlManager.send(request: remoteButtonPress) { (request, response, error) in
                guard response?.success.boolValue == true else {
                    return SDLLog.e("SDL errored increasing target climate temperature with remote button:: \(String(describing: error))")
                }
            }
        }

        let backToHomeButton = SDLSoftButtonObject(name: "Home", text: "Back to Home", artwork: nil) { (buttonPress, buttonEvent) in
            guard buttonPress != nil else { return }
            self.sdlManager.screenManager.softButtonObjects = self.homeButtons
            self.sdlManager.screenManager.changeLayout(SDLTemplateConfiguration(predefinedLayout: .nonMedia))
        }

        return [acOnButton, acOffButton, acMaxToggle, temperatureDecreaseButton, temperatureIncreaseButton, backToHomeButton]
    }
}