Device Details UI BizBundle

Last Updated on : 2023-07-13 07:14:40download

Device Details UI BizBundle supports the following features:

  • Edit device information, such as the device icon, device name, and room.
  • Query device information, such as device ID, signal strength, and more.
  • Configure backup networks.
  • Check device networks.
  • Notify users of devices getting offline.
  • Add shortcuts to the home screen.
  • Show remote controls.
  • Remove a device.

Integrate with the UI BizBundle

Integrate with Pod

Add the components of the Device Details UI BizBundle to Podfile and run the command pod update.

source "https://github.com/tuya/tuya-private-specs"
source 'https://cdn.cocoapods.org/'

target 'your_target_name' do
# Adds the Device Details UI BizBundle.
pod 'ThingSmartDeviceDetailBizBundle'
# Imports Bluetooth components, if you need Bluetooth features
# pod 'ThingBluetoothInterface'
# pod 'ThingBLEMeshInterfaceImpl'
end

Permissions

The UI BizBundle supports the upload of device icons. To implement this feature, the permissions on the system album and camera must be granted. This requires specific declarations of privacy and permissions from Apple.

Add the following permission declaration to info.plist of your project:

<!-- Album -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Authorizes the app to access the album of the mobile phone</string>
<!-- Camera -->
<key>NSCameraUsageDescription</key>
<string>Authorizes the app to access the camera of the mobile phone</string>

If a Bluetooth device is connected, or you use services that rely on Bluetooth capabilities such as creating groups or removing devices, you need to add the following permission declaration to info.plist of your project:

<!-- Bluetooth -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This allows the app to find and connect to Bluetooth accessories. This app can also locate Bluetooth devices by using Bluetooth. </string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>If you want to add or use a Bluetooth device, please turn on the Bluetooth feature of the mobile phone. </string>

Configuration file

  1. Create the configuration file deviceDetailConfig.json in the main project. If the file already exists, skip the creation.

  2. Add the following content to deviceDetailConfig.json:

    [
        {
            "name": "headerSection",
            "items": [
                {
                    "cellType": "header"
                },{
                    "cellType": "device_info"
                },{
                    "cellType": "net_setting"
                },{
                    "cellType":"group_edit_devices"
                }
            ]
        },{
            "name": "offLineWarnSection",
            "header": {
                "cellType":"section_off_line_warn"
            },
            "items": [
                {
                    "cellType" : "off_line_warn"
                }
            ]
        },{
            "name": "otherSection",
            "header": {
                "cellType":"section_other"
            },
            "items": [
                {
                    "cellType":"bind_multi_control_link"
                },
                {
                    "cellType":"group_create"
                },
                {
                    "cellType":"help_and_feedback"
                },
                {
                    "cellType":"add_icon_to_home_screen"
                },
                {
                    "cellType":"show_infrared_gateway_sub_device"
                },
                {
                    "cellType":"check_device_network"
                },
                {
                    "cellType":"check_firmware_update"
                }
            ]
        },{
            "name": "footerSection",
            "items": [
                {
                    "cellType":"footer"
                }
            ],
            "margin": {
                "top": 16
            }
        }
    ]
    
  3. Customize device details.

    The sequence of the item parameter in the deviceDetail array determines the sequence in which each item is displayed on the device details page. If the item is removed, the associated device feature will also be removed from the device details page.

    Item of deviceDetail Device feature
    header View and modify the device icon, device name, and location.
    device_info The device information.
    net_setting Display backup Wi-Fi networks.
    off_line_warn Notify users of devices getting offline.
    section_other This is a section header and serves no purpose.
    group_edit_devices Edit a group.
    group_create Create a group.
    add_icon_to_home_screen Add shortcuts to the home screen.
    show_infrared_gateway_sub_device Show remote controls.
    footer Remove a device.

Service protocol

Dependent services (to be implemented)

ThingSmartHomeDataProtocol is deprecated and ThingFamilyProtocol is recommended.

/**
To use this API method to get the current home data, you must use 'updateCurrentHomeId:' API of the protocol when the current home ID is updated.
Returns the current home. If the current user does not have a home, nil is returned.


@return ThingSmartHome
*/
- (ThingSmartHome *)getCurrentHome;

Objective-C:

#import <ThingSmartBizCore/ThingSmartBizCore.h>
#import <ThingModuleServices/ThingSmartHomeDataProtocol.h>

- (void)initCurrentHome {
    // Registers the protocol to be implemented.
    [[ThingSmartBizCore sharedInstance] registerService:@protocol(ThingSmartHomeDataProtocol) withInstance:self];
}

// Implements the protocol method.
- (ThingSmartHome *)getCurrentHome {
    ThingSmartHome *home = [ThingSmartHome homeWithHomeId:@"Current home ID"];
    return home;
}

Swift:

import ThingSmartDeviceKit

class ThingMessageCenterTest: NSObject,ThingSmartHomeDataProtocol{

    func test() {
        ThingSmartBizCore.sharedInstance().registerService(ThingSmartHomeDataProtocol.self, withInstance: self)
    }

    func getCurrentHome() -> ThingSmartHome! {
        let home = ThingSmartHome.init(homeId: 111)
        return home
    }

}

Implement optional features

  • Bluetooth feature. After you add relevant dependencies and declarations in Podfile and Info.plist, you can call the Bluetooth start method before using it.

    #import <ThingBluetoothInterface/ThingBluetoothInterface.h>
    
    [TheBLEInterfaceService startScan];
    

Provide services (make API requests)

Method Parameter
Navigate to the network detection page The device ID.
Navigate to device details by means of push ThingSmartDeviceModel or ThingSmartGroupModel.
Check whether to display infrared sub-devices devId and spaceId.
Check whether displaying infrared sub-devices is enabled. devId and spaceId.
Display the notification of infrared sub-device status changes /
/// Navigates to the page of network detection.
/// @param devId The device ID.
- (void)gotoDeviceDetailNetworkViewControllerWithDeviceId:(NSString *)devId;

/// Navigates to device details by means of push.
/// @param device The device.
/// @param group The group if any. It is optional.
- (void)gotoDeviceDetailDetailViewControllerWithDevice:(ThingSmartDeviceModel *)device group:(ThingSmartGroupModel *)group;

@optional
/// Checks whether to display infrared sub-devices.
/// @param devId The device ID. If a non-infrared gateway or sub-device is passed in, YES is returned.
/// @param spaceId The space ID.
/// @return The return value. Valid values: YES: display.
NO: not display.
- (BOOL)getInfraredDisplayStatusWithDevId:(nullable NSString *)devId spaceId:(long long)spaceId;

@optional
/// Checks whether displaying infrared sub-devices is enabled.
/// @param devId The device ID.
/// @param spaceId The space ID.
/// @return The return value. Valid values: YES: display.
NO: not display.
- (BOOL)cacheOfInfraredDisplayStatusWithDevId:(nullable NSString *)devId spaceId:(long long)spaceId;

@optional
/// Displays the notification of infrared sub-device status changes.
- (nullable NSNotificationName)subDeviceDisplayStatusChangeNotificationName;

Objective-C:

#import <ThingSmartBizCore/ThingSmartBizCore.h>
#import <ThingModuleServices/ThingDeviceDetailProtocol.h>

id<ThingDeviceDetailProtocol> impl = [[ThingSmartBizCore sharedInstance] serviceOfProtocol:@protocol(ThingDeviceDetailProtocol)];

// Navigates to the page of network detection.
[impl gotoDeviceDetailNetworkViewControllerWithDeviceId:@"Device ID"];

    // Navigates to device details by means of push.

    // For a device, 'new ThingSmartDevice' is called.
ThingSmartDevice * device = [ThingSmartDevice deviceWithDeviceId:@"Device ID"]
    [impl gotoDeviceDetailDetailViewControllerWithDevice: device.deviceModel group: nil];
    // For a group, 'new ThingSmartGroup' is called.
ThingSmartGroup * group = [ThingSmartDevice groupWithGroupId:@"Group ID"]
    [impl gotoDeviceDetailDetailViewControllerWithDevice: nil group: group.deviceModel];

Swift:

let impl = ThingSmartBizCore.sharedInstance().service(of: ThingDeviceDetailProtocol.self) as?ThingDeviceDetailProtocol
// Navigates to the page of network detection.
impl?.gotoDeviceDetailNetworkViewController(withDeviceId: "Device ID")

    // Navigates to device details by means of push.

    // For a device, 'new ThingSmartDevice' is called.
    let device = ThingSmartDevice.init(deviceId: "vdevo160888044089994")
    impl?.gotoDeviceDetailDetailViewController(withDevice: device?.deviceModel, group: nil)
    // For a group, 'new ThingSmartGroup' is called.
    let group = ThingSmartGroup.init(groupId: "Group ID")
    impl?.gotoDeviceDetailDetailViewController(withDevice: nil, group: group?.groupModel)

Navigate to device panels from shortcuts on home screen

Add to Home Screen: This feature allows users to create a bookmark on the home screen. This bookmark works as a shortcut of a device panel. After users tap this bookmark, your app is evoked and users are navigated to the target device panel. To enable this feature, perform the following steps:

  1. Add add_icon_to_home_screen to deviceDetailConfig.json. Otherwise, this feature is unavailable.

  2. Set appScheme in the file thing_custom_config.json. In the following example, you need to replace the default value Your appScheme with another value.

    {
        "config": {
            ...
            "appScheme": "Your appScheme"
        },
        ...
    }
    
  3. Add the value of appScheme in the file thing_custom_config.json to URL Scheme. In the preceding example, this default value is Your appScheme. It must be replaced with another value.

  4. Process routing events in AppDelegate:

    Objective-C:

    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options {
        if ([[url.scheme lowercaseString] isEqualToString:@"oralcaretest"]) {
    
            NSDictionary *params = [self dictionaryFromQuery:url.query usingEncoding:NSUTF8StringEncoding];
            NSString *host = url.host;
    
            if ([host isEqualToString:@"panelEx"]) {
                id <ThingPanelProtocol> impl = [[ThingSmartBizCore sharedInstance] serviceOfProtocol:@protocol(ThingPanelProtocol)];
                ThingSmartDevice *device = [ThingSmartDevice deviceWithDeviceId:params[@"devId"]?: @""];
                [impl getPanelViewControllerWithDeviceModel:device.deviceModel initialProps:nil contextProps:nil completionHandler:^(__kindof UIViewController * _Nullable panelViewController, NSError * _Nullable error) {
                    if (error) {
                        NSLog(@"Load error: %@", error);
                    }
                }];
            }
    
    
            return YES;
        }
        return NO;
    }
    
    - (NSDictionary *)dictionaryFromQuery:(NSString *)query usingEncoding:(NSStringEncoding)encoding {
        if (!([query isKindOfClass:[NSString class]] && query.length > 0)) {
            return nil;
        }
        NSCharacterSet *delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"];
        NSMutableDictionary* pairs = [NSMutableDictionary dictionary];
        NSScanner* scanner = [[NSScanner alloc] initWithString:query];
        while (![scanner isAtEnd]) {
            NSString* pairString = nil;
            [scanner scanUpToCharactersFromSet:delimiterSet intoString:&pairString];
            [scanner scanCharactersFromSet:delimiterSet intoString:NULL];
            NSArray* kvPair = [pairString componentsSeparatedByString:@"="];
            if (kvPair.count == 2) {
                NSString* key = [[kvPair objectAtIndex:0]
                                stringByReplacingPercentEscapesUsingEncoding:encoding];
                NSString* value = [[kvPair objectAtIndex:1]
                                stringByReplacingPercentEscapesUsingEncoding:encoding];
                [pairs setObject:value forKey:key];
            }
        }
    
        return [NSDictionary dictionaryWithDictionary:pairs];
    }
    

    Swift:

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        if let scheme = url.scheme, scheme.lowercased() == "oralcaretest" {
    
            let params = self.params(from: url.query, .utf8)
    
            if let host = url.host, host == "panelEx", let devId = params?["devId"] as? String, let device = ThingSmartDevice(deviceId: devId) {
    
                let impl: ThingPanelProtocol? = ThingSmartBizCore.sharedInstance().service(of: ThingPanelProtocol.self) as? ThingPanelProtocol
                impl?.getPanelViewController(with: device.deviceModel, initialProps: nil, contextProps: nil, completionHandler: { vc, error in
                    if error != nil {
                        print(error!.localizedDescription)
                    }else{
                        print("push the vc")
                    }
                })
            }
    
            return true
        }
        return false
    }
    
    func params(from query: String?, _ encoding: String.Encoding) -> Dictionary<String, Any>? {
        guard let queryString = query, queryString.count > 0 else {
            return nil
        }
    
        let set = CharacterSet(charactersIn: "&;")
        var pairs = Dictionary<String, Any>()
        let scanner = Scanner(string: queryString)
        while !scanner.isAtEnd {
            var pairString: NSString? = ""
            scanner.scanUpToCharacters(from: set, into: &pairString)
            scanner.scanCharacters(from: set, into: nil)
    
            if let kvPair = pairString?.components(separatedBy: "="), kvPair.count == 2 {
                let key: String = kvPair.first!.removingPercentEncoding!
                let value: String = kvPair.last!
                pairs[key] = value
            }
    
        }
        return pairs
    }
    

