mini program: add qcloud_device_linkage_demo

This commit is contained in:
royye62
2020-04-12 03:24:18 +08:00
parent ccde26c1e9
commit 3e757c1688
22 changed files with 3494 additions and 0 deletions

View File

@@ -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,
})
}
}
})

View File

@@ -0,0 +1,13 @@
{
"pages": [
"pages/index/index"
],
"window": {
"backgroundColor": "#F6F6F6",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#F6F6F6",
"navigationBarTitleText": "设备联动Demo",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json"
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
}
},
})

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

View File

@@ -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
}

View File

@@ -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