pATAq 发表于 2020-5-24 23:07:03

#创意# 懒人的快递提醒小助手 Part 2

本帖最后由 pATAq 于 2020-5-26 09:36 编辑

# 前言

欢迎小伙伴们来到第二部分,在前文中 (https://www.cnblogs.com/sjqlwy/p/df51lazy_p1_lp_linux.html) 我们费尽周折配置好了相关环境,本文开始进入第二部分:Core Code to Track the Packages | **包裹监视追踪项目的核心代码**。

在这里我们会用到以下知识:

1. Interactionbetween MPU and MCU on LattePanda with Firmata protocol | **LattePanda上Linux和Arduino使用Firmata协议进行交互**
2. Python GUI Programming with remi | **Python图形化编程(使用remi)**
3. Usage of TinyDB——a lightweight database | **介绍轻量级数据库TinyDB的使用**
4. Wechat notification via ServerChan | **使用Server酱实现微信通知功能**
5. Remote Development with Visual Studio Code | **使用VSC进行远程开发**

这一部分将实现如下功能:

1. 网页前端添加待监视快递信息
2. 查询的所有快递历史记录
3. 自定义查询频率
4. 快递信息变动时微信通知

## the Purpose of this Project | 为什么要做这个项目

可能很多小伙伴要问了,我的淘宝、京东、菜鸟裹裹也可以帮我查快递,为啥还要弄这个哩?

1. 隐私性。现在的信息泄露太严重了,不想使用第三方软件。
2. 合并通知。希望有一个软件可以统一查询大多数的
3. 减少冗杂的程序。不希望安装过多软件,特别国内的一些软件太流氓,捆绑过多无用功能
4. **物流信息的订阅与实时追踪。**一般的是服务端定时 push 物流信息到手机上,可能存在一定滞后性,而主动 pull 物流信息则可以更快获得相关信息。

另外我不知道大家是怎样,我有时候买到心仪的东西,特别盼着早点到货,然后隔一会就去打开手机客户端刷新一下查看快递信息,时间久了浪费生命,所以想着用电脑帮我查,有变动主动告诉我。

## Project Architecture | 项目架构



1. 通过网页前端或者命令行前端,录入待查询快递信息到数据库中,例如 `TinyDB`。
2. 读取数据库中的信息,利用 `Requests` 从互联网上的相关网站获取物流信息。
3. 对获取的物流信息进行处理、如果有变动则录入数据库中,同时调用微信通知接口,使用 Firmata 协议与板载 ArduinoLeonardo 交互。
4. 使用计划任务 `crontab` 定期获取物流信息。
5. 使用 Kismet 进行网络嗅探,判断目标设备是否在附近,是的话就启用额外的通知手段。否则只微信通知和在网页面板上显示。

**注意:** Kismet 网络嗅探将在第三部分中详述。

### 配置VSC远程开发环境

参考 (https://code.visualstudio.com/docs/remote/ssh) 即可。官方提供的工具,非常简单方便。

* 注意一些拓展需要在 Server 上重新安装一遍,
* 支持密钥和账号密码登陆,连接远程服务器时每次都要输入密码,不会保存。建议使用密钥登陆
* Windows 需要ssh-client,我的 Windows_10_1909 默认安装了,没有的在应用-可选里面安装 OpenSSH-Client ,使用 ssh 登陆后会自动配置 server 。



### 安装常用库

* (https://github.com/msiemens/tinydb) 简单轻量的数据库,除了官方文档,也可以看看 [王大桃zzZ](https://www.cnblogs.com/watalo/p/12392518.html) 的系列分享
* (https://github.com/psf/requests) | 优雅好用的 Python HTTP 库,an elegant and simple HTTP library for Python, built for human beings.

### 提供物流信息查询服务API的网站



1. kuaidi100、kuaidi、baidu—— (https://github.com/minosss/express-plus) 上述实行了严格的请求限制,不推荐使用

2. [伙伴数据资源](http://www.kdpt.net/express_api.html) | **推荐** 加客服QQ即可以获得密钥,调用简单,但是支持的快递公司不全

3. (https://www.trackingmore.com/cn.html) | [免费API申请](https://www.trackingmore.com/apply-api-cn.html),不推荐

4. [快宝开放平台](https://open.kuaidihelp.com/serve/wlgz) | 可用,类似快递鸟,需要加密

5. [快递鸟](http://www.kdniao.com) | **推荐**,支持快递公司较全,但是调用麻烦点?参考 (https://github.com/menduo/kdniao_python) 及 (https://www.v2ex.com/t/310616)

这里我使用的是伙伴数据资源提供的API,[首页](http://www.kdpt.net/)可以看到支持的快递公司列表。



### 核心代码(后端)
```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'Code_Paintium'

import requests, time, sys
from tinydb import TinyDB, Query

db = TinyDB("/home/sjqlwy/Projects/kd/db.json")
parcel = Query()

# 填入自己的授权密钥,http://www.kdpt.net/express_api.html
# 支持快递公司列表 http://www.kdpt.net/index.html#companies
id = 'YOURIDKEY'


# 增加数据记录
def AddItem(name, com, postid):
    if db.search(parcel.postid == postid) == []:
      db.insert({'name': str(name), 'com': str(com), 'postid': str(postid), 'Fstatus': {}, 'Cstatus': {}, 'state': ''})


# 微信提醒
def fqtt(desp):
    payload_ftqq = {'text': name, 'desp': str(desp)}
    requests.get('https://sc.ftqq.com/YOURKEY.send', params=payload_ftqq)


# 检查快递状态
def check_status(com, postid, name, Cstatus):
    payload_status = {'id': id, 'com': com, 'nu': postid}
    r = requests.get('http://q.kdpt.net/api', params=payload_status).json()
    if Cstatus != r['data']:
      Cstatus = r['data']
      desp = Cstatus['time'], Cstatus['context']
      fqtt(desp)
      db.update({'Cstatus': Cstatus, 'Fstatus': r['data'], 'state': r['state']}, parcel.postid == postid)


# Main,当没有输入参数时检查已有数据;正确输入参数时创建条目;错误输入时提示
if len(sys.argv) == 4:
    AddItem(sys.argv, sys.argv, sys.argv)
elif len(sys.argv) == 1:
    pass
else:
    print("INVAIND INPUT! pls input name, companny, postid and try again.")
    print("e.g., python3 kd.py Gaoda zhongtong zt1124991")
    exit

# 仅检查未签收的项目,注意调用时间不小于1s
for item in db.search(parcel.state != '3'):
    com = item['com']
    postid = item['postid']
    name = item['name']
    Cstatus = item['Cstatus']
    # print(Cstatus)
    try:
      check_status(com, postid, name, Cstatus)
    except IndexError:
      print("Delivery infomation not found yet! Please try again later.")
    else:
      time.sleep(2)
```

**代码逻辑:**

因为整体代码比较简单,所以就不用面向对象代码了。

* `AddItem()` 向表中写入数据,该表中包括以下内容:
* 物品名称 name
* 快递公司 com
* 快递单号 str
* 物流全程信息 Fstatus
* 最新物流信息 Cstatus
* 是否已签收 state
* `fqtt()` 用来实现微信提醒功能,借助简单好用的 Server酱 的服务
* `check_status()` 获取快递的最新物流信息,通过 [伙伴数据资源](http://www.kdpt.net/) 提供的API,可以联系QQ获取KEY
* 首先分析获取的数据,将最新的物流状态与已保存对比
* 有变化的话更新数据库内容
* 运行程序时如果没有参数,则循环查询各个快递状态,有完整参数时添加新的快递以便追踪,参数不对时报错
* 循环查询时,采用`db.search(parcel.state != '3')`来排除已经签收的* 发现仅有单号但未有物流信息时,r['data'] 为 [] ,此时会 r['data'] 报错 `IndexError: list index out of range` 。



#### 如何使用

* 你需要去 (http://sc.ftqq.com/) 注册登录账号,获取自己的 **SCKEY** ,然后绑定微信即可
* 去伙伴数据联系客服 QQ 免费获取自己的 **IDKEY**
* 设置**数据库存储路径**,建议使用绝对路径,我的是`/home/sjqlwy/Projects/kd/db.json`

效果展示:

```shell
# 首次需要输入参数以向数据库中添加相关信息,为物品名称(自定义)、快递公司(可以选auto来自动识别,如果获取信息错误可以移步伙伴数据获取名称,如shentong)、快递单号。
python3 kd.py FS-T6 auto 773037873703622
# 录入信息后,后续查询不带参数运行则会遍历数据库中所有未签收的快递
python3 kd.py
```





### 定期执行

这里想到最省资源最方便的就是系统自带的计划任务,定时每 1h 无参数运行 `kd.py` 即可。

```shell
crontab -l
crontab -e
0 */1 * * * /usr/bin/python3 /home/sjqlwy/Projects/kd/kd.py
2 */1 * * * /usr/bin/curl -l https://hc-ping.com/6ebdfd32-1fd5-4ecf-******* # use your own
```

同时添加计划任务提醒,这里借助 (https://healthchecks.io/) 。填写任务名称,备注等,然后设置时间和之前的差不多,执行的命令为向下述网址发送一个 http 请求,建议用 `curl -l URL`, 最省事。

![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200524211404.png)

关于 crontab 的使用参考 [之前的文章](https://www.cnblogs.com/sjqlwy/p/ddns.html) ,直接 crontab -e 编辑即可,不需要重启服务,现在都是 systemd 接管。记得设置好时区,参考 (https://wiki.archlinux.org/index.php/System_time_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) 。

## 网页前端

相信了解我的小伙伴知道我之前在 (https://www.cnblogs.com/sjqlwy/p/lattepanda_pyui.html) 一文中介绍过 (https://github.com/dddomodossola/remi), Remi is a GUI library for Python applications that gets rendered in web browsers。



该前端实现的功能就是增加欲追踪包裹数据,发给后端。remi带了一个图形化编辑器,支持拖拽。

## 使用 Firmata协议 交互

之前写过一部分,由于相关库更新,这里写一下。

之前主要介绍三个两款:

*PyMata 和PyMata-AIO,目前已更新为 (https://github.com/MrYsLab/pymata-express) 和 (https://github.com/MrYsLab/pymata4), 支持 (https://github.com/MrYsLab/FirmataExpress) 。
* (https://github.com/tino/pyFirmata) | 仅支持到 Firmata 2.1和 StandardaFirmata

如果你需要的功能不多,用起来越简单越好,就选pyFirmata。如果你有一些高级功能的应用需求,就选上面的两个,同时提供了增强版的(https://github.com/MrYsLab/FirmataExpress) sketch, 相比Arduino IDE 自带的 StandardFirmata 它提供了如下特性:

- **HC-SR04 Ultrasonic Distance Sensors using a single pin.**
- **DHT Humidity/Temperature Sensors.**
- **Stepper Motors.**
- **Piezo Tone Generation.**
- **Baud rate of 115200**

### 更新Arduino IDE

之前用的 `sudo apt install arduino`进行安装的,打开感觉不太对,一股子历史感。一看还是超级老的版本,怀疑根本没人维护源里面的这个软件包,果断官网下载最新的 `arduino ide 1.8.12`。同时通过包管理器安装 ``



```python
from pyfirmata import Arduino, util
board = Arduino('/dev/ttyACM0')
board.digital.write(1)
```

剩下的就简单了,创建一个函数 `ArduCom()` , 想干啥干啥了。

## Additional Functions | 额外功能

### 热敏打印机

今天看了葛老师的帖子,想着也可以用那个标签打印机打印出来,后续会更新。

### 网页面板

类似 Home Assistant 那种

## 下期预告

想要实现一个功能,就是如果探测到人在旁边,就激活额外的提醒功能,例如语音播报等。难点就是如何知道人在旁边,[发现的一个思路]()就是使用 Kismet 这个软件进行无线嗅探。

一般情况下,我们的设备是如何连接到已经保存的无线网的呢?原理大致是设备一直向空中发送数据包,询问名为 XXX0 的无线网络在吗,如果无应答,就继续询问 XXX1 在吗?而如果相应的无线网络存在,就会回复我在,可以连接。

**题外话:** 这也就是网络欺骗的原理之一,设备询问有没有 XXX0 的时候,按规矩如果你不是 XXX0 的话,就不要吱声,而诱骗设备则会告诉设备我就是 XXX0 ,然后接收设备提供的密码进行连接,继而实现 mitm 中间人攻击。

那我们实现无线追踪的方法就是记录设备的 mac 地址,听到该设备在吆喝,就知道人在旁边。因为需要一个支持 monitor 模式的无线网卡,所以我们要额外通过有线网或者第二块无线网卡以供连接。因为板载的无线网卡虽然支持 monitor/station/client 模式的,但是 `iw list` 不支持同时运行2个以上模式。当然 Kismet 也支持蓝牙嗅探,但是需要专门的蓝牙芯片,适用性不那么强,而且并不是所有人的手机蓝牙都是一直开着的,比如我。

效果类似于这样:



好,我们下期再见!## 版本更新历史
1. 2020年5月26日
* 增加核心代码中所查询单号尚未有物流信息的错误。
* 更正 healthchecks.io 图片链接,该网站目前打开特别慢



珠韵 发表于 2020-5-25 09:26:02

这太厉害了,慢慢看,慢慢消化学习

gray6666 发表于 2020-5-26 07:44:43

涨知识,学习了{:6_213:}

rzegkly 发表于 2020-5-26 16:29:55

厉害了

hnyzcj 发表于 2020-5-26 18:36:12

DFHJM_IpFmV 发表于 2020-5-31 12:11:29

gray6666 发表于 2020-5-26 07:44
涨知识,学习了

这个厉害呀!

DFS1w2cb8o8 发表于 2020-6-9 17:49:01

厉害厉害

微笑的rockets 发表于 2020-6-12 10:54:46

关注之。

DFHJM_IpFmV 发表于 2020-7-2 10:05:05

这个lattepanda自己就可以编程吧?

DFHJM_IpFmV 发表于 2020-7-2 10:23:51

就不用软件写入程序吗?

pATAq 发表于 2020-7-2 11:01:01

DFHJM_IpFmV 发表于 2020-7-2 10:23
就不用软件写入程序吗?

啥意思?LattePanda 本身就是一台小电脑,可以直接在 LP 上编程,但是我很多时候不接显示器用,所以用的 VS Code 的远程开发模式

DFHJM_IpFmV 发表于 2020-7-2 11:05:46

pATAq 发表于 2020-7-2 11:01
啥意思?LattePanda 本身就是一台小电脑,可以直接在 LP 上编程,但是我很多时候不接显示器用,所以用的...

哦,好吧{:7_218:}@pATAq
页: [1]
查看完整版本: #创意# 懒人的快递提醒小助手 Part 2