diff --git a/Sources/HAP/Base/Accessory.swift b/Sources/HAP/Base/Accessory.swift index ae07fbbb..a9e5b645 100644 --- a/Sources/HAP/Base/Accessory.swift +++ b/Sources/HAP/Base/Accessory.swift @@ -27,6 +27,8 @@ struct AIDGenerator: Sequence, IteratorProtocol, Codable { open class Accessory: JSONSerializable { public weak var device: Device? + public weak var delegate: AccessoryDelegate? + internal var aid: InstanceID = 0 public let type: AccessoryType public let info: Service.Info @@ -103,11 +105,12 @@ open class Accessory: JSONSerializable { /// Characteristic's value was changed by controller. Used for bubbling up /// to the device, which will notify the delegate. - open func characteristic(_ characteristic: GenericCharacteristic, + internal func characteristic(_ characteristic: GenericCharacteristic, ofService service: Service, didChangeValue newValue: T?) { device?.characteristic(characteristic, ofService: service, ofAccessory: self, didChangeValue: newValue) - } + delegate?.characteristic(characteristic, ofService: service, didChangeValue: newValue) + } public func serialized() -> [String: JSONValueType] { [ @@ -116,3 +119,39 @@ open class Accessory: JSONSerializable { ] } } + +/// A HAP `Characteristic` calls the methods of this delegate to report +/// set/gets from a HAP controller. +/// +/// Implement this protocol in an accessory-specific object (such as a subclass +/// of a given accessory) in order to make the accessory react accordingly. +/// For example, you might want to update the value of certain characteristics +/// if the HAP controller is showing interest or makes a change. +/// +public protocol AccessoryDelegate: AnyObject { + /// Characteristic's value was changed by controller. Used for notifying + func characteristic( + _ characteristic: GenericCharacteristic, + ofService: Service, + didChangeValue: T?) + /// Characteristic's value was observed by controller. Used for lazy updating + func characteristic( + _ characteristic: GenericCharacteristic, + ofService: Service, + didGetValue: T?) + + /// Tells the delegate that identification of the device was requested. + /// + /// When the user configures a device, there might be multiple similar + /// devices. In order to identify the individual device, HAP + /// accommodates for an identification event. When possible, you should + /// make the physical device emit sound and/or light for the user to be + /// able to identify the device. + func didRequestIdentification() + +} + +public extension AccessoryDelegate { + // Default implementation ignores the request + func didRequestIdentification() { } +} diff --git a/Sources/HAP/Base/Characteristic.swift b/Sources/HAP/Base/Characteristic.swift index a946b98d..d2ffb240 100644 --- a/Sources/HAP/Base/Characteristic.swift +++ b/Sources/HAP/Base/Characteristic.swift @@ -18,7 +18,8 @@ protocol Characteristic: class, JSONSerializable { var iid: InstanceID { get set } var type: CharacteristicType { get } var permissions: [CharacteristicPermission] { get } - func getValue() -> JSONValueType? + func jsonValue() -> JSONValueType? + func getValue(fromChannel: Channel?) -> JSONValueType? func setValue(_: Any?, fromChannel: Channel?) throws var description: String? { get } var format: CharacteristicFormat? { get } @@ -41,7 +42,7 @@ extension Characteristic { if permissions.contains(.read) { // TODO: fixit - serialized["value"] = getValue() ?? 0 //NSNull() + serialized["value"] = jsonValue() ?? 0 //NSNull() } if let description = description { serialized["description"] = description } @@ -89,13 +90,33 @@ public class GenericCharacteristic: Characteristic, if let device = service?.accessory?.device { device.fireCharacteristicChangeEvent(self) } + if let service = self.service, + let accessory = service.accessory, + let device = accessory.device { + device.characteristic(self, + ofService: service, + ofAccessory: accessory, + didChangeValue: _value) + } } } - func getValue() -> JSONValueType? { + func jsonValue() -> JSONValueType? { value?.jsonValueType } + // Get Value for HAP controller + func getValue(fromChannel channel: Channel?) -> JSONValueType? { + let currentValue = _value + DispatchQueue.main.async { [weak self] in + if let self = self, let service = self.service { + service.accessory?.delegate?.characteristic(self, ofService: service, didGetValue: currentValue) + } + } + return jsonValue() + } + + // Set Value by HAP controller func setValue(_ newValue: Any?, fromChannel channel: Channel?) throws { switch newValue { case let some?: diff --git a/Sources/HAP/Base/Info.swift b/Sources/HAP/Base/Info.swift index d7c9d8a5..c6dd8131 100644 --- a/Sources/HAP/Base/Info.swift +++ b/Sources/HAP/Base/Info.swift @@ -22,6 +22,7 @@ extension Service { if characteristic === identify { if let accessory = accessory { accessory.device?.delegate?.didRequestIdentificationOf(accessory) + accessory.delegate?.didRequestIdentification() } } super.characteristic(characteristic, didChangeValue: newValue) diff --git a/Sources/HAP/Endpoints/characteristics().swift b/Sources/HAP/Endpoints/characteristics().swift index b844094b..191db6e9 100644 --- a/Sources/HAP/Endpoints/characteristics().swift +++ b/Sources/HAP/Endpoints/characteristics().swift @@ -56,7 +56,7 @@ func characteristics(device: Device, channel: Channel, request: HTTPRequest) -> } var value: Protocol.Value? - switch characteristic.getValue() { + switch characteristic.getValue(fromChannel: channel) { case let _value as Double: value = .double(_value) case let _value as Float: value = .double(Double(_value)) case let _value as UInt8: value = .int(Int(_value)) diff --git a/Sources/HAP/Utils/Event.swift b/Sources/HAP/Utils/Event.swift index d50d9b0e..d4a17650 100644 --- a/Sources/HAP/Utils/Event.swift +++ b/Sources/HAP/Utils/Event.swift @@ -62,7 +62,7 @@ struct Event { guard let aid = char.service?.accessory?.aid else { throw Error.characteristicWithoutAccessory } - payload.append(["aid": aid, "iid": char.iid, "value": char.getValue() ?? NSNull()]) + payload.append(["aid": aid, "iid": char.iid, "value": char.jsonValue() ?? NSNull()]) } let serialized = ["characteristics": payload] guard let body = try? JSONSerialization.data(withJSONObject: serialized, options: []) else { diff --git a/Tests/HAPTests/CharacteristicTests.swift b/Tests/HAPTests/CharacteristicTests.swift index 5270ee15..cfd688c2 100644 --- a/Tests/HAPTests/CharacteristicTests.swift +++ b/Tests/HAPTests/CharacteristicTests.swift @@ -13,7 +13,7 @@ class CharacteristicTests: XCTestCase { func testReadOptionalValueType() { let characteristic = GenericCharacteristic(type: .identify, value: false) - guard let value = characteristic.getValue() as? Bool? else { + guard let value = characteristic.getValue(fromChannel: nil) as? Bool? else { XCTFail("Could not get value") return } @@ -36,7 +36,7 @@ class CharacteristicTests: XCTestCase { } catch { XCTFail("Could not set value: \(error)") } - guard let value = characteristic.getValue() as? Bool? else { + guard let value = characteristic.getValue(fromChannel: nil) as? Bool? else { XCTFail("Could not get value") return } diff --git a/libsodium.23.dylib b/libsodium.23.dylib new file mode 100755 index 00000000..02f5281d Binary files /dev/null and b/libsodium.23.dylib differ