Created
August 7, 2018 07:57
-
-
Save piotr-sekara/6945bfeda28f5e3253a7446ae2c924bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum CharacteristicCellType { | |
case Default | |
case Slider | |
case Switch | |
case Segmented | |
} | |
extension HMCharacteristic { | |
fileprivate struct Constants { | |
static let valueFormatter = NumberFormatter() | |
static let numericFormats = [ | |
HMCharacteristicMetadataFormatInt, | |
HMCharacteristicMetadataFormatFloat, | |
HMCharacteristicMetadataFormatUInt8, | |
HMCharacteristicMetadataFormatUInt16, | |
HMCharacteristicMetadataFormatUInt32, | |
HMCharacteristicMetadataFormatUInt64 | |
] | |
} | |
var supportsEventNotification: Bool { | |
return self.properties.contains(HMCharacteristicPropertySupportsEventNotification) | |
} | |
var localizedCharacteristicType: String { | |
var type = self.localizedDescription | |
var localizedDescription: NSString? = nil | |
if isReadOnly { | |
localizedDescription = NSLocalizedString("Read Only", comment: "Read Only") as NSString | |
} | |
else if isWriteOnly { | |
localizedDescription = NSLocalizedString("Write Only", comment: "Write Only") as NSString | |
} | |
if let localizedDescription = localizedDescription { | |
type = type + " (\(localizedDescription))" | |
} | |
return type | |
} | |
var isInteger: Bool { | |
return self.isNumeric && !self.isFloatingPoint | |
} | |
var isNumeric: Bool { | |
guard let metadata = metadata else { return false } | |
guard let format = metadata.format else { return false } | |
return Constants.numericFormats.contains(format) | |
} | |
var isBoolean: Bool { | |
guard let metadata = metadata else { return false } | |
return metadata.format == HMCharacteristicMetadataFormatBool | |
} | |
var isTextWritable: Bool { | |
guard let metadata = metadata else { return false } | |
return metadata.format == HMCharacteristicMetadataFormatString && properties.contains(HMCharacteristicPropertyWritable) | |
} | |
var isFloatingPoint: Bool { | |
guard let metadata = metadata else { return false } | |
return metadata.format == HMCharacteristicMetadataFormatFloat | |
} | |
var isReadOnly: Bool { | |
return !properties.contains(HMCharacteristicPropertyWritable) && | |
properties.contains(HMCharacteristicPropertyReadable) | |
} | |
var canRead: Bool { | |
return properties.contains(HMCharacteristicPropertyReadable) | |
} | |
var isWriteOnly: Bool { | |
return !properties.contains(HMCharacteristicPropertyReadable) && | |
properties.contains(HMCharacteristicPropertyWritable) | |
} | |
var hasPredeterminedValueDescriptions: Bool { | |
guard let number = self.value as? Int else { return false } | |
return self.predeterminedValueDescriptionForNumber(number) != nil | |
} | |
var isIdentify: Bool { | |
return self.characteristicType == HMCharacteristicTypeIdentify | |
} | |
var localizedUnitDecoration: String { | |
if let units = self.metadata?.units { | |
switch units { | |
case HMCharacteristicMetadataUnitsCelsius: | |
return NSLocalizedString("℃", comment: "Degrees Celsius") | |
case HMCharacteristicMetadataUnitsArcDegree: | |
return NSLocalizedString("º", comment: "Arc Degrees") | |
case HMCharacteristicMetadataUnitsFahrenheit: | |
return NSLocalizedString("℉", comment: "Degrees Fahrenheit") | |
case HMCharacteristicMetadataUnitsPercentage: | |
return NSLocalizedString("%", comment: "Percentage") | |
default: break | |
} | |
} | |
return "" | |
} | |
var cellType: CharacteristicCellType { | |
if self.isReadOnly { | |
return .Default | |
} else if self.isBoolean { | |
return .Switch | |
} else if self.hasPredeterminedValueDescriptions { | |
return .Segmented | |
} else if self.isNumeric { | |
return .Slider | |
} | |
return .Default | |
} | |
var characteristicDescription: String { | |
if isCustomCharacteristic { | |
return metadata?.manufacturerDescription ?? localizedDescription | |
} else { | |
return localizedDescription | |
} | |
} | |
// Methods | |
func predeterminedValueDescriptionForNumber(_ number: Int) -> String? { | |
switch self.characteristicType { | |
case HMCharacteristicTypePowerState, HMCharacteristicTypeInputEvent, HMCharacteristicTypeOutputState: | |
if Bool(number as NSNumber) { | |
return NSLocalizedString("On", comment: "On") | |
} | |
else { | |
return NSLocalizedString("Off", comment: "Off") | |
} | |
case HMCharacteristicTypeOutletInUse, HMCharacteristicTypeMotionDetected, HMCharacteristicTypeAdminOnlyAccess, HMCharacteristicTypeAudioFeedback, HMCharacteristicTypeObstructionDetected: | |
if Bool(number as NSNumber) { | |
return NSLocalizedString("Yes", comment: "Yes") | |
} | |
else { | |
return NSLocalizedString("No", comment: "No") | |
} | |
case HMCharacteristicTypeTargetDoorState, HMCharacteristicTypeCurrentDoorState: | |
if let doorState = HMCharacteristicValueDoorState(rawValue: number) { | |
switch doorState { | |
case .open: | |
return NSLocalizedString("Open", comment: "Open") | |
case .opening: | |
return NSLocalizedString("Opening", comment: "Opening") | |
case .closed: | |
return NSLocalizedString("Closed", comment: "Closed") | |
case .closing: | |
return NSLocalizedString("Closing", comment: "Closing") | |
case .stopped: | |
return NSLocalizedString("Stopped", comment: "Stopped") | |
} | |
} | |
case HMCharacteristicTypePositionState: | |
if let positionState = HMCharacteristicValuePositionState(rawValue: number) { | |
switch positionState { | |
case .closing: | |
return NSLocalizedString("Closing", comment: "Closing") | |
case .opening: | |
return NSLocalizedString("Opening", comment: "Opening") | |
case .stopped: | |
return NSLocalizedString("Stopped", comment: "Stopped") | |
} | |
} | |
case HMCharacteristicTypeTargetHeatingCooling: | |
if let mode = HMCharacteristicValueHeatingCooling(rawValue: number) { | |
switch mode { | |
case .off: | |
return NSLocalizedString("Off", comment: "Off") | |
case .heat: | |
return NSLocalizedString("Heat", comment: "Heat") | |
case .cool: | |
return NSLocalizedString("Cool", comment: "Cool") | |
case .auto: | |
return NSLocalizedString("Auto", comment: "Auto") | |
} | |
} | |
case HMCharacteristicTypeCurrentHeatingCooling: | |
if let mode = HMCharacteristicValueHeatingCooling(rawValue: number) { | |
switch mode { | |
case .off: | |
return NSLocalizedString("Off", comment: "Off") | |
case .heat: | |
return NSLocalizedString("Heating", comment: "Heating") | |
case .cool: | |
return NSLocalizedString("Cooling", comment: "Cooling") | |
case .auto: | |
return NSLocalizedString("Auto", comment: "Auto") | |
} | |
} | |
case HMCharacteristicTypeTargetLockMechanismState, HMCharacteristicTypeCurrentLockMechanismState: | |
if let lockState = HMCharacteristicValueLockMechanismState(rawValue: number) { | |
switch lockState { | |
case .unsecured: | |
return NSLocalizedString("Unsecured", comment: "Unsecured") | |
case .secured: | |
return NSLocalizedString("Secured", comment: "Secured") | |
case .unknown: | |
return NSLocalizedString("Unknown", comment: "Unknown") | |
case .jammed: | |
return NSLocalizedString("Jammed", comment: "Jammed") | |
} | |
} | |
case HMCharacteristicTypeTemperatureUnits: | |
if let unit = HMCharacteristicValueTemperatureUnit(rawValue: number) { | |
switch unit { | |
case .celsius: | |
return NSLocalizedString("Celsius", comment: "Celsius") | |
case .fahrenheit: | |
return NSLocalizedString("Fahrenheit", comment: "Fahrenheit") | |
} | |
} | |
case HMCharacteristicTypeLockMechanismLastKnownAction: | |
if let lastKnownAction = HMCharacteristicValueLockMechanismLastKnownAction(rawValue: number) { | |
switch lastKnownAction { | |
case .securedUsingPhysicalMovementInterior: | |
return NSLocalizedString("Interior Secured", comment: "Interior Secured") | |
case .unsecuredUsingPhysicalMovementInterior: | |
return NSLocalizedString("Exterior Unsecured", comment: "Exterior Unsecured") | |
case .securedUsingPhysicalMovementExterior: | |
return NSLocalizedString("Exterior Secured", comment: "Exterior Secured") | |
case .unsecuredUsingPhysicalMovementExterior: | |
return NSLocalizedString("Exterior Unsecured", comment: "Exterior Unsecured") | |
case .securedWithKeypad: | |
return NSLocalizedString("Keypad Secured", comment: "Keypad Secured") | |
case .unsecuredWithKeypad: | |
return NSLocalizedString("Keypad Unsecured", comment: "Keypad Unsecured") | |
case .securedRemotely: | |
return NSLocalizedString("Secured Remotely", comment: "Secured Remotely") | |
case .unsecuredRemotely: | |
return NSLocalizedString("Unsecured Remotely", comment: "Unsecured Remotely") | |
case .securedWithAutomaticSecureTimeout: | |
return NSLocalizedString("Secured Automatically", comment: "Secured Automatically") | |
case .securedUsingPhysicalMovement: | |
return NSLocalizedString("Secured Using Physical Movement", comment: "Secured Using Physical Movement") | |
case .unsecuredUsingPhysicalMovement: | |
return NSLocalizedString("Unsecured Using Physical Movement", comment: "Unsecured Using Physical Movement") | |
} | |
} | |
case HMCharacteristicTypeRotationDirection: | |
if let rotationDirection = HMCharacteristicValueRotationDirection(rawValue: number) { | |
switch rotationDirection { | |
case .clockwise: | |
return NSLocalizedString("Clockwise", comment: "Clockwise") | |
case .counterClockwise: | |
return NSLocalizedString("Counter Clockwise", comment: "Counter Clockwise") | |
} | |
} | |
case HMCharacteristicTypeAirParticulateSize: | |
if let size = HMCharacteristicValueAirParticulateSize(rawValue: number) { | |
switch size { | |
case .size10: | |
return NSLocalizedString("Size 10", comment: "Size 10") | |
case .size2_5: | |
return NSLocalizedString("Size 2.5", comment: "Size 2.5") | |
} | |
} | |
case HMCharacteristicTypePositionState: | |
if let state = HMCharacteristicValuePositionState(rawValue: number) { | |
switch state { | |
case .opening: | |
return NSLocalizedString("Opening", comment: "Opening") | |
case .closing: | |
return NSLocalizedString("Closing", comment: "Closing") | |
case .stopped: | |
return NSLocalizedString("Stopped", comment: "Stopped") | |
} | |
} | |
case HMCharacteristicTypeCurrentSecuritySystemState: | |
if let state = HMCharacteristicValueCurrentSecuritySystemState(rawValue: number) { | |
switch state { | |
case .awayArm: | |
return NSLocalizedString("Away", comment: "Away") | |
case .stayArm: | |
return NSLocalizedString("Home", comment: "Home") | |
case .nightArm: | |
return NSLocalizedString("Night", comment: "Night") | |
case .disarmed: | |
return NSLocalizedString("Disarm", comment: "Disarm") | |
case .triggered: | |
return NSLocalizedString("Triggered", comment: "Triggered") | |
} | |
} | |
case HMCharacteristicTypeTargetSecuritySystemState: | |
if let state = HMCharacteristicValueTargetSecuritySystemState(rawValue: number) { | |
switch state { | |
case .awayArm: | |
return NSLocalizedString("Away", comment: "Away") | |
case .stayArm: | |
return NSLocalizedString("Home", comment: "Home") | |
case .nightArm: | |
return NSLocalizedString("Night", comment: "Night") | |
case .disarm: | |
return NSLocalizedString("Disarm", comment: "Disarm") | |
} | |
} | |
case HMCharacteristicTypeAirQuality: | |
if let state = HMCharacteristicValueAirQuality(rawValue: number) { | |
switch state { | |
case .excellent: | |
return NSLocalizedString("Excellent", comment: "Excellent") | |
case .fair: | |
return NSLocalizedString("Fair", comment: "Fair") | |
case .good: | |
return NSLocalizedString("Good", comment: "Good") | |
case .inferior: | |
return NSLocalizedString("Inferior", comment: "Inferior") | |
case .poor: | |
return NSLocalizedString("Poor", comment: "Poor") | |
case .unknown: | |
return NSLocalizedString("Unknown", comment: "Unknown") | |
} | |
} | |
case HMCharacteristicTypeLockPhysicalControls: | |
if let state = HMCharacteristicValueLockPhysicalControlsState(rawValue: number) { | |
switch state { | |
case .locked: | |
return NSLocalizedString("Control lock enabled", comment: "Control lock enabled") | |
case .notLocked: | |
return NSLocalizedString("Control lock disabled", comment: "Control lock disabled") | |
} | |
} | |
case HMCharacteristicTypeTargetFanState: | |
if let state = HMCharacteristicValueTargetFanState(rawValue: number) { | |
switch state { | |
case .automatic: | |
return NSLocalizedString("Auto", comment: "Auto") | |
case .manual: | |
return NSLocalizedString("Manual", comment: "Manual") | |
} | |
} | |
case HMCharacteristicTypeCurrentFanState: | |
if let state = HMCharacteristicValueCurrentFanState(rawValue: number) { | |
switch state { | |
case .active: | |
return NSLocalizedString("Blowing Air", comment: "Blowing Air") | |
case .idle: | |
return NSLocalizedString("Idle", comment: "Idle") | |
case .inactive: | |
return NSLocalizedString("Inactive", comment: "Inactive") | |
} | |
} | |
case HMCharacteristicTypeActive: | |
if let state = HMCharacteristicValueActivationState(rawValue: number) { | |
switch state { | |
case .active: | |
return NSLocalizedString("Active", comment: "Active") | |
case .inactive: | |
return NSLocalizedString("Inactive", comment: "Inactive") | |
} | |
} | |
case HMCharacteristicTypeSwingMode: | |
if let state = HMCharacteristicValueSwingMode(rawValue: number) { | |
switch state { | |
case .disabled: | |
return NSLocalizedString("Swing disabled", comment: "Swing disabled") | |
case .enabled: | |
return NSLocalizedString("Swing enabled", comment: "Swing enabled") | |
} | |
} | |
default: | |
break | |
} | |
return nil | |
} | |
func localizedDescriptionForValue(_ value: AnyObject) -> String { | |
if self.isWriteOnly { | |
return NSLocalizedString("Write-Only Characteristic", comment: "Write-Only Characteristic") | |
} | |
else if self.isBoolean { | |
if let boolValue = value.boolValue { | |
return boolValue ? NSLocalizedString("On", comment: "On") : NSLocalizedString("Off", comment: "Off") | |
} | |
} | |
if let number = value as? Double { | |
if number.truncatingRemainder(dividingBy: 1) == 0 { | |
let numberInt = value as? Int | |
if let predeterminedValueString = self.predeterminedValueDescriptionForNumber(numberInt!) { | |
return predeterminedValueString | |
} | |
if let stepValue = self.metadata?.stepValue { | |
Constants.valueFormatter.minimumFractionDigits = Int(log10(1.0 / stepValue.doubleValue)) | |
if let string = Constants.valueFormatter.string(from: numberInt as! NSNumber) { | |
return string + self.localizedUnitDecoration | |
} | |
} | |
} else { | |
return "\(number)" | |
} | |
} | |
return "\(value)" | |
} | |
func localizedValueDescription() -> String { | |
return self.localizedDescriptionForValue(self.value as AnyObject) | |
} | |
func textColorForValue(_ value: AnyObject) -> UIColor? { | |
if value is NSNull { return nil } | |
switch self.characteristicType { | |
case HMCharacteristicTypeAirQuality: | |
if let state = HMCharacteristicValueAirQuality(rawValue: value as! Int) { | |
switch state { | |
case .excellent: | |
return UIColor.green | |
case .good: | |
return UIColor(red: 192/255, green: 239/255, blue: 84/255, alpha: 1) | |
case .fair: | |
return UIColor.sunflower() | |
case .inferior: | |
return UIColor.carrot() | |
case .poor: | |
return UIColor.red | |
case .unknown : | |
return UIColor.white | |
} | |
} | |
default: | |
return nil | |
} | |
return nil | |
} | |
func valueTextColor() -> UIColor? { | |
return self.textColorForValue(self.value as AnyObject) | |
} | |
func canWriteValue(_ value: NSNumber) -> Bool { | |
if self.isBoolean { | |
if value.boolValue == true || value.boolValue == false { | |
return true | |
} | |
} else if self.isInteger { | |
return (self.metadata?.minimumValue?.intValue)! <= value.intValue && (self.metadata?.maximumValue?.intValue)! >= value.intValue | |
} else if self.isNumeric { | |
return (self.metadata?.minimumValue?.floatValue)! <= value.floatValue && (self.metadata?.maximumValue?.floatValue)! >= value.floatValue | |
} | |
return false | |
} | |
func tryToEnableNotification(completion: ((Error?) -> Void)?) { | |
if self.supportsEventNotification && !self.isNotificationEnabled { | |
self.enableNotification(true, completionHandler: { error in | |
if error != nil { | |
print("Something goes wrong while trying to enable primary characteristic notification \(error!)") | |
} | |
completion?(error) | |
}) | |
} | |
} | |
func tryToMakeInitialValueRead(completion: ((Error?) -> Void)?) { | |
if self.canRead { | |
self.readValue(completionHandler: { error in | |
if error != nil { | |
print("Something goes wrong while reading value of characteristic \(error!)") | |
} | |
completion?(error) | |
}) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment