IoT云工坊 · 2020年12月28日

如何基于 SDK 快速开发一款 IoT App 控制智能灯泡(Android 版)

对于现在上班打工族来说,当你带着惺忪的双眼,背着沉重的电脑包,爬着长长的楼梯,回到租的房屋,一开门,乌七八黑,顿时片刻的孤独席卷而来,奈何还需要用手沿着冰冷的墙壁,去摸索着开关。一开灯,刺眼的光芒射进眼球里,本来就沉重的心,又浇了一壶冰水。时光机往后拨一下,当你晚上回家,打开门的时候,一束温暖的灯光,伴随着门的打开流露出来,有点像小学课本里,游子在家门不远处看到母亲打着灯笼的光若隐若散。时光机回到现在,对于新世纪的程序员,我们艰巨着改变世界的重任,实现一款智能灯APP,改变大家对灯的认识是非常重要的。比如早上起床,伴随着闹钟,冷光灯亮起,走到厨房,自动启动照明,晚上回家,暖光在开门前就已经亮起。

那么如何实现一款智能灯APP呢?首先先了解下智能灯的基础功能

智能灯APP基础功能

智能定时功能

可以按照日、周进行设计定时器,可以实现单次,循环定时,可以对分组进行设置定时。

远程控制功能

通过家用无线路由器组成的局域网与其他终端设备(手机、平板等)进行通讯,还可以远程遥控灯光操作,实现对灯光的开,关,调光,场景,彩光模式等控制。

色彩调节功能

通过色彩调节功能,可以实现16777216 种颜色的调节。

地理围栏功能

智能灯控APP可以为用户实现离家和到家模式。

智能音乐灯功能

可以通过手机音乐和灯结合在一起,实现灯随着音乐有节律的闪烁。

智能场景功能

通过专家精心调优出四大场景功能,可以实现柔光模式、缤纷模式、炫彩模式、斑斓模式。

智能灯应用场景

办公场景

针对办公场景,智能灯具有白光调节模式,还拥有阅读模式的场景可以选择

客厅餐厅会客日常场景

可以采用休闲模式,调节气氛。

客厅——卧室场景

卧室场景推荐使用暖光模式

起床唤醒场景

可以通过设置智能定时实现起床播放音乐,并唤醒音乐灯。

打造智能灯如何快速实现?

准备工作

注册开发者账号

前往 涂鸦智能开发平台 注册开发者账号、创建产品、创建功能点等,具体流程请参考接入流程

创建 SDK 应用

在涂鸦 IoT 平台中 “App 工作台” 中点击 “App SDK”,点击“创建 App”。

image.png

填写 App 相关信息,点击确认

应用名称:填写您的 App 名称。
iOS 应用包名:填写您的 iOS App 包名(建议格式:com.xxxxx.xxxxx)。
安卓应用包名:填写您的安卓 App 包名(两者可以保持一致,也可以不一致)。
渠道标识符:不是必填项,如果不填写,系统会根据包名自动生成。
image.png

您可以根据实际需求选择需要的选择方案,支持多选,然后根据 Podfile 和 Gradle 进行 SDK 的集成。

image.png

点击获取密码,获取 SDK 的 AppKey,AppSecret,安全图片等信息。

image.png

集成 Home SDK

创建工程

在 Android Studio 中新建工程。

配置 build.gradle

build.gradle 文件里添加集成准备中下载的 dependencies 依赖库。

android {
    defaultConfig {
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a"
        }
    }
    packagingOptions {
        pickFirst 'lib/*/libc++_shared.so' // 多个aar存在此so,需要选择第一个
    }
}
dependencies {
    implementation 'com.alibaba:fastjson:1.1.67.android'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.9'
      
    // Tuya Home 最新稳定版:
    implementation 'com.tuya.smart:tuyasmart:3.20.0'
}

在根目录的 build.gradle 文件中增加 jcenter() 仓库

repositories {
    jcenter()
}

[!TIP]

涂鸦智能 3.10.0 之前的版本的 sdk 默认只支持 armeabi-v7a,
3.11.0 版本后已经将 armeabi-v7a、arm64-v8a 集成进 sdk,请将本地手动放入的 sdk 的相关 so 库移除,使用 sdk 中提供的。
如果集成新版本 so 库。请移除之前老版本手动集成的库,防止冲突或者代码版本不一致导致的问题
如有其他平台需要可前往 GitHub 获取。

集成安全图片

点击 "下载安全图片" ——"安全图片下载" 下载安全图片。

image.png

image.png

在集成准备中点击“下载安全图片”。将下载的安全图片命名为 “t_s.bmp”,放置到工程目录的 assets 文件夹下。

image.png

设置 Appkey 和 AppSecret

在 AndroidManifest.xml 文件里配置 appkey 和 appSecret,在配置相应的权限等

<meta-data
android:name="TUYA_SMART_APPKEY"
android:value="应用 Appkey" />
<meta-data
android:name="TUYA_SMART_SECRET"
android:value="应用密钥 AppSecret" />

混淆配置

在 proguard-rules.pro 文件配置相应混淆配置

#fastJson
-keep class com.alibaba.fastjson.**{*;}
-dontwarn com.alibaba.fastjson.**

#mqtt
-keep class com.tuya.smart.mqttclient.mqttv3.** { *; }
-dontwarn com.tuya.smart.mqttclient.mqttv3.**

#OkHttp3
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**

-keep class okio.** { *; }
-dontwarn okio.**

-keep class com.tuya.**{*;}
-dontwarn com.tuya.**

初始化 SDK

描述

用于初始化 SDK,请在 Application 中初始化 SDK,确保所有进程都能初始化。

示例代码
public class TuyaSmartApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        TuyaHomeSdk.init(this);
    }
}

appId 和 appSecret 需要配置 AndroidManifest.xml 文件里,也可以在初始化代码里初始化。

TuyaHomeSdk.init(Application application, String appkey, String appSerect)

注销涂鸦智能云连接

在退出应用的时候调用以下接口注销掉。

TuyaHomeSdk.onDestroy();

调试开关

在 debug 模式下可以开启 SDK 的日志开关,查看更多的日志信息,帮助快速定位问题。在 release 模式下建议关闭日志开关。

TuyaHomeSdk.setDebugMode(true);

集成照明控制SDK

在接入 照明控制 SDK 之前,您可以先了解一下 照明灯的DEMO,需要把DEMO跑起来,登陆成功之后,在进行下列操作,照明控制 SDK 需要依赖 Home SDK 其中的一部分,下面的文档也会介绍到依赖的这一部分。

依赖说明

// homesdk 依赖,注意,必须使用大于等于此版本的SDK
implementation 'com.tuya.smart:tuyasmart:3.20.0'
// 控制SDK依赖
implementation 'com.tuya.smart:tuyasmart-centralcontrol:1.0.2'

需要注意的是,tuyasmart-centralcontrol使用了kotlin编译,需要引入kotlin库确保其正常使用。

项目中已引入kotlin的可忽略下面的配置。
kotlin接入

在根目录的build.gradle中引入kotlin插件的依赖:

