├── .gitignore ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Info-macOS.plist ├── LICENSE ├── README.md ├── Sources ├── Base │ ├── BridgeAccessConfig.swift │ ├── BridgeResourceModels │ │ ├── AppData.swift │ │ ├── Backup.swift │ │ ├── BridgeConfiguration.swift │ │ ├── BridgeResource.swift │ │ ├── Error.swift │ │ ├── Group.swift │ │ ├── Light.swift │ │ ├── LightState.swift │ │ ├── PortalState.swift │ │ ├── Rule │ │ │ ├── Rule.swift │ │ │ ├── RuleAction.swift │ │ │ └── RuleCondition.swift │ │ ├── Scene.swift │ │ ├── Schedule │ │ │ ├── Schedule.swift │ │ │ └── ScheduleCommand.swift │ │ ├── Sensors │ │ │ ├── DaylightSensor │ │ │ │ ├── DaylightSensor.swift │ │ │ │ ├── DaylightSensorConfig.swift │ │ │ │ └── DaylightSensorState.swift │ │ │ ├── GenericFlagSensor │ │ │ │ ├── GenericFlagSensor.swift │ │ │ │ ├── GenericFlagSensorConfig.swift │ │ │ │ └── GenericFlagSensorState.swift │ │ │ ├── GenericStatusSensor │ │ │ │ ├── GenericStatusSensor.swift │ │ │ │ ├── GenericStatusSensorConfig.swift │ │ │ │ └── GenericStatusSensorState.swift │ │ │ ├── HumiditySensor │ │ │ │ ├── HumiditySensor.swift │ │ │ │ ├── HumiditySensorConfig.swift │ │ │ │ └── HumiditySensorState.swift │ │ │ ├── LightLevelSensor │ │ │ │ ├── LightLevelSensor.swift │ │ │ │ ├── LightLevelSensorConfig.swift │ │ │ │ └── LightLevelSensorState.swift │ │ │ ├── OpenCloseSensor │ │ │ │ ├── OpenCloseSensor.swift │ │ │ │ ├── OpenCloseSensorConfig.swift │ │ │ │ └── OpenCloseSensorState.swift │ │ │ ├── PresenceSensor │ │ │ │ ├── PresenceSensor.swift │ │ │ │ ├── PresenceSensorConfig.swift │ │ │ │ └── PresenceSensorState.swift │ │ │ ├── Sensor.swift │ │ │ ├── SensorConfig.swift │ │ │ ├── SensorState.swift │ │ │ ├── SwitchSensor │ │ │ │ ├── SwitchSensor.swift │ │ │ │ ├── SwitchSensorConfig.swift │ │ │ │ └── SwitchSensorState.swift │ │ │ └── TemperatureSensor │ │ │ │ ├── TemperatureSensor.swift │ │ │ │ ├── TemperatureSensorConfig.swift │ │ │ │ └── TemperatureSensorState.swift │ │ ├── SoftwareUpdateStatus.swift │ │ ├── SoftwareUpdateStatusDeviceTypes.swift │ │ └── WhitelistEntry.swift │ ├── BridgeResourcesCache.swift │ ├── BridgeSendAPI.swift │ ├── Dictionary+Extension.swift │ ├── HeartbeatManager.swift │ ├── Logger │ │ └── LoggerConfig.swift │ ├── NSDateFormatter+Extension.swift │ ├── ReplaceMe.swift │ ├── ResourceAPI.swift │ ├── ResourceCacheHeartbeatProcessor.swift │ ├── SwiftyHue.swift │ ├── TestRequester.swift │ └── Utilities.swift ├── BridgeServices │ ├── BridgeAuthenticator │ │ ├── BridgeAuthenticator.swift │ │ └── BridgeAuthenticatorDelegate.swift │ └── BridgeFinder │ │ ├── BridgeFinder.swift │ │ ├── BridgeFinderDelegate.swift │ │ ├── HueBridge.swift │ │ ├── HueBridgeIcon.swift │ │ ├── Scanner │ │ ├── IPScanner.swift │ │ ├── NUPNPScanner.swift │ │ ├── SSDPScanner.swift │ │ ├── Scanner.swift │ │ └── ScannerDelegate.swift │ │ └── Validator │ │ ├── BridgeResultParser.swift │ │ └── BridgeValidator.swift ├── Info-iOS.plist ├── Info-tvOS.plist ├── Info-watchOS.plist └── SwiftyHue.h ├── SwiftyHue Example Watch Extension ├── Assets.xcassets │ └── README__ignoredByTemplate__ ├── ExtensionDelegate.swift ├── Info.plist └── InterfaceController.swift ├── SwiftyHue Example Watch ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Interface.storyboard └── Info.plist ├── SwiftyHue Example tvOS ├── AppDelegate.swift ├── Assets.xcassets │ ├── App Icon & Top Shelf Image.brandassets │ │ ├── App Icon - Large.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── App Icon - Small.imagestack │ │ │ ├── Back.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ └── Middle.imagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Top Shelf Image.imageset │ │ │ └── Contents.json │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf ├── Base.lproj │ └── Main.storyboard ├── FirstViewController.swift ├── Info.plist └── SecondViewController.swift ├── SwiftyHue Example ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── BridgeAccessConfigPresentationViewController.swift ├── BridgePushLinkViewController.swift ├── BridgeResourceTableViewController.swift ├── BridgeSelectionTableViewController.swift ├── CreateBridgeAccessController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── PressSmartbridgeV2.imageset │ │ ├── Contents.json │ │ └── press_smartbridgeV2.pdf ├── Info.plist ├── SwiftyHue Example-Bridging-Header.h ├── UITableViewController+Extensions.swift └── ViewController.swift ├── SwiftyHue iOSTests ├── BridgeAuthenticatorConsumer.swift ├── BridgeAuthenticatorTests.swift ├── BridgeFinderConsumer.swift ├── BridgeFinderTests.swift ├── BridgeResultParserTests.swift ├── Info.plist ├── TestBadValidator.swift ├── TestGoodValidator.swift ├── TestScanner1.swift ├── TestScanner2.swift └── bridge_response.xml ├── SwiftyHue macOSTests └── Info.plist ├── SwiftyHue.playground ├── Contents.swift └── contents.xcplayground ├── SwiftyHue.podspec ├── SwiftyHue.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── SwiftyHue iOS.xcscheme │ ├── SwiftyHue macOS.xcscheme │ ├── SwiftyHue tvOS.xcscheme │ └── SwiftyHue watchOS.xcscheme ├── SwiftyHue.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── bin └── setup /.gitignore: -------------------------------------------------------------------------------- 1 | #.DS_Store 2 | .DS_Store 3 | docs/ 4 | 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | .build/ 43 | 44 | # CocoaPods 45 | # 46 | # We recommend against adding the Pods directory to your .gitignore. However 47 | # you should judge for yourself, the pros and cons are mentioned at: 48 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 49 | # 50 | Pods/ 51 | 52 | # Carthage 53 | # 54 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 55 | 56 | Carthage/Checkouts 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 65 | 66 | fastlane/report.xml 67 | fastlane/screenshots 68 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.8.0" 2 | github "robbiehanson/CocoaAsyncSocket" "7.6.3" 3 | github "hkellaway/Gloss" "3.1.0" 4 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "luisobo/Nocilla" ~> 0.11.0 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.8.0" 2 | github "hkellaway/Gloss" "3.1.0" 3 | github "luisobo/Nocilla" "0.11.0" 4 | github "robbiehanson/CocoaAsyncSocket" "7.6.3" 5 | -------------------------------------------------------------------------------- /Info-macOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Marcel Dittmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/Base/BridgeAccessConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeAccessConfig.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 06.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public struct BridgeAccessConfig: JSONDecodable { 13 | 14 | public let bridgeId: String; 15 | public let ipAddress: String; 16 | public let username: String; 17 | 18 | public init(bridgeId: String, ipAddress: String, username: String) { 19 | 20 | self.bridgeId = bridgeId; 21 | self.ipAddress = ipAddress; 22 | self.username = username; 23 | 24 | } 25 | 26 | public init?(json: JSON) { 27 | 28 | guard let bridgeId: String = "id" <~~ json, 29 | let ipAddress: String = "ipaddress" <~~ json, 30 | let username: String = "username" <~~ json 31 | 32 | else { return nil } 33 | 34 | self.bridgeId = bridgeId 35 | self.ipAddress = ipAddress 36 | self.username = username 37 | 38 | } 39 | 40 | public func toJSON() -> JSON? { 41 | 42 | let json = jsonify([ 43 | "id" ~~> bridgeId, 44 | "ipaddress" ~~> ipAddress, 45 | "username" ~~> username 46 | ]) 47 | 48 | return json 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/AppData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppData.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public struct AppData: Glossy { 13 | 14 | /** 15 | App specific version of the data field. App should take versioning into account when parsing the data string. 16 | */ 17 | public let version: Int 18 | 19 | /** 20 | App specific data. Free format string. 21 | */ 22 | public let data: String 23 | 24 | public init(version: Int, data: String) { 25 | 26 | self.version = version 27 | self.data = data 28 | } 29 | 30 | public init?(json: JSON) { 31 | 32 | guard let version: Int = "version" <~~ json, let data: String = "data" <~~ json else { 33 | return nil 34 | } 35 | 36 | self.version = version 37 | self.data = data 38 | } 39 | 40 | public func toJSON() -> JSON? { 41 | 42 | return jsonify([ 43 | "version" ~~> version, 44 | "data" ~~> data, 45 | ]) 46 | } 47 | } 48 | 49 | extension AppData: Hashable { 50 | 51 | public func hash(into hasher: inout Hasher) { 52 | hasher.combine(version) 53 | } 54 | } 55 | public func ==(lhs: AppData, rhs: AppData) -> Bool { 56 | return lhs.version == rhs.version && lhs.data == rhs.data 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Backup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Backup.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum BackupStatus: String { 13 | 14 | case idle, startmigration, fileready_disabled, prepare_restore, restoring 15 | } 16 | 17 | public enum BackupError: Int { 18 | 19 | case none, exportFailed, importFailed 20 | } 21 | 22 | public struct Backup: JSONDecodable { 23 | 24 | public let status: BackupStatus? 25 | public let errorcode: BackupError? 26 | 27 | public init?(json: JSON) { 28 | 29 | status = "status" <~~ json 30 | errorcode = "errorcode" <~~ json 31 | 32 | } 33 | 34 | public func toJSON() -> JSON? { 35 | 36 | let json = jsonify([ 37 | "status" ~~> status, 38 | "errorcode" ~~> errorcode 39 | ]) 40 | 41 | return json 42 | } 43 | } 44 | extension Backup: Hashable { 45 | 46 | public func hash(into hasher: inout Hasher) { 47 | 48 | hasher.combine(1) 49 | } 50 | } 51 | public func ==(lhs: Backup, rhs: Backup) -> Bool { 52 | return lhs.status == rhs.status && lhs.errorcode == rhs.errorcode 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/BridgeResource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeResource.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum BridgeResourceType: String { 13 | case light, group, scene, sensor, rule, config, schedule, whitelistEntry 14 | } 15 | 16 | public protocol BridgeResource: Glossy { 17 | 18 | var identifier: String {get} 19 | var name: String {get} 20 | var resourceType: BridgeResourceType {get}; 21 | } 22 | 23 | /** 24 | Can create a dictionary of a specific BridgeResource (BridgeResourceType) from a JSON ([String: AnyObject]). 25 | */ 26 | public protocol BridgeResourceDictGenerator { 27 | 28 | associatedtype AssociatedBridgeResourceType: BridgeResource 29 | 30 | static func dictionaryFromResourcesJSON(_ json: JSON) -> [String: AssociatedBridgeResourceType] 31 | } 32 | 33 | public extension BridgeResourceDictGenerator { 34 | 35 | static func dictionaryFromResourcesJSON(_ json: JSON) -> [String: AssociatedBridgeResourceType] { 36 | 37 | var dict = [String: AssociatedBridgeResourceType](); 38 | 39 | for (key, value) in json { 40 | 41 | var resourceJSON = value as! JSON 42 | resourceJSON["id"] = key 43 | 44 | if let resource = AssociatedBridgeResourceType(json: resourceJSON) { 45 | dict[key ] = resource; 46 | } 47 | } 48 | 49 | return dict; 50 | } 51 | } 52 | 53 | //extension BridgeResource { 54 | // 55 | // public init?(json: JSON) { 56 | // 57 | // self.init() 58 | // 59 | // identifier = "id" <~~ json 60 | // name = "name" <~~ json 61 | // 62 | // } 63 | // 64 | // /** 65 | // Object encoded as JSON 66 | // */ 67 | // public func toJSON() -> JSON? { 68 | // 69 | // return jsonify([ 70 | // "id" ~~> self.identifier, 71 | // "login" ~~> self.name 72 | // ]) 73 | // } 74 | // 75 | // public static func dictionaryOf(json: JSON) -> [String: BridgeResource]? { 76 | // 77 | // var dict = [String: BridgeResource](); 78 | // 79 | // for (key, value) in json { 80 | // 81 | // var groupJSON = value as! JSON 82 | // groupJSON["id"] = key 83 | // 84 | // if let group = self.init(json: groupJSON) { 85 | // dict[key as! String] = group; 86 | // } 87 | // 88 | // } 89 | // 90 | // return nil; 91 | // } 92 | //} 93 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 29.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum SHErrorType: Int { 13 | case unknownError = 0 14 | case unauthorizedUser = 1 15 | case bodyContainsInvalidJSON = 2 16 | case resourceNotAvailable = 3 17 | case methodNotAvailableForResource = 4 18 | case missingParametersBody = 5 19 | case parameterNotAvailable = 6 20 | case invalidValueParameter = 7 21 | case parameterIsNotModifiable = 8 22 | case tooManyItemsInList = 9 23 | case portalConnectionRequired = 10 24 | case internalError = 901 25 | } 26 | 27 | public class HueError: NSError, JSONDecodable { 28 | 29 | public let address: String 30 | public let errorDescription: String 31 | public let type: SHErrorType 32 | 33 | public required init?(json: JSON) { 34 | 35 | if let _: Any = "success" <~~ json { 36 | return nil 37 | } 38 | 39 | guard let type: Int = "error.type" <~~ json, 40 | let address: String = "error.address" <~~ json, 41 | let errorDescription: String = "error.description" <~~ json 42 | else { print("Can't create Error Object from JSON:\n \(json)"); return nil } 43 | 44 | if let type = SHErrorType(rawValue: type) { 45 | self.type = type 46 | } else { 47 | self.type = .unknownError 48 | } 49 | 50 | self.address = address 51 | self.errorDescription = errorDescription 52 | 53 | super.init(domain: address, code: type, userInfo: ["description": errorDescription]) 54 | } 55 | 56 | public init(address: String, errorDescription: String, type: SHErrorType) { 57 | 58 | self.type = type 59 | self.address = address 60 | self.errorDescription = errorDescription 61 | 62 | super.init(domain: address, code: type.rawValue, userInfo: ["description": errorDescription]) 63 | } 64 | 65 | public init?(address: String, errorDescription: String, type: Int) { 66 | 67 | guard let errorType = SHErrorType(rawValue: type) else {return nil}; 68 | 69 | self.type = errorType; 70 | self.address = address 71 | self.errorDescription = errorDescription 72 | 73 | super.init(domain: address, code: type, userInfo: ["description": errorDescription]) 74 | } 75 | 76 | required public init?(coder aDecoder: NSCoder) { 77 | fatalError("init(coder:) has not been implemented") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Group.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Group.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum GroupType: String { 13 | 14 | case LightGroup, Room, Luminaire, LightSource, Entertainment, Zone 15 | } 16 | 17 | public enum RoomClass: String { 18 | 19 | case LivingRoom = "Living room", Kitchen, Dining, Bedroom, KidsBedroom = "Kids bedroom", Bathroom, Nursery, Recreation, Office, Gym, Hallway, Toilet, FrontDoor = "Front door", Garage, Terrace, Garden, Driveway, Carport, Other, Home, Downstairs, Upstairs, TopFloor = "Top floor", Attic, GuestRoom = "Guest room", Staircase, Lounge, ManCave = "Man cave", Computer, Studio, Music, TV, Reading, Closet, Storage, LaundryRoom = "Laundry room", Balcony, Porch, Barbecue, Pool 20 | } 21 | 22 | public class Group: BridgeResourceDictGenerator, BridgeResource { 23 | 24 | public typealias AssociatedBridgeResourceType = Group; 25 | 26 | public var resourceType: BridgeResourceType { 27 | return .group 28 | }; 29 | 30 | public var identifier: String 31 | public var name: String 32 | 33 | /** 34 | The light state of one of the lamps in the group. 35 | */ 36 | public let action: LightState; 37 | 38 | /** 39 | The IDs of the lights that are in the group. 40 | */ 41 | public let lightIdentifiers: [String]?; 42 | 43 | /** 44 | As of 1.4. If not provided upon creation "LightGroup" is used. Can be "LightGroup", "Room" or either "Luminaire" or "LightSource" if a Multisource Luminaire is present in the system. 45 | */ 46 | public let type: GroupType 47 | 48 | /** 49 | As of 1.4. Uniquely identifies the hardware model of the luminaire. Only present for automatically created Luminaires. 50 | */ 51 | public let modelId: String? 52 | 53 | /** 54 | As of 1.9. Unique Id in AA:BB:CC:DD format for Luminaire groups or AA:BB:CC:DD-XX format for Lightsource groups, where XX is the lightsource position. 55 | */ 56 | public let uniqueId: String? 57 | 58 | /** 59 | As of 1.11. Category of Room types. Default is: Other. 60 | */ 61 | public let roomClass: RoomClass 62 | 63 | public required init?(json: JSON) { 64 | 65 | guard let identifier: String = "id" <~~ json, 66 | let name: String = "name" <~~ json, 67 | let action: LightState = "action" <~~ json, 68 | let type: GroupType = "type" <~~ json, 69 | let roomClass: RoomClass = RoomClass(rawValue: ("class" <~~ json) ?? "Other") 70 | 71 | else { print("Can't create Group from JSON:\n \(json)"); return nil } 72 | 73 | self.identifier = identifier 74 | self.name = name 75 | self.action = action 76 | self.type = type 77 | self.roomClass = roomClass 78 | 79 | uniqueId = "uniqueid" <~~ json 80 | modelId = "modelid" <~~ json 81 | lightIdentifiers = "lights" <~~ json 82 | } 83 | 84 | public func toJSON() -> JSON? { 85 | 86 | let json = jsonify([ 87 | "id" ~~> identifier, 88 | "name" ~~> name, 89 | "action" ~~> action, 90 | "lights" ~~> lightIdentifiers, 91 | "type" ~~> type, 92 | "modelid" ~~> modelId, 93 | "uniqueid" ~~> uniqueId, 94 | "class" ~~> roomClass.rawValue 95 | ]) 96 | 97 | return json 98 | } 99 | 100 | } 101 | 102 | extension Group: Hashable { 103 | 104 | public func hash(into hasher: inout Hasher) { 105 | 106 | hasher.combine(Int(self.identifier)!) 107 | } 108 | } 109 | 110 | public func ==(lhs: Group, rhs: Group) -> Bool { 111 | return lhs.identifier == rhs.identifier 112 | } 113 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Light.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Light.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class Light: BridgeResource, BridgeResourceDictGenerator { 13 | 14 | public typealias AssociatedBridgeResourceType = Light 15 | 16 | public var resourceType: BridgeResourceType { 17 | return .light 18 | }; 19 | 20 | /** 21 | Identifier of the light. 22 | */ 23 | public let identifier: String 24 | 25 | /** 26 | A unique, editable name given to the light. 27 | */ 28 | public let name: String 29 | 30 | /** 31 | Details the state of the light. 32 | */ 33 | public let state: LightState 34 | 35 | /** 36 | A fixed name describing the type of light e.g. “Extended color light”. 37 | */ 38 | public let type: String 39 | 40 | /** 41 | The hardware model of the light. 42 | */ 43 | public let modelId: String 44 | 45 | /** 46 | As of 1.4. Unique id of the device. The MAC address of the device with a unique endpoint id in the form: AA:BB:CC:DD:EE:FF:00:11-XX 47 | */ 48 | public let uniqueId: String 49 | 50 | /** 51 | As of 1.7. The manufacturer name. 52 | */ 53 | public let manufacturerName: String 54 | 55 | /** 56 | As of 1.9. Unique ID of the luminaire the light is a part of in the format: AA:BB:CC:DD-XX-YY. AA:BB:, ... represents the hex of the luminaireid, XX the lightsource position (incremental but may contain gaps) and YY the lightpoint position (index of light in luminaire group). A gap in the lightpoint position indicates an incomplete luminaire (light search required to discover missing light points in this case). 57 | */ 58 | public let luminaireUniqueId: String? 59 | 60 | /** 61 | An identifier for the software version running on the light. 62 | */ 63 | public let swVersion: String 64 | 65 | /** 66 | This parameter is reserved for future functionality. As from 1.11 point symbols are no longer returned. 67 | */ 68 | public let pointsymbol: String? 69 | 70 | public required init?(json: JSON) { 71 | 72 | guard let identifier: String = "id" <~~ json, 73 | let name: String = "name" <~~ json, 74 | let state: LightState = "state" <~~ json, 75 | let type: String = "type" <~~ json, 76 | let modelid: String = "modelid" <~~ json, 77 | let uniqueid: String = "uniqueid" <~~ json, 78 | let manufacturername: String = "manufacturername" <~~ json, 79 | let swversion: String = "swversion" <~~ json 80 | 81 | else { print("Can't create Light from JSON:\n \(json)"); return nil } 82 | 83 | self.identifier = identifier 84 | self.name = name 85 | self.state = state 86 | self.type = type 87 | self.modelId = modelid 88 | self.uniqueId = uniqueid 89 | self.manufacturerName = manufacturername 90 | self.swVersion = swversion 91 | 92 | luminaireUniqueId = "luminaireuniqueid" <~~ json 93 | pointsymbol = "pointsymbol" <~~ json 94 | 95 | } 96 | 97 | public func toJSON() -> JSON? { 98 | 99 | let json = jsonify([ 100 | "id" ~~> identifier, 101 | "name" ~~> name, 102 | "state" ~~> state, 103 | "type" ~~> type, 104 | "modelid" ~~> modelId, 105 | "uniqueid" ~~> uniqueId, 106 | "manufacturername" ~~> manufacturerName, 107 | "luminaireuniqueid" ~~> luminaireUniqueId, 108 | "swversion" ~~> swVersion, 109 | "pointsymbol" ~~> pointsymbol 110 | ]) 111 | 112 | return json 113 | } 114 | } 115 | 116 | extension Light: Hashable { 117 | 118 | public func hash(into hasher: inout Hasher) { 119 | 120 | hasher.combine(Int(self.identifier)!) 121 | } 122 | } 123 | 124 | public func ==(lhs: Light, rhs: Light) -> Bool { 125 | return lhs.identifier == rhs.identifier 126 | } 127 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/LightState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightState.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public struct LightState: JSONDecodable, JSONEncodable { 13 | 14 | /** 15 | The on off status to set the light to. 16 | true means on, false means off. 17 | */ 18 | public var on: Bool?; 19 | 20 | /** 21 | The brightness to set the light to. 22 | Range: 0 (lowest brightness, but not off) to 254 (highest brightness). 23 | */ 24 | public var brightness: Int?; 25 | 26 | /** 27 | The hue to set the light to, representing a color. 28 | Range: 0 - 65535 (which represents 0-360 degrees) 29 | Explanation: http://en.wikipedia.org/wiki/Hue 30 | */ 31 | public var hue: Int?; 32 | 33 | /** 34 | The saturation to set the light to. 35 | Range: 0 (least saturated, white) - 254 (most saturated, vivid). 36 | */ 37 | public var saturation: Int?; 38 | 39 | public var xy: [Double]?; 40 | 41 | /** 42 | The colortemperature to set the light to in Mirek 43 | Range of 2012 hue bulb: 153 (coldest white) - 500 (warmest white) 44 | Range of 2014 tone light module: 153 (coldest white) - 454 (warmest white) 45 | Explanation: http://en.wikipedia.org/wiki/Mired 46 | */ 47 | public var ct: Int?; 48 | public var alert: String?; 49 | public var effect: String?; 50 | public var colormode: String?; 51 | public var reachable: Bool?; 52 | public var transitiontime: Int?; 53 | 54 | public init() { 55 | 56 | } 57 | 58 | public init?(json: JSON) { 59 | 60 | self.on = "on" <~~ json 61 | self.brightness = "bri" <~~ json 62 | self.hue = "hue" <~~ json 63 | self.saturation = "sat" <~~ json 64 | self.xy = "xy" <~~ json 65 | self.ct = "ct" <~~ json 66 | self.alert = "alert" <~~ json 67 | self.effect = "effect" <~~ json 68 | self.colormode = "colormode" <~~ json 69 | self.reachable = "reachable" <~~ json 70 | self.transitiontime = "transitiontime" <~~ json 71 | } 72 | 73 | public func toJSON() -> JSON? { 74 | 75 | return jsonify([ 76 | "on" ~~> on, 77 | "bri" ~~> brightness, 78 | "hue" ~~> hue, 79 | "sat" ~~> saturation, 80 | "xy" ~~> xy, 81 | "ct" ~~> ct, 82 | "alert" ~~> alert, 83 | "effect" ~~> effect, 84 | "colormode" ~~> colormode, 85 | "reachable" ~~> reachable, 86 | "transitiontime" ~~> transitiontime 87 | 88 | ]) 89 | } 90 | } 91 | 92 | extension LightState: Hashable { 93 | 94 | public func hash(into hasher: inout Hasher) { 95 | hasher.combine(self.brightness ?? 0) 96 | hasher.combine(self.hue ?? 0) 97 | hasher.combine(self.saturation ?? 0) 98 | } 99 | } 100 | 101 | public func ==(lhs: LightState, rhs: LightState) -> Bool { 102 | return lhs.on == rhs.on && 103 | lhs.brightness == rhs.brightness && 104 | lhs.hue == rhs.hue && 105 | lhs.saturation == rhs.saturation && 106 | (lhs.xy ?? [-1, -1]) == (rhs.xy ?? [-1, -1]) && 107 | lhs.ct == rhs.ct && 108 | lhs.alert == rhs.alert && 109 | lhs.effect == rhs.effect && 110 | lhs.colormode == rhs.colormode && 111 | lhs.reachable == rhs.reachable && 112 | lhs.transitiontime == rhs.transitiontime 113 | } 114 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/PortalState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PortalState.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum PortalStateCommunication: String { 13 | 14 | case connected, connecting, disconnected, unknown 15 | } 16 | 17 | public struct PortalState: JSONDecodable { 18 | 19 | /** 20 | The bridge is signed on the portal 21 | */ 22 | public let signedon: Bool? 23 | 24 | /** 25 | The bridge is able to send messages to the portal 26 | */ 27 | public let incoming: Bool? 28 | 29 | /** 30 | The bridge is able to recieve messages from the portal 31 | */ 32 | public let outgoing: Bool? 33 | 34 | /** 35 | The bridge is communicating with SmartPortal 36 | */ 37 | public let communication: PortalStateCommunication? 38 | 39 | public init?(json: JSON) { 40 | 41 | signedon = "signedon" <~~ json 42 | incoming = "incoming" <~~ json 43 | outgoing = "outgoing" <~~ json 44 | communication = "communication" <~~ json 45 | 46 | } 47 | 48 | public func toJSON() -> JSON? { 49 | 50 | let json = jsonify([ 51 | "signedon" ~~> signedon, 52 | "incoming" ~~> incoming, 53 | "outgoing" ~~> outgoing, 54 | "communication" ~~> communication 55 | ]) 56 | 57 | return json 58 | } 59 | } 60 | 61 | extension PortalState: Hashable { 62 | 63 | public func hash(into hasher: inout Hasher) { 64 | 65 | hasher.combine(1) 66 | } 67 | } 68 | 69 | public func ==(lhs: PortalState, rhs: PortalState) -> Bool { 70 | return lhs.signedon == rhs.signedon && 71 | lhs.incoming == rhs.incoming && 72 | lhs.outgoing == rhs.outgoing && 73 | lhs.communication == rhs.communication 74 | } 75 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Rule/Rule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Rule.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class Rule: BridgeResource, BridgeResourceDictGenerator { 13 | 14 | public typealias AssociatedBridgeResourceType = Rule 15 | 16 | public var resourceType: BridgeResourceType { 17 | return .rule 18 | }; 19 | 20 | public let identifier: String 21 | public let name: String 22 | public let lasttriggered: Date? 23 | public let created: Date 24 | public let timestriggered: Int 25 | public let owner: String 26 | public let status: String 27 | public let conditions: [RuleCondition] 28 | public let actions: [RuleAction] 29 | 30 | public required init?(json: JSON) { 31 | 32 | let dateFormatter = DateFormatter.hueApiDateFormatter 33 | 34 | guard let identifier: String = "id" <~~ json else { 35 | print("Can't create Rule, missing required attribute \"id\" in JSON:\n \(json)"); return nil 36 | } 37 | 38 | guard let name: String = "name" <~~ json else { 39 | print("Can't create Rule, missing required attribute \"name\" in JSON:\n \(json)"); return nil 40 | } 41 | 42 | guard let created: Date = Decoder.decode(dateForKey:"created", dateFormatter:dateFormatter)(json) else { 43 | print("Can't create Rule, missing required attribute \"created\" in JSON:\n \(json)"); return nil 44 | } 45 | 46 | guard let timestriggered: Int = "timestriggered" <~~ json else { 47 | print("Can't create Rule, missing required attribute \"timestriggered\" in JSON:\n \(json)"); return nil 48 | } 49 | 50 | guard let owner: String = "owner" <~~ json else { 51 | print("Can't create Rule, missing required attribute \"owner\" in JSON:\n \(json)"); return nil 52 | } 53 | 54 | guard let status: String = "status" <~~ json else { 55 | print("Can't create Rule, missing required attribute \"status\" in JSON:\n \(json)"); return nil 56 | } 57 | 58 | guard let conditionJSONs: [JSON] = "conditions" <~~ json else { 59 | print("Can't create Rule, missing required attribute \"conditions\" in JSON:\n \(json)"); return nil 60 | } 61 | 62 | guard let actionJSONs: [JSON] = "actions" <~~ json else { 63 | print("Can't create Rule, missing required attribute \"actions\" in JSON:\n \(json)"); return nil 64 | } 65 | 66 | 67 | self.identifier = identifier 68 | self.name = name 69 | self.created = created as Date 70 | self.lasttriggered = Decoder.decode(dateForKey:"lasttriggered", dateFormatter:dateFormatter)(json) 71 | self.timestriggered = timestriggered 72 | self.owner = owner 73 | self.status = status 74 | self.conditions = [RuleCondition].from(jsonArray: conditionJSONs)! 75 | self.actions = [RuleAction].from(jsonArray: actionJSONs)! 76 | } 77 | 78 | public func toJSON() -> JSON? { 79 | 80 | let dateFormatter = DateFormatter.hueApiDateFormatter 81 | 82 | let json = jsonify([ 83 | "id" ~~> self.identifier, 84 | "name" ~~> self.name, 85 | Encoder.encode(dateForKey: "created", dateFormatter: dateFormatter)(self.created), 86 | Encoder.encode(dateForKey: "lasttriggered", dateFormatter: dateFormatter)(self.lasttriggered), 87 | "timestriggered" ~~> self.timestriggered, 88 | "owner" ~~> self.owner, 89 | "status" ~~> self.status, 90 | "conditions" ~~> self.conditions, 91 | "actions" ~~> self.actions 92 | ]) 93 | 94 | return json 95 | } 96 | } 97 | 98 | extension Rule: Hashable { 99 | 100 | public func hash(into hasher: inout Hasher) { 101 | 102 | hasher.combine(Int(self.identifier)!) 103 | } 104 | } 105 | 106 | public func ==(lhs: Rule, rhs: Rule) -> Bool { 107 | return lhs.identifier == rhs.identifier && 108 | lhs.name == rhs.name && 109 | lhs.created == rhs.created && 110 | lhs.lasttriggered == rhs.lasttriggered && 111 | lhs.timestriggered == rhs.timestriggered && 112 | lhs.owner == rhs.owner && 113 | lhs.status == rhs.status && 114 | lhs.conditions == rhs.conditions && 115 | lhs.actions == rhs.actions 116 | } 117 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Rule/RuleAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuleAction.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class RuleAction: Glossy { 13 | 14 | public let address: String 15 | public let method: String 16 | public let body: [String: Any] 17 | 18 | public required init?(json: JSON) { 19 | 20 | guard let address: String = "address" <~~ json else { 21 | print("Can't create RuleAction, missing required attribute \"address\" in JSON:\n \(json)"); return nil 22 | } 23 | 24 | guard let method: String = "method" <~~ json else { 25 | print("Can't create RuleAction, missing required attribute \"method\" in JSON:\n \(json)"); return nil 26 | } 27 | 28 | guard let body: JSON = "body" <~~ json else { 29 | print("Can't create RuleAction, missing required attribute \"body\" in JSON:\n \(json)"); return nil 30 | } 31 | 32 | self.address = address 33 | self.method = method 34 | self.body = body 35 | } 36 | 37 | public func toJSON() -> JSON? { 38 | 39 | let json = jsonify([ 40 | "address" ~~> address, 41 | "method" ~~> method, 42 | "body" ~~> body 43 | ]) 44 | 45 | return json 46 | } 47 | } 48 | 49 | extension RuleAction: Hashable { 50 | 51 | public func hash(into hasher: inout Hasher) { 52 | 53 | hasher.combine(1) 54 | } 55 | } 56 | 57 | public func ==(lhs: RuleAction, rhs: RuleAction) -> Bool { 58 | return lhs.address == rhs.address && 59 | lhs.method == rhs.method// && 60 | // lhs.body == rhs.body 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Rule/RuleCondition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuleCondition.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum RuleConditionOperator: String { 13 | case EQ = "eq", GT = "gt", LT = "lt", DX = "dx", DDX = "ddx" 14 | 15 | } 16 | 17 | public class RuleCondition: Glossy { 18 | 19 | public let address: String 20 | public let conditionOperator: RuleConditionOperator? 21 | public let value: String? 22 | 23 | public required init?(json: JSON) { 24 | 25 | guard let address: String = "address" <~~ json else { 26 | print("Can't create RuleCondition, missing required attribute \"address\" in JSON:\n \(json)"); return nil 27 | } 28 | 29 | // guard let conditionOperator: RuleConditionOperator = "operator" <~~ json else { 30 | // Log.error("Can't create RuleCondition, missing required attribute \"operator\" in JSON:\n \(json)"); return nil 31 | // } 32 | 33 | self.address = address 34 | 35 | self.conditionOperator = "operator" <~~ json 36 | self.value = "value" <~~ json 37 | } 38 | 39 | public func toJSON() -> JSON? { 40 | 41 | let json = jsonify([ 42 | "address" ~~> address, 43 | "operator" ~~> conditionOperator, 44 | "value" ~~> value 45 | ]) 46 | 47 | return json 48 | } 49 | } 50 | 51 | extension RuleCondition: Hashable { 52 | 53 | public func hash(into hasher: inout Hasher) { 54 | 55 | hasher.combine(1) 56 | } 57 | } 58 | 59 | public func ==(lhs: RuleCondition, rhs: RuleCondition) -> Bool { 60 | return lhs.address == rhs.address && 61 | lhs.conditionOperator == rhs.conditionOperator && 62 | lhs.value == rhs.value 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Scene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Scene.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 21.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | //public class Scene: PartialScene { 13 | // 14 | // public let lightstates: [LightState]?; 15 | // 16 | // public required init?(json: JSON) { 17 | // 18 | // var lightStates = json["lightstates"] 19 | // 20 | // if lightStates != nil { 21 | // 22 | // var lightstateJSONS = TestRequester.convert((lightStates as! NSDictionary).mutableCopy() as! NSMutableDictionary) 23 | // self.lightstates = Array.from(jsonArray: lightstateJSONS); 24 | // 25 | // } else { 26 | // self.lightstates = nil; 27 | // } 28 | // 29 | // super.init(json: json) 30 | // } 31 | // 32 | //} 33 | 34 | public class PartialScene: BridgeResource, BridgeResourceDictGenerator { 35 | 36 | public typealias AssociatedBridgeResourceType = PartialScene 37 | 38 | public var resourceType: BridgeResourceType { 39 | return .scene 40 | }; 41 | 42 | /** 43 | The identifier of this scene. 44 | */ 45 | public let identifier: String 46 | 47 | /** 48 | The name of this scene. 49 | */ 50 | public let name: String 51 | 52 | /** 53 | The identifiers of the lights controlled by this scene. 54 | */ 55 | public let lightIdentifiers: [String]? 56 | 57 | /** 58 | Whitelist user that created or modified the content of the scene. Note that changing name does not change the owner.. 59 | */ 60 | public let owner: String 61 | 62 | /** 63 | Indicates whether the scene can be automatically deleted by the bridge. Only available by POSTSet to 'false' when omitted. Legacy scenes created by PUT are defaulted to true. When set to 'false' the bridge keeps the scene until deleted by an application. 64 | */ 65 | public let recycle: Bool 66 | 67 | /** 68 | Indicates that the scene is locked by a rule or a schedule and cannot be deleted until all resources requiring or that reference the scene are deleted. 69 | */ 70 | public let locked: Bool 71 | 72 | /** 73 | App specific data linked to the scene. Each individual application should take responsibility for the data written in this field. 74 | */ 75 | public let appData: AppData? 76 | 77 | /** 78 | UTC time the scene has been created or has been updated by a PUT. Will be null when unknown (legacy scenes). 79 | */ 80 | public let lastUpdated: Date? 81 | 82 | /** 83 | Version of scene document: 84 | 1 - Scene created via PUT, lightstates will be empty. 85 | 2 - Scene created via POST lightstates available. 86 | */ 87 | public let version: Int 88 | 89 | public required init?(json: JSON) { 90 | 91 | guard let identifier: String = "id" <~~ json, 92 | let name: String = "name" <~~ json, 93 | let lightIdentifiers: [String] = "lights" <~~ json, 94 | let owner: String = "owner" <~~ json, 95 | let recycle: Bool = "recycle" <~~ json, 96 | let locked: Bool = "locked" <~~ json, 97 | let version: Int = "version" <~~ json 98 | 99 | else { print("Can't create Partial Scene from JSON:\n \(json)"); return nil } 100 | 101 | self.identifier = identifier 102 | self.name = name 103 | self.lightIdentifiers = lightIdentifiers 104 | self.owner = owner 105 | self.recycle = recycle 106 | self.locked = locked 107 | self.version = version 108 | 109 | let dateFormatter = DateFormatter.hueApiDateFormatter 110 | 111 | self.appData = "appdata" <~~ json 112 | lastUpdated = Decoder.decode(dateForKey:"lastupdated", dateFormatter:dateFormatter)(json) 113 | } 114 | 115 | public func toJSON() -> JSON? { 116 | 117 | let dateFormatter = DateFormatter.hueApiDateFormatter 118 | 119 | let json = jsonify([ 120 | "id" ~~> identifier, 121 | "name" ~~> name, 122 | "lights" ~~> lightIdentifiers, 123 | "owner" ~~> owner, 124 | "recycle" ~~> recycle, 125 | "locked" ~~> locked, 126 | "appdata" ~~> appData, 127 | Encoder.encode(dateForKey: "lastupdated", dateFormatter: dateFormatter)(lastUpdated), 128 | "version" ~~> version 129 | ]) 130 | 131 | return json 132 | } 133 | 134 | } 135 | 136 | extension PartialScene: Hashable { 137 | 138 | public func hash(into hasher: inout Hasher) { 139 | 140 | hasher.combine(Int(self.identifier)!) 141 | } 142 | } 143 | 144 | public func ==(lhs: PartialScene, rhs: PartialScene) -> Bool { 145 | 146 | return lhs.identifier == rhs.identifier && 147 | lhs.name == rhs.name && 148 | (lhs.lightIdentifiers ?? []) == (rhs.lightIdentifiers ?? []) && 149 | lhs.owner == rhs.owner && 150 | lhs.recycle == rhs.recycle && 151 | lhs.locked == rhs.locked && 152 | lhs.appData == rhs.appData && 153 | lhs.lastUpdated == rhs.lastUpdated && 154 | lhs.version == rhs.version 155 | } 156 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Schedule/Schedule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Schedule.swift 3 | // 4 | // 5 | // Created by Jerome Schmitz on 05.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class Schedule: BridgeResource, BridgeResourceDictGenerator { 13 | 14 | public typealias AssociatedBridgeResourceType = Schedule 15 | 16 | public var resourceType: BridgeResourceType { 17 | return .schedule 18 | }; 19 | 20 | public let identifier: String 21 | public let name: String 22 | public let scheduleDescription: String 23 | public let command: ScheduleCommand 24 | public let localtime: String? 25 | public let status: String 26 | public let autodelete: Bool? 27 | public let recycle: Bool? 28 | 29 | public required init?(json: JSON) { 30 | 31 | guard let identifier: String = "id" <~~ json else { 32 | print("Can't create Schedule, missing required attribute \"id\" in JSON:\n \(json)"); return nil 33 | } 34 | 35 | guard let name: String = "name" <~~ json else { 36 | print("Can't create Schedule, missing required attribute \"name\" in JSON:\n \(json)"); return nil 37 | } 38 | 39 | guard let scheduleDescription: String = "description" <~~ json else { 40 | print("Can't create Schedule, missing required attribute \"description\" in JSON:\n \(json)"); return nil 41 | } 42 | 43 | guard let command: ScheduleCommand = "command" <~~ json else { 44 | print("Can't create Schedule, missing required attribute \"command\" in JSON:\n \(json)"); return nil 45 | } 46 | 47 | guard let status: String = "status" <~~ json else { 48 | print("Can't create Schedule, missing required attribute \"status\" in JSON:\n \(json)"); return nil 49 | } 50 | 51 | self.identifier = identifier 52 | self.name = name 53 | self.scheduleDescription = scheduleDescription 54 | self.command = command 55 | self.status = status 56 | self.recycle = "recycle" <~~ json 57 | self.autodelete = "autodelete" <~~ json 58 | self.localtime = "localtime" <~~ json 59 | } 60 | 61 | public func toJSON() -> JSON? { 62 | 63 | let json = jsonify([ 64 | "id" ~~> identifier, 65 | "name" ~~> name, 66 | "description" ~~> scheduleDescription, 67 | "command" ~~> command, 68 | "localtime" ~~> localtime, 69 | "status" ~~> status, 70 | "autodelete" ~~> autodelete, 71 | "recycle" ~~> recycle 72 | ]) 73 | 74 | return json 75 | } 76 | } 77 | 78 | extension Schedule: Hashable { 79 | 80 | public func hash(into hasher: inout Hasher) { 81 | 82 | hasher.combine(1) 83 | } 84 | } 85 | 86 | public func ==(lhs: Schedule, rhs: Schedule) -> Bool { 87 | return lhs.identifier == rhs.identifier && 88 | lhs.name == rhs.name && 89 | lhs.scheduleDescription == rhs.scheduleDescription && 90 | lhs.command == rhs.command && 91 | lhs.status == rhs.status && 92 | lhs.recycle == rhs.recycle && 93 | lhs.autodelete == rhs.autodelete && 94 | lhs.localtime == rhs.localtime 95 | } 96 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Schedule/ScheduleCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScheduleCommand.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 05.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class ScheduleCommand: JSONDecodable { 13 | 14 | public let address: String 15 | public let method: String 16 | public let body: [String: Any] 17 | 18 | public required init?(json: JSON) { 19 | 20 | guard let address: String = "address" <~~ json else { 21 | print("Can't create ScheduleCommand, missing required attribute \"address\" in JSON:\n \(json)"); return nil 22 | } 23 | 24 | guard let method: String = "method" <~~ json else { 25 | print("Can't create ScheduleCommand, missing required attribute \"method\" in JSON:\n \(json)"); return nil 26 | } 27 | 28 | guard let body: JSON = "body" <~~ json else { 29 | print("Can't create ScheduleCommand, missing required attribute \"body\" in JSON:\n \(json)"); return nil 30 | } 31 | 32 | self.address = address 33 | self.method = method 34 | self.body = body 35 | } 36 | 37 | public func toJSON() -> JSON? { 38 | 39 | let json = jsonify([ 40 | "address" ~~> address, 41 | "method" ~~> method, 42 | "body" ~~> body 43 | ]) 44 | 45 | return json 46 | } 47 | } 48 | 49 | extension ScheduleCommand: Hashable { 50 | 51 | public func hash(into hasher: inout Hasher) { 52 | 53 | hasher.combine(1) 54 | } 55 | } 56 | 57 | public func ==(lhs: ScheduleCommand, rhs: ScheduleCommand) -> Bool { 58 | return lhs.address == rhs.address && 59 | lhs.method == rhs.method //&& 60 | // !zip(lhs.body, rhs.body).contains {$0 != $1} 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/DaylightSensor/DaylightSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DaylightSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class DaylightSensor: PartialSensor { 13 | 14 | let config: DaylightSensorConfig 15 | let state: DaylightSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: DaylightSensorConfig = DaylightSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: DaylightSensorState = DaylightSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: DaylightSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: DaylightSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: DaylightSensor, rhs: DaylightSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/DaylightSensor/DaylightSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DaylightSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class DaylightSensorConfig: PartialSensorConfig { 13 | 14 | public let long: String? 15 | public let lat: String? 16 | public let sunriseOffset: Int? 17 | public let sunsetOffset: Int? 18 | 19 | init?(sensorConfig: SensorConfig) { 20 | 21 | // guard let long: String = sensorConfig.long else { 22 | // Log.error("Can't create DaylightSensorConfig, missing required attribute \"long\""); return nil 23 | // } 24 | // 25 | // guard let lat: String = sensorConfig.lat else { 26 | // Log.error("Can't create DaylightSensorConfig, missing required attribute \"lat\""); return nil 27 | // } 28 | 29 | self.long = sensorConfig.long 30 | self.lat = sensorConfig.lat 31 | self.sunriseOffset = sensorConfig.sunriseOffset 32 | self.sunsetOffset = sensorConfig.sunsetOffset 33 | 34 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 35 | } 36 | 37 | required public init?(json: JSON) { 38 | 39 | // guard let long: String = "long" <~~ json else { 40 | // Log.error("Can't create DaylightSensorConfig, missing required attribute \"long\" in JSON:\n \(json)"); return nil 41 | // } 42 | // 43 | // guard let lat: String = "lat" <~~ json else { 44 | // Log.error("Can't create DaylightSensorConfig, missing required attribute \"lat\" in JSON:\n \(json)"); return nil 45 | // } 46 | 47 | self.long = "long" <~~ json 48 | self.lat = "lat" <~~ json 49 | self.sunriseOffset = "sunriseoffset" <~~ json 50 | self.sunsetOffset = "sunsetoffset" <~~ json 51 | 52 | super.init(json: json) 53 | } 54 | 55 | public override func toJSON() -> JSON? { 56 | 57 | if var superJson = super.toJSON() { 58 | let json = jsonify([ 59 | "long" ~~> long, 60 | "lat" ~~> lat, 61 | "sunriseoffset" ~~> sunriseOffset, 62 | "sunsetoffset" ~~> sunsetOffset 63 | ]) 64 | superJson.unionInPlace(json!) 65 | return superJson 66 | } 67 | 68 | return nil 69 | } 70 | } 71 | 72 | public func ==(lhs: DaylightSensorConfig, rhs: DaylightSensorConfig) -> Bool { 73 | return lhs.on == rhs.on && 74 | lhs.reachable == rhs.reachable && 75 | lhs.battery == rhs.battery && 76 | lhs.url == rhs.url && 77 | lhs.long == rhs.long && 78 | lhs.lat == rhs.lat && 79 | lhs.sunriseOffset == rhs.sunriseOffset && 80 | lhs.sunsetOffset == rhs.sunsetOffset 81 | } 82 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/DaylightSensor/DaylightSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DaylightSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class DaylightSensorState: PartialSensorState { 13 | 14 | public let daylight: Bool? 15 | 16 | init?(state: SensorState) { 17 | 18 | self.daylight = state.daylight 19 | 20 | super.init(lastUpdated: state.lastUpdated) 21 | } 22 | 23 | required public init?(json: JSON) { 24 | 25 | guard let daylight: Bool = "daylight" <~~ json else { 26 | print("Can't create DaylightSensorState, missing required attribute \"daylight\" in JSON:\n \(json)"); return nil 27 | } 28 | 29 | self.daylight = daylight 30 | 31 | super.init(json: json) 32 | } 33 | 34 | public override func toJSON() -> JSON? { 35 | 36 | if var superJson = super.toJSON() { 37 | let json = jsonify([ 38 | "daylight" ~~> self.daylight 39 | ]) 40 | superJson.unionInPlace(json!) 41 | return superJson 42 | } 43 | 44 | return nil 45 | } 46 | } 47 | 48 | public func ==(lhs: DaylightSensorState, rhs: DaylightSensorState) -> Bool { 49 | return lhs.lastUpdated == rhs.lastUpdated && 50 | lhs.daylight == rhs.daylight 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericFlagSensor/GenericFlagSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericFlagSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericFlagSensor: PartialSensor { 13 | 14 | let config: GenericFlagSensorConfig 15 | let state: GenericFlagSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: GenericFlagSensorConfig = GenericFlagSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: GenericFlagSensorState = GenericFlagSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: GenericFlagSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: GenericFlagSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: GenericFlagSensor, rhs: GenericFlagSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericFlagSensor/GenericFlagSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericFlagSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericFlagSensorConfig: PartialSensorConfig { 13 | 14 | init?(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | 23 | } 24 | 25 | public func ==(lhs: GenericFlagSensorConfig, rhs: GenericFlagSensorConfig) -> Bool { 26 | return lhs.on == rhs.on && 27 | lhs.reachable == rhs.reachable && 28 | lhs.battery == rhs.battery && 29 | lhs.url == rhs.url 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericFlagSensor/GenericFlagSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericFlagSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericFlagSensorState: PartialSensorState { 13 | 14 | public let flag: Bool 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let flag: Bool = state.flag else { 19 | print("Can't create GenericFlagSensorState, missing required attribute \"flag\""); return nil 20 | } 21 | 22 | self.flag = flag 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let flag: Bool = "flag" <~~ json else { 30 | print("Can't create GenericFlagSensorState, missing required attribute \"flag\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.flag = flag 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "flag" ~~> flag 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | 51 | } 52 | 53 | public func ==(lhs: GenericFlagSensorState, rhs: GenericFlagSensorState) -> Bool { 54 | return lhs.lastUpdated == rhs.lastUpdated && 55 | lhs.flag == rhs.flag 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericStatusSensor/GenericStatusSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericStatusSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericStatusSensor: PartialSensor { 13 | 14 | let config: GenericStatusSensorConfig 15 | let state: GenericStatusSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: GenericStatusSensorConfig = GenericStatusSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: GenericStatusSensorState = GenericStatusSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: GenericStatusSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: GenericStatusSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: GenericStatusSensor, rhs: GenericStatusSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericStatusSensor/GenericStatusSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericStatusSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericStatusSensorConfig: PartialSensorConfig { 13 | 14 | init?(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | 21 | super.init(json: json) 22 | } 23 | } 24 | 25 | public func ==(lhs: GenericStatusSensorConfig, rhs: GenericStatusSensorConfig) -> Bool { 26 | return lhs.on == rhs.on && 27 | lhs.reachable == rhs.reachable && 28 | lhs.battery == rhs.battery && 29 | lhs.url == rhs.url 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/GenericStatusSensor/GenericStatusSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericStatusState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class GenericStatusSensorState: PartialSensorState { 13 | 14 | public let status: Int 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let status: Int = state.status else { 19 | print("Can't create GenericStatusState, missing required attribute \"status\""); return nil 20 | } 21 | 22 | self.status = status 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let status: Int = "status" <~~ json else { 30 | print("Can't create GenericStatusState, missing required attribute \"status\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.status = status 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "status" ~~> status 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | public func ==(lhs: GenericStatusSensorState, rhs: GenericStatusSensorState) -> Bool { 53 | return lhs.lastUpdated == rhs.lastUpdated && 54 | lhs.status == rhs.status 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/HumiditySensor/HumiditySensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Humidity.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class HumiditySensor: PartialSensor { 13 | 14 | let config: HumiditySensorConfig 15 | let state: HumiditySensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: HumiditySensorConfig = HumiditySensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: HumiditySensorState = HumiditySensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: HumiditySensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: HumiditySensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: HumiditySensor, rhs: HumiditySensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/HumiditySensor/HumiditySensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HumiditySensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class HumiditySensorConfig: PartialSensorConfig { 13 | 14 | init?(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | } 23 | 24 | public func ==(lhs: HumiditySensorConfig, rhs: HumiditySensorConfig) -> Bool { 25 | return lhs.on == rhs.on && 26 | lhs.reachable == rhs.reachable && 27 | lhs.battery == rhs.battery && 28 | lhs.url == rhs.url 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/HumiditySensor/HumiditySensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HumiditySensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class HumiditySensorState: PartialSensorState { 13 | 14 | public let humidity: Int 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let humidity: Int = state.humidity else { 19 | print("Can't create HumiditySensorState, missing required attribute \"humidity\""); return nil 20 | } 21 | 22 | self.humidity = humidity 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let humidity: Int = "humidity" <~~ json else { 30 | print("Can't create HumiditySensorState, missing required attribute \"humidity\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.humidity = humidity 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "humidity" ~~> self.humidity 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | public func ==(lhs: HumiditySensorState, rhs: HumiditySensorState) -> Bool { 53 | return lhs.lastUpdated == rhs.lastUpdated && 54 | lhs.humidity == rhs.humidity 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/LightLevelSensor/LightLevelSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightLevelSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 08.11.19. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class LightLevelSensor: PartialSensor { 13 | 14 | let config: LightLevelSensorConfig 15 | let state: LightLevelSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: LightLevelSensorConfig = LightLevelSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: LightLevelSensorState = LightLevelSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: LightLevelSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: LightLevelSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: LightLevelSensor, rhs: LightLevelSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/LightLevelSensor/LightLevelSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightLevelSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 08.11.19. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class LightLevelSensorConfig: PartialSensorConfig { 13 | 14 | /** 15 | Threshold the user configured to be used in rules to determine insufficient lightlevel (ie below threshold). Default value 16000 16 | */ 17 | public let tholddark: Int 18 | 19 | /** 20 | Threshold the user configured to be used in rules to determine sufficient lightlevel (ie above threshold). Specified as relative offset to the “dark” threshold. Shall be >=1. Default value 7000 21 | */ 22 | public let tholdoffset: Int 23 | 24 | init?(sensorConfig: SensorConfig) { 25 | 26 | guard let tholddark: Int = sensorConfig.tholddark else { 27 | print("Can't create LightlevelSensorConfig, missing required attribute \"tholddark\""); return nil 28 | } 29 | self.tholddark = tholddark 30 | 31 | guard let tholdoffset: Int = sensorConfig.tholdoffset else { 32 | print("Can't create LightlevelSensorConfig, missing required attribute \"tholdoffset\""); return nil 33 | } 34 | self.tholdoffset = tholdoffset 35 | 36 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 37 | } 38 | 39 | required public init?(json: JSON) { 40 | 41 | guard let tholddark: Int = "tholddark" <~~ json else { 42 | print("Can't create LightlevelSensorConfig, missing required attribute \"tholddark\""); return nil 43 | } 44 | self.tholddark = tholddark 45 | 46 | guard let tholdoffset: Int = "tholdoffset" <~~ json else { 47 | print("Can't create LightlevelSensorConfig, missing required attribute \"tholdoffset\""); return nil 48 | } 49 | self.tholdoffset = tholdoffset 50 | 51 | super.init(json: json) 52 | } 53 | 54 | public override func toJSON() -> JSON? { 55 | 56 | if var superJson = super.toJSON() { 57 | let json = jsonify([ 58 | "tholddark" ~~> tholddark, 59 | "tholdoffset" ~~> tholdoffset 60 | ]) 61 | superJson.unionInPlace(json!) 62 | return superJson 63 | } 64 | 65 | return nil 66 | } 67 | } 68 | 69 | public func ==(lhs: LightLevelSensorConfig, rhs: LightLevelSensorConfig) -> Bool { 70 | return lhs.on == rhs.on && 71 | lhs.reachable == rhs.reachable && 72 | lhs.battery == rhs.battery && 73 | lhs.url == rhs.url && 74 | lhs.tholddark == rhs.tholddark && 75 | lhs.tholdoffset == rhs.tholdoffset 76 | } 77 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/LightLevelSensor/LightLevelSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LightlevelSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 08.11.19. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class LightLevelSensorState: PartialSensorState { 13 | 14 | public let lightlevel: Int? 15 | public let dark: Bool? 16 | public let daylight: Bool? 17 | 18 | init?(state: SensorState) { 19 | 20 | self.lightlevel = state.lightlevel 21 | self.dark = state.dark 22 | self.daylight = state.daylight 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let lightlevel: Int = "lightlevel" <~~ json else { 30 | print("Can't create LightlevelSensorState, missing required attribute \"lightlevel\" in JSON:\n \(json)"); return nil 31 | } 32 | self.lightlevel = lightlevel 33 | 34 | guard let dark: Bool = "dark" <~~ json else { 35 | print("Can't create LightlevelSensorState, missing required attribute \"dark\" in JSON:\n \(json)"); return nil 36 | } 37 | self.dark = dark 38 | 39 | guard let daylight: Bool = "daylight" <~~ json else { 40 | print("Can't create LightlevelSensorState, missing required attribute \"daylight\" in JSON:\n \(json)"); return nil 41 | } 42 | self.daylight = daylight 43 | 44 | super.init(json: json) 45 | } 46 | 47 | public override func toJSON() -> JSON? { 48 | 49 | if var superJson = super.toJSON() { 50 | let json = jsonify([ 51 | "lightlevel" ~~> self.lightlevel, 52 | "dark" ~~> self.dark, 53 | "daylight" ~~> self.daylight 54 | ]) 55 | superJson.unionInPlace(json!) 56 | return superJson 57 | } 58 | 59 | return nil 60 | } 61 | } 62 | 63 | public func ==(lhs: LightLevelSensorState, rhs: LightLevelSensorState) -> Bool { 64 | return lhs.lastUpdated == rhs.lastUpdated && 65 | lhs.lightlevel == rhs.lightlevel && 66 | lhs.dark == lhs.dark && 67 | lhs.daylight == lhs.daylight 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/OpenCloseSensor/OpenCloseSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenCloseSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class OpenCloseSensor: PartialSensor { 13 | 14 | let config: OpenCloseSensorConfig 15 | let state: OpenCloseSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | let config: OpenCloseSensorConfig = OpenCloseSensorConfig(sensorConfig: sensorConfig) 28 | 29 | guard let state: OpenCloseSensorState = OpenCloseSensorState(state: sensorState) else { 30 | return nil 31 | } 32 | 33 | self.config = config 34 | self.state = state 35 | 36 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 37 | } 38 | 39 | public required init?(json: JSON) { 40 | 41 | guard let config: OpenCloseSensorConfig = "config" <~~ json else { 42 | return nil 43 | } 44 | 45 | guard let state: OpenCloseSensorState = "state" <~~ json else { 46 | return nil 47 | } 48 | 49 | self.config = config 50 | self.state = state 51 | 52 | super.init(json: json) 53 | } 54 | } 55 | 56 | public func ==(lhs: OpenCloseSensor, rhs: OpenCloseSensor) -> Bool { 57 | return lhs.identifier == rhs.identifier && 58 | lhs.name == rhs.name && 59 | lhs.state == rhs.state && 60 | lhs.config == rhs.config && 61 | lhs.type == rhs.type && 62 | lhs.modelId == rhs.modelId && 63 | lhs.manufacturerName == rhs.manufacturerName && 64 | lhs.swVersion == rhs.swVersion 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/OpenCloseSensor/OpenCloseSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenCloseSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class OpenCloseSensorConfig: PartialSensorConfig { 13 | 14 | init(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | } 23 | 24 | public func ==(lhs: OpenCloseSensorConfig, rhs: OpenCloseSensorConfig) -> Bool { 25 | return lhs.on == rhs.on && 26 | lhs.reachable == rhs.reachable && 27 | lhs.battery == rhs.battery && 28 | lhs.url == rhs.url 29 | } -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/OpenCloseSensor/OpenCloseSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenCloseSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class OpenCloseSensorState: PartialSensorState { 13 | 14 | public let open: Bool 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let open: Bool = state.open else { 19 | print("Can't create OpenCloseSensorState, missing required attribute \"open\""); return nil 20 | } 21 | 22 | self.open = open 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let open: Bool = "open" <~~ json else { 30 | print("Can't create OpenCloseSensorState, missing required attribute \"open\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.open = open 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "open" ~~> self.open 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | public func ==(lhs: OpenCloseSensorState, rhs: OpenCloseSensorState) -> Bool { 53 | return lhs.lastUpdated == rhs.lastUpdated && 54 | lhs.open == rhs.open 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/PresenceSensor/PresenceSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresenceSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class PresenceSensor: PartialSensor { 13 | 14 | let config: PresenceSensorConfig 15 | let state: PresenceSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | let config: PresenceSensorConfig = PresenceSensorConfig(sensorConfig: sensorConfig) 28 | 29 | guard let state: PresenceSensorState = PresenceSensorState(state: sensorState) else { 30 | return nil 31 | } 32 | 33 | self.config = config 34 | self.state = state 35 | 36 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 37 | } 38 | 39 | public required init?(json: JSON) { 40 | 41 | guard let config: PresenceSensorConfig = "config" <~~ json else { 42 | return nil 43 | } 44 | 45 | guard let state: PresenceSensorState = "state" <~~ json else { 46 | return nil 47 | } 48 | 49 | self.config = config 50 | self.state = state 51 | 52 | super.init(json: json) 53 | } 54 | } 55 | 56 | public func ==(lhs: PresenceSensor, rhs: PresenceSensor) -> Bool { 57 | return lhs.identifier == rhs.identifier && 58 | lhs.name == rhs.name && 59 | lhs.state == rhs.state && 60 | lhs.config == rhs.config && 61 | lhs.type == rhs.type && 62 | lhs.modelId == rhs.modelId && 63 | lhs.manufacturerName == rhs.manufacturerName && 64 | lhs.swVersion == rhs.swVersion 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/PresenceSensor/PresenceSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresenceSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class PresenceSensorConfig: PartialSensorConfig { 13 | 14 | init(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | } 23 | 24 | public func ==(lhs: PresenceSensorConfig, rhs: PresenceSensorConfig) -> Bool { 25 | return lhs.on == rhs.on && 26 | lhs.reachable == rhs.reachable && 27 | lhs.battery == rhs.battery && 28 | lhs.url == rhs.url 29 | } -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/PresenceSensor/PresenceSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresenceSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class PresenceSensorState: PartialSensorState { 13 | 14 | public let presence: Bool 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let presence: Bool = state.presence else { 19 | print("Can't create PresenceSensorState, missing required attribute \"presence\""); return nil 20 | } 21 | 22 | self.presence = presence 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let presence: Bool = "presence" <~~ json else { 30 | print("Can't create PresenceSensorState, missing required attribute \"presence\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.presence = presence 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "presence" ~~> self.presence 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | public func ==(lhs: PresenceSensorState, rhs: PresenceSensorState) -> Bool { 53 | return lhs.lastUpdated == rhs.lastUpdated && 54 | lhs.presence == rhs.presence 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/SensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum SensorAlertMode { 13 | case unknown, // It is unkown what the current alert value is 14 | none, // No alert active 15 | select, // Select alert (1 indication cycle) is active 16 | lSelect // Select alert (30 seconds of indication cycles) is active 17 | } 18 | 19 | public class PartialSensorConfig: JSONDecodable { 20 | 21 | public var on: Bool 22 | public var reachable: Bool? 23 | public var battery: Int? 24 | public var url: String? 25 | 26 | init(on: Bool, reachable: Bool?, battery: Int?, url: String?) { 27 | 28 | self.on = on 29 | self.reachable = reachable 30 | self.battery = battery 31 | self.url = url 32 | } 33 | 34 | required public init?(json: JSON) { 35 | 36 | guard let on: Bool = "on" <~~ json else { 37 | print("Can't create SensorConfig, missing required attribute \"on\" in JSON:\n \(json)"); return nil 38 | } 39 | 40 | self.on = on 41 | self.reachable = "reachable" <~~ json 42 | self.battery = "battery" <~~ json 43 | self.url = "url" <~~ json 44 | 45 | } 46 | 47 | public func toJSON() -> JSON? { 48 | 49 | let json = jsonify([ 50 | "on" ~~> on, 51 | "reachable" ~~> reachable, 52 | "battery" ~~> battery, 53 | "url" ~~> url 54 | ]) 55 | 56 | return json 57 | } 58 | } 59 | 60 | public class SensorConfig: PartialSensorConfig { 61 | 62 | // DaylightSensorConfig 63 | public let long: String? 64 | public let lat: String? 65 | public let sunriseOffset: Int? 66 | public let sunsetOffset: Int? 67 | 68 | // LightlevelSensorConfig 69 | public let tholddark: Int? 70 | public let tholdoffset: Int? 71 | 72 | required public init?(json: JSON) { 73 | 74 | long = "long" <~~ json 75 | lat = "lat" <~~ json 76 | sunriseOffset = "sunriseoffset" <~~ json 77 | sunsetOffset = "sunsetoffset" <~~ json 78 | tholddark = "tholddark" <~~ json 79 | tholdoffset = "tholdoffset" <~~ json 80 | 81 | super.init(json: json) 82 | } 83 | 84 | public override func toJSON() -> JSON? { 85 | 86 | let json = jsonify([ 87 | "on" ~~> on, 88 | "reachable" ~~> reachable, 89 | "battery" ~~> battery, 90 | "url" ~~> url, 91 | "long" ~~> long, 92 | "lat" ~~> lat, 93 | "sunriseoffset" ~~> sunriseOffset, 94 | "sunsetoffset" ~~> sunsetOffset, 95 | "tholddark" ~~> tholddark, 96 | "tholdoffset" ~~> tholdoffset 97 | ]) 98 | 99 | return json 100 | } 101 | } 102 | 103 | 104 | extension SensorConfig: Hashable { 105 | 106 | public func hash(into hasher: inout Hasher) { 107 | 108 | hasher.combine(1) 109 | } 110 | } 111 | 112 | public func ==(lhs: SensorConfig, rhs: SensorConfig) -> Bool { 113 | return lhs.on == rhs.on && 114 | lhs.reachable == rhs.reachable && 115 | lhs.battery == rhs.battery && 116 | lhs.url == rhs.url && 117 | lhs.long == rhs.long && 118 | lhs.lat == rhs.lat && 119 | lhs.sunriseOffset == rhs.sunriseOffset && 120 | lhs.sunsetOffset == rhs.sunsetOffset && 121 | lhs.tholddark == rhs.tholddark && 122 | lhs.tholdoffset == rhs.tholdoffset 123 | } 124 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/SensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum ButtonEvent: Int { 13 | /** 14 | Tap Button 1 15 | */ 16 | case button_1 = 34 17 | /** 18 | Tap Button 2 19 | */ 20 | case button_2 = 16 21 | /** 22 | Tap Button 3 23 | */ 24 | case button_3 = 17 25 | /** 26 | Tap Button 4 27 | */ 28 | case button_4 = 18 29 | /** 30 | INITIAL_PRESS Button 1 (ON) 31 | */ 32 | case initial_PRESS_BUTTON_1 = 1000 33 | /** 34 | HOLD Button 1 (ON) 35 | */ 36 | case hold_BUTTON_1 = 1001 37 | /** 38 | SHORT_RELEASED Button 1 39 | */ 40 | case short_RELEASED_BUTTON_1 = 1002 41 | /** 42 | LONG_RELEASED Button 1 43 | */ 44 | case long_RELEASED_BUTTON_1 = 1003 45 | /** 46 | INITIAL_PRESS Button 2 (ON) 47 | */ 48 | case initial_PRESS_BUTTON_2 = 2000 49 | /** 50 | HOLD Button 2 (ON) 51 | */ 52 | case hold_BUTTON_2 = 2001 53 | /** 54 | SHORT_RELEASED Button 2 55 | */ 56 | case short_RELEASED_BUTTON_2 = 2002 57 | /** 58 | LONG_RELEASED Button 2 59 | */ 60 | case long_RELEASED_BUTTON_2 = 2003 61 | /** 62 | INITIAL_PRESS Button 3 (ON) 63 | */ 64 | case initial_PRESS_BUTTON_3 = 3000 65 | /** 66 | HOLD Button 3 (ON) 67 | */ 68 | case hold_BUTTON_3 = 3001 69 | /** 70 | SHORT_RELEASED Button 3 71 | */ 72 | case short_RELEASED_BUTTON_3 = 3002 73 | /** 74 | LONG_RELEASED Button 3 75 | */ 76 | case long_RELEASED_BUTTON_3 = 3003 77 | /** 78 | INITIAL_PRESS Button 4 (ON) 79 | */ 80 | case initial_PRESS_BUTTON_4 = 4000 81 | /** 82 | HOLD Button 4 (ON) 83 | */ 84 | case hold_BUTTON_4 = 4001 85 | /** 86 | SHORT_RELEASED Button 4 87 | */ 88 | case short_RELEASED_BUTTON_4 = 4002 89 | /** 90 | LONG_RELEASED Button 4 91 | */ 92 | case long_RELEASED_BUTTON_4 = 4003 93 | } 94 | 95 | public func ==(lhs: PartialSensorState, rhs: PartialSensorState) -> Bool { 96 | return lhs.lastUpdated == rhs.lastUpdated 97 | } 98 | 99 | public class PartialSensorState: JSONDecodable, Equatable { 100 | 101 | public let lastUpdated: Date? 102 | 103 | init(lastUpdated: Date?) { 104 | self.lastUpdated = lastUpdated 105 | } 106 | 107 | required public init?(json: JSON) { 108 | 109 | let dateFormatter = DateFormatter.hueApiDateFormatter 110 | 111 | lastUpdated = Decoder.decode(dateForKey: "lastupdated", dateFormatter: dateFormatter)(json) 112 | } 113 | 114 | public func toJSON() -> JSON? { 115 | 116 | let dateFormatter = DateFormatter.hueApiDateFormatter 117 | 118 | let json = jsonify([ 119 | Encoder.encode(dateForKey: "lastupdated", dateFormatter: dateFormatter)(lastUpdated) 120 | ]) 121 | 122 | return json 123 | } 124 | } 125 | 126 | public class SensorState: PartialSensorState { 127 | 128 | // Daylight 129 | public let daylight: Bool? 130 | 131 | // GenericFlagSensor 132 | public let flag: Bool? 133 | 134 | // GenericStatusState 135 | public let status: Int? 136 | 137 | // HumiditySensorState 138 | public let humidity: Int? 139 | 140 | // OpenCloseSensorState 141 | public let open: Bool? 142 | 143 | // PresenceSensorState 144 | public let presence: Bool? 145 | 146 | // SwitchSensorState 147 | public let buttonEvent: ButtonEvent? 148 | 149 | // TemperatureSensorState 150 | public let temperature: Int? 151 | 152 | // LightlevelSensorState 153 | public let lightlevel: Int? 154 | public let dark: Bool? 155 | 156 | required public init?(json: JSON) { 157 | 158 | daylight = "daylight" <~~ json 159 | flag = "flag" <~~ json 160 | status = "status" <~~ json 161 | humidity = "humidity" <~~ json 162 | open = "open" <~~ json 163 | presence = "presence" <~~ json 164 | buttonEvent = "buttonevent" <~~ json 165 | temperature = "temperature" <~~ json 166 | lightlevel = "lightlevel" <~~ json 167 | dark = "dark" <~~ json 168 | 169 | super.init(json: json) 170 | } 171 | 172 | public override func toJSON() -> JSON? { 173 | 174 | let json = jsonify([ 175 | "daylight" ~~> daylight, 176 | "flag" ~~> flag, 177 | "status" ~~> status, 178 | "humidity" ~~> humidity, 179 | "open" ~~> open, 180 | "presence" ~~> presence, 181 | "buttonevent" ~~> buttonEvent, 182 | "temperature" ~~> temperature, 183 | "lightlevel" ~~> lightlevel, 184 | "dark" ~~> dark 185 | ]) 186 | 187 | return json 188 | } 189 | 190 | } 191 | 192 | 193 | public func ==(lhs: SensorState, rhs: SensorState) -> Bool { 194 | return lhs.lastUpdated == rhs.lastUpdated && 195 | lhs.daylight == rhs.daylight && 196 | lhs.flag == rhs.flag && 197 | lhs.status == rhs.status && 198 | lhs.open == rhs.open && 199 | lhs.presence == rhs.presence && 200 | lhs.buttonEvent == rhs.buttonEvent && 201 | lhs.temperature == rhs.temperature && 202 | lhs.lightlevel == rhs.lightlevel && 203 | lhs.dark == rhs.dark 204 | } 205 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/SwitchSensor/SwitchSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class SwitchSensor: PartialSensor { 13 | 14 | let config: SwitchSensorConfig 15 | let state: SwitchSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: SwitchSensorConfig = SwitchSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: SwitchSensorState = SwitchSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: SwitchSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: SwitchSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: SwitchSensor, rhs: SwitchSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/SwitchSensor/SwitchSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class SwitchSensorConfig: PartialSensorConfig { 13 | 14 | init?(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | } 23 | 24 | public func ==(lhs: SwitchSensorConfig, rhs: SwitchSensorConfig) -> Bool { 25 | return lhs.on == rhs.on && 26 | lhs.reachable == rhs.reachable && 27 | lhs.battery == rhs.battery && 28 | lhs.url == rhs.url 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/SwitchSensor/SwitchSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | 13 | public class SwitchSensorState: PartialSensorState { 14 | 15 | public let buttonEvent: ButtonEvent? 16 | 17 | init?(state: SensorState) { 18 | 19 | // guard let buttonEvent: ButtonEvent = state.buttonEvent else { 20 | // Log.error("Can't create SwitchSensorState, missing required attribute \"buttonevent\""); return nil 21 | // } 22 | 23 | self.buttonEvent = state.buttonEvent 24 | 25 | super.init(lastUpdated: state.lastUpdated) 26 | } 27 | 28 | required public init?(json: JSON) { 29 | 30 | // guard let buttonEvent: ButtonEvent = "buttonevent" <~~ json else { 31 | // Log.error("Can't create SwitchSensorState, missing required attribute \"buttonevent\" in JSON:\n \(json)"); return nil 32 | // } 33 | 34 | self.buttonEvent = "buttonevent" <~~ json 35 | 36 | super.init(json: json) 37 | } 38 | 39 | public override func toJSON() -> JSON? { 40 | 41 | if var superJson = super.toJSON() { 42 | let json = jsonify([ 43 | "buttonevent" ~~> self.buttonEvent 44 | ]) 45 | superJson.unionInPlace(json!) 46 | return superJson 47 | } 48 | 49 | return nil 50 | } 51 | } 52 | 53 | public func ==(lhs: SwitchSensorState, rhs: SwitchSensorState) -> Bool { 54 | return lhs.lastUpdated == rhs.lastUpdated && 55 | lhs.buttonEvent == rhs.buttonEvent 56 | } -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/TemperatureSensor/TemperatureSensor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TemperatureSensor.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class TemperatureSensor: PartialSensor { 13 | 14 | let config: TemperatureSensorConfig 15 | let state: TemperatureSensorState 16 | 17 | required public init?(sensor: Sensor) { 18 | 19 | guard let sensorConfig = sensor.config else { 20 | return nil 21 | } 22 | 23 | guard let sensorState = sensor.state else { 24 | return nil 25 | } 26 | 27 | guard let config: TemperatureSensorConfig = TemperatureSensorConfig(sensorConfig: sensorConfig) else { 28 | return nil 29 | } 30 | 31 | guard let state: TemperatureSensorState = TemperatureSensorState(state: sensorState) else { 32 | return nil 33 | } 34 | 35 | self.config = config 36 | self.state = state 37 | 38 | super.init(identifier: sensor.identifier, uniqueId: sensor.uniqueId, name: sensor.name, type: sensor.type, modelId: sensor.modelId, manufacturerName: sensor.manufacturerName, swVersion: sensor.swVersion, recycle: sensor.recycle) 39 | } 40 | 41 | public required init?(json: JSON) { 42 | 43 | guard let config: TemperatureSensorConfig = "config" <~~ json else { 44 | return nil 45 | } 46 | 47 | guard let state: TemperatureSensorState = "state" <~~ json else { 48 | return nil 49 | } 50 | 51 | self.config = config 52 | self.state = state 53 | 54 | super.init(json: json) 55 | } 56 | } 57 | 58 | public func ==(lhs: TemperatureSensor, rhs: TemperatureSensor) -> Bool { 59 | return lhs.identifier == rhs.identifier && 60 | lhs.name == rhs.name && 61 | lhs.state == rhs.state && 62 | lhs.config == rhs.config && 63 | lhs.type == rhs.type && 64 | lhs.modelId == rhs.modelId && 65 | lhs.manufacturerName == rhs.manufacturerName && 66 | lhs.swVersion == rhs.swVersion 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/TemperatureSensor/TemperatureSensorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TemperatureSensorConfig.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class TemperatureSensorConfig: PartialSensorConfig { 13 | 14 | init?(sensorConfig: SensorConfig) { 15 | 16 | super.init(on: sensorConfig.on, reachable: sensorConfig.reachable, battery: sensorConfig.battery, url: sensorConfig.url) 17 | } 18 | 19 | required public init?(json: JSON) { 20 | super.init(json: json) 21 | } 22 | } 23 | 24 | public func ==(lhs: TemperatureSensorConfig, rhs: TemperatureSensorConfig) -> Bool { 25 | return lhs.on == rhs.on && 26 | lhs.reachable == rhs.reachable && 27 | lhs.battery == rhs.battery && 28 | lhs.url == rhs.url 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/Sensors/TemperatureSensor/TemperatureSensorState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TemperatureSensorState.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 01.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public class TemperatureSensorState: PartialSensorState { 13 | 14 | public let temperature: Int 15 | 16 | init?(state: SensorState) { 17 | 18 | guard let temperature: Int = state.temperature else { 19 | print("Can't create TemperatureSensorState, missing required attribute \"temperature\""); return nil 20 | } 21 | 22 | self.temperature = temperature 23 | 24 | super.init(lastUpdated: state.lastUpdated) 25 | } 26 | 27 | required public init?(json: JSON) { 28 | 29 | guard let temperature: Int = "temperature" <~~ json else { 30 | print("Can't create TemperatureSensorState, missing required attribute \"temperature\" in JSON:\n \(json)"); return nil 31 | } 32 | 33 | self.temperature = temperature 34 | 35 | super.init(json: json) 36 | } 37 | 38 | public override func toJSON() -> JSON? { 39 | 40 | if var superJson = super.toJSON() { 41 | let json = jsonify([ 42 | "temperature" ~~> self.temperature 43 | ]) 44 | superJson.unionInPlace(json!) 45 | return superJson 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | public func ==(lhs: TemperatureSensorState, rhs: TemperatureSensorState) -> Bool { 53 | return lhs.lastUpdated == rhs.lastUpdated && 54 | lhs.temperature == rhs.temperature 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/SoftwareUpdateStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareUpdateStatus.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public enum UpdateState: Int { 13 | 14 | case noUpdate, downloading, readyForInstall, installed 15 | } 16 | 17 | public struct SoftwareUpdateStatus: JSONDecodable { 18 | 19 | public let updatestate: UpdateState? 20 | 21 | /** 22 | Check for update flag of the bridge 23 | */ 24 | public let checkforupdate: Bool? 25 | 26 | /** 27 | Details of device type specific updates available 28 | */ 29 | public let devicetypes: SoftwareUpdateStatusDeviceTypes? 30 | 31 | /** 32 | Release Notes Url 33 | */ 34 | public let url: String? 35 | 36 | /** 37 | Update Text 38 | */ 39 | public let text: String? 40 | 41 | /** 42 | Flag that turns to true when update is available. Can only be updated when its state is true and it is being set to false. All other transitions are invalid and will return an error. 43 | Updating this flag constitutes acceptance by the app of notification of the firmware update 44 | */ 45 | public let notify: Bool? 46 | 47 | public init?(json: JSON) { 48 | 49 | updatestate = "updatestate" <~~ json 50 | checkforupdate = "checkforupdate" <~~ json 51 | devicetypes = "devicetypes" <~~ json 52 | url = "url" <~~ json 53 | text = "text" <~~ json 54 | notify = "notify" <~~ json 55 | 56 | } 57 | 58 | public func toJSON() -> JSON? { 59 | 60 | let json = jsonify([ 61 | "updatestate" ~~> updatestate, 62 | "checkforupdate" ~~> checkforupdate, 63 | "devicetypes" ~~> devicetypes, 64 | "url" ~~> url, 65 | "text" ~~> text, 66 | "notify" ~~> notify 67 | ]) 68 | 69 | return json 70 | } 71 | } 72 | 73 | extension SoftwareUpdateStatus: Hashable { 74 | 75 | public func hash(into hasher: inout Hasher) { 76 | 77 | hasher.combine(1) 78 | } 79 | } 80 | 81 | public func ==(lhs: SoftwareUpdateStatus, rhs: SoftwareUpdateStatus) -> Bool { 82 | return lhs.updatestate == rhs.updatestate && 83 | lhs.checkforupdate == rhs.checkforupdate && 84 | lhs.devicetypes == rhs.devicetypes && 85 | lhs.url == rhs.url && 86 | lhs.text == rhs.text && 87 | lhs.notify == rhs.notify 88 | } 89 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/SoftwareUpdateStatusDeviceTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareUpdateStatusDeviceTypes.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public struct SoftwareUpdateStatusDeviceTypes: JSONDecodable { 13 | 14 | /** 15 | Flag for when bridge update is avaliable 16 | */ 17 | public let bridge: Bool? 18 | 19 | /** 20 | List of IDs of lights to be updated. 21 | */ 22 | public let lights: [String]? 23 | 24 | /** 25 | List of IDs of sensors to be updated 26 | */ 27 | public let sensors: [String]? 28 | 29 | public init?(json: JSON) { 30 | 31 | bridge = "bridge" <~~ json 32 | lights = "lights" <~~ json 33 | sensors = "sensors" <~~ json 34 | 35 | } 36 | 37 | public func toJSON() -> JSON? { 38 | 39 | let json = jsonify([ 40 | "bridge" ~~> bridge, 41 | "lights" ~~> lights, 42 | "sensors" ~~> sensors, 43 | ]) 44 | 45 | return json 46 | } 47 | } 48 | 49 | extension SoftwareUpdateStatusDeviceTypes: Hashable { 50 | 51 | public func hash(into hasher: inout Hasher) { 52 | 53 | hasher.combine(1) 54 | } 55 | } 56 | 57 | public func ==(lhs: SoftwareUpdateStatusDeviceTypes, rhs: SoftwareUpdateStatusDeviceTypes) -> Bool { 58 | return (lhs.bridge ?? false) == (rhs.bridge ?? false) && 59 | (lhs.lights ?? []) == (rhs.lights ?? []) && 60 | (lhs.sensors ?? []) == (rhs.sensors ?? []) 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Base/BridgeResourceModels/WhitelistEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WhitelistEntry.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Gloss 11 | 12 | public struct WhitelistEntry: BridgeResource, BridgeResourceDictGenerator { 13 | 14 | public typealias AssociatedBridgeResourceType = WhitelistEntry 15 | 16 | public var resourceType: BridgeResourceType { 17 | return .whitelistEntry 18 | }; 19 | 20 | public let identifier: String 21 | 22 | /** 23 | The date when the entry is used for the last time 24 | */ 25 | public let lastUseDate: Date? 26 | 27 | /** 28 | Creation date of the entry 29 | */ 30 | public let createDate: Date? 31 | 32 | /** 33 | Name of the entry 34 | */ 35 | public let name: String 36 | 37 | public var username: String? { 38 | return identifier; 39 | } 40 | 41 | public init?(json: JSON) { 42 | 43 | let dateFormatter = DateFormatter.hueApiDateFormatter 44 | 45 | guard let identifier: String = "id" <~~ json, 46 | let name: String = "name" <~~ json, 47 | let lastUseDate: Date = Decoder.decode(dateForKey: "last use date", dateFormatter:dateFormatter)(json), 48 | let createDate: Date = Decoder.decode(dateForKey: "create date", dateFormatter: dateFormatter)(json) 49 | else { return nil } 50 | 51 | self.identifier = identifier 52 | self.name = name 53 | self.lastUseDate = lastUseDate as Date 54 | self.createDate = createDate as Date 55 | 56 | } 57 | 58 | public func toJSON() -> JSON? { 59 | 60 | let dateFormatter = DateFormatter.hueApiDateFormatter 61 | 62 | let json = jsonify([ 63 | "id" ~~> identifier, 64 | "name" ~~> name, 65 | Encoder.encode(dateForKey: "last use date", dateFormatter: dateFormatter)(lastUseDate), 66 | Encoder.encode(dateForKey: "create date", dateFormatter: dateFormatter)(createDate) 67 | ]) 68 | 69 | return json 70 | } 71 | } 72 | 73 | extension WhitelistEntry: Hashable { 74 | 75 | public func hash(into hasher: inout Hasher) { 76 | 77 | hasher.combine(Int(self.identifier)!) 78 | } 79 | } 80 | 81 | public func ==(lhs: WhitelistEntry, rhs: WhitelistEntry) -> Bool { 82 | return lhs.identifier == rhs.identifier && 83 | lhs.name == rhs.identifier && 84 | lhs.lastUseDate == rhs.lastUseDate && 85 | lhs.createDate == rhs.createDate 86 | } 87 | -------------------------------------------------------------------------------- /Sources/Base/Dictionary+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+Extension.swift 3 | // Pods 4 | // 5 | // Created by Jerome Schmitz on 16.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension Dictionary { 12 | mutating func unionInPlace( 13 | _ dictionary: Dictionary) { 14 | for (key, value) in dictionary { 15 | self[key] = value 16 | } 17 | } 18 | 19 | // Thanks Airspeed Velocity 20 | mutating func unionInPlace(_ sequence: S) where 21 | S.Iterator.Element == (Key,Value) { 22 | for (key, value) in sequence { 23 | self[key] = value 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Base/Logger/LoggerConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggerConfig.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 15.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | //import Log 11 | 12 | /*let Log = Logger(formatter: .Detailed, theme: .TomorrowNight) 13 | 14 | extension Formatters { 15 | static let Detailed = Formatter("[%@] %@.%@:%@ %@: %@", [ 16 | .date("yyyy-MM-dd HH:mm:ss.SSS"), 17 | .file(fullPath: false, fileExtension: false), 18 | .function, 19 | .line, 20 | .level, 21 | .message 22 | ]) 23 | } 24 | 25 | extension Themes { 26 | static let TomorrowNight = Theme( 27 | trace: "#C5C8C6", 28 | debug: "#81A2BE", 29 | info: "#B5BD68", 30 | warning: "#F0C674", 31 | error: "#CC6666" 32 | ) 33 | }*/ 34 | -------------------------------------------------------------------------------- /Sources/Base/NSDateFormatter+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDateFormatter+Extension.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 22.04.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public extension DateFormatter { 12 | 13 | static public var hueApiDateFormatter: DateFormatter { 14 | 15 | let dateFormatter = DateFormatter() 16 | dateFormatter.locale = Locale(identifier: "en_US") 17 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" 18 | 19 | return dateFormatter 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Base/ReplaceMe.swift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spriter/SwiftyHue/5309c5b8473085b607469ff5a0c26b4bfea0867b/Sources/Base/ReplaceMe.swift -------------------------------------------------------------------------------- /Sources/Base/ResourceAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResourceAPI.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 02.11.17. 6 | // 7 | 8 | import Foundation 9 | 10 | import Foundation 11 | import Alamofire 12 | import Gloss 13 | 14 | public class ResourceAPI { 15 | 16 | private var bridgeAccessConfig: BridgeAccessConfig?; 17 | 18 | func setBridgeAccessConfig(_ bridgeAccessConfig: BridgeAccessConfig) { 19 | 20 | self.bridgeAccessConfig = bridgeAccessConfig 21 | } 22 | 23 | public func fetch(resource: HeartbeatBridgeResourceType, completionHandler:@escaping (Alamofire.Result<[String: Resource]>) -> ()) where Resource.AssociatedBridgeResourceType == Resource { 24 | 25 | guard let bridgeAccessConfig = bridgeAccessConfig else { 26 | 27 | completionHandler(Alamofire.Result.failure(HueError(address: "SwiftyHue", errorDescription: "No bridgeAccessConfig available", type: 1)!)) 28 | return 29 | } 30 | 31 | let url = "http://\(bridgeAccessConfig.ipAddress)/api/\(bridgeAccessConfig.username)/\(resource.rawValue.lowercased())" 32 | 33 | Alamofire.request(url).responseJSON { response in 34 | 35 | switch response.result { 36 | 37 | case .success(let data) where data is JSON: 38 | 39 | let resources = Resource.dictionaryFromResourcesJSON(data as! JSON) 40 | completionHandler(.success(resources)) 41 | 42 | case .success(_): 43 | completionHandler(.failure(NSError(domain: "unexpected response", code: 0, userInfo: nil))) 44 | 45 | case .failure(let error): 46 | completionHandler(.failure(error)) 47 | } 48 | } 49 | } 50 | 51 | public func fetchLights(_ completionHandler: @escaping (Alamofire.Result<[String: Light]>) -> ()) { 52 | fetch(resource: .lights, completionHandler: completionHandler) 53 | } 54 | 55 | public func fetchGroups(_ completionHandler: @escaping (Alamofire.Result<[String: Group]>) -> ()) { 56 | fetch(resource: .groups, completionHandler: completionHandler) 57 | } 58 | 59 | public func fetchScenes(_ completionHandler: @escaping (Alamofire.Result<[String: PartialScene]>) -> ()) { 60 | fetch(resource: .scenes, completionHandler: completionHandler) 61 | } 62 | 63 | public func fetchRules(_ completionHandler: @escaping (Alamofire.Result<[String: Rule]>) -> ()) { 64 | fetch(resource: .rules, completionHandler: completionHandler) 65 | } 66 | 67 | public func fetchSensors(_ completionHandler: @escaping (Alamofire.Result<[String: Sensor]>) -> ()) { 68 | fetch(resource: .sensors, completionHandler: completionHandler) 69 | } 70 | 71 | public func fetchSchedules(_ completionHandler: @escaping (Alamofire.Result<[String: Schedule]>) -> ()) { 72 | fetch(resource: .schedules, completionHandler: completionHandler) 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Sources/Base/SwiftyHue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyHue.swift 3 | // Pods 4 | // 5 | // Created by Marcel Dittmann on 15.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | //import Log 11 | 12 | public class SwiftyHue: NSObject { 13 | 14 | // MARK: Public Interface 15 | public var resourceCache: BridgeResourcesCache?; 16 | 17 | public var bridgeSendAPI: BridgeSendAPI = BridgeSendAPI() 18 | 19 | public var resourceAPI: ResourceAPI = ResourceAPI() 20 | 21 | public func setBridgeAccessConfig(_ bridgeAccessConfig: BridgeAccessConfig, resourceCacheHeartbeatProcessorDelegate: ResourceCacheHeartbeatProcessorDelegate? = nil) { 22 | 23 | self.bridgeAccessConfig = bridgeAccessConfig; 24 | self.resourceCacheHeartbeatProcessor = ResourceCacheHeartbeatProcessor(delegate: resourceCacheHeartbeatProcessorDelegate ?? self); 25 | self.bridgeSendAPI.setBridgeAccessConfig(bridgeAccessConfig) 26 | self.resourceAPI.setBridgeAccessConfig(bridgeAccessConfig) 27 | self.heartbeatManager = HeartbeatManager(bridgeAccesssConfig: bridgeAccessConfig, heartbeatProcessors: [resourceCacheHeartbeatProcessor!]); 28 | } 29 | 30 | public func setLocalHeartbeatInterval(_ interval: TimeInterval, forResourceType resourceType: HeartbeatBridgeResourceType) { 31 | 32 | heartbeatManager?.setLocalHeartbeatInterval(interval, forResourceType: resourceType) 33 | } 34 | 35 | public func removeLocalHeartbeat(forResourceType resourceType: HeartbeatBridgeResourceType) { 36 | 37 | heartbeatManager?.removeLocalHeartbeat(forResourceType: resourceType) 38 | } 39 | 40 | public func startHeartbeat() { 41 | 42 | heartbeatManager?.startHeartbeat() 43 | } 44 | 45 | public func stopHeartbeat() { 46 | 47 | heartbeatManager?.stopHeartbeat() 48 | } 49 | 50 | // MARK: Logging 51 | 52 | public func enableLogging(_ enabled: Bool) { 53 | //Log.enabled = enabled 54 | } 55 | 56 | /** 57 | The minimum level of severity for the Logger. 58 | */ 59 | /*public func setMinLevelForLogMessages(_ level: Level) { 60 | 61 | //Log.minLevel = level 62 | }*/ 63 | 64 | // MARK: Private 65 | private var bridgeAccessConfig: BridgeAccessConfig?; 66 | private var heartbeatManager: HeartbeatManager?; 67 | private var resourceCacheHeartbeatProcessor: ResourceCacheHeartbeatProcessor?; 68 | 69 | /*public override init() { 70 | super.init() 71 | //enableLogging(false) 72 | }*/ 73 | } 74 | 75 | extension SwiftyHue: ResourceCacheHeartbeatProcessorDelegate { 76 | 77 | public func resourceCacheUpdated(_ resourceCache: BridgeResourcesCache) { 78 | 79 | self.resourceCache = resourceCache; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeAuthenticator/BridgeAuthenticatorDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeAuthenticatorDelegate.swift 3 | // Pods 4 | // 5 | // Created by Nils Lattek on 05.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol BridgeAuthenticatorDelegate: class { 12 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFinishAuthentication username: String) 13 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFailWithError error: NSError) 14 | // you should now ask the user to press the link button 15 | func bridgeAuthenticatorRequiresLinkButtonPress(_ authenticator: BridgeAuthenticator, secondsLeft: TimeInterval) 16 | // user did not press the link button in time, you restart the process and try again 17 | func bridgeAuthenticatorDidTimeout(_ authenticator: BridgeAuthenticator) 18 | } 19 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/BridgeFinder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeFinder.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | //import Log 11 | 12 | /// Use this class to find HueBridges on the current network. 13 | public class BridgeFinder: NSObject, ScannerDelegate { 14 | private var foundBridges = [HueBridge]() 15 | private let allScannerClasses: [Scanner.Type] 16 | private var remainingScannerClasses: [Scanner.Type] = [] 17 | private var currentScanner: Scanner? 18 | private let validator: BridgeValidator 19 | public weak var delegate: BridgeFinderDelegate? 20 | 21 | public override convenience init() { 22 | // The recommended search order from Philips is SSDPScanner, NUPNPScanner, 23 | // IPScanner (not implemented currently). 24 | self.init(validator: BridgeValidator(), scannerClasses: [SSDPScanner.self, NUPNPScanner.self]) 25 | } 26 | 27 | init(validator: BridgeValidator, scannerClasses: [Scanner.Type]) { 28 | self.validator = validator 29 | // Given the recommened order from Phillips and in order to avoid 30 | // dealing with checking the length of this array using remove(at:0) 31 | // so we don't get an IndexError, we are going to use popLast() -> T? 32 | // to pull the items out of it. To do that and maintain the 33 | // recommened order from Phillips we need to reverse the array as we 34 | // declared it's contents in the recommended order. 35 | self.allScannerClasses = scannerClasses.reversed() 36 | super.init() 37 | } 38 | 39 | /// Start scanning, make sure to assign a delegate to get notified about the results. 40 | public func start() { 41 | remainingScannerClasses = allScannerClasses 42 | startNextScanner() 43 | } 44 | 45 | private func startNextScanner() { 46 | guard let scannerClass = remainingScannerClasses.popLast() else { 47 | // all scanners finished, no bridges found 48 | DispatchQueue.main.async { 49 | self.delegate?.bridgeFinder(self, didFinishWithResult: self.foundBridges) 50 | } 51 | return 52 | } 53 | 54 | //Log.trace("Scanner started: \(scannerClass)") 55 | currentScanner = scannerClass.init(delegate: self) 56 | currentScanner?.start() 57 | } 58 | 59 | private func validateBridges(_ ips: [String]) { 60 | // create mutable copy, use recursion to check if every bridge has been validated 61 | var ips = ips 62 | 63 | guard let ip = ips.popLast() else { 64 | // all ips validated for current scanner 65 | ipValidationFinished() 66 | return 67 | } 68 | 69 | validator.validate(ip, success: { [weak self] (bridge) in 70 | if let this = self { 71 | this.foundBridges.append(bridge) 72 | this.validateBridges(ips) 73 | } 74 | }, failure: { [weak self] (error) in 75 | if let this = self { 76 | this.validateBridges(ips) 77 | } 78 | }) 79 | } 80 | 81 | private func ipValidationFinished() { 82 | if foundBridges.count == 0 { 83 | // no bridges found, continue with next scanner 84 | startNextScanner() 85 | } else { 86 | DispatchQueue.main.async { 87 | self.delegate?.bridgeFinder(self, didFinishWithResult: self.foundBridges) 88 | } 89 | } 90 | } 91 | 92 | // MARK: - ScannerDelegate 93 | 94 | func scanner(_ scanner: Scanner, didFinishWithResults ips: [String]) { 95 | //Log.trace("Scanner finished: \(scanner) with result count: \(ips.count)") 96 | validateBridges(ips) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/BridgeFinderDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeFinderDelegate.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Protocol for handling BridgeFinder results 12 | public protocol BridgeFinderDelegate: class { 13 | /** 14 | Search for HueBridges finished. 15 | 16 | - Parameters: 17 | - finder: The BridgeFinder 18 | - bridges: An array containing all found bridges. If none was found the array will be empty. 19 | */ 20 | func bridgeFinder(_ finder: BridgeFinder, didFinishWithResult bridges: [HueBridge]) 21 | } 22 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/HueBridge.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HueBridge.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct HueBridge { 12 | public let ip: String 13 | public let deviceType: String 14 | public let friendlyName: String 15 | public let modelDescription: String 16 | public let modelName: String 17 | public let serialNumber: String 18 | public let UDN: String 19 | public let icons: [HueBridgeIcon] 20 | 21 | public init(ip: String, deviceType: String, friendlyName: String, modelDescription: String, modelName: String, serialNumber: String, UDN: String, icons: [HueBridgeIcon]) { 22 | self.ip = ip 23 | self.deviceType = deviceType 24 | self.friendlyName = friendlyName 25 | self.modelDescription = modelDescription 26 | self.modelName = modelName 27 | self.serialNumber = serialNumber 28 | self.UDN = UDN 29 | self.icons = icons 30 | } 31 | } -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/HueBridgeIcon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HueBridgeIcon.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct HueBridgeIcon { 12 | public let mimetype: String 13 | public let height: Int 14 | public let width: Int 15 | public let name: String 16 | } -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Scanner/IPScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IPScanner.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Scanner/NUPNPScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NUPNPScanner.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class NUPNPScanner: NSObject, Scanner { 12 | weak var delegate: ScannerDelegate? 13 | 14 | required init(delegate: ScannerDelegate? = nil) { 15 | self.delegate = delegate 16 | super.init() 17 | } 18 | 19 | func start() { 20 | let request = createRequest() 21 | startRequest(request as URLRequest) 22 | } 23 | 24 | func stop() { 25 | 26 | } 27 | 28 | private func createRequest() -> URLRequest { 29 | let url = URL(string: "https://www.meethue.com/api/nupnp")! 30 | 31 | var request = URLRequest(url: url) 32 | request.httpMethod = "GET" 33 | 34 | return request 35 | } 36 | 37 | private func startRequest(_ request: URLRequest) { 38 | let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in 39 | guard let this = self else { 40 | return 41 | } 42 | 43 | if error != nil || data == nil { 44 | this.delegate?.scanner(this, didFinishWithResults: []) 45 | return 46 | } 47 | 48 | let ips = this.parseResults(data!) 49 | this.delegate?.scanner(this, didFinishWithResults: ips) 50 | } 51 | 52 | task.resume() 53 | } 54 | 55 | private func parseResults(_ data: Data) -> [String] { 56 | var ips = [String]() 57 | 58 | do { 59 | if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: String]] { 60 | for bridgeJson in json { 61 | if let ip = bridgeJson["internalipaddress"] { 62 | ips.append(ip) 63 | } 64 | } 65 | } 66 | 67 | 68 | } catch let error as NSError { 69 | print("Error while parsing nupnp results: \(error)") 70 | } 71 | 72 | return ips 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Scanner/SSDPScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SSDPScanner.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CocoaAsyncSocket 11 | 12 | class SSDPScanner: NSObject, Scanner, GCDAsyncUdpSocketDelegate { 13 | private let ssdpSocket: GCDAsyncUdpSocket 14 | private let delegateQueue = DispatchQueue(label: "com.blowfishlab.SwiftyHue.ssdpscanner") 15 | private var results = Set() 16 | weak var delegate: ScannerDelegate? 17 | 18 | required init(delegate: ScannerDelegate? = nil) { 19 | self.delegate = delegate 20 | ssdpSocket = GCDAsyncUdpSocket() 21 | super.init() 22 | ssdpSocket.setDelegate(self, delegateQueue: delegateQueue) 23 | } 24 | 25 | func start() { 26 | do { 27 | try ssdpSocket.enableBroadcast(true) 28 | let searchData = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nST: ssdp:all\r\n\r\n".data(using: .utf8)! 29 | let host = "239.255.255.250" 30 | let port: UInt16 = 1900 31 | // listen 102 error: https://github.com/robbiehanson/CocoaAsyncSocket/issues/376 32 | try ssdpSocket.bind(toPort: 0) 33 | 34 | ssdpSocket.send(searchData, toHost: host, port: port, withTimeout: 5, tag: 1) 35 | try ssdpSocket.beginReceiving() 36 | 37 | let receiveTimeout: TimeInterval = 5 38 | Timer.scheduledTimer(timeInterval: receiveTimeout, target: self, selector: #selector(SSDPScanner.stop), userInfo: self, repeats: false) 39 | 40 | } catch let error as NSError { 41 | print("Exception: \(error)") 42 | } 43 | } 44 | 45 | @objc func stop() { 46 | ssdpSocket.close() 47 | let ips = Array(results) 48 | delegate?.scanner(self, didFinishWithResults: ips) 49 | } 50 | 51 | // MARK: - GCDAsyncUdpSocketDelegate 52 | 53 | func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext: Any?){ 54 | guard let result = String(data: data, encoding:.ascii) else { 55 | print("Could not decode ssdp data") 56 | return 57 | } 58 | 59 | if result.contains("IpBridge") { 60 | var host: NSString? 61 | var port: UInt16 = 0 62 | 63 | GCDAsyncUdpSocket.getHost(&host, port: &port, fromAddress: address) 64 | if let host = host as String? { 65 | results.insert(host) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Scanner/Scanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Scanner.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | 10 | protocol Scanner { 11 | var delegate: ScannerDelegate? { get set } 12 | 13 | init(delegate: ScannerDelegate?) 14 | func start() 15 | func stop() 16 | } 17 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Scanner/ScannerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScannerDelegate.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ScannerDelegate: class { 12 | func scanner(_ scanner: Scanner, didFinishWithResults ips: [String]) 13 | } 14 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Validator/BridgeResultParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeResultParser.swift 3 | // HueSDK 4 | // 5 | // Created by Nils Lattek on 24.04.16. 6 | // Copyright © 2016 Nils Lattek. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BridgeResultParser: NSObject, XMLParserDelegate { 12 | private let parser: XMLParser 13 | private var element: String = "" 14 | private var bridge: HueBridge? 15 | private var successBlock: ((_ bridge: HueBridge) -> Void)? 16 | private var failureBlock: ((_ error: NSError) -> Void)? 17 | 18 | private var urlBase: String = "" 19 | private var ip: String = "" 20 | private var deviceType: String = "" 21 | private var friendlyName: String = "" 22 | private var modelDescription: String = "" 23 | private var modelName: String = "" 24 | private var serialNumber: String = "" 25 | private var UDN: String = "" 26 | private var icons = [HueBridgeIcon]() 27 | private var mimetype: String = "" 28 | private var height: String = "" 29 | private var width: String = "" 30 | private var iconName: String = "" 31 | 32 | init(xmlData: Data) { 33 | parser = XMLParser(data: xmlData) 34 | super.init() 35 | parser.delegate = self 36 | } 37 | 38 | func parse(_ success: @escaping (_ bridge: HueBridge) -> Void, failure: @escaping (_ error: NSError) -> Void) { 39 | self.successBlock = success 40 | self.failureBlock = failure 41 | 42 | parser.parse() 43 | } 44 | 45 | private func cancelWithError(_ errorMessage: String) { 46 | parser.abortParsing() 47 | if let failureBlock = failureBlock { 48 | failureBlock(NSError(domain: "HueBridgeParser", code: 500, userInfo: [NSLocalizedDescriptionKey: errorMessage])) 49 | } 50 | } 51 | 52 | // MARK: - NSXMLParserDelegate 53 | 54 | public func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { 55 | element = elementName 56 | 57 | if elementName == "root" { 58 | if attributeDict["xmlns"] == nil || attributeDict["xmlns"] != "urn:schemas-upnp-org:device-1-0" { 59 | cancelWithError("XML is not a known HueBridge XML.") 60 | } 61 | } else if elementName == "icon" { 62 | mimetype = "" 63 | height = "" 64 | width = "" 65 | iconName = "" 66 | } 67 | } 68 | 69 | public func parser(_ parser: XMLParser, foundCharacters string: String) { 70 | switch element { 71 | case "deviceType": 72 | deviceType += string 73 | case "friendlyName": 74 | friendlyName += string 75 | case "modelDescription": 76 | modelDescription += string 77 | case "modelName": 78 | modelName += string 79 | case "serialNumber": 80 | serialNumber += string 81 | case "UDN": 82 | UDN += string 83 | case "URLBase": 84 | urlBase += string 85 | case "mimetype": 86 | mimetype += string 87 | case "height": 88 | height += string 89 | case "width": 90 | width += string 91 | case "url": 92 | iconName += string 93 | default: break 94 | } 95 | } 96 | 97 | public func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { 98 | element = "" 99 | 100 | if elementName == "device" { 101 | if isBridgeDataValid() { 102 | bridge = HueBridge(ip: ip, deviceType: deviceType, friendlyName: friendlyName, modelDescription: modelDescription, modelName: modelName, serialNumber: serialNumber, UDN: UDN, icons: icons) 103 | } else { 104 | cancelWithError("HueBridge data not valid.") 105 | } 106 | } else if elementName == "URLBase" { 107 | let url = URL(string: urlBase) 108 | if let host = url?.host { 109 | ip = host 110 | } 111 | } else if elementName == "icon" { 112 | if let height = Int(height), let width = Int(width) { 113 | icons.append(HueBridgeIcon(mimetype: mimetype, height: height, width: width, name: iconName)) 114 | } 115 | } 116 | } 117 | 118 | public func parserDidEndDocument(_ parser: XMLParser) { 119 | if let successBlock = successBlock, let bridge = bridge { 120 | successBlock(bridge) 121 | } 122 | } 123 | 124 | private func isBridgeDataValid() -> Bool { 125 | return ip.count != 0 && deviceType.count != 0 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Sources/BridgeServices/BridgeFinder/Validator/BridgeValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VerifyBridge.swift 3 | // HueSDK 4 | // 5 | // Given an IP-Adress it tries to retrieve more information about the HUE Bridge. 6 | // 7 | // Created by Nils Lattek on 24.04.16. 8 | // Copyright © 2016 Nils Lattek. All rights reserved. 9 | // 10 | 11 | import Foundation 12 | 13 | class BridgeValidator { 14 | func validate(_ ip: String, success: @escaping (_ bridge: HueBridge) -> Void, failure: @escaping (_ error: NSError) -> Void) { 15 | let request = createRequest(ip) 16 | startRequest(request as URLRequest, success: success, failure: failure) 17 | } 18 | 19 | private func createRequest(_ ip: String) -> NSMutableURLRequest { 20 | let url = URL(string: "http://\(ip)/description.xml")! 21 | 22 | let request = NSMutableURLRequest(url: url) 23 | request.httpMethod = "GET" 24 | 25 | return request 26 | } 27 | 28 | private func startRequest(_ request: URLRequest, success: @escaping (_ bridge: HueBridge) -> Void, failure: @escaping (_ error: NSError) -> Void) { 29 | let task = URLSession.shared.dataTask(with: request) { (data, response, error) in 30 | if let error = error { 31 | failure(error as NSError) 32 | return 33 | } 34 | 35 | guard let data = data else { 36 | failure(NSError(domain: "HueBridgeValidator", code: 500, userInfo: [NSLocalizedDescriptionKey: "No data from bridge received."])) 37 | return 38 | } 39 | 40 | // TODO: check specVersion/xmlns and use correct parser 41 | let parser = BridgeResultParser(xmlData: data) 42 | parser.parse(success, failure: failure) 43 | } 44 | 45 | task.resume() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Info-iOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Info-watchOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/SwiftyHue.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyHue iOS.h 3 | // SwiftyHue iOS 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftyHue iOS. 12 | FOUNDATION_EXPORT double SwiftyHueVersionNumber; 13 | 14 | //! Project version string for SwiftyHue iOS. 15 | FOUNDATION_EXPORT const unsigned char SwiftyHueVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch Extension/Assets.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // SwiftyHue Example Watch Extension 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | import WatchKit 10 | 11 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 12 | 13 | func applicationDidFinishLaunching() { 14 | // Perform any final initialization of your application. 15 | } 16 | 17 | func applicationDidBecomeActive() { 18 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 19 | } 20 | 21 | func applicationWillResignActive() { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, etc. 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | SwiftyHue Example Watch Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | com.blowfishlab.SwiftyHue-Example.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | WKExtensionDelegateClassName 36 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 37 | 38 | 39 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // SwiftyHue Example Watch Extension 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | 12 | 13 | class InterfaceController: WKInterfaceController { 14 | 15 | // override func awakeWithContext(context: AnyObject?) { 16 | // super.awake(withContext: context) 17 | // 18 | // // Configure interface objects here. 19 | // } 20 | 21 | override func willActivate() { 22 | // This method is called when watch view controller is about to be visible to user 23 | super.willActivate() 24 | } 25 | 26 | override func didDeactivate() { 27 | // This method is called when watch view controller is no longer visible 28 | super.didDeactivate() 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "86x86", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "quickLook", 41 | "subtype" : "38mm" 42 | }, 43 | { 44 | "size" : "98x98", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "42mm" 49 | } 50 | ], 51 | "info" : { 52 | "version" : 1, 53 | "author" : "xcode" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SwiftyHue Example Watch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | SwiftyHue Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | com.blowfishlab.SwiftyHue-Example 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftyHue Example tvOS 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "size" : "1280x768", 5 | "idiom" : "tv", 6 | "filename" : "App Icon - Large.imagestack", 7 | "role" : "primary-app-icon" 8 | }, 9 | { 10 | "size" : "400x240", 11 | "idiom" : "tv", 12 | "filename" : "App Icon - Small.imagestack", 13 | "role" : "primary-app-icon" 14 | }, 15 | { 16 | "size" : "1920x720", 17 | "idiom" : "tv", 18 | "filename" : "Top Shelf Image.imageset", 19 | "role" : "top-shelf-image" 20 | } 21 | ], 22 | "info" : { 23 | "version" : 1, 24 | "author" : "xcode" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "landscape", 5 | "idiom" : "tv", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "9.0", 8 | "scale" : "1x" 9 | } 10 | ], 11 | "info" : { 12 | "version" : 1, 13 | "author" : "xcode" 14 | } 15 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spriter/SwiftyHue/5309c5b8473085b607469ff5a0c26b4bfea0867b/SwiftyHue Example tvOS/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spriter/SwiftyHue/5309c5b8473085b607469ff5a0c26b4bfea0867b/SwiftyHue Example tvOS/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // SwiftyHue Example tvOS 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class FirstViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SwiftyHue Example tvOS/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // SwiftyHue Example tvOS 4 | // 5 | // Created by Marcel Dittmann on 25.05.16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class SecondViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /SwiftyHue Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 04/21/2016. 6 | // Copyright (c) 2016 Marcel Dittmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(_ application: UIApplication) { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | func applicationDidEnterBackground(_ application: UIApplication) { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(_ application: UIApplication) { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | func applicationDidBecomeActive(_ application: UIApplication) { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | func applicationWillTerminate(_ application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /SwiftyHue Example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SwiftyHue Example/BridgeAccessConfigPresentationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeAccessConfigPresentationViewController.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 08.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyHue 11 | 12 | class BridgeAccessConfigPresentationViewController: UIViewController { 13 | 14 | var bridgeAccesssConfig: BridgeAccessConfig! 15 | 16 | @IBOutlet var usernameLabel: UILabel! 17 | @IBOutlet var bridgeIdLabel: UILabel! 18 | @IBOutlet var ipLabel: UILabel! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | // Do any additional setup after loading the view. 24 | } 25 | 26 | override func viewDidAppear(_ animated: Bool) { 27 | super.viewDidAppear(animated) 28 | 29 | usernameLabel?.text = bridgeAccesssConfig.username 30 | bridgeIdLabel?.text = bridgeAccesssConfig.bridgeId 31 | ipLabel?.text = bridgeAccesssConfig.ipAddress 32 | } 33 | 34 | override func didReceiveMemoryWarning() { 35 | super.didReceiveMemoryWarning() 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | @IBAction func okButtonTapped(_ sender: AnyObject) { 40 | 41 | (navigationController as! CreateBridgeAccessController).bridgeAccessCreated(bridgeAccessConfig: bridgeAccesssConfig) 42 | } 43 | 44 | /* 45 | // MARK: - Navigation 46 | 47 | // In a storyboard-based application, you will often want to do a little preparation before navigation 48 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 49 | // Get the new view controller using segue.destinationViewController. 50 | // Pass the selected object to the new view controller. 51 | } 52 | */ 53 | 54 | } 55 | -------------------------------------------------------------------------------- /SwiftyHue Example/BridgePushLinkViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgePushLinkViewController.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 08.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyHue 11 | 12 | class BridgePushLinkViewController: UIViewController { 13 | 14 | var bridge: HueBridge!; 15 | var bridgeAuthenticator: BridgeAuthenticator! 16 | var bridgeAccessConfig: BridgeAccessConfig! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | // Do any additional setup after loading the view. 22 | } 23 | 24 | override func didReceiveMemoryWarning() { 25 | super.didReceiveMemoryWarning() 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | override func viewDidAppear(_ animated: Bool) { 30 | super.viewDidAppear(animated) 31 | 32 | bridgeAuthenticator = BridgeAuthenticator(bridge: bridge, uniqueIdentifier: "swiftyhue#\(UIDevice.current.name)") 33 | bridgeAuthenticator.delegate = self; 34 | bridgeAuthenticator.start() 35 | } 36 | 37 | 38 | /* 39 | // MARK: - Navigation 40 | 41 | // In a storyboard-based application, you will often want to do a little preparation before navigation 42 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 43 | // Get the new view controller using segue.destinationViewController. 44 | // Pass the selected object to the new view controller. 45 | } 46 | */ 47 | 48 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 49 | let destController = segue.destination as! BridgeAccessConfigPresentationViewController; 50 | destController.bridgeAccesssConfig = bridgeAccessConfig; 51 | } 52 | 53 | } 54 | 55 | extension BridgePushLinkViewController: BridgeAuthenticatorDelegate { 56 | 57 | // you should now ask the user to press the link button 58 | func bridgeAuthenticatorRequiresLinkButtonPress(_ authenticator: BridgeAuthenticator, secondsLeft: TimeInterval) { 59 | 60 | } 61 | 62 | 63 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFinishAuthentication username: String) { 64 | 65 | self.bridgeAccessConfig = BridgeAccessConfig(bridgeId: "BridgeId", ipAddress: bridge.ip, username: username) 66 | 67 | self.performSegue(withIdentifier: "BridgeAccessConfigPresentationViewControllerSegue", sender: self) 68 | } 69 | 70 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFailWithError error: NSError) { 71 | 72 | print(error) 73 | } 74 | 75 | 76 | 77 | // user did not press the link button in time, you restart the process and try again 78 | func bridgeAuthenticatorDidTimeout(_ authenticator: BridgeAuthenticator) { 79 | 80 | print("timeout") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /SwiftyHue Example/BridgeResourceTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeResourceTableViewController.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 09.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyHue 11 | 12 | class BridgeResourceTableViewController: UITableViewController { 13 | 14 | var resourceTypeToDisplay: HeartbeatBridgeResourceType = .lights 15 | var bridgeResources = [BridgeResource]() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | updateResources() 21 | } 22 | 23 | func updateResources() { 24 | 25 | if let resourceCache = swiftyHue.resourceCache { 26 | 27 | switch resourceTypeToDisplay { 28 | 29 | case .lights: 30 | 31 | for light in resourceCache.lights.values { 32 | bridgeResources.append(light) 33 | } 34 | 35 | case .groups: 36 | 37 | for group in resourceCache.groups.values { 38 | bridgeResources.append(group) 39 | } 40 | 41 | case .scenes: 42 | 43 | for scene in resourceCache.scenes.values { 44 | bridgeResources.append(scene) 45 | } 46 | 47 | case .rules: 48 | 49 | for rule in resourceCache.rules.values { 50 | bridgeResources.append(rule) 51 | } 52 | 53 | case .schedules: 54 | 55 | for schedule in resourceCache.schedules.values { 56 | bridgeResources.append(schedule) 57 | } 58 | 59 | case .sensors: 60 | 61 | for sensor in resourceCache.sensors.values { 62 | bridgeResources.append(sensor) 63 | } 64 | 65 | case .config: 66 | 67 | if let config = resourceCache.bridgeConfiguration { 68 | bridgeResources.append(config) 69 | } 70 | 71 | } 72 | } 73 | } 74 | 75 | required init?(coder aDecoder: NSCoder) { 76 | super.init(coder: aDecoder) 77 | } 78 | 79 | override func didReceiveMemoryWarning() { 80 | super.didReceiveMemoryWarning() 81 | // Dispose of any resources that can be recreated. 82 | } 83 | 84 | // MARK: - Table view data source 85 | 86 | override func numberOfSections(in tableView: UITableView) -> Int { 87 | 88 | // #warning Incomplete implementation, return the number of sections 89 | return 1 90 | } 91 | 92 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 93 | return bridgeResources.count 94 | } 95 | 96 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 97 | 98 | let cell = tableView.dequeueReusableCell(withIdentifier: "BridgeResourceTableViewCell", for: indexPath) 99 | 100 | let bridgeResource = self.bridgeResources[indexPath.row] 101 | 102 | cell.textLabel?.text = bridgeResource.name 103 | 104 | return cell 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /SwiftyHue Example/BridgeSelectionTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeSelectionTableViewController.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 08.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyHue 11 | 12 | class BridgeSelectionTableViewController: UITableViewController { 13 | 14 | var bridgeFinder = BridgeFinder() 15 | var bridges: [HueBridge]?; 16 | var selectedBridge: HueBridge? 17 | 18 | override func viewDidLoad() { 19 | 20 | self.setBackgroundMessage(message: "Searching Bridge") 21 | 22 | } 23 | 24 | override func viewDidAppear(_ animated: Bool) { 25 | super.viewDidAppear(animated) 26 | 27 | bridgeFinder.delegate = self; 28 | bridgeFinder.start() 29 | } 30 | 31 | override func numberOfSections(in tableView: UITableView) -> Int { 32 | 33 | return 1 34 | } 35 | 36 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 37 | 38 | return bridges?.count ?? 0 39 | } 40 | 41 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 42 | 43 | let bridge = self.bridges![indexPath.row] 44 | 45 | let cell = tableView.dequeueReusableCell(withIdentifier: "BridgeCell", for: indexPath) 46 | cell.textLabel?.text = bridge.friendlyName 47 | 48 | return cell 49 | } 50 | 51 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 52 | 53 | selectedBridge = bridges![indexPath.row] 54 | 55 | performSegue(withIdentifier: "BridgePushLinkViewControllerSegue", sender: self) 56 | } 57 | 58 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 59 | 60 | let destController = segue.destination as! BridgePushLinkViewController; 61 | destController.bridge = selectedBridge 62 | } 63 | } 64 | 65 | extension BridgeSelectionTableViewController: BridgeFinderDelegate { 66 | 67 | func bridgeFinder(_ finder: BridgeFinder, didFinishWithResult bridges: [HueBridge]) { 68 | 69 | self.bridges = bridges; 70 | self.setBackgroundMessage(message: nil) 71 | self.tableView.reloadData() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftyHue Example/CreateBridgeAccessController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateBridgeAccessController.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 08.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyHue 11 | 12 | public protocol CreateBridgeAccessControllerDelegate: class { 13 | 14 | func bridgeAccessCreated(bridgeAccessConfig: BridgeAccessConfig) 15 | } 16 | 17 | public class CreateBridgeAccessController: UINavigationController { 18 | 19 | public weak var bridgeAccessCreationDelegate: CreateBridgeAccessControllerDelegate? 20 | 21 | func bridgeAccessCreated(bridgeAccessConfig: BridgeAccessConfig) { 22 | 23 | dismiss(animated: true, completion: nil) 24 | bridgeAccessCreationDelegate?.bridgeAccessCreated(bridgeAccessConfig: bridgeAccessConfig) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SwiftyHue Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftyHue Example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftyHue Example/Images.xcassets/PressSmartbridgeV2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "press_smartbridgeV2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /SwiftyHue Example/Images.xcassets/PressSmartbridgeV2.imageset/press_smartbridgeV2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spriter/SwiftyHue/5309c5b8473085b607469ff5a0c26b4bfea0867b/SwiftyHue Example/Images.xcassets/PressSmartbridgeV2.imageset/press_smartbridgeV2.pdf -------------------------------------------------------------------------------- /SwiftyHue Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SwiftyHue Example/SwiftyHue Example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /SwiftyHue Example/UITableViewController+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewController+Extensions.swift 3 | // SwiftyHue 4 | // 5 | // Created by Marcel Dittmann on 08.05.16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UITableViewController { 12 | 13 | func setBackgroundMessage(message: String?) { 14 | if let message = message { 15 | // Display a message when the table is empty 16 | let messageLabel = UILabel() 17 | 18 | messageLabel.text = message 19 | messageLabel.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body) 20 | messageLabel.textColor = UIColor.lightGray 21 | messageLabel.textAlignment = .center 22 | messageLabel.sizeToFit() 23 | 24 | tableView.backgroundView = messageLabel 25 | tableView.separatorStyle = .none 26 | } 27 | else { 28 | tableView.backgroundView = nil 29 | tableView.separatorStyle = .singleLine 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/BridgeAuthenticatorConsumer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeAuthenticatorConsumer.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import XCTest 10 | import SwiftyHue 11 | 12 | class BridgeAuthenticatorConsumer: BridgeAuthenticatorDelegate { 13 | var asyncExpectation: XCTestExpectation? 14 | var timeoutCalled = false 15 | var failedWithError: NSError? 16 | var finishWithUsername: String? 17 | var ignoreLinkButtonCall = false 18 | var requiresLinkButtonCallCount = 0 19 | 20 | func bridgeAuthenticatorDidTimeout(_ authenticator: BridgeAuthenticator) { 21 | guard let expectation = asyncExpectation else { 22 | XCTFail("Set expectation in test") 23 | return 24 | } 25 | 26 | timeoutCalled = true 27 | expectation.fulfill() 28 | } 29 | 30 | func bridgeAuthenticatorRequiresLinkButtonPress(_ authenticator: BridgeAuthenticator) { 31 | requiresLinkButtonCallCount += 1 32 | if ignoreLinkButtonCall { 33 | return 34 | } 35 | 36 | guard let expectation = asyncExpectation else { 37 | XCTFail("Set expectation in test") 38 | return 39 | } 40 | 41 | expectation.fulfill() 42 | } 43 | 44 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFailWithError error: NSError) { 45 | guard let expectation = asyncExpectation else { 46 | XCTFail("Set expectation in test") 47 | return 48 | } 49 | 50 | failedWithError = error 51 | expectation.fulfill() 52 | } 53 | 54 | func bridgeAuthenticator(_ authenticator: BridgeAuthenticator, didFinishAuthentication username: String) { 55 | guard let expectation = asyncExpectation else { 56 | XCTFail("Set expectation in test") 57 | return 58 | } 59 | 60 | finishWithUsername = username 61 | expectation.fulfill() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/BridgeFinderConsumer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeFinderConsumer.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftyHue 11 | 12 | class BridgeFinderConsumer: BridgeFinderDelegate { 13 | var resultBridges: [HueBridge]? 14 | var asyncExpectation: XCTestExpectation? 15 | 16 | func bridgeFinder(_ finder: BridgeFinder, didFinishWithResult bridges: [HueBridge]) { 17 | guard let expectation = asyncExpectation else { 18 | XCTFail("Set expectation in test") 19 | return 20 | } 21 | 22 | resultBridges = bridges 23 | expectation.fulfill() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/BridgeFinderTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeFinderTests.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | @testable import SwiftyHue 12 | 13 | class BridgeFinderTests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func testTriesMultipleScanners() { 26 | let validator = TestBadValidator() 27 | let finder = BridgeFinder(validator: validator, scannerClasses: [TestScanner1.self, TestScanner2.self]) 28 | let consumer = BridgeFinderConsumer() 29 | finder.delegate = consumer 30 | consumer.asyncExpectation = expectation(description: "performFind") 31 | 32 | finder.start() 33 | 34 | waitForExpectations(timeout: 1) { (error) in 35 | if let error = error { 36 | XCTFail("\(error)") 37 | } 38 | 39 | XCTAssertNotNil(TestScanner1.calledAt) 40 | XCTAssertNotNil(TestScanner2.calledAt) 41 | XCTAssertTrue(TestScanner1.calledAt!.compare(TestScanner2.calledAt!) == ComparisonResult.orderedAscending) 42 | XCTAssertEqual(consumer.resultBridges!.count, 0) 43 | } 44 | } 45 | 46 | func testValidateIps() { 47 | let validator = TestBadValidator() 48 | TestScanner1.results = ["127.0.0.1", "192.168.2.1"] 49 | TestScanner2.results = ["192.168.2.2"] 50 | let finder = BridgeFinder(validator: validator, scannerClasses: [TestScanner1.self, TestScanner2.self]) 51 | let consumer = BridgeFinderConsumer() 52 | finder.delegate = consumer 53 | consumer.asyncExpectation = expectation(description: "performFind") 54 | 55 | finder.start() 56 | 57 | waitForExpectations(timeout: 1) { (error) in 58 | if let error = error { 59 | XCTFail("\(error)") 60 | } 61 | XCTAssertEqual(validator.calledForIps.count, 3) 62 | XCTAssertEqual(validator.calledForIps.first!, "192.168.2.1") 63 | XCTAssertEqual(validator.calledForIps.last!, "192.168.2.2") 64 | XCTAssertEqual(consumer.resultBridges!.count, 0) 65 | } 66 | } 67 | 68 | func testValidateIpsWithSuccess() { 69 | let validator = TestGoodValidator() 70 | TestScanner1.results = ["127.0.0.1", "192.168.2.1"] 71 | TestScanner2.results = ["192.168.2.2"] 72 | let finder = BridgeFinder(validator: validator, scannerClasses: [TestScanner1.self, TestScanner2.self]) 73 | let consumer = BridgeFinderConsumer() 74 | finder.delegate = consumer 75 | consumer.asyncExpectation = expectation(description: "performFind") 76 | 77 | finder.start() 78 | 79 | waitForExpectations(timeout: 1) { (error) in 80 | if let error = error { 81 | XCTFail("\(error)") 82 | } 83 | XCTAssertEqual(validator.calledForIps.count, 2) 84 | XCTAssertEqual(validator.calledForIps.first!, "192.168.2.1") 85 | XCTAssertEqual(validator.calledForIps.last!, "127.0.0.1") 86 | XCTAssertEqual(consumer.resultBridges!.count, 2) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/BridgeResultParserTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BridgeResultParserTests.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftyHue 11 | 12 | class BridgeResultParserTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testPerform() { 25 | let path = Bundle(for: BridgeResultParserTests.self).path(forResource: "bridge_response", ofType: "xml")! 26 | let xml = try! Data(contentsOf: URL(fileURLWithPath: path)) 27 | let parser = BridgeResultParser(xmlData: xml) 28 | 29 | let asyncExpectation = expectation(description: "parseBridgeXML") 30 | parser.parse({ (bridge) in 31 | XCTAssertEqual(bridge.ip, "192.168.1.130") 32 | XCTAssertEqual(bridge.deviceType, "urn:schemas-upnp-org:device:Basic:1") 33 | XCTAssertEqual(bridge.friendlyName, "Philips hue (192.168.1.130)") 34 | XCTAssertEqual(bridge.modelDescription, "Philips hue Personal Wireless Lighting") 35 | XCTAssertEqual(bridge.modelName, "Philips hue bridge 2012") 36 | XCTAssertEqual(bridge.serialNumber, "001788102201") 37 | XCTAssertEqual(bridge.UDN, "uuid:2f402f80-da50-11e1-9b23-001788102201") 38 | XCTAssertEqual(bridge.icons.count, 2) 39 | XCTAssertEqual(bridge.icons.first?.mimetype, "image/png") 40 | XCTAssertEqual(bridge.icons.first?.height, 48) 41 | XCTAssertEqual(bridge.icons.first?.width, 48) 42 | XCTAssertEqual(bridge.icons.first?.name, "hue_logo_0.png") 43 | asyncExpectation.fulfill() 44 | }, failure: { (error) in 45 | 46 | }) 47 | 48 | self.waitForExpectations(timeout: 2, handler: nil) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/TestBadValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | @testable import SwiftyHue 11 | 12 | class TestBadValidator: BridgeValidator { 13 | var calledForIps = [String]() 14 | 15 | override func validate(_ ip: String, success: (bridge: HueBridge) -> Void, failure: (error: NSError) -> Void) { 16 | calledForIps.append(ip) 17 | failure(error: NSError(domain: "test", code: 500, userInfo: [NSLocalizedDescriptionKey: "Validation always fails"])) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/TestGoodValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestGoodValidator.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | import Foundation 10 | @testable import SwiftyHue 11 | 12 | class TestGoodValidator: BridgeValidator { 13 | var calledForIps = [String]() 14 | 15 | override func validate(_ ip: String, success: (bridge: HueBridge) -> Void, failure: (error: NSError) -> Void) { 16 | calledForIps.append(ip) 17 | success(bridge: HueBridge(ip: ip, deviceType: "test device", friendlyName: "name", modelDescription: "model", modelName: "model", serialNumber: "serial", UDN: "udn", icons: [])) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/TestScanner1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestScanner1.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | @testable import SwiftyHue 10 | 11 | class TestScanner1: NSObject, Scanner { 12 | weak var delegate: ScannerDelegate? 13 | static var results = [String]() 14 | static var calledAt: Date? 15 | 16 | required init(delegate: ScannerDelegate? = nil) { 17 | self.delegate = delegate 18 | super.init() 19 | } 20 | 21 | func start() { 22 | TestScanner1.calledAt = Date() 23 | delegate?.scanner(self, didFinishWithResults: TestScanner1.results) 24 | } 25 | 26 | func stop() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/TestScanner2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestScanner2.swift 3 | // SwiftyHue 4 | // 5 | // Created by Nils Lattek on 28.05.16. 6 | // 7 | // 8 | 9 | @testable import SwiftyHue 10 | 11 | class TestScanner2: NSObject, Scanner { 12 | weak var delegate: ScannerDelegate? 13 | static var results = [String]() 14 | static var calledAt: Date? 15 | 16 | required init(delegate: ScannerDelegate? = nil) { 17 | self.delegate = delegate 18 | super.init() 19 | } 20 | 21 | func start() { 22 | TestScanner2.calledAt = Date() 23 | delegate?.scanner(self, didFinishWithResults: TestScanner2.results) 24 | } 25 | 26 | func stop() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SwiftyHue iOSTests/bridge_response.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | 0 5 | 6 | http://192.168.1.130:80/ 7 | 8 | urn:schemas-upnp-org:device:Basic:1 9 | Philips hue (192.168.1.130) 10 | Royal Philips Electronics 11 | http://www.philips.com 12 | Philips hue Personal Wireless Lighting 13 | Philips hue bridge 2012 14 | 929000226503 15 | http://www.meethue.com 16 | 001788102201 17 | uuid:2f402f80-da50-11e1-9b23-001788102201 18 | index.html 19 | 20 | 21 | image/png 22 | 48 23 | 48 24 | 24 25 | hue_logo_0.png 26 | 27 | 28 | image/png 29 | 120 30 | 120 31 | 24 32 | hue_logo_3.png 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SwiftyHue macOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftyHue.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | 5 | var str = "Hello, playground" 6 | 7 | protocol SensorConfigProvider { 8 | associatedtype SensorConfigType: SensorConfig 9 | 10 | var config: SensorConfigType {get} 11 | } 12 | 13 | protocol SensorStateProvider { 14 | associatedtype SensorStateType: SensorState 15 | 16 | var state: SensorStateType {get} 17 | } 18 | 19 | func ==(lhs: GenericFlagSensor, rhs: GenericFlagSensor) -> Bool { 20 | return lhs.id != rhs.id 21 | } 22 | 23 | protocol Sensor { 24 | 25 | var id: String {get} 26 | var name: String {get} 27 | 28 | } 29 | 30 | protocol SensorConfig { 31 | 32 | } 33 | 34 | protocol SensorState { 35 | 36 | } 37 | 38 | class GenericFlagSensor: Sensor, SensorConfigProvider, SensorStateProvider, Equatable { 39 | 40 | typealias SensorConfigType = GenericFlagSensorConfig 41 | typealias SensorStateType = GenericFlagSensorState 42 | 43 | var id: String 44 | var name: String 45 | var config: GenericFlagSensorConfig 46 | var state: GenericFlagSensorState 47 | 48 | init() { 49 | 50 | id = "" 51 | name = "" 52 | config = GenericFlagSensorConfig() 53 | state = GenericFlagSensorState() 54 | 55 | } 56 | } 57 | 58 | 59 | 60 | class GenericFlagSensorConfig: SensorConfig { 61 | 62 | init(){} 63 | } 64 | 65 | class GenericFlagSensorState: SensorState { 66 | 67 | init(){} 68 | 69 | } 70 | 71 | // TestCode 72 | 73 | var mySensors: GenericFlagSensor = GenericFlagSensor() 74 | 75 | var nsdict: NSDictionary = ["1": mySensors] 76 | 77 | var dict: [String: GenericFlagSensor] = ["1": mySensors] 78 | 79 | print((nsdict as! [String: GenericFlagSensor]) == dict) 80 | -------------------------------------------------------------------------------- /SwiftyHue.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SwiftyHue.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint SwiftyHue.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "SwiftyHue" 11 | s.version = "0.5.9" 12 | s.summary = "Philips Hue SDK written in swift." 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | s.description = "Philips Hue SDK written in swift. Work in progress." 20 | 21 | s.homepage = "https://github.com/Spriter/SwiftyHue.git" 22 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 23 | s.license = 'MIT' 24 | s.authors = { "Marcel Dittmann" => "marceldittmann@gmx.de", "Jerome Schmitz" => "jerome.schmitz@gmx.net", "Nils Lattek" => "nilslattek@gmail.com" } 25 | s.source = { :git => "https://github.com/Spriter/SwiftyHue.git", :tag => "0.5.9" } 26 | 27 | # s.social_media_url = 'https://twitter.com/' 28 | 29 | s.ios.deployment_target = '9.0' 30 | s.tvos.deployment_target = '9.0' 31 | s.osx.deployment_target = '10.11' 32 | 33 | s.pod_target_xcconfig = { 'ENABLE_TESTABILITY[config=Debug]' => 'YES' } 34 | s.source_files = 'Sources/SwiftyHue.h' 35 | 36 | s.swift_version = '5.0' 37 | 38 | s.subspec 'Base' do |base| 39 | 40 | base.ios.deployment_target = '9.0' 41 | base.tvos.deployment_target = '9.0' 42 | base.watchos.deployment_target = '2.2' 43 | base.osx.deployment_target = '10.11' 44 | 45 | base.source_files = 'Sources/Base/**/*.{h,swift}' 46 | base.dependency 'Alamofire', '4.8.0' 47 | base.dependency 'Gloss', '3.1.0' 48 | end 49 | 50 | s.subspec 'BridgeServices' do |bridgeservices| 51 | bridgeservices.source_files = 'Sources/BridgeServices/**/*.{h,swift}' 52 | 53 | bridgeservices.ios.deployment_target = '9.0' 54 | bridgeservices.tvos.deployment_target = '9.0' 55 | bridgeservices.osx.deployment_target = '10.11' 56 | 57 | bridgeservices.dependency 'Alamofire', '4.8.0' 58 | bridgeservices.dependency 'Gloss', '3.1.0' 59 | bridgeservices.dependency 'CocoaAsyncSocket', '7.6.3' 60 | 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /SwiftyHue.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftyHue.xcodeproj/xcshareddata/xcschemes/SwiftyHue iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SwiftyHue.xcodeproj/xcshareddata/xcschemes/SwiftyHue macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SwiftyHue.xcodeproj/xcshareddata/xcschemes/SwiftyHue tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SwiftyHue.xcodeproj/xcshareddata/xcschemes/SwiftyHue watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /SwiftyHue.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /SwiftyHue.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if ! command -v carthage > /dev/null; then 4 | printf 'Carthage is not installed.\n' 5 | printf 'See https://github.com/Carthage/Carthage for install instructions.\n' 6 | exit 1 7 | fi 8 | 9 | carthage update 10 | --------------------------------------------------------------------------------