|
|
|
AIWROK软件手机电量充电监控入库
- /**
- * 案例19:电量/充电监控入库(纯 printl 日志版,输出到 H5 日志终端)
- *
- * 经典实用案例"电池监控/充电记录/低电量报警" :
- * device.getBatteryLevel()/isCharging() 读系统电池状态(零权限,红米真机100%可用)
- * → 每 5 秒采一条(电量/充电状态) → SQLite 入库(battery_records 表)
- * → 逐条完整打印 + 变化检测:插拔充电器打 ⚡/🔌、低电量打 ⚠
- * 另挂 event.onBroadcastEvent 广播监听(系统充电/电量广播一来,msg 立刻打印,事件驱动彩蛋)
- *
- * 与案例15~18 的区别:数据源不再是外部 HTTP 接口,而是**手机自身传感器/系统状态**——
- * 断网也能跑,是"设备监控型"自动化(电量曲线、耗电分析、充电习惯统计都靠它)。
- *
- * 教学演示方法(效果立竿见影):
- * 跑起来后每 5 秒日志跳一条电量记录;拔充电器→立刻打 🔌停止充电,
- * 插上→立刻打 ⚡开始充电,日志和入库同时可见,不用等任何外部条件。
- *
- * 新知识点:
- * 1. device 设备信息 API(getBatteryLevel/isCharging),不依赖网络和无障碍权限;
- * 2. 变化检测(state diff):全局变量记住上一次采样值,本轮对比出"事件"——
- * 连续采样里挖出离散事件,这是监控类脚本的通用手法;
- * 3. event.onBroadcastEvent:系统广播(充电/断充/电量变化)会推给回调,msg 直接打印。
- */
- // ========== 配置区(改这里) ==========
- var 采样间隔秒 = 5; // 每 5 秒采一条(录像教学用;长期挂机可改 60)
- var 低电量报警阈值 = 20; // 电量 < 20% 持续打 ⚠
- var 电量骤变阈值 = 3; // 与上一条比变化 >= 3 个百分点打 ⚠(异常掉电检测)
- var SQLite路径 = "/sdcard/电量记录.db";
- var 表名 = "battery_records";
- // ========== 全局变量顶部声明 ==========
- var gStop = false;
- var 数据库连接 = null;
- var 数据库可用 = false;
- var 累计入库条数 = 0;
- var 上次电量 = -1; // -1=还没采过(首轮不做对比)
- var 上次充电中 = null; // null=还没采过;之后为 true/false
- // ========== importClass 只能放最外层 ==========
- importClass(android.database.sqlite.SQLiteDatabase);
- importClass(java.io.File);
- // ========== 工具函数(先定义后调用) ==========
- // 连接 SQLite(零配置;如需远程库可仿照案例14扩展 mysql 分支)
- function dbConnect() {
- try {
- 数据库连接 = SQLiteDatabase.openOrCreateDatabase(new File(SQLite路径), null);
- 数据库可用 = true;
- printl("[DB] ✅ SQLite 已打开: " + SQLite路径);
- } catch (e) {
- 数据库可用 = false;
- printl("[DB] ⚠ 打开失败(只打印不入库): " + e);
- }
- }
- // 建表(存在则跳过):level/charging/collected_at
- function dbEnsureTable() {
- if (!数据库可用) return;
- try {
- var sql = "CREATE TABLE IF NOT EXISTS " + 表名 + " (" +
- "id INTEGER PRIMARY KEY AUTOINCREMENT," +
- "level INTEGER, charging INTEGER, collected_at INTEGER)";
- 数据库连接.execSQL(sql);
- printl("[DB] ✅ 表已就绪: " + 表名);
- } catch (e) {
- printl("[DB] ⚠ 建表失败: " + e);
- 数据库可用 = false;
- }
- }
- // 单条电量采样入库(charging: 1=充电中 0=未充电)
- function dbInsertBattery(level, charging) {
- if (!数据库可用) return false;
- try {
- var stmt = 数据库连接.compileStatement("INSERT INTO " + 表名 +
- " (level, charging, collected_at) VALUES (?,?,?)");
- stmt.bindLong(1, level);
- stmt.bindLong(2, charging);
- stmt.bindLong(3, time.nowStamp()); // nowStamp=毫秒数字;time.now()是格式化字符串不能bindLong
- stmt.executeInsert();
- stmt.close();
- return true;
- } catch (e) {
- printl("[DB] ❌ 入库失败: " + e);
- return false;
- }
- }
- function dbClose() {
- if (数据库可用 && 数据库连接 != null) {
- try { 数据库连接.close(); } catch (e) {}
- 数据库连接 = null;
- }
- }
- // 读取手机电池状态(零权限;个别机型返回 0~1 小数,统一换算成百分比)
- function 读取电量() {
- try {
- var 电量 = device.getBatteryLevel();
- if (电量 > 0 && 电量 <= 1) 电量 = 电量 * 100;
- var 充电 = false;
- try { 充电 = device.isCharging(); } catch (e) {}
- return { 电量: Math.round(电量), 充电: !!充电 };
- } catch (e) {
- printl("[设备] ❌ 读取电量失败: " + e);
- return null;
- }
- }
- // 变化检测:充电状态翻转=事件,电量骤变/低电量=报警(连续采样里挖离散事件,监控脚本通用手法)
- function 检查状态变化(数据) {
- if (上次充电中 != null && 数据.充电 !== 上次充电中) {
- if (数据.充电) {
- printl("⚡ 事件:开始充电!(充电器已插入) 当前电量 " + 数据.电量 + "%");
- } else {
- printl("🔌 事件:停止充电(充电器已拔出) 当前电量 " + 数据.电量 + "%");
- }
- }
- if (上次电量 >= 0) {
- var 差值 = 数据.电量 - 上次电量;
- if (Math.abs(差值) >= 电量骤变阈值) {
- printl("⚠ 报警:电量骤变 " + (差值 > 0 ? "+" : "") + 差值 + " 个百分点(" +
- 上次电量 + "% → " + 数据.电量 + "%),疑似异常掉电/系统跳变");
- }
- }
- if (数据.电量 < 低电量报警阈值) {
- printl("⚠ 报警:低电量 " + 数据.电量 + "%(阈值 " + 低电量报警阈值 + "%)" +
- (数据.充电 ? ",正在充电中" : ",请尽快充电!"));
- }
- 上次电量 = 数据.电量;
- 上次充电中 = 数据.充电;
- }
- // 一次完整采样:读取→逐条打印→入库→变化检测
- function 采样一次() {
- var 数据 = 读取电量();
- if (数据 == null) return;
- var 状态文本 = 数据.充电 ? "⚡充电中" : "🔋未充电";
- printl("🔋 采样 #" + (累计入库条数 + 1) + " 电量 " + 数据.电量 + "% | " + 状态文本);
- printl(" [1] 电量 : " + 数据.电量 + " %");
- printl(" [2] 充电状态: " + (数据.充电 ? "充电中(1)" : "未充电(0)"));
- printl(" [3] 采集时间: " + time.StampToTime(time.nowStamp()));
- if (dbInsertBattery(数据.电量, 数据.充电 ? 1 : 0)) {
- 累计入库条数++;
- printl(" ✓ 已入库 [" + 累计入库条数 + "] level=" + 数据.电量 +
- " charging=" + (数据.充电 ? 1 : 0) + " → " + SQLite路径);
- } else {
- printl(" ✗ 入库失败");
- }
- 检查状态变化(数据);
- }
- // ========== 主流程 ==========
- printl("===== 案例19 电量/充电监控入库 启动 =====");
- // 步骤0:连库建表
- dbConnect();
- dbEnsureTable();
- printl("⚙ 配置:采样间隔 " + 采样间隔秒 + " 秒 · 低电量阈值 " + 低电量报警阈值 +
- "% · 骤变阈值 " + 电量骤变阈值 + " 个百分点");
- printl("ℹ 零权限数据源:读手机自身电池状态,断网也能跑(设备监控型,区别于15-18的外接API)");
- printl("ℹ 演示方法:拔充电器→日志立刻跳 🔌,插上→立刻跳 ⚡,每 " + 采样间隔秒 + " 秒一条采样");
- // 步骤1:广播监听彩蛋(系统充电/电量广播一来,msg 立刻打印;收不到也不影响主流程)
- try {
- event.onBroadcastEvent(function (msg) {
- var 文本 = "" + msg;
- if (文本.indexOf("BATTERY") >= 0 || 文本.indexOf("POWER") >= 0 ||
- 文本.indexOf("battery") >= 0 || 文本.indexOf("power") >= 0) {
- printl("📻 广播事件(电池相关): " + 文本 + " [" + time.StampToTime(time.nowStamp()) + "]");
- 采样一次(); // 广播驱动立刻补采一条,事件响应比轮询更快
- }
- });
- printl("🎧 广播监听已注册(电池相关广播会触发即时补采)");
- } catch (e) {
- printl("ℹ 广播监听注册失败(不影响轮询采样): " + e);
- }
- // 停止按钮清理(顺序:先置标志,stopAllRuning 放最后)
- event.onStopBtnEvent(function () {
- gStop = true;
- dbClose();
- runTime.stopAllRuning();
- });
- // 步骤2:定时循环采样(每 采样间隔秒 一条,画面持续跳动,方便观察/录像)
- var 轮次 = 0;
- while (!gStop) {
- 轮次++;
- printl("\n---- 第 " + 轮次 + " 轮采样 ----");
- 采样一次();
- var 剩余 = 采样间隔秒;
- while (剩余 > 0 && !gStop) {
- var 步长 = 剩余 >= 5 ? 5 : 剩余;
- sleep.second(秒 = 步长);
- 剩余 = 剩余 - 步长;
- }
- }
- dbClose();
- printl("===== 流程结束:共 " + 轮次 + " 轮采样,累计入库 " + 累计入库条数 + " 条 =====");
复制代码
|
|