buildscript {
    ext.kotlin_version = '1.3.72'
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

在app的build.gradle中引入kotlin插件和kotlin包:

apply plugin: 'kotlin-android'
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

标准指令说明

使用标准指令之前

未使用标准控制指令时,设备控制 一般使用这种方式:

ITuyaDevice mDevice = TuyaHomeSdk.newDeviceInstance(String devId);
// 监听控制结果
mDevice.registerDevListener(new IDevListener() {
    @Override
    public void onDpUpdate(String devId, String dpStr) {

    }
    @Override
    public void onRemoved(String devId) {

    }
    @Override
    public void onStatusChanged(String devId, boolean online) {

    }
    @Override
    public void onNetworkStatusChanged(String devId, boolean status) {

    }
    @Override
    public void onDevInfoUpdate(String devId) {

    }
});
mDevice.publishDps("{\"101\": true}", new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Toast.makeText(mContext, "开灯失败", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSuccess() {
        Toast.makeText(mContext, "开灯成功", Toast.LENGTH_SHORT).show();
    }
});

这种方式控制时,会发送dpId,如101、102之类的给设备来控制。其中101就是这个设备定义的开关dpId。

这么做的缺点是,如果另一个设备也有开关功能,但是不是101控制开关,你就需要传入不同的参数来控制。而当n个设备都有开关功能,但是却dpId都不同,就要写非常多的适配逻辑。

为了解决同一个功能定义的id不同的问题,引入了标准指令的概念。

判断当前产品是否支持标准指令

根据产品 id 判断当前产品是否支持标准指令。

使用标准指令需要判断当前设备是否支持标准指令控制,不支持的设备不可以使用该控制方式,只能使用之前的接口控制。

示例代码:

boolean isStandard = TuyaHomeSdk.getDataInstance().isStandardProduct("your_product_id");

其中的 productId 是产品 id,可从 DeviceBean 中获取。

使用标准指令之后

什么是标准指令

标准指令就是特定功能的标准编号。如照明类设备的开灯功能,其标准指令一定是"switch_led"。发送控制指令switch_led,一定可以控制照明设备的开关。

在集成了此SDK之后,调用方式变化如下:

ITuyaDevice mDevice = TuyaHomeSdk.newDeviceInstance(String devId);
// 注意:这里方法是registerDeviceListener,注册的 Listener 是 IDeviceListener
tuyaDevice.registerDeviceListener(new IDeviceListener() {
    @Override
    public void onDpUpdate(String devId, Map<String, Object> dpCodeMap) {

    }

    @Override
    public void onRemoved(String devId) {

    }

    @Override
    public void onStatusChanged(String devId, boolean online) {

    }

    @Override
    public void onNetworkStatusChanged(String devId, boolean status) {

    }

    @Override
    public void onDevInfoUpdate(String devId) {

    }
});
HashMap<String, Object> dpCodeMap = new HashMap<>();
dpCodeMap.put("switch_led", true);
// 发送标准指令
tuyaDevice.publishCommands(dpCodeMap, new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Toast.makeText(mContext, "开灯失败", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSuccess() {
        Toast.makeText(mContext, "开灯成功", Toast.LENGTH_SHORT).show();
    }
});

注意:标准指令使用方法registerDeviceListener注册监听, 非标准是registerDevListener

值得注意的是,目前不是所有设备都支持标准指令控制,后文会说明如何判断该设备是否支持标准指令控制。

如果不支持的设备,而又必须使用标准控制,需要联系涂鸦适配。

标准指令文档

所有标准指令都可以在涂鸦智能平台查找到:

灯具(dj) 标准指令集

开关-插座-排插(kg,cz,pc) 标准指令集

场景开关(cjkg) 标准指令集

等等。

有了tuyaDevice.publishCommands方法和上面的指令,就可以发送标准指令来控制设备。

品类说明

涂鸦 iot 平台上有很多品类的iot设备,不同的品类在涂鸦平台上都有固定的编号(category)。

开发者文档上体现在每个品类指令集的标题上,如 灯具(dj) 标准指令集 中dj,dj就是灯具的category值。

使用 category 字段可以判断当前设备是什么产品,来展示不同的面板。

品类列表
此表格包含大多数支持的品类,具体可参见 iot 平台。

image.png
image.png
image.png

获取产品的品类值(category)

通过产品 id 获取产品的品类值。

示例代码:

String category = TuyaHomeSdk.getDataInstance().getStandardProductConfig("your_product_id").category;

照明设备控制

涂鸦照明设备同时存在v1和v2新旧两种固件,即使使用了标准指令,也需要开发两套控制逻辑。

因此对照明设备功能进行封装,封装了灯具设备的开关、工作模式切换、亮度控制、冷暖控制、彩光控制和四种情景模式的控制。

快速使用

首先,创建ITuyaLightDevice对象,灯相关的方法均封装在此方法中。

ITuyaLightDevice lightDevice = new TuyaLightDevice(String devId);

该对象封装了灯的所有dp点,包括控制指令的下发和上报。

这里提供几个简单的调用示例:

// 创建lightDevice
ITuyaLightDevice lightDevice = new TuyaLightDevice("vdevo159793004250542");

// 注册监听
lightDevice.registerLightListener(new ILightListener() {
    @Override
    public void onDpUpdate(LightDataPoint dataPoint) { // 返回LightDataPoint,包含灯所有功能点的值
        Log.i("test_light", "onDpUpdate:" + dataPoint);
    }

    @Override
    public void onRemoved() {
        Log.i("test_light", "onRemoved");
    }

    @Override
    public void onStatusChanged(boolean status) {
        Log.i("test_light", "onDpUpdate:" + status);
    }

    @Override
    public void onNetworkStatusChanged(boolean status) {
        Log.i("test_light", "onDpUpdate:" + status);
    }

    @Override
    public void onDevInfoUpdate() {
        Log.i("test_light", "onDevInfoUpdate:");
    }
});
// 开灯
lightDevice.powerSwitch(true, new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Log.i("test_light", "powerSwitch onError:" + code + error);
    }

    @Override
    public void onSuccess() {
        Log.i("test_light", "powerSwitch onSuccess:");
    }
});
// 晚安场景
lightDevice.scene(LightScene.SCENE_GOODNIGHT, new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Log.i("test_light", "scene onError:" + code + error);
    }

    @Override
    public void onSuccess() {
        Log.i("test_light", "scene onSuccess:");
    }
});
// 设置颜色
lightDevice.colorHSV(100, 100, 100, new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Log.i("test_light", "colorHSV onError:" + code + error);
    }
    @Override
    public void onSuccess() {
        Log.i("test_light", "colorHSV onSuccess:");
    }
});

