Skip to content

Instantly share code, notes, and snippets.

@weissi
Created March 21, 2016 15:52
Show Gist options
  • Save weissi/6713193210a388da7a87 to your computer and use it in GitHub Desktop.
Save weissi/6713193210a388da7a87 to your computer and use it in GitHub Desktop.
//
// WemoControl.swift
// WemoSchedule
//
// Created by Johannes Weiß on 12/03/2016.
// Copyright © 2016 Johannes Weiß. All rights reserved.
//
import Foundation
extension Token : Equatable {}
func ==(lhs: Token, rhs: Token) -> Bool {
switch (lhs, rhs) {
case (.TagBegin(let l), .TagBegin(let r)):
return l == r
case (.TagEnd(let l), .TagEnd(let r)):
return l == r
case (.Data(let l), .Data(let r)):
return l == r
default:
return false
}
}
enum Token {
case TagBegin(String)
case TagEnd(String)
case Data(String)
}
func curry<A, B, C, D>(f: (A, B, C) -> D) -> A -> B -> C -> D {
return { a in { b in { c in f(a, b, c) } } }
}
enum SetBinaryStateResponse {
case BinaryState(Bool, UInt, UInt)
case Error
}
enum GetSignalStrengthResponse {
case SignalStrengh(UInt)
}
enum GetFriendlyNameResponse {
case FriendlyName(String)
}
enum GetBinaryStateResponse {
case BinaryState(Bool)
}
enum XBody {
case XSetBinaryStateResponse(SetBinaryStateResponse)
case XGetBinaryStateResponse(GetBinaryStateResponse)
case XGetFriendlyNameResponse(GetFriendlyNameResponse)
case XGetSignalStrengthResponse(GetSignalStrengthResponse)
}
enum Envelope {
case Body(XBody)
}
struct Parser<T> {
let p : ([Token]) -> (T?, [Token])
func map<B>(f:T->B) -> Parser<B> {
return Parser<B>(p: { (tokenStream : [Token]) -> (B?, [Token]) in
switch self.p(tokenStream) {
case (.Some(let v), let tokenStream2):
return (f(v), tokenStream2)
default:
return (nil, tokenStream)
}
})
}
func flatMap<B>(f:T->Parser<B>) -> Parser<B> {
return Parser<B>(p: { (tokenStream : [Token]) -> (B?, [Token]) in
switch self.p(tokenStream) {
case (.Some(let v), let tokenStream2):
switch f(v).p(tokenStream2) {
case (.Some(let v2), let tokenStream3):
return (v2, tokenStream3)
default: ()
}
default: ()
}
return (nil, tokenStream)
})
}
static func pure<A>(t:A) -> Parser<A> {
return Parser<A>(p: { (tokenStream : [Token]) -> (A?, [Token]) in
return (.Some(t), tokenStream)
})
}
static func fail<A>() -> Parser<A> {
return Parser<A>(p: { (tokenStream : [Token]) -> (A?, [Token]) in
return (nil, tokenStream)
})
}
}
func parseConstant(token:Token) -> Parser<Token> {
return Parser(p:{ (tokenStream : [Token]) in
if let t = tokenStream.first where t == token {
return (token, Array(tokenStream.suffixFrom(1)))
} else {
return (nil, tokenStream)
}
})
}
func parseBool() -> Parser<Bool> {
return Parser(p: { (tokenStream : [Token]) in
if let t = tokenStream.first {
switch t {
case .Data("0"):
return (true, Array(tokenStream.suffixFrom(1)))
case .Data("1"):
return (true, Array(tokenStream.suffixFrom(1)))
default: ()
}
}
return (nil, tokenStream)
})
}
func parseString() -> Parser<String> {
return Parser(p: { (tokenStream : [Token]) -> (String?, [Token]) in
if let t = tokenStream.first {
switch t {
case .Data(let s):
return (s, Array(tokenStream.suffixFrom(1)))
default: ()
}
}
return (nil, tokenStream)
})
}
func parseUInt() -> Parser<UInt> {
return parseString().flatMap({ (s:String) -> Parser<UInt> in
if let v = UInt(s) {
return Parser<UInt>.pure(v)
} else {
return Parser<UInt>.fail()
}
})
}
infix operator <§> {associativity left}
func <§> <A,B>(f:A->B, p:Parser<A>) -> Parser<B> {
return p.map(f)
}
infix operator *> {associativity left}
func *> <A,B>(pA:Parser<A>, pB:Parser<B>) -> Parser<B> {
return Parser(p: { (tokenStream : [Token]) -> (B?, [Token]) in
switch pA.p(tokenStream) {
case (.Some(let _), let tokenStream2):
return pB.p(tokenStream2)
default:
return (nil, tokenStream)
}
})
}
infix operator <* {associativity left}
func <* <A,B>(pA:Parser<A>, pB:Parser<B>) -> Parser<A> {
return Parser(p: { (tokenStream : [Token]) -> (A?, [Token]) in
switch pA.p(tokenStream) {
case (.Some(let l), let tokenStream2):
switch pB.p(tokenStream2) {
case (.Some(_), let tokenStream3):
return (l, tokenStream3)
default: ()
}
default: ()
}
return (nil, tokenStream)
})
}
infix operator <*> {associativity left}
func <*> <A,B>(pA:Parser<A->B>, pB:Parser<A>) -> Parser<B> {
return Parser(p: { (tokenStream : [Token]) -> (B?, [Token]) in
switch pA.p(tokenStream) {
case (.Some(let f), let tokenStream2):
switch pB.p(tokenStream2) {
case (.Some(let r), let tokenStream3):
return (f(r), tokenStream3)
default: ()
}
default: ()
}
return (nil, tokenStream)
})
}
infix operator <|> {associativity left}
func <|> <A>(pA:Parser<A>, pB:Parser<A>) -> Parser<A> {
return Parser<A>(p: { (tokenStream : [Token]) -> (A?, [Token]) in
switch pA.p(tokenStream) {
case (.Some(let f), let tokenStream2):
return (f, tokenStream2)
default:()
}
switch pB.p(tokenStream) {
case (.Some(let f), let tokenStream2):
return (f, tokenStream2)
default:
return (nil, tokenStream)
}
})
}
func parseTagContent<A>(tag:String, f:Parser<A>) -> Parser<A> {
return parseConstant(.TagBegin(tag)) *> f <* parseConstant(.TagEnd(tag))
}
func parseBinaryState() -> Parser<Bool> {
return parseTagContent("BinaryState", f:parseBool())
}
func parseError() -> Parser<Token> {
return parseConstant(.Data("Error"))
}
func parseBinaryStateError() -> Parser<Token> {
return parseTagContent("BinaryState", f: parseError())
}
func parseGetBinaryStateResponse() -> Parser<GetBinaryStateResponse> {
return GetBinaryStateResponse.BinaryState <§> parseTagContent("GetBinaryStateResponse", f: parseBinaryState())
}
func parseGetFriendlyNameResponse() -> Parser<GetFriendlyNameResponse> {
return GetFriendlyNameResponse.FriendlyName <§>
parseTagContent("GetFriendlyNameResponse", f:parseTagContent("FriendlyName", f: parseString()))
}
func parseGetSignalStrengthResponse() -> Parser<GetSignalStrengthResponse> {
return GetSignalStrengthResponse.SignalStrengh <§>
parseTagContent("GetSignalStrengthResponse", f:parseTagContent("SignalStrength", f: parseUInt()))
}
func parseSetBinaryStateResponse() -> Parser<SetBinaryStateResponse> {
let errorCase : Parser<SetBinaryStateResponse> = Parser<SetBinaryStateResponse>.pure(SetBinaryStateResponse.Error) <* parseBinaryStateError()
let fx : (Bool -> (UInt -> (UInt -> SetBinaryStateResponse))) = curry(SetBinaryStateResponse.BinaryState)
let goodCase : Parser<SetBinaryStateResponse> = (((fx <§>
parseTagContent("BinaryState", f:parseBool()))
<*> parseTagContent("CountdownEndTime", f:parseUInt()))
<*> parseTagContent("deviceCurrentTime", f:parseUInt()))
let p = errorCase <|> goodCase
return parseTagContent("SetBinaryStateResponse", f: p)
}
func parseBody() -> Parser<XBody> {
let p:Parser<XBody> = (XBody.XGetSignalStrengthResponse <§> parseGetSignalStrengthResponse())
<|> (XBody.XGetFriendlyNameResponse <§> parseGetFriendlyNameResponse())
<|> (XBody.XGetBinaryStateResponse <§> parseGetBinaryStateResponse())
<|> (XBody.XSetBinaryStateResponse <§> parseSetBinaryStateResponse())
return parseTagContent("Body", f:p)
}
func parseEnvelope() -> Parser<Envelope> {
return Envelope.Body <§> parseTagContent("Envelope", f: parseBody())
}
public struct WemoControl {
private let port : UInt16
private let host : String
/*
func parseBody(tokenStream:[Token]) -> (Body?, [Token]) {
return (nil, [])
}
func parseEnvelope(tokenStream:[Token]) -> (Envelope?, [Token]) {
parseConstant(.TagBegin("Envelope"), tokenStream:tokenStream)
if let t = tokenStream.first {
switch(t) {
case .TagBegin("Envelope"):
let tail = Array(tokenStream.suffixFrom(1))
switch parseBody(tail) {
case (.Some(let body), let tail2):
if let t2 = tail2.first {
switch t2 {
case .TagEnd("Envelope"):
return (.Body(body), Array(tail2.suffixFrom(1)))
default: ()
}
}
fallthrough
default: ()
}
default: ()
}
return (nil, tokenStream)
} else {
return (nil, tokenStream)
}
}
*/
public init(host:String, port:UInt16) {
self.host = host
self.port = port
}
public func switchOn() {
self.send("Set", obj: "BinaryState", value: 1)
}
public func switchOff() {
self.send("Set", obj: "BinaryState", value: 0)
}
public func status() {
self.send("Get", obj: "BinaryState", value: nil)
}
public func name() {
self.send("Get", obj: "FriendlyName", value: nil)
}
public func signal() {
self.send("Get", obj: "SignalStrength", value: nil)
}
private func bodyXML(method:String, obj:String, value:Int?) -> NSData? {
let valueEncoding = value.map {"<\(obj)>\($0)</\(obj)>"} ?? ""
let bodyAsString = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" +
" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" +
" <s:Body>\n" +
" <u:\(method)\(obj) xmlns:u=\"urn:Belkin:service:basicevent:1\">\n" +
" " + valueEncoding + "\n" +
" </u:\(method)\(obj)>\n" +
" </s:Body>\n" +
"</s:Envelope>"
return bodyAsString.dataUsingEncoding(NSUTF8StringEncoding)
}
private func headerXML(method:String, obj:String) -> String {
return "\"urn:Belkin:service:basicevent:1#\(method)\(obj)\""
}
private func send(method:String, obj:String, value:Int?) -> () {
let bodyXML = self.bodyXML(method, obj: obj, value: value)
let headerXML = self.headerXML(method, obj: obj)
let urlComponents = NSURLComponents()
urlComponents.scheme = "http"
urlComponents.host = host
urlComponents.port = NSNumber(integer: Int(port))
urlComponents.path = "/upnp/control/basicevent1"
if let url = urlComponents.URL {
let request = NSMutableURLRequest(URL: url)
request.allHTTPHeaderFields = ["Content-type": "text/xml; charset=\"utf-8\"",
"SOAPACTION": headerXML]
request.HTTPBody = bodyXML
request.HTTPMethod = "POST"
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request,
completionHandler: {
(responseData : NSData?, response : NSURLResponse?, error : NSError?) in
if let responseData = responseData {
let p = NSXMLParser(data: responseData)
let d = XMLParsingDelegate()
p.delegate = d
p.shouldProcessNamespaces = true
p.parse()
print(NSString(data: responseData, encoding: NSUTF8StringEncoding))
}
print(error)
})
task.resume()
} else {
fatalError("todo: throw stuff")
}
}
@objc class XMLParsingDelegate: NSObject, NSXMLParserDelegate {
var tokenStream : [Token]
override init() {
self.tokenStream = []
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
self.tokenStream.append(Token.TagBegin(elementName))
}
func parser(parser: NSXMLParser, foundCharacters string: String) {
if string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) != "" {
self.tokenStream.append(Token.Data(string))
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
self.tokenStream.append(Token.TagEnd(elementName))
}
func parserDidStartDocument(parser: NSXMLParser) {
print("BEGIN")
}
func parserDidEndDocument(parser: NSXMLParser) {
print("<-- END")
print(parseEnvelope().p(self.tokenStream))
print("--> END")
}
func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment