/** * * Copyright (c) 2020 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #import "MTRBaseDevice.h" #import #include #include #include #include #include #include #include #include #include #include #include @class MTRDeviceController; // An AttestationResponse command needs to have an attestationChallenge // to make sense of the results. Encode that with a profile-specific tag under // the Apple vendor id. Let's select profile 0xFFFF just because, and use 0xFF // for the actual tag number, so that if someone accidentally casts it to a // uint8 (aka context tag) that will not collide with anything interesting. inline constexpr chip::TLV::Tag kAttestationChallengeTag = chip::TLV::ProfileTag(chip::VendorId::Apple, 0xFFFF, 0xFF); // We have no way to extract the tag value as a single thing, so just do it // manually. inline constexpr unsigned kProfileIdShift = 32; inline constexpr uint64_t kAttestationChallengeTagProfile = chip::TLV::ProfileIdFromTag(kAttestationChallengeTag); inline constexpr uint64_t kAttestationChallengeTagNumber = chip::TLV::TagNumFromTag(kAttestationChallengeTag); inline constexpr uint64_t kAttestationChallengeTagValue = (kAttestationChallengeTagProfile << kProfileIdShift) | kAttestationChallengeTagNumber; NS_ASSUME_NONNULL_BEGIN static inline MTRTransportType MTRMakeTransportType(chip::Transport::Type type) { static_assert(MTRTransportTypeUndefined == (uint8_t) chip::Transport::Type::kUndefined, "MTRTransportType != Transport::Type"); static_assert(MTRTransportTypeUDP == (uint8_t) chip::Transport::Type::kUdp, "MTRTransportType != Transport::Type"); static_assert(MTRTransportTypeBLE == (uint8_t) chip::Transport::Type::kBle, "MTRTransportType != Transport::Type"); static_assert(MTRTransportTypeTCP == (uint8_t) chip::Transport::Type::kTcp, "MTRTransportType != Transport::Type"); return static_cast(type); } @interface MTRBaseDevice () - (instancetype)initWithPASEDevice:(chip::DeviceProxy *)device controller:(MTRDeviceController *)controller; /** * Invalidate the CASE session, so an attempt to getConnectedDevice for this * device id will have to create a new CASE session. Ideally this API will go * away. */ - (void)invalidateCASESession; /** * Whether this device represents a PASE session or not. */ @property (nonatomic, assign, readonly) BOOL isPASEDevice; /** * Controller that that this MTRDevice was gotten from. */ @property (nonatomic, strong, readonly) MTRDeviceController * deviceController; /** * Node id for this MTRDevice. If this device represents a CASE session, this * is set to the node ID of the target node. If this device represents a PASE * session, this is set to the device id of the PASE device. */ @property (nonatomic, assign, readonly) chip::NodeId nodeID; /** * Initialize the device object as a CASE device with the given node id and * controller. This will always succeed, even if there is no such node id on * the controller's fabric, but attempts to actually use the MTRBaseDevice will * fail (asynchronously) in that case. */ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; /** * Create a report, suitable in including in the sort of data structure that * gets passed to MTRDeviceResponseHandler, from a given event header and * already-decoded event data. The data is allowed to be nil in error cases * (e.g. when TLV decoding failed). */ + (NSDictionary *)eventReportForHeader:(const chip::app::EventHeader &)header andData:(id _Nullable)data; /** * Extract a data-value for the given response command from the given response-value * dictionary, encode it to TLV, and return a System::PacketBufferHandle with * the encoded data. * * Will return a null handle and an error if the given response-value does not represent a * data command response or is the wrong response command, or if encoding to TLV fails. */ + (chip::System::PacketBufferHandle)_responseDataForCommand:(NSDictionary *)responseValue clusterID:(chip::ClusterId)clusterID commandID:(chip::CommandId)commandID error:(NSError * __autoreleasing *)error; /** * Like the public invokeCommandWithEndpointID but allows passing through a * serverSideProcessingTimeout and controlling whether we log the call (so we * can not log when the call is not actually originating with MTRBaseDevice). */ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID commandID:(NSNumber *)commandID commandFields:(id)commandFields timedInvokeTimeout:(NSNumber * _Nullable)timeoutMs serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout logCall:(BOOL)logCall queue:(dispatch_queue_t)queue completion:(MTRDeviceResponseHandler)completion; /** * Like the public invokeCommandWithEndpointID but: * * 1) Allows passing through a serverSideProcessingTimeout. * 2) Expects one of the command payload structs as commandPayload * 3) On success, returns an instance of responseClass via the completion (or * nil if there is no responseClass, which indicates a status-only command). */ - (void)_invokeKnownCommandWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID commandID:(NSNumber *)commandID commandPayload:(id)commandPayload timedInvokeTimeout:(NSNumber * _Nullable)timeout serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout responseClass:(Class _Nullable)responseClass queue:(dispatch_queue_t)queue completion:(void (^)(id _Nullable response, NSError * _Nullable error))completion; /** * Like the public subscribeToAttributesWithEndpointID but: * * 1) Takes a concrete attribute path (not nullable). * 2) Requires non-nil MTRSubscribeParams (since that's what MTRBaseClusters * have anyway). * 3) For the report handler, hands out the right type of value for the given * attribute path. This means we have to know the type ofthe attribute. * * The attribute path is not represented as MTRAttributePath just because it's * probably less code to pass in the three numbers instead of creating an object * at all the (numerous) callsites. */ - (void)_subscribeToKnownAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID params:(MTRSubscribeParams *)params queue:(dispatch_queue_t)queue reportHandler:(void (^)(id _Nullable value, NSError * _Nullable error))reportHandler subscriptionEstablished:(MTRSubscriptionEstablishedHandler _Nullable)subscriptionEstablished; /** * Like the public readAttributesWithEndpointID but: * * 1) Takes a concrete attribute path (not nullable). * 2) For the completion handler, hands out the right type of value for the given * attribute path. This means we have to know the type ofthe attribute. * 3) Only calls the completion with a single value. * * The attribute path is not represented as MTRAttributePath just because it's * probably less code to pass in the three numbers instead of creating an object * at all the (numerous) callsites. */ - (void)_readKnownAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params queue:(dispatch_queue_t)queue completion:(void (^)(id _Nullable value, NSError * _Nullable error))completion; /** * Same as the public -readAttributePaths:eventPaths:params:queue:completion: except also include the data version in the data-value dictionary in the response dictionary, if the includeDataVersion argument is set to YES. */ - (void)readAttributePaths:(NSArray * _Nullable)attributePaths eventPaths:(NSArray * _Nullable)eventPaths params:(MTRReadParams * _Nullable)params includeDataVersion:(BOOL)includeDataVersion queue:(dispatch_queue_t)queue completion:(MTRDeviceResponseHandler)completion; /** * Same as the public version, except for logging. For use from MTRDevice only. */ - (void)_writeAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID value:(id)value timedWriteTimeout:(NSNumber * _Nullable)timeoutMs queue:(dispatch_queue_t)queue completion:(MTRDeviceResponseHandler)completion; @end @interface MTRClusterPath () - (instancetype)initWithPath:(const chip::app::ConcreteClusterPath &)path; @end @interface MTRAttributePath () - (instancetype)initWithPath:(const chip::app::ConcreteDataAttributePath &)path; @end @interface MTREventPath () - (instancetype)initWithPath:(const chip::app::ConcreteEventPath &)path; @end @interface MTRCommandPath () - (instancetype)initWithPath:(const chip::app::ConcreteCommandPath &)path; @end @interface MTRAttributeReport () - (instancetype)initWithPath:(const chip::app::ConcreteDataAttributePath &)path value:(id _Nullable)value error:(NSError * _Nullable)error; @end @interface MTREventReport () - (instancetype)initWithPath:(const chip::app::ConcreteEventPath &)path error:(NSError *)error; - (instancetype)initWithPath:(const chip::app::ConcreteEventPath &)path eventNumber:(NSNumber *)eventNumber priority:(chip::app::PriorityLevel)priority timestamp:(const chip::app::Timestamp &)timestamp value:(id)value; @end @interface MTRAttributeRequestPath () - (void)convertToAttributePathParams:(chip::app::AttributePathParams &)params; @end @interface MTREventRequestPath () - (void)convertToEventPathParams:(chip::app::EventPathParams &)params; @end // Exported utility function // Convert TLV data into data-value dictionary as described in MTRDeviceResponseHandler NSDictionary * _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV(chip::TLV::TLVReader * data, NSNumber * _Nullable dataVersion = nil); // Convert a data-value dictionary as described in MTRDeviceResponseHandler into // TLV Data with an anonymous tag. This method assumes the encoding of the // value fits in a single UDP MTU; for lists this method might need to be used // on each list item separately. NSData * _Nullable MTREncodeTLVFromDataValueDictionary(NSDictionary * value, NSError * __autoreleasing * error); NS_ASSUME_NONNULL_END