更多API请参考下面的文档。

注册监听

方法说明

/**
 * 注册监听
 */
void registerLightListener(ILightListener listener);

其中,ILightListener回调如下:

public interface ILightListener {
    /**
     * 监听照明设备dp点变化
     *
     * @param dataPoint 该灯具所有dp点的状态
     */
    void onDpUpdate(LightDataPoint dataPoint);

    /**
     * 设备移除
     */
    void onRemoved();

    /**
     * 设备上下线
     */
    void onStatusChanged(boolean online);

    /**
     * 网络状态
     */
    void onNetworkStatusChanged(boolean status);

    /**
     * 设备信息更新例如name之类的
     */
    void onDevInfoUpdate();
}

参数说明
值得说明的是LightDataPoint对象,该对象封装了当前设备所有功能点。当功能点发生变化时,将会回调。每次回调的都会是完整的对象。

以下是该对象参数的具体含义:

public class LightDataPoint {
    /**
     * 开关
     */
    public boolean powerSwitch;
    /**
     * 工作模式。
     * <p>
     * MODE_WHITE为白光模式;
     * MODE_COLOUR为彩光模式;
     * MODE_SCENE为情景模式;
     */
    public LightMode workMode;

    /**
     * 亮度百分比,从0到100
     */
    public int brightness;

    /**
     * 色温百分比,从0到100
     */
    public int colorTemperature;

    /**
     * 颜色值,HSV色彩空间.
     * <p>
     * 其中H为色调,取值范围0-360;
     * 其中S为饱和度,取值范围0-100;
     * 其中V为明度,取值范围0-100;
     */
    public LightColourData colorHSV;
    /**
     * 彩灯情景。
     *
     * SCENE_GOODNIGHT为晚安情景;
     * SCENE_WORK为工作情景;
     * SCENE_READ为阅读情景;
     * SCENE_CASUAL为休闲情景;
     */
    public LightScene scene;
}
获取当前灯的类型

灯共分为一路灯(仅有白光)、二路灯(白光+冷暖控制)、三路灯(仅有彩光模式)、四路灯(白光+彩光)、五路灯(白光+彩光+冷暖)。

这5种灯具在功能定义上有所区别,在开发相应的UI和控制时有所区别。

该方法可获取当前灯的类型。

/**
 * 获取当前是几路灯
 *
 * @return {@link LightType}
 */
LightType lightType();

其中LightType中定义的类型有:

/**
 * 白光灯,dpCode:bright_value
 */
TYPE_C,
/**
 * 白光+冷暖,dpCode:bright_value + temp_value
 */
TYPE_CW,
/**
 * RGB,dpCode:colour_data
 */
TYPE_RGB,
/**
 * 白光+RGB,dpCode:bright_value + colour_data
 */
