mini program: add qcloud_device_linkage_demo
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
//app.js
|
||||
App({
|
||||
globalData: {
|
||||
},
|
||||
onLaunch: function () {
|
||||
wx.setKeepScreenOn({
|
||||
keepScreenOn: true
|
||||
})
|
||||
if (!wx.cloud) {
|
||||
console.error('请使用 2.2.3 或以上的基础库以使用云能力')
|
||||
} else {
|
||||
wx.cloud.init({
|
||||
// env 参数说明:
|
||||
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
|
||||
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
|
||||
// 如不填则使用默认环境(第一个创建的环境)
|
||||
env: "tos-demo",
|
||||
traceUser: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/index/index"
|
||||
],
|
||||
"window": {
|
||||
"backgroundColor": "#F6F6F6",
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#F6F6F6",
|
||||
"navigationBarTitleText": "设备联动Demo",
|
||||
"navigationBarTextStyle": "black"
|
||||
},
|
||||
"sitemapLocation": "sitemap.json"
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
/**app.wxss**/
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button {
|
||||
background: initial;
|
||||
}
|
||||
|
||||
button:focus{
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
button::after{
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
page {
|
||||
background: #f6f6f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.userinfo, .uploader, .tunnel {
|
||||
margin-top: 40rpx;
|
||||
height: 140rpx;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
.userinfo-avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin: 20rpx;
|
||||
border-radius: 50%;
|
||||
background-size: cover;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.userinfo-avatar:after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.userinfo-nickname {
|
||||
font-size: 32rpx;
|
||||
color: #007aff;
|
||||
background-color: white;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.userinfo-nickname::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.uploader, .tunnel {
|
||||
height: auto;
|
||||
padding: 0 0 0 40rpx;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.uploader-text, .tunnel-text {
|
||||
width: 100%;
|
||||
line-height: 52px;
|
||||
font-size: 34rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.uploader-container {
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
padding: 20rpx 20rpx 20rpx 0;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.uploader-image {
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.tunnel {
|
||||
padding: 0 0 0 40rpx;
|
||||
}
|
||||
|
||||
.tunnel-text {
|
||||
position: relative;
|
||||
color: #222;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tunnel-text:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.tunnel-switch {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: -2rpx;
|
||||
}
|
||||
|
||||
.disable {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.service {
|
||||
position: fixed;
|
||||
right: 40rpx;
|
||||
bottom: 40rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(#007aff, #0063ce);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
.service-button {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
}
|
||||
|
||||
.service:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.request-text {
|
||||
padding: 20rpx 0;
|
||||
font-size: 24rpx;
|
||||
line-height: 36rpx;
|
||||
word-break: break-all;
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
// 传感设备配置
|
||||
const SensorDevice = {
|
||||
// 腾讯云物联网通信平台中获取 产品ID和设备名称
|
||||
ProductId: "ZLN6Q20QHU", // 产品ID
|
||||
DeviceName: "dev001", // 设备名称
|
||||
// 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
|
||||
SecretId: "XXXX",
|
||||
SecretKey: "XXXX",
|
||||
}
|
||||
|
||||
// 执行设备配置
|
||||
const ExecuteDevice = {
|
||||
ProductId: "U1BZWHF7F9", // 产品ID
|
||||
DeviceName: "dev_01", // 设备名称
|
||||
// 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey
|
||||
SecretId: "XXXX",
|
||||
SecretKey: "XXXX",
|
||||
Topic: "U1BZWHF7F9/dev_01/data"
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SensorDevice: SensorDevice,
|
||||
ExecuteDevice: ExecuteDevice,
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,266 @@
|
||||
const app = getApp()
|
||||
const config = require('../../config.js')
|
||||
var util = require('../../utils/util.js');
|
||||
var wxCharts = require('../../utils/wxcharts.js');
|
||||
var lineChart = null;
|
||||
var startPos = null;
|
||||
|
||||
Page({
|
||||
data: {
|
||||
tempHigh: 20,
|
||||
sensorDevice: {},
|
||||
executeDevice: {},
|
||||
chartDataTemp: [],
|
||||
chartDataHumi: [],
|
||||
chartCategories: [],
|
||||
},
|
||||
onLoad: async function(options) {
|
||||
console.log("index onLoad")
|
||||
while (true) {
|
||||
let executor = await this.queryExecuteDeviceStatus()
|
||||
let sensor = await this.querySensorDeviceStatus()
|
||||
this.renderChart(sensor.temperature.Value, sensor.humidity.Value)
|
||||
let temp = sensor.temperature.Value
|
||||
console.log(temp)
|
||||
if (temp >= this.data.tempHigh) {
|
||||
if (executor.light == 0) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: `温度达到门限(${this.data.tempHigh}),打开风扇和灯`,
|
||||
showCancel: false,
|
||||
})
|
||||
this.turnOnLight()
|
||||
this.turnOnFan()
|
||||
}
|
||||
} else {
|
||||
// this.turnOffLight()
|
||||
// this.turnOffFan()
|
||||
}
|
||||
await util.delayMs(5000)
|
||||
}
|
||||
},
|
||||
sliderChange(e) {
|
||||
console.log(e.detail)
|
||||
this.setData({
|
||||
tempHigh: e.detail.value
|
||||
})
|
||||
},
|
||||
renderChart(temperature, humidity) {
|
||||
let epoch = Math.round(new Date().getTime() / 1000)
|
||||
let timestamp = util.formatEpoch(epoch)
|
||||
if (this.data.chartDataTemp.length > 5) {
|
||||
this.data.chartDataTemp.pop();
|
||||
this.data.chartDataHumi.pop();
|
||||
this.data.chartCategories.pop();
|
||||
}
|
||||
this.data.chartDataTemp.unshift(temperature)
|
||||
this.data.chartDataHumi.unshift(humidity)
|
||||
this.data.chartCategories.unshift(timestamp.substring(11))
|
||||
this.setData({
|
||||
timestamp: timestamp,
|
||||
chartDataTemp: this.data.chartDataTemp,
|
||||
chartDataHumi: this.data.chartDataHumi,
|
||||
chartCategories: this.data.chartCategories,
|
||||
})
|
||||
this.renderChartTemp(400, 160)
|
||||
this.renderChartHumi(400, 160)
|
||||
},
|
||||
// 查询传感器设备状态
|
||||
async querySensorDeviceStatus() {
|
||||
let res = await wx.cloud.callFunction({
|
||||
name: 'query',
|
||||
data: config.SensorDevice
|
||||
})
|
||||
console.log("sensorDevice status", res.result.Data)
|
||||
let data = JSON.parse(res.result.Data)
|
||||
this.setData({
|
||||
sensorDevice: data
|
||||
})
|
||||
return this.data.sensorDevice
|
||||
},
|
||||
// 查询执行设备状态
|
||||
async queryExecuteDeviceStatus() {
|
||||
let res = await wx.cloud.callFunction({
|
||||
name: 'iothub-shadow-query',
|
||||
data: config.ExecuteDevice,
|
||||
})
|
||||
console.log("executeDevice status", res.result)
|
||||
let deviceData = JSON.parse(res.result.Data)
|
||||
// this.setData({
|
||||
// executeDevice: deviceData.payload.state.reported
|
||||
// })
|
||||
return deviceData.payload.state.reported
|
||||
},
|
||||
async turnOnLight() {
|
||||
await this.control({
|
||||
light: 1
|
||||
})
|
||||
this.setData({
|
||||
[`executeDevice.light`]: 1
|
||||
})
|
||||
},
|
||||
async turnOffLight() {
|
||||
await this.control({
|
||||
light: 0
|
||||
})
|
||||
this.setData({
|
||||
[`executeDevice.light`]: 0
|
||||
})
|
||||
},
|
||||
async turnOnFan() {
|
||||
await this.control({
|
||||
motor: 1
|
||||
})
|
||||
this.setData({
|
||||
[`executeDevice.motor`]: 1
|
||||
})
|
||||
},
|
||||
async turnOffFan() {
|
||||
await this.control({
|
||||
motor: 0
|
||||
})
|
||||
this.setData({
|
||||
[`executeDevice.motor`]: 0
|
||||
})
|
||||
},
|
||||
async control(obj) {
|
||||
let data = config.ExecuteDevice
|
||||
data.Payload = JSON.stringify(obj)
|
||||
await wx.cloud.callFunction({
|
||||
name: 'iothub-publish',
|
||||
data: data
|
||||
})
|
||||
},
|
||||
async switchChange(e) {
|
||||
let value = 0
|
||||
if (e.detail.value == true) {
|
||||
value = 1
|
||||
}
|
||||
let item = e.currentTarget.dataset.item
|
||||
let obj = {
|
||||
[`${item}`]: value
|
||||
}
|
||||
wx.showLoading()
|
||||
let data = config.ExecuteDevice
|
||||
data.Payload = JSON.stringify(obj)
|
||||
console.log(data)
|
||||
await wx.cloud.callFunction({
|
||||
name: 'iothub-publish',
|
||||
data: data
|
||||
})
|
||||
wx.hideLoading()
|
||||
},
|
||||
touchHandler: function(e) {
|
||||
if (!lineChart) {
|
||||
return;
|
||||
}
|
||||
lineChart.scrollStart(e);
|
||||
},
|
||||
moveHandler: function(e) {
|
||||
if (!lineChart) {
|
||||
return;
|
||||
}
|
||||
lineChart.scroll(e);
|
||||
},
|
||||
touchEndHandler: function(e) {
|
||||
if (!lineChart) {
|
||||
return;
|
||||
}
|
||||
lineChart.scrollEnd(e);
|
||||
lineChart.showToolTip(e, {
|
||||
format: function(item, category) {
|
||||
return category + ' ' + item.name + ':' + item.data
|
||||
}
|
||||
});
|
||||
},
|
||||
renderChartHumi: function(windowWidth, windowHeight) {
|
||||
lineChart = new wxCharts({
|
||||
canvasId: 'chartHumi',
|
||||
type: 'line',
|
||||
categories: this.data.chartCategories, // simulationData.categories,
|
||||
animation: false,
|
||||
series: [{
|
||||
name: '湿度',
|
||||
data: this.data.chartDataHumi, // simulationData.data2,
|
||||
format: function(val, name) {
|
||||
return val.toFixed(2) + "%";
|
||||
// return val + "%";
|
||||
}
|
||||
}],
|
||||
xAxis: {
|
||||
disableGrid: false,
|
||||
fontColor: '#666666',
|
||||
},
|
||||
yAxis: {
|
||||
// title: '湿度 (%)',
|
||||
// format: function (val) {
|
||||
// return val.toFixed(2);
|
||||
// },
|
||||
max: 100,
|
||||
min: 50,
|
||||
disabled: false,
|
||||
},
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
legend: true,
|
||||
dataLabel: true,
|
||||
dataPointShape: true,
|
||||
enableScroll: true,
|
||||
extra: {
|
||||
lineStyle: 'curve'
|
||||
},
|
||||
});
|
||||
},
|
||||
renderChartTemp: function(windowWidth, windowHeight) {
|
||||
lineChart = new wxCharts({
|
||||
canvasId: 'chartTemp',
|
||||
type: 'line',
|
||||
categories: this.data.chartCategories, // simulationData.categories,
|
||||
animation: false,
|
||||
series: [{
|
||||
name: '温度',
|
||||
data: this.data.chartDataTemp, //simulationData.data,
|
||||
format: function(val, name) {
|
||||
return val.toFixed(2) + "℃";
|
||||
// return val + "℃";
|
||||
}
|
||||
}],
|
||||
xAxis: {
|
||||
disableGrid: false
|
||||
},
|
||||
yAxis: {
|
||||
// title: '温度 (℃)',
|
||||
// format: function (val) {
|
||||
// return val.toFixed(2);
|
||||
// },
|
||||
min: 15,
|
||||
max: 25,
|
||||
disabled: false,
|
||||
},
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
legend: true,
|
||||
dataLabel: true,
|
||||
dataPointShape: true,
|
||||
enableScroll: true,
|
||||
extra: {
|
||||
lineStyle: 'curve'
|
||||
},
|
||||
});
|
||||
},
|
||||
createSimulationData: function() {
|
||||
var categories = [];
|
||||
var data = [];
|
||||
var data2 = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
categories.push('201620162-' + (i + 1));
|
||||
data.push(Math.random() * (20 - 10) + 10);
|
||||
data2.push(Math.random() * (20 - 10) + 30);
|
||||
}
|
||||
return {
|
||||
categories: categories,
|
||||
data: data,
|
||||
data2: data2
|
||||
}
|
||||
},
|
||||
})
|
@@ -0,0 +1 @@
|
||||
{}
|
@@ -0,0 +1,66 @@
|
||||
<!-- <wxs module="dateUtil" src="../../utils/timeutil.wxs"> -->
|
||||
|
||||
<view style="display:flex; flex-direction:column; align-items:center;">
|
||||
<image style="width:200px; height:60px;" src="../../images/TencentOS_tiny_log.png" mode="cover"></image>
|
||||
</view>
|
||||
|
||||
<view class="body">
|
||||
<form bindsubmit="">
|
||||
<view style="display:flex; flex-direction:row; justify-content: space-between;">
|
||||
<view style="font-weight: bold;">
|
||||
传感设备
|
||||
</view>
|
||||
<view>
|
||||
{{timestamp}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="box">
|
||||
<view class="cell">
|
||||
<view class="left">温度</view>
|
||||
<view class="right">
|
||||
{{sensorDevice.temperature.Value}}
|
||||
</view>
|
||||
<view class="unit">℃</view>
|
||||
</view>
|
||||
<canvas canvas-id="chartTemp" disable-scroll="false" class="canvas" bindtouchstart="touchHandler" bindtouchmove="moveHandler" bindtouchend="touchEndHandler"></canvas>
|
||||
<view class="cell">
|
||||
<view class="left">湿度</view>
|
||||
<view class="right">
|
||||
{{sensorDevice.humidity.Value}}
|
||||
</view>
|
||||
<view class="unit">%</view>
|
||||
</view>
|
||||
</view>
|
||||
<canvas canvas-id="chartHumi" disable-scroll="false" class="canvas" bindtouchstart="touchHandler" bindtouchmove="moveHandler" bindtouchend="touchEndHandler"></canvas>
|
||||
|
||||
</form>
|
||||
|
||||
<form bindsubmit="">
|
||||
<view style="display:flex; flex-direction:row; justify-content: space-between;">
|
||||
<view style="font-weight: bold;">
|
||||
执行设备
|
||||
</view>
|
||||
</view>
|
||||
<view class="box">
|
||||
<view class="cell">
|
||||
<view>照明</view>
|
||||
<view>
|
||||
<switch name="light" data-item="light" checked="{{executeDevice.light}}" bindchange="switchChange" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="cell">
|
||||
<view>风扇</view>
|
||||
<view>
|
||||
<switch name="motor" data-item="motor" checked="{{executeDevice.motor}}" bindchange="switchChange" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="cell">
|
||||
<view>温度门限</view>
|
||||
</view>
|
||||
<slider bindchange="sliderChange" min="0" max="100" value="{{tempHigh}}" show-value/>
|
||||
</view>
|
||||
</form>
|
||||
</view>
|
||||
<view>
|
||||
|
||||
</view>
|
@@ -0,0 +1,32 @@
|
||||
.body {
|
||||
margin: 10rpx 20rpx;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 0rpx 20rpx;
|
||||
border-top: 2px solid #000;
|
||||
}
|
||||
|
||||
.cell {
|
||||
margin-top: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 50%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 40%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.unit {
|
||||
width: 10%;
|
||||
text-align: right;
|
||||
}
|
||||
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
|
||||
"rules": [{
|
||||
"action": "allow",
|
||||
"page": "*"
|
||||
}]
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
var formatNumber = function (n) {
|
||||
n = n.toString()
|
||||
return n[1] ? n : '0' + n
|
||||
}
|
||||
|
||||
var regYear = getRegExp("(y+)", "i");
|
||||
|
||||
var dateFormat = function (timestamp, format) {
|
||||
if (!format) {
|
||||
format = "yyyy-MM-dd hh:mm:ss";
|
||||
}
|
||||
timestamp = parseInt(timestamp);
|
||||
var realDate = getDate(timestamp);
|
||||
function timeFormat(num) {
|
||||
return num < 10 ? '0' + num : num;
|
||||
}
|
||||
var date = [
|
||||
["M+", timeFormat(realDate.getMonth() + 1)],
|
||||
["d+", timeFormat(realDate.getDate())],
|
||||
["h+", timeFormat(realDate.getHours())],
|
||||
["m+", timeFormat(realDate.getMinutes())],
|
||||
["s+", timeFormat(realDate.getSeconds())],
|
||||
["q+", Math.floor((realDate.getMonth() + 3) / 3)],
|
||||
["S+", realDate.getMilliseconds()],
|
||||
];
|
||||
var reg1 = regYear.exec(format);
|
||||
// console.log(reg1[0]);
|
||||
if (reg1) {
|
||||
|
||||
format = format.replace(reg1[1], (realDate.getFullYear() + '').substring(4 - reg1[1].length));
|
||||
}
|
||||
for (var i = 0; i < date.length; i++) {
|
||||
var k = date[i][0];
|
||||
var v = date[i][1];
|
||||
|
||||
var reg2 = getRegExp("(" + k + ")").exec(format);
|
||||
if (reg2) {
|
||||
format = format.replace(reg2[1], reg2[1].length == 1
|
||||
? v : ("00" + v).substring(("" + v).length));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
dateFormat: dateFormat
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
// await util.delayMs(1000)
|
||||
const delayMs = (ms) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function Appendzero(obj) {
|
||||
if (obj < 10) return "0" + "" + obj;
|
||||
else return obj;
|
||||
}
|
||||
|
||||
// epoch => datetime str
|
||||
// 1578450878 => 2019-10-23 18:05:30
|
||||
function formatEpoch(epoch) {
|
||||
var dateTime = new Date(parseInt(epoch) * 1000)
|
||||
var year = dateTime.getFullYear();
|
||||
var month = dateTime.getMonth() + 1;
|
||||
var day = dateTime.getDate();
|
||||
var hour = dateTime.getHours();
|
||||
var minute = dateTime.getMinutes();
|
||||
var second = dateTime.getSeconds();
|
||||
var now = new Date();
|
||||
var now_new = Date.parse(now.toDateString()); //typescript转换写法
|
||||
var milliseconds = now_new - dateTime;
|
||||
var timeSpanStr = year + '-' + Appendzero(month) + '-' + Appendzero(day) + ' ' + Appendzero(hour) + ':' + Appendzero(minute) + ':' + Appendzero(second);
|
||||
return timeSpanStr;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
delayMs: delayMs,
|
||||
formatEpoch: formatEpoch,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user