Hide location data from device details

By default, the SDK does not show the feature that allows users to hide location data from device details. To show the feature, set is_support_home_manager to true in thing_custom_config.json.

{
    "config": {
        ...
        "is_support_home_manager": true,
    },
   ...
}

Customize the return page when removing a device

Customize ThingDeviceDetailExternalProtocol.

/// @brief Customizes which page to return to after a device is removed.
/// @return The return value. Valid values: YES: custom. NO: default logic.
- (BOOL)customExitWhenDeleted;

Customize specific features

Specific features can be implemented with the configuration file deviceDetailConfig.json. You can add the custom parameter type to deviceDetail in deviceDetailConfig.json.

The value of type must start with c_. For example, c_test_insert and c_test_async_insert can be added.

[
    {
        "name": "headerSection",
        "items": [
            {
                "cellType": "header"
            },{
                "cellType": "device_info"
            },{
                "cellType": "net_setting"
            },{
                "cellType":"group_edit_devices"
            }
        ]
    },{
        "name": "offLineWarnSection",
        "header": {
            "cellType":"section_off_line_warn"
        },
        "items": [
            {
                "cellType" : "off_line_warn"
            }
        ]
    },{
        "name": "otherSection",
        "header": {
            "cellType":"section_other"
        },
        "items": [
            {
                "cellType":"bind_multi_control_link"
            },
            {
                "cellType":"group_create"
            },
            {
                "cellType":"help_and_feedback"
            },
            {
                "cellType":"add_icon_to_home_screen"
            },
            {
                "cellType":"show_infrared_gateway_sub_device"
            },
            {
                "cellType":"check_device_network"
            },
            {
                "cellType":"check_firmware_update"
            },
            {
                "cellType":"c_test_insert"
            },
            {
                "cellType":"c_test_async_insert"
            }
        ]
    },{
        "name": "footerSection",
        "items": [
            {
                "cellType":"footer"
            }
        ],
        "margin": {
            "top": 16
        }
    }
]

Return item data

API description: clear the data

Clears data before the custom item is inserted.

/// Clears the custom item.
- (void)clearInsertItem;

API description: Return item data synchronously

/// Implements the synchronous callback.
insertDevMenuItemBlock returns the callback when device details are refreshed. type is equivalent to cellType in the configuration file.
-(void)insertDevMenuItem:(InsertDevMenuItemBlock) insertDevMenuItemBlock customType:(NSString *)type;
//@param type The custom type specified in configList.json.
//@param device  The device model.
//@param group   The group model. If the value of the group is nil, device data is returned. Otherwise, group data is returned.
//@return The object that follows the protocol  ThingDeviceDetailCustomMenuModel. If nil is returned, this type of item is not displayed.
typedef id<ThingDeviceDetailCustomMenuModel> _Nullable (^InsertDevMenuItemBlock)(NSString* _Nonnull type,
        ThingSmartDeviceModel* _Nullable device,
        ThingSmartGroupModel* _Nullable group);

API description: Return item data asynchronously

/// Implements the asynchronous callback. insertDevMenuItemAsyncBlock returns the callback when device details are refreshed. type is equivalent to cellType in the configuration file.
-(void)insertDevMenuItemAsync:(InsertDevMenuItemAsyncBlock) insertDevMenuItemAsyncBlock customType:(NSString *)type;
// Inserts the asynchronous operation to process the item. At the end of the operation, call complete(id<ThingDeviceDetailCustomMenuMode>). The result data is returned to the device details page and the list is refreshed.
typedef void (^InsertDevMenuItemAsyncBlock)(NSString* _Nonnull type,
        ThingSmartDeviceModel* _Nullable device,
        ThingSmartGroupModel* _Nullable group,
        InsertDevMenuItemComplete _Nonnull complete);

