// // MainViewController.swift // XIONControlPanel // // Created by Charles Magahern on 12/29/15. // Copyright © 2015 XION. All rights reserved. // import UIKit class MainViewController: UIViewController, SwitchesViewControllerDelegate, ServerMultiplexDelegate { fileprivate var _serverMultiplex: ServerMultiplex = ServerMultiplex() fileprivate var _visualizationController: VisualizationViewController = VisualizationViewController() fileprivate var _switchesController: SwitchesViewController = SwitchesViewController() fileprivate var _headerView: HeaderView = HeaderView() fileprivate var _updateDevices: Bool = false override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) _serverMultiplex.delegate = self _serverMultiplex.addServer(HubitatServer(URL(string: "https://midna.xionsf.com:6000/apps/api/1/")!)) _serverMultiplex.addServer(WemoServer(URL(string: "http://midna.xionsf.com:5000")!)) } required init?(coder aDecoder: NSCoder) { fatalError("unsupported") } // MARK: Overrides override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.black self.addChild(_visualizationController) self.view.addSubview(_visualizationController.view) _switchesController.delegate = self self.addChild(_switchesController) self.view.addSubview(_switchesController.view) self.view.addSubview(_headerView) _updateConnectivityStatus(.disconnected) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let bounds = self.view.bounds let headerBounds = CGRect( x: 0.0, y: 0.0, width: bounds.size.width, height: rint(bounds.size.height / 8.0) ) let bodyBounds = CGRect( x: 0.0, y: headerBounds.maxY, width: bounds.size.width, height: bounds.size.height - headerBounds.size.height ) _headerView.frame = headerBounds let visualizationFrame = CGRect( x: bodyBounds.origin.x, y: bodyBounds.origin.y, width: rint(0.5 * bodyBounds.size.width), height: bodyBounds.size.height ) _visualizationController.view.frame = visualizationFrame var switchesOriginX: CGFloat = 0.0 var switchesWidth: CGFloat = 0.0 if (_visualizationShouldBeVisible()) { switchesOriginX = visualizationFrame.maxX switchesWidth = bodyBounds.size.width - visualizationFrame.size.width } else { switchesOriginX = 0.0 switchesWidth = bodyBounds.size.width } let switchesControllerFrame = CGRect( x: switchesOriginX, y: bodyBounds.origin.y, width: switchesWidth, height: bodyBounds.size.height ) _switchesController.view.frame = switchesControllerFrame } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) UIApplication.shared.isIdleTimerDisabled = true _headerView.xionLogoView.beginAnimating() if _serverMultiplex.devices.count == 0 { // Do initial refresh _updateConnectivityStatus(.connecting) _serverMultiplex.refreshDevices() _startUpdatingDevices() } } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) UIApplication.shared.isIdleTimerDisabled = false } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) _updateSizeClassPresentation() } override var prefersStatusBarHidden : Bool { return true } // MARK: SwitchesViewControllerDelegate func switchesViewControllerDidToggleDevices(_ controller: SwitchesViewController, devices: [AnyDevice]) { _updateVisualization(true) for device in devices { _serverMultiplex.toggleDeviceState(device, state: device.state, completion: { (error: Error?) -> Void in }) } } // MARK: Internal internal func _visualizationShouldBeVisible() -> Bool { switch self.traitCollection.horizontalSizeClass { case .regular: return true case .compact: return false default: return false } } internal func _updateSizeClassPresentation() { _visualizationController.view.isHidden = !_visualizationShouldBeVisible() } internal func _updateVisualization(_ animated: Bool) { let activatedDevicesCount = _serverMultiplex.devices.filter { $0.state == .on }.count let totalDeviceCount = _serverMultiplex.devices.count let percentageActivated = (totalDeviceCount > 0 ? Float(activatedDevicesCount) / Float(totalDeviceCount) : 0.0) if (percentageActivated != _visualizationController.percentActivated) { _visualizationController.setPercentActivated(percentageActivated, animated: animated) } } internal func _updateConnectivityStatus(_ status: ConnectionStatus) { DispatchQueue.main.async { () -> Void in self._headerView.connectionStatusView.connectivityStatus = status self._headerView.setNeedsLayout() self._visualizationController.connectionStatus = status } } internal func _startUpdatingDevices() { _updateDevices = true let interval = DispatchTime.now() + Double(Int64(10 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) DispatchQueue.main.asyncAfter(deadline: interval) { () -> Void in if (self._updateDevices) { self._serverMultiplex.refreshDevices() self._startUpdatingDevices() } } } internal func _stopUpdatingDevices() { _updateDevices = false } // MARK: Server Multiplex Delegate func serverMultiplex(_ multiplex: ServerMultiplex, didAddDevices devices: [AnyDevice]) { _switchesController.devices = Array(multiplex.devices) _updateVisualization(false) } func serverMultiplex(_ multiplex: ServerMultiplex, devicesStateChanged devices: [AnyDevice]) { _switchesController.devicesStateChanged(devices) } func serverMultiplex(_ multiplex: ServerMultiplex, didReceiveAcknowledgementFromServer server: Server) { // Update connection status too, if applicable let groupConnectionStatus = multiplex.groupConnectionStatus() _updateConnectivityStatus(groupConnectionStatus) } func serverMultiplex(_ multiplex: ServerMultiplex, didEncounterError error: Error) { _updateConnectivityStatus(.error) var stderr = StandardErrorOutputStream() print(error.localizedDescription, to: &stderr) } }