Server code to fetch and toggle devices

This commit is contained in:
Charles Magahern
2015-12-31 19:51:51 -08:00
parent e85e9173e5
commit e6dd661338
11 changed files with 402 additions and 44 deletions

View File

@@ -9,10 +9,24 @@
import UIKit
class MainViewController: UIViewController, SwitchesViewControllerDelegate {
private var _server: WemoServer
private var _visualizationController: VisualizationViewController = VisualizationViewController()
private var _switchesController: SwitchesViewController = SwitchesViewController()
private var _headerView: HeaderView = HeaderView()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)
{
let url = NSURL(string: "http://10.0.1.4:8000")
_server = WemoServer(url!)
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("unsupported")
}
// MARK: Overrides
override func viewDidLoad()
@@ -30,12 +44,7 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
self.view.addSubview(_headerView)
// DEBUG
for _ in 0 ..< 10 {
let device = WemoDevice()
device.name = "beatmania IIDX"
self.devices.append(device)
}
_updateConnectivityStatus(.Disconnected)
}
override func viewDidLayoutSubviews()
@@ -78,6 +87,15 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
override func viewDidAppear(animated: Bool)
{
_headerView.xionLogoView.beginAnimating()
_updateConnectivityStatus(.Connecting)
_server.connect { (error: NSError?) -> Void in
if (error == nil) {
self._reloadDevices()
} else {
self._updateConnectivityStatus(.Error)
}
}
}
override func viewDidDisappear(animated: Bool)
@@ -106,6 +124,10 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice])
{
_updateVisualization(true)
for device in devices {
_server.toggleDevice(device, state: device.state, completion: { (error: NSError?) -> Void in })
}
}
// MARK: Internal
@@ -124,4 +146,26 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate {
_visualizationController.setPercentActivated(percentageActivated, animated: animated)
}
}
internal func _updateConnectivityStatus(status: ConnectionStatus)
{
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self._headerView.connectionStatusView.connectivityStatus = status
}
}
internal func _reloadDevices()
{
_server.fetchDevices({ (devices: [WemoDevice], error: NSError?) -> Void in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if (error == nil) {
self.devices = devices
self._updateConnectivityStatus(.Connected)
} else {
self.devices = []
self._updateConnectivityStatus(.Error)
}
}
})
}
}

View File

@@ -69,6 +69,8 @@ class SwitchesViewController: UIViewController,
_collectionView.registerClass(WemoDeviceCellView.self, forCellWithReuseIdentifier: deviceCellReuseID)
_collectionView.registerClass(WemoActionCellView.self, forCellWithReuseIdentifier: actionCellReuseID)
self.view.addSubview(_collectionView)
self.devices = []
}
override func viewDidLayoutSubviews()
@@ -102,6 +104,7 @@ class SwitchesViewController: UIViewController,
let reuseID = SwitchesViewController.collectionViewActionCellReuseIdentifier
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseID, forIndexPath: indexPath) as! WemoActionCellView
cell.textLabel.text = ActionCell(rawValue: indexPath.item)?.name().uppercaseString
cell.enabled = (self.devices.count > 0)
return cell
} else {
@@ -136,8 +139,8 @@ class SwitchesViewController: UIViewController,
{
if (indexPath.item < ActionCell.count) {
let tappedActionCell = ActionCell(rawValue: indexPath.item)
var currentDelay: NSTimeInterval = 0.0
for var i = ActionCell.count; i < collectionView.numberOfItemsInSection(indexPath.section); ++i {
let cell = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: i, inSection: indexPath.section)) as! WemoDeviceCellView
@@ -164,4 +167,22 @@ class SwitchesViewController: UIViewController,
self.delegate?.switchesViewControllerDidToggleDevices(self, devices: [device])
}
}
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
if (indexPath.item < ActionCell.count) {
return (self.devices.count > 0)
} else {
return true
}
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
if (indexPath.item < ActionCell.count) {
return (self.devices.count > 0)
} else {
return true
}
}
}

View File

@@ -0,0 +1,134 @@
//
// WemoServer.swift
// XIONControlPanel
//
// Created by Charles Magahern on 12/31/15.
// Copyright © 2015 XION. All rights reserved.
//
import Foundation
enum ConnectionStatus {
case Disconnected
case Connecting
case Connected
case Error
}
class WemoServer {
private(set) var baseURL: NSURL
private(set) var connected: Bool = false
private var _urlSession: NSURLSession
private var _errorStream: StandardErrorOutputStream = StandardErrorOutputStream()
init(_ url: NSURL)
{
self.baseURL = url
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
_urlSession = NSURLSession(configuration: config)
}
func connect(completion: (NSError?) -> Void)
{
if (!self.connected) {
let url = self.baseURL.URLByAppendingPathComponent("api/environment")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let task = _urlSession.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
var clientError: NSError? = nil
if (error != nil) {
clientError = NSError.xionError(.ConnectionError, underlying: error!)
self._logError("Error connecting to server", error: clientError!)
} else {
self.connected = true
}
completion(clientError)
}
task.resume()
} else {
completion(nil)
}
}
func disconnect(completion: (NSError?) -> Void)
{
self.connected = false
completion(nil)
}
func fetchDevices(completion: ([WemoDevice], NSError?) -> Void)
{
if (self.connected) {
let url = self.baseURL.URLByAppendingPathComponent("api/environment")
let task = _urlSession.dataTaskWithURL(url) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
var devices: [WemoDevice] = []
var clientError: NSError? = nil
if (data != nil) {
devices = self._parseDevices(data!)
} else {
clientError = NSError.xionError(.ConnectionError, underlying: error)
self._logError("Error fetching devices", error: clientError!)
}
completion(devices, clientError)
}
task.resume()
} else {
let err = NSError.xionError(.ConnectionError)
completion([], err)
}
}
func toggleDevice(device: WemoDevice, state: WemoDevice.State, completion: (NSError?) -> Void)
{
if (self.connected) {
let stateArg = (state == .On ? "on" : "off")
let url = self.baseURL.URLByAppendingPathComponent("api/device/\(device.name)").URLByAppendingRequestParameters(["state" : stateArg])
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let task = _urlSession.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
var clientError: NSError? = nil
if (error != nil) {
clientError = NSError.xionError(.ConnectionError, underlying: error)
}
completion(clientError)
}
task.resume()
} else {
let err = NSError.xionError(.ConnectionError)
completion(err)
}
}
// MARK: Internal
internal func _logError(description: String, error: NSError)
{
print("ERROR: \(description) \(error)", toStream: &_errorStream)
}
internal func _parseDevices(data: NSData) -> [WemoDevice]
{
var devices: [WemoDevice] = []
if let responseDict = (try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? NSDictionary) {
for responseObj in (responseDict?.allValues)! {
if let responseDict = responseObj as? NSDictionary {
let device = WemoDevice(responseDict)
devices.append(device)
}
}
}
return devices
}
}