基于Thingsboard和Flutter的花园IoT项目,实现警报推送功能
一 配置环境
thingsboard平台为开发者提供了三种部署方案本地部署,在线演示以及云服务,这里不对此过多展开,详细教程请见ThingsBoard专业版安装选项 | ThingsBoard专业版 (ithingsboard.com)
二 需求分析
本项目旨在基于thingsboard平台来通过网络监控以及管理花园具体需求如下
(1)温度的测量
分别使用两个温度传感器来分别遥测室内和室外的温度。
(2)湿度的测量
分别使用两个湿度传感器来遥测空气中以及土壤中的湿度。
(3)室内恒温系统
将花园内的温度控制在25度。
(4)灌溉系统
通过湿度传感器传回的遥测数据判断是否需要灌溉,灌溉系统中包括水箱,水泵两个设备来保证灌溉系统的运转。
(5)照明系统
照明系统将通过时间以及天气预报等信息等决策照明系统的光照强度。
(6)空气质量的检测
通过空气质量传感器来传回空气质量的遥测数据。
(7)空气循环系统
通过空气循环系统保证花园内空气的流通。
三 实现流程
(1)创建设备
点击加号添加设备。
(2)设计仪表盘
(3)设计规则引擎
新建一个规则链
第一步
我们实现对低温以及高温的情况下实现警报
其中高温的脚本内容如下
return msg.temperature > 30;
低温的脚本内容如下
return msg.temperature<5;
第二步
在检测到土壤湿度低于等于50时向灌溉系统发出rpc request打开灌溉系统,高于等于80时关闭
rpc msg节点代码
var newMsg = {};
var value = msg.humidity;
if(value<=50){
newMsg.method='turnon';
newMsg.params =true;
}
if(value>=80){
newMsg.method='turnoff';
newMsg.params =false;
}
if(newMsg.method == 'turnon' || 'turnoff'){
msgType = 'RPC message';
}
return {msg: newMsg, metadata: metadata, msgType: msgType};
check节点代码
return msgType == 'RPC message';
第三步
在检测到亮度处于20以下以及80以上时进入判断是否需要对照明系统进行开关的操作
亮度检测
return msg.brightness<20||msg.brightness>80;
照明系统rpc msg
var newMsg = {};
var value = msg.brightness;
if(value<=20){
newMsg.method='lighton';
newMsg.params =true;
}
if(value>=80){
newMsg.method='lightoff';
newMsg.params =false;
}
if(newMsg.method == 'lighton' || 'lightoff'){
msgType = 'RPC message';
}
return {msg: newMsg, metadata: metadata, msgType: msgType};
check节点代码
return msg.brightness<20||msg.brightness>80;
第四步
根据温度传感器传入的警报控制恒温系统的温度
降温脚本
var newMsg = {};
newMsg.method='tempaturedown';
newMsg.params =true;
msgType = 'RPC message';
return {msg: newMsg, metadata: metadata, msgType: msgType};
升温脚本
var newMsg = {};
newMsg.method='tempatureup';
newMsg.params =true;
msgType = 'RPC message';
return {msg: newMsg, metadata: metadata, msgType: msgType};
第五步
根据空气质量决定当天的空气循环模式
空气质量脚本
return msg.airquality > 20||msg.airquality<40;
空气循环系统
var newMsg = {};
var value = msg.airquality;
if(value<=20){
newMsg.method='indoor';
newMsg.params =true;
}
if(value>=80){
newMsg.method='outdoor';
newMsg.params =false;
}
if(newMsg.method == 'indoor' || 'outdoor'){
msgType = 'RPC message';
}
return {msg: newMsg, metadata: metadata, msgType: msgType};
check节点
return msgType == 'RPC message';
(4)配置thingsboard移动应用程序
详情参考ThingsBoard 移动应用程序入门 |ThingsBoard Mobile 应用程序 (ithingsboard.com)
thingsboard源码
git clone https://github.com/thingsboard/flutter_thingsboard_app.git
(5)在编译器中配置插件
flutter_notification_listener和jpush_flutter两个插件
打开pubspec.yaml这个文件
在
dependencies:
flutter_notification_listener: <latest_version>
jpush_flutter:<latest_version>
剩余的配置请参照 SDK 集成指南 – 极光文档 (jiguang.cn)
(6)代码编写
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_notification_listener/flutter_notification_listener.dart';
import 'package:universal_platform/universal_platform.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:thingsboard_app/config/routes/router.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/modules/dashboard/main_dashboard_page.dart';
import 'package:thingsboard_app/widgets/two_page_view.dart';
import 'config/themes/tb_theme.dart';
import 'generated/l10n.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
final appRouter = ThingsboardAppRouter();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// await FlutterDownloader.initialize();
// await Permission.storage.request();
if (UniversalPlatform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
final jpushManager = JPushManager();
await jpushManager.initJPush();
runApp(ThingsboardApp());
}
class ThingsboardApp extends StatefulWidget {
ThingsboardApp({Key? key}) : super(key: key);
@override
ThingsboardAppState createState() => ThingsboardAppState();
}
class ThingsboardAppState extends State<ThingsboardApp>
with TickerProviderStateMixin
implements TbMainDashboardHolder {
final TwoPageViewController _mainPageViewController = TwoPageViewController();
final MainDashboardPageController _mainDashboardPageController =
MainDashboardPageController();
final GlobalKey mainAppKey = GlobalKey();
final GlobalKey dashboardKey = GlobalKey();
@override
void initState() {
super.initState();
appRouter.tbContext.setMainDashboardHolder(this);
}
@override
Future<void> navigateToDashboard(String dashboardId,
{String? dashboardTitle,
String? state,
bool? hideToolbar,
bool animate = true}) async {
await _mainDashboardPageController.openDashboard(dashboardId,
dashboardTitle: dashboardTitle, state: state, hideToolbar: hideToolbar);
_openDashboard(animate: animate);
}
@override
Future<bool> dashboardGoBack() async {
if (_mainPageViewController.index == 1) {
var canGoBack = await _mainDashboardPageController.dashboardGoBack();
if (canGoBack) {
closeDashboard();
}
return false;
}
return true;
}
@override
Future<bool> openMain({bool animate = true}) async {
return _openMain(animate: animate);
}
@override
Future<bool> closeMain({bool animate = true}) async {
return _closeMain(animate: animate);
}
@override
Future<bool> openDashboard({bool animate = true}) async {
return _openDashboard(animate: animate);
}
@override
Future<bool> closeDashboard({bool animate = true}) {
return _closeDashboard(animate: animate);
}
bool isDashboardOpen() {
return _mainPageViewController.index == 1;
}
Future<bool> _openMain({bool animate = true}) async {
var res = await _mainPageViewController.open(0, animate: animate);
if (res) {
await _mainDashboardPageController.deactivateDashboard();
}
return res;
}
Future<bool> _closeMain({bool animate = true}) async {
if (!isDashboardOpen()) {
await _mainDashboardPageController.activateDashboard();
}
return _mainPageViewController.close(0, animate: animate);
}
Future<bool> _openDashboard({bool animate = true}) async {
if (!isDashboardOpen()) {
_mainDashboardPageController.activateDashboard();
}
return _mainPageViewController.open(1, animate: animate);
}
Future<bool> _closeDashboard({bool animate = true}) async {
var res = await _mainPageViewController.close(1, animate: animate);
if (res) {
_mainDashboardPageController.deactivateDashboard();
}
return res;
}
// define the handler for ui
void onData(NotificationEvent event) {
JPushManager pusher=new JPushManager();
print(event.toString());
// 检查接收到的通知是否是期望的
if (event.packageName == "your.package.name" && event.title == "Your Notification Title") {
// 自定义本地通知的内容
pusher.sendLocalNotification(
id: 1,
title: "Alarm Notification",
content: "This is an alarm notification.",
fireTime: DateTime.now(),
);
}
}
Future<void> initPlatformState() async {
NotificationsListener.initialize();
// register you event handler in the ui logic.
NotificationsListener.receivePort?.listen((evt) => onData(evt));
}
void startListening() async {
print("start listening");
var hasPermission = await NotificationsListener.hasPermission;
if (!hasPermission!) {
print("no permission, so open settings");
NotificationsListener.openPermissionSettings();
return;
}
var isR = await NotificationsListener.isRunning;
if (!isR!) {
await NotificationsListener.startService();
}
bool started=false;
setState(() => started = true);
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: Colors.white,
statusBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.light));
return MaterialApp(
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
onGenerateTitle: (BuildContext context) => S.of(context).appTitle,
themeMode: ThemeMode.light,
home: TwoPageView(
controller: _mainPageViewController,
first: MaterialApp(
key: mainAppKey,
scaffoldMessengerKey: appRouter.tbContext.messengerKey,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
onGenerateTitle: (BuildContext context) => S.of(context).appTitle,
theme: tbTheme,
themeMode: ThemeMode.light,
darkTheme: tbDarkTheme,
onGenerateRoute: appRouter.router.generator,
navigatorObservers: [appRouter.tbContext.routeObserver],
),
second: MaterialApp(
key: dashboardKey,
// scaffoldMessengerKey: appRouter.tbContext.messengerKey,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
onGenerateTitle: (BuildContext context) => S.of(context).appTitle,
theme: tbTheme,
themeMode: ThemeMode.light,
darkTheme: tbDarkTheme,
home: MainDashboardPage(appRouter.tbContext,
controller: _mainDashboardPageController),
)));
}
}
class JPushManager {
final JPush jpush;
JPushManager() : jpush = JPush();
Future<void> initJPush() async {
jpush.setup(
appKey: "fef07427951f3ee386047668", // 替换成你自己的 JPush AppKey
channel: "flutter_channel",
production: false,
debug: true,
);
}
void sendLocalNotification(
{required int id,
required String title,
required String content,
required DateTime fireTime,
int? buildId, // 添加 buildId 参数
Map<String, String>? extra,
int badge = 0,
String? soundName,
String? subtitle,
Function(String? res)? onCallback}) {
// 一秒后出发本地推送
var fireDate = DateTime.fromMillisecondsSinceEpoch(
DateTime.now().millisecondsSinceEpoch + 1000);
var localNotification = LocalNotification(
id: id,
buildId: buildId, // 传递 buildId
title: title,
content: content,
fireTime: fireTime,
subtitle: subtitle,
badge: badge,
extra: extra);
jpush.sendLocalNotification(localNotification).then((res) {
if (onCallback != null) {
onCallback(res);
}
});
}
}
if (event.packageName == "your.package.name" && event.title == "Your Notification Title")这段代码需要自行修改来实现在特定情况下推送通知。