0%

Node-Red第二弹-快递最新动态查询跟踪

前言

成果展示

先看最终成果:


起因

话说都 2023 年了,我想要查个快递为什么还那么不省心呢?

前些年,我用淘宝购物比较多,那时候查快递就会选择用 菜鸟裹裹 App (后来改名 菜鸟),主要是我在淘宝上下单之后,在 菜鸟 App 上就能立即查看到,快递数据就直接自动导入了。用起来也是很方便。

不过,后来越发觉得这个 App 有些用着不爽。

一般情况下就是查快递比较多,而里面除了查快递外,其他的功能我根本不会用到。更有甚者,每个页面都是满屏满屏的广告、推荐、商城等等。

而每每看到其越来越大的安装包体积,真是让人有一种 食之无味弃之可惜 的感觉。

20230205104635

后来,我又尝试过使用 快递100 微信小程序

虽说这玩意儿使用上确实是挺简单的,但里面还是充斥着各种广告推广,稍不留神就点进去了。

渐渐的,我也不太喜欢用了。

再后来,听说 支付宝 上也支持查询跟踪快递动态,试过之后才知道,当我想要查询圆通快递,我要先授权登录;当我想要查询韵达快递,我要先授权登录;当我想要查询顺丰快递,我要先授权登录。。。

算了,算了。

最后,还是感觉原生App的快递动态看着顺眼点。而结果就是:

淘宝上的快递,我要去淘宝上查看;京东上的快递,我要去京东上查看;拼多多上的快递,我又要打开拼多多去查看。。。

那有人就说了,这些购物 App 上的快递都会有通知提示的,并不需要你每次都要主动去查看的呀!

然而,这些 App 的系统通知我都是默认禁用的。因为如果你开启系统通知的话,你收到的可就不仅仅是快递通知了。

特别是最近两年,快递派送时总是不会给送上门,而是直接放到快递驿站或快递柜中;或者有的快递送到之后没有打电话通知就莫名被签收了。

基于以上种种的 伪需求 ,我就想自己实现一个快递查询的工具。


探索

我理想中的快递查询工具,至少要满足下面的两点:

  • 无广告(这个肯定不用多说)
  • 当快递动态有变化时,能够第一时间主动通知到我,而不需要我主动的去查看

总结下来就是:及时 省心

很早的一段时间我一直有想要自己实现一个快递查询App的想法。但后来某天突然间就醒悟了:这个需求很简单,而开发一个App的成本却很高,慢慢地也就不了了之了。

再后来接触了智能家居相关的知识之后,发现 Node-Red 简直是一款神器。通过简单的节点拖拽就能实现很复杂的功能逻辑,而且实现的成本也很低。

当然,这还是需要一定的编程基础的。


实现

由于我目前使用的快递查询接口是在网上找的免费接口,仅适用于学习和研究,不便于分享,所以项目暂时没有开源计划 (后续看朋友们的支持情况待定)。

我个人是比较推崇 “授人以鱼不如授人以渔” 的观点,所以具体的实现逻辑部分如果有不太理解的都可以提出来互相讨论。

对于快递查询接口,可以自行去网上查找,可用的还是比较多的。

这里,我把一些关键步骤做一下简单的介绍。

完整流程

先来分享一下完整的流程:

20230205103717

20230205103745

20230205103758

除了NR还需要什么

主要的逻辑就是通过 Node-Red 来实现的。其他的附属包括:

  • 为了实现手动输入快递单号,使用了一个HA的 input_text 辅助元素
  • 为了显示最新动态列表,用的HA的 markdown 卡片
  • 语音TTS播报通过树莓派的蓝牙连接天猫精灵音箱实现,语音TTS选择的是 edge_tts ,中文发音更接近于真人
  • 手机端的通知用的是 Bark Finb/Bark: Bark is an iOS App which allows you to push custom notifications to your iPhone (github.com) 主要是相比其他的通知类工具可以同时设置通知显示的 标题内容
  • 使用了MQTT将Node-Red组装好的数据反馈到HA面板展示

如何开始

我刚开始实现的流程很简单,通过 inject 节点 手动输入一个快点单号,然后 http request 节点 发送请求,解析返回的 json 格式数据,提取最新的一条快递动态,返回输出即可。

其他的功能再不断地进行完善。


暂存快递信息实现

下面是 读取快递记录 节点的代码逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

// 计数器,用于控制请求频率
var exp_count = context.get('exp_count') || 0;

// 获取存储的单号列表
var express = global.get('express','file') || [];

// 查询所有待处理的单号
var exp_list = express.filter(e => e.status < 2);

// 遍历快递单号
for (const exp of exp_list){
msg.payload={
'id':exp.id,
'number':exp.number,
'exname':exp.ex_name,
'remark':exp.remark,
'unix':exp.latest_unix,
}

// 如果当前快递处于派送中,则立即执行
if (exp.latest_code == 8){
node.send(msg);
}else{
// 其他状态下,轮询3次之后才触发执行
if (exp_count % 3 == 0){
node.send(msg);
}
}

}

// 计数器递增存储
exp_count += 1;
if(exp_count >= 9){
exp_count = 0;
}
context.set('exp_count',exp_count);

// 对快递单号进行优先级排序:
// `配送中` -- `运输中` -- `三日内已签收`

express.sort(function(m,n){
return m.status < n.status;
});

var exp_tmp8 = [], exp_tmp3 = [],exp_tmp_other = [];
for(const exp of express){
if(exp.latest_code == 8){
exp_tmp8.push(exp);
}else if(exp.latest_code == 3){
exp_tmp3.push(exp);
}else{
exp_tmp_other.push(exp);
}
}

