监控
TL;DR: 被动蓝牙存在检测信标、手机和其他蓝牙设备。对基于MQTT的家庭自动化很有用,特别是当脚本运行在分布在整个房产中的多个设备上时。
安装说明
从头开始设置树莓派
树莓派Zero W安装说明
SD卡设置
-
从这里下载最新版的raspbian
-
从etcher.io下载etcher
-
将raspbian lite buster镜像刻录到SD卡上。说明在这里。
-
挂载刻录好的SD卡的boot分区(拔出后再插入)
-
要启用ssh,在根目录创建一个无扩展名的空文件叫ssh
-
设置Wi-Fi,在根目录创建wpa_supplicant.conf文件,添加家庭Wi-Fi的详细信息:
country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid="您的网络名称"
psk="您的网络密码"
key_mgmt=WPA-PSK
}
- 第一次启动时,插入SD卡并开启树莓派Zero W。第一次启动时,新创建的wpa_supplicant.conf文件和ssh将被移动到适当的目录。通过路由器找到Pi的IP地址。
配置和设置
- 通过SSH登录到树莓派(默认密码: raspberry):
ssh pi@theipaddress
- 更改默认密码:
sudo passwd pi
- 更新和升级:
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
sudo reboot
- 如果需要,安装蓝牙固件:
#为Pi Zero W安装蓝牙驱动
sudo apt-get install pi-bluetooth
- 重启:
sudo reboot
- 安装Mosquitto 1.5+(重要步骤!):
# 获取仓库密钥
wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
#添加仓库
sudo apt-key add mosquitto-repo.gpg.key
#下载适当的列表文件
cd /etc/apt/sources.list.d/
sudo wget http://repo.mosquitto.org/debian/mosquitto-buster.list
#更新缓存并安装
sudo apt-cache search mosquitto
sudo apt-get update
sudo apt-get install -f libmosquitto-dev mosquitto mosquitto-clients libmosquitto1
监控设置
设置 monitor
- 克隆
monitor
Git:
#安装git
cd ~
sudo apt-get install git
#克隆此仓库
git clone https://github.com/andrewjfreyer/monitor.git
#进入 `monitor` 目录
cd monitor/
#(可选) 切换到beta分支以获取最新的更新和功能(可能不稳定)
git checkout beta
- 首次运行:
将创建配置文件,使用默认首选项。任何未安装的可执行文件都将被报告。所有这些都可以通过 apt-get install ...
进行安装。
sudo bash monitor.sh
- 编辑 mqtt_preferences 文件:
sudo nano mqtt_preferences
- 编辑 known_static_addresses (手机、笔记本电脑、一些智能手表):
sudo nano known_static_addresses
或者,向 monitor/setup/ADD STATIC DEVICE
发送一个MQTT消息,消息包含MAC地址和别名,用空格分隔:
topic: monitor/setup/ADD STATIC DEVICE
message: 00:11:22:33:44:55 alias
使用 monitor/setup/DELETE STATIC DEVICE
并给出包含MAC地址的消息从所有 monitor
节点中删除一个设备。
- 阅读帮助文件:
sudo bash monitor.sh -h
现在基本设置就完成了。您的代理应该正在接收消息,每次树莓派启动时 monitor
服务都会重新启动。按照当前的配置,您应该几次从命令行运行 sudo bash monitor.sh
以了解脚本的工作方式。
背景和技术细节
亮点
monitor
向指定的代理发送一个包含置信度值(0到100)的JSON格式的MQTT消息,当一个指定的蓝牙设备对 name
查询做出响应时。默认情况下,在接收到先前未见过的设备(如以外围模式广告连接能力的设备)的匿名广告后,会触发 name
查询。
示例JSON数据包:
topic: monitor/{{monitor安装名称}}/{{mac地址}}
message: {
"id":"{{mac地址}}",
"confidence":"{{从0到100}}",
"name":"{{如果可用}}",
"manufacturer":{{如果可用}}",
"type":"KNOWN_MAC",
"retained":"{{消息保留?}}",
"timestamp":"{{发送此消息的格式化日期}}",
"version":"{{monitor版本}}"
}
此外,还可以选择向同一代理报告当公开广告的信标设备或iBeacon设备广告时的JSON格式的MQTT消息。
示例JSON数据包:
topic: monitor/{{monitor安装名称}}/{{mac地址或ibeacon uuid}}
message: {
"id":"{{mac地址或ibeacon uuid}}",
"report_delay":"{{第一次检测到到这条消息的延迟秒数}}",
"flags":"{{GAP标志}}",
"movement":"stationary",
"confidence":"{{从0到100}}",
"name":"{{如果可用}}",
"power":"{{如果可用}}",
"rssi":"{{如果可用}}",
"mac":"{{如果是ibeacon,与uuid关联的当前mac地址}}",
"manufacturer":{{如果可用}}",
"type":"{{GENERIC_BEACON_PUBLIC or APPLE_IBEACON}},
"retained":"{{消息保留?}}",
"timestamp":"{{发送此消息的格式化日期}}",
"version":"{{monitor版本}}"
}
关于蓝牙存在问题的简单类比
想象你被蒙上眼睛在一个大房间里和其他人在一起。我们想知道哪些朋友在场,哪些朋友不在场:
房间里的一些人会定期发出声音(比如吃薯片、打喷嚏、咳嗽等),另一些人则安静地坐着,除非你专门叫他们的名字,还有一些人会定期大声宣布自己的名字,无论你是否想让他们这么做:
这里的问题是,你不能大喊"谁在这里",因为这样大家都会同时说自己的名字,你就无法分辨了。同样,出于显而易见的原因,你也不能简单地问"谁不在这里?"
所以,你像点名一样。房间里的每个人只有在叫他们的名字时才会回应。
所以,一种点名的方法是一个一个地大声喊出朋友的名字,然后等待一会儿再喊一次。
一旦一个朋友停止响应(一段时间内),你就presumed他或她已经离开了:
这种技术应该可以正常工作,但有一个小问题。你一直在对着房间大喊,这意味着你很难听到轻微的响应,其他人也很难进行对话。还有什么其他的方法吗?我们能利用那些随机的声音吗?
这样,你就不必一直向房间里的所有朋友询问了。高效!
这个技术是对 monitor
如何为手机(好友列表)和信标(大声宣布名称)等设备工作的一个非常简化的描述。这也让人了解了 monitor
如何使用匿名声音来减少向蓝牙环境发送询问的次数。
蓝牙低功耗规范旨在使用户连接蓝牙设备更加简单。不再需要引脚码、代码验证、"发现模式"等 - 大部分情况下是如此。它还被设计得比以前的蓝牙实现更加私密。但是,当您想要能够在没有干预的情况下连接到未知设备时,要保持隐私是很困难的。
monitor
结合使用了名称请求、匿名广告和信标广告,以逻辑确定(1)何时发出名称请求以确定设备是否在场,以及(2)何时发出名称请求以确定设备是否离开。该脚本还会监听信标。
例如,如果 known_static_addresses
文件中列出了两个手机地址,且这两个设备都在场,则永远不会发生到达扫描。同样,如果这两个地址都离开,则永远不会发生离开扫描。如果只有一个设备在场,到达扫描只会扫描当前不在的设备。
为了减少名称请求的数量,monitor
会监听匿名广告,并为每个新的匿名地址触发到达扫描。
该脚本还会在向 monitor/scan/arrive
主题发布 mqtt 消息时触发到达扫描。广告触发的扫描可以通过使用触发器参数 -ta
禁用,这会导致 monitor
仅在收到 mqtt 消息时触发到达扫描。
如果 monitor
已经很长时间没有收到某个特定的匿名地址,它会触发一次离开扫描。该脚本还会在向 monitor/scan/depart
主题发布 mqtt 消息时触发离开扫描。到期触发的扫描可以通过使用触发器参数 -td
禁用,这会导致 monitor
仅在收到 mqtt 消息时触发离开扫描。
为了进一步减少扫描,monitor
可以过滤用于到达扫描的哪些类型的匿名广告。这些被称为"过滤器",定义在名为 behavior_preferences
的文件中。过滤器是 bash RegEx 字符串,可以通过或拒绝与过滤器匹配的匿名广告。
我个人在家中使用了四个树莓派零 w,其中我们的主要 monitor
节点或传感器位于一楼。我们位于二楼、三楼和车库的其他"节点"仅在收到 mqtt 消息时设置为触发使用 - 它们将使用选项 -tad
仅扫描到达和离开。一楼的节点被设置为通过包含 -tr
标志("报告"到其他节点何时触发到达或离开扫描)向这些节点发送 mqtt 抵达/离开扫描指令。
以下是英语文本的中文翻译:
首层持续监控信标(-b
)广告和匿名广告,这些可能由我们在known_static_addresses
文件中列出的手机发送。对于新的匿名广告,monitor
将启动一次ARRIVAL扫描,以查找缺席的手机。如果发现其中一台设备,将向Home Assistant发送一个mqtt消息,报告扫描到的手机"在家"并且置信度为100%。此外,还将向二楼、三楼和车库发送一个mqtt消息,以触发这些楼层的扫描。通过此配置,当我们离开家时,可以通过前门或车库门触发一个10秒延迟的monitor/scan/depart
mqtt触发器,从而触发我们之前已知存在的设备的离开扫描。10秒的延迟给我们一个机会在"离开"扫描被触发之前退出蓝牙范围。不同的房子/公寓可能需要不同的延迟。
更具体地说,每个monitor
节点都使用相同的设备名称,以便Home Assistant可以轻松跟踪状态。例如,在每个节点上,我的known_static_addresses
文件看起来像这样(注意00:00:00:00:00:00是一个示例地址 - 这应该是你手机的私有静态蓝牙地址):
00:00:00:00:00:00 alias #忽略此注释
我想跟踪的地址与我想在Home Assistant中使用的别名之间用空格隔开。如果您更喜欢使用地址而不是别名,请在behavior_preferences
文件中将PREF_ALIAS_MODE
设置为false
。
通过这种方式,Home Assistant接收mqtt消息并将值存储为几个mqtt传感器的输入。这些传感器的输出被组合以提供准确的数字占用置信度:
- platform: mqtt
state_topic: 'monitor/first floor/alias'
value_template: '{{ value_json.confidence }}'
unit_of_measurement: '%'
name: '一楼'
- platform: mqtt
state_topic: 'monitor/second floor/alias'
value_template: '{{ value_json.confidence }}'
unit_of_measurement: '%'
name: '二楼'
- platform: mqtt
state_topic: 'monitor/third floor/alias'
value_template: '{{ value_json.confidence }}'
unit_of_measurement: '%'
name: '三楼'
- platform: mqtt
state_topic: 'monitor/garage/alias'
value_template: '{{ value_json.confidence }}'
unit_of_measurement: '%'
name: '车库'
这些传感器可以使用min_max组合:
- platform: min_max
name: "家中占用置信度"
type: max
round_digits: 0
entity_ids:
- sensor.third_floor
- sensor.second_floor
- sensor.first_floor
- sensor.garage
之后,我在自动化中使用sensor.home_occupancy_confidence实体来控制代表用户在家或不在家的input_boolean的状态。
例如:
- alias: 有人在家
trigger:
- platform: numeric_state
entity_id: sensor.home_occupancy_confidence
above: 10
action:
- service: input_boolean.turn_on
data:
entity_id: input_boolean.occupancy
- alias: 没人在家
trigger:
- platform: numeric_state
entity_id: sensor.home_occupancy_confidence
below: 10
action:
- service: input_boolean.turn_off
data:
entity_id: input_boolean.occupancy
如果您更喜欢在Home Assistant中使用device_tracker
平台,一个独特的解决方案是使用未记录的device_tracker.see
服务:
例如:
- alias: Andrew 在家
trigger:
- platform: numeric_state
entity_id: sensor.andrew_occupancy_confidence
above: 10
action:
- service: device_tracker.see
data:
dev_id: andrew
location_name: home
source_type: bluetooth
- alias: Andrew 不在家
trigger:
- platform: numeric_state
entity_id: sensor.andrew_occupancy_confidence
below: 10
action:
- service: device_tracker.see
data:
dev_id: andrew
location_name: not_home
source_type: bluetooth
如果您只有一个节点,@limych 提供的附加组件可能是一个很好的选择!
如果有多个节点和大量用户,@Odianosen25 有一个AppDaemon应用程序可以轻松地将这个系统集成到Home Assistant中。更多信息可以在这里找到。
选项 | 默认值 | 描述 |
---|---|---|
PREF_ARRIVAL_SCAN_ATTEMPTS | 1 | 这是monitor 在决定设备尚未到达之前发送名称请求的次数。数值越高,到达检测的错误就越少,但在多设备安装中识别所有设备已回家的时间也可能越长。 |
PREF_DEPART_SCAN_ATTEMPTS | 2 | 这是monitor 在决定设备尚未离开之前发送名称请求的次数。数值越高,离开检测的错误就越少,但在多设备安装中识别所有设备已离开的时间也可能越长。 |
PREF_BEACON_EXPIRATION | 180 | 这是在没有观察到广告的情况下,信标被视为过期的秒数。 |
PREF_MINIMUM_TIME_BETWEEN_SCANS | 15 | 这是"到达"扫描或"离开"扫描之间所需的最小秒数。增加该值将降低干扰,但也会增加到达和离开检测时间。 |
PREF_PASS_FILTER_ADV_FLAGS_ARRIVE | .* | 请参见上文。 |
PREF_PASS_FILTER_MANUFACTURER_ARRIVE | .* | 请参见上文。 |
PREF_FAIL_FILTER_ADV_FLAGS_ARRIVE | NONE | 请参见上文。 |
PREF_FAIL_FILTER_MANUFACTURER_ARRIVE | NONE | 请参见上文。 |
PREF_ALIAS_MODE | true | 禁用或启用别名模式;如果禁用,MQTT消息将使用设备的MAC地址发送。 |
- 高级配置选项:
除了上述描述的选项外,还有许多高级选项可供用户设置。要修改任何这些选项,请将一行添加到behavior_preferences
文件中。
选项 | 默认值 | 描述 |
---|---|---|
PREF_INTERSCAN_DELAY | 3 | 这是name 请求之间的固定延迟。增加该值将降低干扰,但会降低响应性。降低该值将会风险蓝牙硬件故障。 |
PREF_RANDOM_DEVICE_EXPIRATION_INTERVAL | 75 | 这是匿名广告MAC地址被视为过期的间隔。增加此值将减少到达扫描频率,但也会增加内存占用(最小)并减少离开扫描的频率。 |
PREF_RSSI_CHANGE_THRESHOLD | -20 | 如果一个信标的RSSI至少变化这个值,则该信标将通过mqtt再次报告。 |
PREF_RSSI_IGNORE_BELOW | -75 | 如果一个匿名广告"更远"(RSSI更低),忽略该广告。 |
PREF_HCI_DEVICE | hci0 | 选择monitor 应该使用的hci设备。 |
PREF_COOPERATIVE_SCAN_THRESHOLD | 60 | 一旦对已知设备的信心低于该值,向其他monitor 节点发送MQTT消息,以开始到达扫描或离开扫描。 |
PREF_MQTT_REPORT_SCAN_MESSAGES | false | 此值为true或false,确定monitor 是否发布扫描开始和扫描结束。 |
PREF_PERCENT_CONFIDENCE_REPORT_THRESHOLD | 59 | 此值定义了信标开始报告信心下降的阈值。 |
PREF_PASS_FILTER_PDU_TYPE | 各种。请参见FAQ。 | 这些是monitor 应该注意的PDU类型。 |
PREF_DEVICE_TRACKER_REPORT | false | 如果为true,此值将导致monitor 向... /device_tracker 报告'home'或'not_home'消息,以符合device_tracker mqtt协议。 |
PREF_DEVICE_TRACKER_HOME_STRING | home | 如果PREF_DEVICE_TRACKER_REPORT 为true,这是在设备处于"home"状态时报告给device_tracker的字符串。 |
PREF_DEVICE_TRACKER_AWAY_STRING | not_home | 如果PREF_DEVICE_TRACKER_REPORT 为true,这是在设备处于"not_home"状态时报告给device_tracker的字符串。 |
PREF_DEVICE_TRACKER_TOPIC_BRANCH | device_tracker | 如果PREF_DEVICE_TRACKER_REPORT 为true,这是用于发布设备跟踪器消息的MQTT主题路径的最后一个路径元素。 |
PREF_ADVERTISEMENT_OBSERVED_INTERVAL_STEP | 15 | 这是用于估算MQTT消息中报告的广告间隔的最小间隔(以秒为单位)。 |
PREF_DEPART_SCAN_INTERVAL | 30 | 如果使用周期性扫描模式,这是自动触发离开扫描的最小间隔(以秒为单位)。 |
PREF_ARRIVE_SCAN_INTERVAL | 15 | 如果使用周期性扫描模式,这是自动触发到达扫描的最小间隔(以秒为单位)。 |
RSSI跟踪
该脚本还可以跟踪一天中的RSSI变化。这可用于非常基本的房间或楼层级别的跟踪。只有在known_static_addresses
中的设备被配对到monitor
节点后,才能跟踪其RSSI。以下是配对方法:
- 停止
monitor
服务:
sudo systemctl stop monitor
- 运行
monitor
并使用-c
标志,后跟要连接的已知设备的MAC地址:
sudo bash monitor.sh -c 00:11:22:33:44:55
在此之后,按照monitor
给出的提示进行操作,您的设备将被连接。之后,当您重新启动monitor时,它将定期(大约每1.5分钟一次)连接到您的手机,采取三个RSSI样本,平均样本,并向与置信度报告相同的路径报告一个字符串消息,并添加*/rssi*路径组件。因此,如果一个monitor
节点被命名为'first floor',将向以下位置报告一个rssi消息:
topic: monitor/first floor/00:11:22:33:44:55/rssi
message: -99 through 0
如果无法获得RSSI测量值,则发送值-99。
报告已知状态
还可以通过向类似monitor/first floor/KNOWN DEVICE STATES
的MQTT消息发送消息来告知monitor报告所有当前已知的设备状态。monitor.sh将遍历所有已知的静态地址,并报告当前的置信度级别。这可能在home assistant中很有用,在home assistant重启后获取当前状态。