对于现在上班打工族来说,当你带着惺忪的双眼,背着沉重的电脑包,爬着长长的楼梯,回到租的房屋,一开门,乌七八黑,顿时片刻的孤独席卷而来,奈何还需要用手沿着冰冷的墙壁,去摸索着开关。一开灯,刺眼的光芒射进眼球里,本来就沉重的心,又浇了一壶冰水。时光机往后拨一下,当你晚上回家,打开门的时候,一束温暖的灯光,伴随着门的打开流露出来,有点像小学课本里,游子在家门不远处看到母亲打着灯笼的光若隐若散。时光机回到现在,对于新世纪的程序员,我们艰巨着改变世界的重任,实现一款智能灯 APP,改变大家对灯的认识是非常重要的。比如早上起床,伴随着闹钟,冷光灯亮起,走到厨房,自动启动照明,晚上回家,暖光在开门前就已经亮起。
那么如何实现一款智能灯 APP 呢?首先先了解下智能灯的基础功能
智能灯 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)
复制代码
注销涂鸦智能云连接
在退出应用的时候调用以下接口注销掉。
调试开关
在 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 平台。
获取产品的品类值(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) {
}
});
复制代码
评论