Example

  1. First, create a Model class that follows the protocol ThingDeviceDetailCustomMenuModel.

    Objective-C:

    // Customizes a class that adopts the protocol ThingDeviceDetailCustomMenuModel.
    @interface CustomMenuModel : NSObject<ThingDeviceDetailCustomMenuModel>
    /// The title.
    @property (nonatomic,copy) NSString *title;
    /// The subtitle.
    @property (nonatomic,copy) NSString *detail;
    @end
    
    @implementation CustomMenuModel
    @end
    

    Swift:

    class CustomMenuModel: NSObject, ThingDeviceDetailCustomMenuModel{
        var title : String?
        var detail : String?
    }
    
  2. Then, configure the callback block.

    Objective-C:

    id<ThingDeviceDetailProtocol> impl = [[ThingSmartBizCore sharedInstance] serviceOfProtocol:@protocol(ThingDeviceDetailProtocol)];
            [impl clearInsertItem];// Clears data before the custom item is inserted.
            [impl insertDevMenuItem:^ id<ThingDeviceDetailCustomMenuModel> (NSString * _Nonnull type, ThingSmartDeviceModel * _Nonnull device, ThingSmartGroupModel * _Nonnull group) {
                if ([type isEqualToString:@"c_test_insert"]) {
                    CustomMenuModel *model = [CustomMenuModel new];
                    if (group) { // If the value of the group is nil, device data is returned. Otherwise, group data is returned.
                        model.title = type;
                        model.detail = @"group";
                    }else{
                        model.title = type;
                        model.detail = @"device";
                    }
                    return model;
                }
                return nil;
            } customType:@"c_test_insert"];
    
            [impl insertDevMenuItemAsync:^(NSString * _Nonnull type, ThingSmartDeviceModel * _Nonnull device, ThingSmartGroupModel * _Nonnull group, InsertDevMenuItemComplete _Nonnull complete) {
                if ([type isEqualToString:@"c_test_async_insert"]) {
                // The time-consuming operation.
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    CustomMenuModel *model = [CustomMenuModel new];
                    if (group) { // If the value of the group is nil, device data is returned. Otherwise, group data is returned.
                        model.title = type;
                        model.detail = @"group";
                    }else{
                        model.title = type;
                        model.detail = @"device";
                    }
                    complete(model);
                });
                }
    
            } customType:@"c_test_async_insert"];
    

    Swift:

    let impl = ThingSmartBizCore.sharedInstance().service(of: ThingDeviceDetailProtocol.self) as?ThingDeviceDetailProtocol
        impl?.clearInsertItem // Clears data before the custom item is inserted.
        impl?.insertDevMenuItem({ (type, deviceModel, groupModel) -> ThingDeviceDetailCustomMenuModel?in
                if type == "c_test_insert" {// If the value of the group is nil, device data is returned. Otherwise, group data is returned.
                    let model = CustomMenuModel.init()
                    if groupModel != nil {
                        model.title = type
                        model.detail = "group"
                    }else{
                        model.title = type
                        model.detail = "device"
                    }
                    return model;
                }
                return nil;
            } , customType: "c_test_insert")
    
    impl?.insertDevMenuItemAsync({ (type, deviceModel, groupModel, complete) in
                if type == "c_test_async_insert" {
                    // The time-consuming operation.
                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                        let model = CustomMenuModel.init()
                        if groupModel != nil {
                            model.title = type
                            model.detail = "group"
                        }else{
                            model.title = type
                            model.detail = "device"
                        }
                        complete(model);
                    }
                }
            } , customType: "c_test_async_insert");
    

Insert handler of item tapping event

Objective-C:

      id<ThingDeviceDetailProtocol> impl = [[ThingSmartBizCore sharedInstance] serviceOfProtocol:@protocol(ThingDeviceDetailProtocol)];
        [impl clickMenuItem: ^(NSString * _Nonnull type, ThingSmartDeviceModel * _Nonnull device, ThingSmartGroupModel * _Nonnull group) {
            NSLog(@"clickItem: type:%@",type);
        }];

Swift:

let impl = ThingSmartBizCore.sharedInstance().service(of: ThingDeviceDetailProtocol.self) as? ThingDeviceDetailProtocol

impl?.clickMenuItem({ (type, deviceModel, groupModel) in
            print("clickItem: type:"+type);
    })