更新时间:2024-06-26 02:01:38下载pdf
本文介绍场景创建的业务实现,包括条件创建和动作创建。
总体流程
开发者(下文简称为 您)可以通过 查询条件列表 获得涂鸦平台支持的条件类型,并可以考虑结合本地自定义的条件类型,展示可供用户选择的条件类型列表。
ThingHomeSdk.getSceneServiceInstance().conditionService().getConditionAll(
homeId, // 家庭 ID
true, "", object : IResultCallback<ConditionItemList?>() {
override fun onSuccess(result: ConditionItemList?) {
// 在 result 基础上可以继续添加本地自定义的条件类型
}
override fun onError(errorCode: String?, errorMessage: String?) {
}
})
当用户点选具体的条件类型,应当提供不同的条件类型点选后的 UI 交互。
具体而言,涂鸦提供了一个开源的 示例项目,便于您理解上述的过程。尽管示例项目中部分细节交互进行了简化处理,但是依旧可以传递出上述的流程示意。
示例中直接构建了一个固定的地理围栏的场景条件。建议您提供基于 Google 或者华为的地理围栏组件完成地理围栏监控功能,并选用合适的地图进行围栏的圈选设置。当然您也可以选择涂鸦包装的地理围栏监控组件,下文将详细介绍接入方式。
具体而言,从您的地理围栏圈选页获得参数 radius
、latitude
、longitude
和 address
,并在用户选择地理围栏类型 geofenceType
后,然后将前述参数通过 创建位置变化时条件 创建出位置变化时条件。
val radius = 100
val latitude: Double = 30.30288959184809
val longitude: Double = 120.0640840491766
val address = "XX 影视"
val geofenceType = GeofencingType.GEOFENCING_TYPE_ENTER.type
val geofenceConditionBuilder = GeofenceConditionBuilder(radius, latitude, longitude, address, geofenceType)
val conditionBase = geofenceConditionBuilder.build() as ConditionBase
val geoCondition = SceneCondition(conditionBase).apply {
this.entityName = address
}
目前基于 Google 和华为的两套地理围栏的实现都已经集成在业务拓展 SDK 组件中。两套实现的具体竞选规则:优先使用基于华为的地理围栏实现,其次是基于 Google 的地理围栏实现,两者只取其一,并非同时生效。
组件接入。
// 以下依赖若已包含在上述开发准备步骤内,则忽略,但版本以此处为准
implementation 'com.huawei.hms:maps:6.0.0.301'
implementation 'com.huawei.hms:location:6.0.0.302'
implementation "com.thingclips.smart:thingsmart:${sdk_version}"
// 业务拓展 SDK 组件
implementation "com.thingclips.smart:thingsmart-expansion-sdk:${expansion_version}"
// 以下依赖若有,则忽略
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.alibaba:fastjson:1.1.67.android'
编码。
使用。
对于地理围栏具体的使用方式,创建位置变化时条件 章节对 API 进行了详细的说明。更多的使用限制,参考 地理围栏开发-约束条件 以及 支持地理围栏的设备。
组件接入。
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation "com.thingclips.smart:thingsmart:${sdk_version}"
// 业务拓展 SDK 组件
implementation("com.thingclips.smart:thingsmart-expansion-sdk:${expansion_version}"){
// 注意:需要您在全局的依赖中排除基于华为的地理围栏的组件,这样可以采用基于 Google 的地理围栏的实现
exclude group: "com.thingclips.smart", module: "thingsmart-geofence-huawei"
}
// 以下依赖若有,则忽略
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.alibaba:fastjson:1.1.67.android'
工程配置。
App 模块 build.gradle
全局依赖排除华为的地理围栏组件。
android{
configurations.all{
exclude group: "com.thingclips.smart", module: "thingsmart-geofence-huawei"
}
}
App 模块 AndroidManifest.xml
清单文件配置。
<!-- 请将以下 ${GOOGLE_MAP_KEY} 替换成您的应用从 Google 申请的地图 Key -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_MAP_KEY}" />
编码。
建议您在清单文件中声明位置相关权限,并在选择 位置变化时 的条件后,立即处理运行时位置权限的申请。具体信息,参考 位置信息权限声明、权限申请的处理,否则可能会由于内部的权限检查而导致部分的 API 报错。
使用。
对于地理围栏具体的使用方式,创建位置变化时条件 章节已经对 API 进行了详细的说明。更多的使用指引,参考 创建和监控地理围栏-限制 以及 使用地理围栏最佳做法。
示例中直接构建了一个固定时间的场景条件。建议您提供时间 picker
组件进行定时的设置。
具体而言,从您的时间 picker
组件获得参数 time
和 date
,通过 Calendar.getInstance().timeZone.id
或 TimeZone.getDefault().id
获得参数 timeZoneId
,并在用户选择重复类型 loops
后,将前述参数通过 创建定时条件 创建出定时条件。
val conditionBase: ConditionBase = TimingConditionBuilder(
timeZoneId,
loops,
time,
date,
).build() as ConditionBase
val timerCondition = SceneCondition(conditionBase).apply {
entityName = "定时"
exprDisplay = time
}
示例中直接构建了一处固定位置的特定气象类型的场景条件。建议您完整展示根据 查询条件列表 获得的气象变化条件列表 envConds
,并在用户选择气象类型后,提供交互 UI 以供用户设置具体的气象目标值。接下来对每个气象类型进行细分讲解。
您可以将 定位的经纬度信息 通过调用 根据经纬度查询城市信息,获取当前所在城市的信息,取用 cityId
和 city
字段。或者通过 查询城市列表 后,待用户选取城市后可得。
温度
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_TEMP,
operator = "<", // 用户设置
chooseValue = chooseValue, // 用户设置
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的温度气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
风速
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_WIND,
operator = "<", // 用户设置
chooseValue = chooseValue, // 用户设置
).setWindSpeedUnit(unit) // 风速的单位,该字段使用条件列表的风速气象类型详情数据,具体是 property.property.unit
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的风速气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
湿度
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_HUMIDITY,
operator = null,
chooseValue = chooseValue, // 用户选择
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的湿度气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
天气
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_CONDITION,
operator = null,
chooseValue = chooseValue, // 用户选择
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的天气气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
PM2.5
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_PM,
operator = null,
chooseValue = chooseValue, // 用户选择
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的 PM2.5 气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
空气质量
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_AQI,
operator = null,
chooseValue = chooseValue, // 用户选择
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的空气质量气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
日出日落
val weatherConditionBuilder = WeatherConditionBuilder(
cityId = cityId,
cityName = city,
entityType = 3,
weatherType = WeatherType.WEATHER_TYPE_SUN,
operator = null,
chooseValue = chooseValue, // 用户选择
)
val conditionBase = weatherConditionBuilder.build() as ConditionBase
val weatherCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的日出日落气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
日出日落前后时间点
val conditionBase: ConditionBase = SunRiseSetConditionBuilder(
cityId,
SunSetRiseRule.SunType.SUNSET, // 用户选择,SUNSET 或 SUNRISE
20 // 前/后分钟数
).build() as ConditionBase
val sunRiseSetCondition = SceneCondition(conditionBase).apply {
entityName = city
iconUrl = weatherData.icon // 选中气象类型的 icon,该字段使用条件列表的日出日落气象类型详情数据的 newIcon 字段
exprDisplay = exprDis // 条件表达式的可视化展示
}
示例中直接构建了一个固定设备的固定功能的场景条件。建议您完整展示根据 查询条件设备列表 获得的设备列表,并在用户选取设备后,通过 查询条件设备的 DP 列表 获得功能列表,将 条件设备功能数据模型转化为 DTO 模型 后进行功能展示以供用户设置,然后将用户选设通过 创建设备状态变化时条件 创建出设备条件。接下来对具体设备类型进行细分讲解。
deviceConditionData
从 条件设备功能数据模型转化为 DTO 模型 而来,并要求在编辑后及时对其进行字段更新。例如,更新 ValueTypeData.value
、ValueTypeData.operators
、OtherTypeData.checked
和 ConditionExtraInfo.timeWindow
。
普通设备
val builder = DeviceConditionBuilder(
deviceId = deviceConditionData.deviceId,
dpId = deviceConditionData.datapointId.toString(),
entityType = deviceConditionData.entityType ?: CONDITION_TYPE_DEVICE,
deviceConditionData = deviceConditionData, // 条件设备功能数据模型转化为 DTO 模型后就获得了 conditionData 的列表
chooseValue = chooseValue // 用户设置
)
val conditionBase = builder.build() as ConditionBase
val deviceCondition = SceneCondition(conditionBase).apply {
entityName = deviceBean.name // 设备名称
entitySubIds = deviceConditionData.datapointId.toString() // 设备功能 DP ID
iconUrl = deviceBean.getIconUrl()// 设备 icon
exprDisplay = displayString // 设备条件表达式的可视化展示
}
普通设备温度 DP
convertTemp
map 结构内部需要存放不同温度单位的 DP 值。originTempUnit
指定为当前 DP 的温度单位。tempUnit
指定为当前应用偏好设置的温度单位。dpScale
指定为当前 DP 的小数点系数。val builder = DeviceConditionBuilder(
deviceId = deviceConditionData.deviceId,
dpId = deviceConditionData.datapointId.toString(),
entityType = deviceConditionData.entityType ?: CONDITION_TYPE_DEVICE,
deviceConditionData = deviceConditionData, // 条件设备功能数据模型转化为 DTO 模型后就获得了 conditionData 的列表
chooseValue = chooseValue // 用户设置
)
val originTempUnit = "celsius" // 当前功能点的温度单位
val tempUnit = "fahrenheit" // 应用内偏好设置的温度展示单位
val dpScale = 1
val tempMap = mutableMapOf<String, Int>()
tempMap[originTempUnit] = 120
tempMap[tempUnit] = (120/10 * 1.8 + 32) * 10 // 摄氏度转换为华氏度
builder.setConvertTemp(tempMap)
.setTempUnit(tempUnit)
.setOriginTempUnit(originTempUnit)
.setDpScale(dpScale)
val conditionBase = builder.build() as ConditionBase
val deviceCondition = SceneCondition(conditionBase).apply {
entityName = deviceBean.name // 设备名称
entitySubIds = deviceConditionData.datapointId.toString() // 设备功能 DP ID
iconUrl = deviceBean.getIconUrl()// 设备 icon
exprDisplay = displayString // 设备条件表达式的可视化展示
}
PIR 设备
val builder = DeviceConditionBuilder(
deviceId = deviceConditionData.deviceId,
dpId = deviceConditionData.datapointId.toString(),
entityType = deviceConditionData.entityType ?: CONDITION_TYPE_DEVICE,
deviceConditionData = deviceConditionData, // 条件设备功能数据模型转化为 DTO 模型后就获得了 conditionData 的列表
chooseValue = chooseValue // 用户设置
).setDelayTime(otherTypeData?.datapointKey ?: "") // 从 conditionData.otherTypeData.datapointKey 获得
val conditionBase = builder.build() as ConditionBase
val deviceCondition = SceneCondition(conditionBase).apply {
entityName = deviceBean.name // 设备名称
entitySubIds = deviceConditionData.datapointId.toString() // 设备功能 DP ID
iconUrl = deviceBean.getIconUrl()// 设备 icon
exprDisplay = displayString // 设备条件表达式的可视化展示
}
设备 DP 持续时间
deviceConditionData.entityType = CONDITION_TYPE_WITH_TIME
val builder = DeviceConditionBuilder(
deviceId = deviceConditionData.deviceId,
dpId = deviceConditionData.datapointId.toString(),
entityType = deviceConditionData.entityType,
deviceConditionData = deviceConditionData, // 条件设备功能数据模型转化为 DTO 模型后就获得了 conditionData 的列表
chooseValue = chooseValue // 用户设置
).setCalType(COND_TYPE_DURATION)
.setTimeWindow(conditionData.extraInfo?.timeWindow ?: 0)
val conditionBase = builder.build() as ConditionBase
val deviceCondition = SceneCondition(conditionBase).apply {
entityName = deviceBean.name // 设备名称
entitySubIds = deviceConditionData.datapointId.toString() // 设备功能 DP ID
iconUrl = deviceBean.getIconUrl()// 设备 icon
exprDisplay = displayString // 设备条件表达式的可视化展示
}
示例中直接构建了一个固定门锁设备的固定成员的场景条件。建议您完整展示家庭下的门锁设备列表,并在用户选取设备后,完整展示家庭成员列表以供用户选设。
devConds
从 查询条件列表 处获得。
val membersString = "zhangsan,lisi" // 将选中的家庭成员名称联合,以逗号分隔
val memberIds = "123,345" // 将选中的家庭成员 ID 联合,以逗号分隔
val conditionBase = DeviceConditionBuilder(
deviceId = deviceId ?: "", // 门锁设备 ID
dpId = devConds?.find { it.entityType == ConditionEntityType.LOCK_MEMBER_GO_HOME.type }?.entitySubId ?: "",
entityType = ConditionEntityType.LOCK_MEMBER_GO_HOME.type,
deviceConditionData = null,
chooseValue = memberIds
).setMembers(membersString)
.build() as ConditionBase
val lockCondition = SceneCondition(conditionBase).apply {
entityName = membersString
exprDisplay = display // 设备条件表达式的可视化展示
}
总体流程
展示可供用户选择的动作类型列表。
当用户点选具体的动作类型,应当提供不同的动作类型点选后的 UI 交互。
示例中直接构建了一个固定的延时动作。建议您提供时间 picker
组件进行延时的设置。
具体而言,从您的时间 picker
组件获得时分秒三个单位体系的设置,并转换成两个单位体系参数 minutes
和 seconds
,然后将前述参数通过 创建延时执行动作 创建出延时动作。
val minutes = 62
val seconds = 20
val actionBase: ActionBase = DelayActionBuilder(minutes, seconds).build() as ActionBase
val delayAction = SceneAction(actionBase).apply {
entityName = "延时执行"
}
示例中直接构建了一个消息中心的动作。建议您根据不同的通知类型,提供交互以供用户选设。
具体而言,参数 notifyType
由用户选设具体的类型决定,消息中心为常量 ACTION_TYPE_MESSAGE
,短信提醒为常量 ACTION_TYPE_SMS
,电话提醒为常量 ACTION_TYPE_PHONE
,这些常量定义在 package
: com.thingclips.smart.scene.model.constant.*
内。然后将前述参数通过 创建发送通知提醒动作 创建出通知型动作。
短信和电话提醒是增值服务,需要您在涂鸦开发者平台上购买开通后,方可正常使用。此处仅指导您创建出具体通知类型的场景动作数据模型。
消息中心
val actionBase: ActionBase = NotifyActionBuilder(ACTION_TYPE_MESSAGE).build() as ActionBase
val notifyAction = SceneAction(actionBase).apply {
entityName = "消息中心"
}
短信提醒
关于增值服务的状态显示,您可以通过对场景动作 actionDisplayNew
属性中置入合适的文案提醒。当您在 UI 上需要显示通知类型场景动作副标题时,即可从 actionDisplayNew
中解析出展示。具体请见下述示例代码:
val unableTip = "短信提醒增值服务不可用的提示文案"
val actionBase: ActionBase = NotifyActionBuilder(ACTION_TYPE_SMS).build() as ActionBase
val notifyAction = SceneAction(actionBase).apply {
entityName = "短信通知"
actionDisplayNew = mutableMapOf(
"package_has_expired" to listOf(unableTip)
)
}
电话提醒
val unableTip = "电话提醒增值服务不可用的提示文案"
val actionBase: ActionBase = NotifyActionBuilder(ACTION_TYPE_PHONE).build() as ActionBase
val notifyAction = SceneAction(actionBase).apply {
entityName = "电话提醒"
actionDisplayNew = mutableMapOf(
"voice_package_has_expired" to listOf(unableTip)
)
}
示例中首先将 查询简易场景列表 获得的包含一键执行和自动化的场景列表进行展示,待用户完成勾选后构建出智能场景的动作。建议您可以选择从本地数据源获取场景数据,但前提是您已经做了列表数据的本地缓存,已经完成了增删改对本地缓存的更新,并提供不同类型场景数据的交互,以供用户选设。
具体而言,您将从选中的场景条目中获得场景 ID 参数 linkageRuleId
,参数 linkageOperator
由用户选择的场景类型决定,一键执行为常量 ACTION_TYPE_TRIGGER
,自动化启用为常量 ACTION_TYPE_ENABLE_AUTOMATION
,自动化禁用为常量 ACTION_TYPE_DISABLE_AUTOMATION
。然后将前述参数通过 创建选择已有智能场景动作 创建出智能场景动作。
选择 一键执行 动作
val actionExecutor = ACTION_TYPE_TRIGGER
val actionBase: ActionBase = LinkageRuleActionBuilder(checkedItem.id, actionExecutor).build() as ActionBase // checkedItem.id 是一键执行的 ID
SceneAction(actionBase).apply {
entityName = checkedItem.name // 一键执行的名称
}
选择 自动化 动作
val actionExecutor = ACTION_TYPE_ENABLE_AUTOMATION
val actionBase: ActionBase = LinkageRuleActionBuilder(checkedItem.id, actionExecutor).build() as ActionBase
// checkedItem.id 是自动化的 ID
SceneAction(actionBase).apply {
entityName = checkedItem.name // 自动化的名称
}
示例中创建了一个固定设备固定功能点的场景动作。建议您完整展示通过 查询动作设备列表 获得的设备、群组列表,并在用户选取设备或群组后,通过 查询动作设备的 DP 列表 或 查询动作设备群组的 DP 列表 获得功能列表,将 动作设备功能数据模型转化为 DTO 模型 并进行功能展示以供用户设置,然后将用户选设通过 创建控制单个设备动作 创建出设备动作。接下来对具体设备类型进行细分讲解。
deviceActionDetailBean
从 动作设备功能数据模型转化为 DTO 模型 而来。selDpValue
为选择的 DP 值,selDpDisplayValue
为选择的 DP 值的可视化展示。
普通设备
val builder = DeviceActionBuilder(deviceId, deviceActionDetailBean, selDpValue, selDpDisplayValue ?: "")
val actionBase: ActionBase = builder.build() as ActionBase
SceneAction(actionBase).apply {
actionDisplayNew = actionDisplayMap // 同样是 DP 功能选择后的可视化展示
extService?.getDevice(deviceId)?.let {
this.devIcon = it.iconUrl // 设备 icon
this.isDevOnline = it.isOnline // 设备在线状态
this.entityName = it.name // 设备名称
}
}
普通设备步进 DP
val type = DeviceStepType.HIGH
val builder = DeviceActionBuilder(deviceId, deviceActionDetailBean, selDpValue, selDpDisplayValue ?: "", isStep = true)
.apply{
this.setType(type)
}
val actionBase: ActionBase = builder.build() as ActionBase
SceneAction(actionBase).apply {
actionDisplayNew = actionDisplayMap // 同样是 DP 功能选择后的可视化展示
extService?.getDevice(deviceId)?.let {
this.devIcon = it.iconUrl // 设备 icon
this.isDevOnline = it.isOnline // 设备在线状态
this.entityName = it.name // 设备名称
}
}
普通设备温度 DP
convertTemp
map 结构内部需要存放不同温度单位的 DP 值。originTempUnit
指定为当前 DP 的温度单位。tempUnit
指定为当前应用偏好设置的温度单位。dpScale
指定为当前 DP 的小数点系数。val originTempUnit = "celsius" // 当前功能点的温度单位
val tempUnit = "fahrenheit" // 应用内偏好设置的温度展示单位
val dpScale = 1
val convertTemp = mutableMapOf<String, Int>()
convertTemp[originTempUnit] = 120
convertTemp[tempUnit] = (120/10 * 1.8 + 32) * 10 // 摄氏度转换为华氏度
val builder = DeviceActionBuilder(deviceId, deviceActionDetailBean, selDpValue, selDpDisplayValue ?: "")
.apply {
originTempUnit?.let {
this.setOriginTempUnit(it)
}
dpScale?.let {
this.setDpScale(it)
}
convertTemp?.let {
this.setConvertTemp(it)
}
}
val actionBase: ActionBase = builder.build() as ActionBase
SceneAction(actionBase).apply {
actionDisplayNew = actionDisplayMap // 同样是 DP 功能选择后的可视化展示
extService?.getDevice(deviceId)?.let {
this.devIcon = it.iconUrl // 设备 icon
this.isDevOnline = it.isOnline // 设备在线状态
this.entityName = it.name // 设备名称
}
}
群组动作
val builder = DeviceActionBuilder(groupId.toString(), deviceActionDetailBean, selDpValue, selDpDisplayValue ?: "")
val actionBase: ActionBase = builder.build() as ActionBase
SceneAction(actionBase).apply {
actionDisplayNew = actionDisplayMap // 同样是 DP 功能选择后的可视化展示
this.actionExecutor = ACTION_TYPE_DEVICE_GROUP // actionExecutor 调整为群组动作 actionExecutor
extService?.getGroupDevice(groupId)?.let {
this.devIcon = it.iconUrl // 群组 icon
this.isDevOnline = it.isOnline // 群组在线状态
this.entityName = it.name // 群组名称
}
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