TYPE_RGBC,
/**
 * 白光+冷暖+RGB,dpCode:bright_value + temp_value + colour_data
 */
TYPE_RGBCW
获取当前设备所有功能的值

打开一个设备面板时,需要获取所有功能点值来展示。可通过此接口获取上面提到的LightDataPoint对象。

/**
 * 获取灯所有功能点的值
 */
LightDataPoint getLightDataPoint();
开关

控制灯的开关

方法说明

/**
 * 开灯 or 关灯
 *
 * @param status         true or false
 * @param resultCallback callback
 */
void powerSwitch(boolean status, IResultCallback resultCallback);

**参数说明
**
image.png

工作模式

控制工作模式的切换。

方法说明

/**
 * 切换工作模式
 *
 * @param mode           工作模式
 * @param resultCallback callback
 */
void workMode(LightMode mode, IResultCallback resultCallback);

参数说明

image.png

调用示例

如切换到彩光模式:

lightDevice.workMode(LightMode.MODE_COLOUR, new IResultCallback() {
    @Override
    public void onError(String code, String error) {
        Log.i("test_light", "workMode onError:" + code + error);
    }

    @Override
    public void onSuccess() {
        Log.i("test_light", "workMode onSuccess");
    }
});

注意:部分灯具必须切换到对应的工作模式才可以控制,比如控制彩光,必须先切换到彩光模式才可以发颜色的值。

亮度

控制亮度

方法说明

/**
 * 亮度控制。
 *
 * @param status         亮度的百分比,取值范围0-100
 * @param resultCallback callback
 */
void brightness(int status, IResultCallback resultCallback);

参数说明

image.png

冷暖

控制灯的冷暖值

方法说明

/**
 * 色温控制
 *
 * @param status         色温的百分比,取值范围0-100
 * @param resultCallback callback
 */
void colorTemperature(int status, IResultCallback resultCallback);

参数说明

image.png

彩光

控制彩色灯的颜色

方法说明

/**
 * 设置彩灯的颜色
 *
 * @param hue            色调 (范围:0-360)
 * @param saturation     饱和度(范围:0-100)
 * @param value          明度(范围:0-100)
 * @param resultCallback callback
 */
void colorHSV(int hue, int saturation, int value, IResultCallback resultCallback);
情景

切换彩灯的情景模式,目前共有四种模式:

LightScene.SCENE_GOODNIGHT为晚安情景;
LightScene.SCENE_WORK为工作情景;
LightScene.SCENE_READ为阅读情景;
LightScene.SCENE_CASUAL为休闲情景;

方法说明

/**
 * @param lightScene     {@link LightScene}
 * @param resultCallback callback
 */
void scene(LightScene lightScene, IResultCallback resultCallback);
定时器

现有的定时器不支持标准Code 定时,需要进行标准Code转义成Id才能进行设置定时器,参考原有定时器

方法调用

/**
 * @param dpCodes  标准DpCode指令
 * @param devId    设备ID
 */
Map<String, Object> convertCodeToIdMap(Map<String, Object> dpCodes, String devId);

示例代码

Map<String, Object> dps=TuyaHomeSdk.getDataInstance().getStandardConverter().convertCodeToIdMap(Map<String, Object> dpCodes, String devId);
TuyaTimerBuilder builder = new TuyaTimerBuilder.Builder()
        .taskName(mTaskName)
        .devId("efw9990wedsew")
        .deviceType(TimerDeviceTypeEnum.DEVICE)
        .actions(dps)  
        .loops("1100011")
        .aliasName("Test")
        .status(1)
        .appPush(true)
        .build();
TuyaHomeSdk.getTimerInstance().addTimer(builder, new IResultCallback() {
    @Override
    public void onSuccess() {
            }

    @Override
    public void onError(String errorCode, String errorMsg) {

    }
});
推荐阅读
关注数
1205
内容数
19
涂鸦IoT开发平台为客户提供一站式物联网解决方案,涵盖设备智能化、云服务、App软件开发、运营维护等各方面,助力各行业客户实现研发端、生产端、销售端、运营端全产业互联网化。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息