Skip to content

Instantly share code, notes, and snippets.

@piotr-sekara
Created August 7, 2018 07:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piotr-sekara/6945bfeda28f5e3253a7446ae2c924bb to your computer and use it in GitHub Desktop.
Save piotr-sekara/6945bfeda28f5e3253a7446ae2c924bb to your computer and use it in GitHub Desktop.
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