// 按顺序合并
var express_new = exp_tmp8.concat(exp_tmp3,exp_tmp_other);


// 移除超过2天的已签收快递信息
var time_now = Date.parse(new Date()) / 1000;
var exp_rmv = express_new.find(e=> time_now - e.latest_unix > 172800 );
if (exp_rmv){
// 移除指定项
var index = express_new.indexOf(exp_rmv);
express_new.splice(index,1);
// 更新本地存储
global.set('express',express_new,'file');
}

逻辑说明:

快递数据的存储我使用的是 Node-Redlocalfilesystem 方式,需要在配置文件 settings.js 中手动开启。这样即使 Node-Red 服务重启数据也不会丢失。

为了降低请求查询的频率,我使用 context 实现了一个节点内的计数器逻辑,达到的效果是:

如果当前快递处于 配送中 的状态,就每 20min 查询一次最新动态;而快递处于 运输中或其他的状态时,就每 60min 查询一次最新动态。

对于 已签收 超过2天的快递信息,要自动删除,不再在HA面板中继续展示。


数据展示

提取后的 MQTT 数据为json格式,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"express":[
{
"remark":"6937 电池",
"company":"韵达快递",
"time":"2023-01-30 19:02:04",
"status":"在途中",
"content":"【深圳市】已离开 广东深圳分拨交付中心;发往 北京分拨交付中心"
},
{
"remark":"0605 灯带",
"company":"圆通快递",
"time":"2023-01-30 21:01:58",
"status":"在途中",
"content":"【东莞转运中心】 已发出 下一站 【华北转运中心公司】"
}
]
}

在 HomeAssistant 中如何展示:

1
2
3
4
5
6
7
8
**4909 电池** 【在途中】
- 深圳市 已离开 深圳分拨交付中心 发往 北京分拨交付中心
- 2023-02-01 00:49:54 (韵达快递)
----
**4909 电池** 【在途中】
- 深圳市 已离开 深圳分拨交付中心 发往 北京分拨交付中心
- 2023-02-01 00:49:54 (韵达快递)
----

Markdown 模板:

1
2
3
4
5
6
7
8
9
10
11
12
{% set item_count = states('sensor.express_tracking') %}
{% if item_count | int > 0 %}
您当前共有 {{ item_count }} 个快递
----
{% endif %}
{% set item_list = state_attr('sensor.express_tracking','express') %}
{% for item in item_list %}
**{{item.remark}}** 【{{item.status}}】
- {{item.content}}
- {{item.time}} ({{item.company}})
----
{% endfor %}

其中,sensor.express_tracking 是一个 MQTT 传感器, mqtt.yaml

1
2
3
4
5
6
7
8
9
sensor:
# 快递动态跟踪
- unique_id: "express_tracking"
object_id: "express_tracking"
name: "快递动态跟踪"
state_topic: "express_tracking"
value_template: '{{ value_json.express | list | count }}'
json_attributes_topic: "express_tracking"
json_attributes_template: '{{ value_json | tojson }}'

手机号查询

在网上也经常看到有朋友问是否能通过手机号直接查询到该手机号下相关的所有快递单号信息,我也做了一定的了解。

这个效果前几年我记得某些手机上就内置了这种功能,但近两年国家对快递信息个人隐私的逐渐重视,慢慢的有些快递api就不再支持查询了。

我也找了一些api接口,有的要么需要公司资质开通,有的就是限制比较多。可见国家对快递行业的用户隐私重视的力度还是很大的。

所以这方面还是不要太苛求了。


功能

目前已经实现的功能如下:

v1.0

  • 支持手动输入快递单号及备注信息
  • 输入快递单号后立即查询并播报通知当前最新动态
  • 支持快递单号实时动态自动跟踪
  • 定时查询,设置每隔 1 小时查询一次最新动态,频率为 1次/60min
  • 有新动态时发送消息通知到手机端同时天猫精灵 TTS 播报

v2.0

  • 支持多个快递单号的查询
  • 已签收 的快递信息超过 3 日自动删除
  • 在 HA 面板显示最新快递动态
  • 支持 夜间勿扰模式,设置仅在每天 7:00~23:00 之间执行查询及播报通知操作
  • 支持手动删除某快递信息不再跟踪

v3.0

  • 定时查询增加 随机延迟 机制,防止查询播报冲突,降低接口请求频率,当前为 2~10min
  • 单号后4位+备注 相同时判定为重复单号,自动去重并立即查询最新动态
  • 对快递信息按照 状态优先顺序 排序展示在 HA 面板 : 派送中在途中三日内已签收
  • 对处于 派件中已签收 状态下的动态内容做优化,去除一些不重要信息
  • 对处于 派件中 状态的快递提高检测查询效率,设定频率为 1次/20min
    - 调整定时查询频率为 `1次/20min`  
    - 当快递状态为 `派件中` 时,使用频率为 `1次/20min`  
    - 当快递状态为其他时,使用频率为 `1次/60min`  
    - 通过 `context` 节点设置变量取 `1~9` 的数值和 `3` 取余得到,示例代码如下  
    - 调整延迟频率为 `2~10min` 内
  • 支持手动触发 立即查询 最新动态操作
  • 优化数字字符串的 TTS 播报:解决 tts 播报数字类似 155 读成一百五十五的问题
  • 优化: 已签收 快递信息留存时间改为 2 天内

如有疑问或需要技术讨论,请留言或发邮件到 service@itfanr.cc