最近买的小米家的产品越来越多,发现小米支持在内网通过组播与设备通信,于是想让智能设备联动起来,做到人走灯灭人来灯亮的效果。但是人体传感器不但贵,还要换电池,还不适合搁宿舍墙上,打一开始就不在我的考虑范围内。我打算用我的斐讯N1盒子带的蓝牙解决这个问题。
然后折腾了半天,不是搜索速度太慢,就是无法判断断连,或者蓝牙广播停滞。后来思路一转,我不是有个小米手环吗,小米手环不是能解锁电脑吗,那个和这个原理岂不是差不多。然后我就 did it 了。期间还尝试了使用tasker(一种app)和路由器来替代,都不够好,差点放弃了。
正文开始
所需材料:
- 支持解锁电脑或手机的智能手环
- 支持蓝牙的Linux设备例如斐讯N1
一、将手环与设备配对
首先需要打开手环的笔记本解锁功能
在Linux中依次输入
bluetoothctl
power on
agent on
然后等待手环被扫描到,手环的蓝牙ID可以在APP中看到
当手环被扫描到后,执行
trust xx:xx:xx:xx:xx
pair xx:xx:xx:xx:xx
然后输入yes,即配对完成
二、编写物联代码
只要循环连接,就能检测回家,监听断连,就是出门
以下是一个简单示例,需要把蓝牙ID中的冒号改为下划线后填入device.NewDevice1函数中
如果你的设备默认蓝牙设备不是hci0,就要把它一起改了
package main
import (
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/device"
"log"
"time"
)
func Run() error {
defer api.Exit()
dev,e:=device.NewDevice1("/org/bluez/hci0/dev_xx_xx_xx_xx_xx_xx")
if e!=nil {
panic(e)
}
for {
for dev.Connect()!=nil {
log.Println("try failed")
time.Sleep(time.Second*5)
}
log.Println("connected")
c,e:=dev.WatchProperties()
if e!=nil {
panic(e)
}
for {
d:=<-c
if d.Name=="Connected" && d.Value.(bool) ==false {
log.Println("disconnected")
break
}
}
}
return nil
}
func main() {
if e := Run(); e != nil {
panic(e)
}
}
运行结果:
然后是一个完整的更智能的亮灯示例
package modules
import (
"github.com/godbus/dbus/v5"
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/adapter"
"github.com/muka/go-bluetooth/bluez/profile/device"
"log"
"sync"
"time"
)
type ble struct {
path dbus.ObjectPath
dev *device.Device1
connected bool
connLock sync.Mutex
running bool
runnerLock sync.Mutex
runnerChan chan bool
autoClosed bool
}
var BLE = ble{
path: "/org/bluez/hci0/dev_xx_xx_xx_xx_xx_xx",
runnerChan: make(chan bool, 1),
}
func init() {
var e error
BLE.dev, e = device.NewDevice1(BLE.path)
if e != nil {
panic(e)
}
go BLE.Run()
}
func (s *ble) discovery() {
a, e := api.GetDefaultAdapter()
if e != nil {
panic(e)
}
discovery, cancel, e := api.Discover(a, nil)
if e != nil {
panic(e)
}
defer cancel()
for ev := range discovery {
s.runnerLock.Lock()
if ev.Type == adapter.DeviceRemoved {
if s.running && !s.connected && ev.Path == s.path {
if len(s.runnerChan) == 1 {
<-s.runnerChan
}
s.runnerChan <- false
}
continue
}
if !s.running && ev.Path == s.path {
go s.Run()
}
s.runnerLock.Unlock()
}
}
func (s *ble) Run() {
s.runnerLock.Lock()
s.running = true
s.runnerLock.Unlock()
for {
var end bool
for {
s.connLock.Lock()
if s.dev.Connect() == nil {
s.connected = true
s.connLock.Unlock()
break
}
s.connLock.Unlock()
select {
case <-time.After(time.Second * 5):
case <-s.runnerChan:
s.runnerLock.Lock()
s.running = false
s.runnerLock.Unlock()
end = true
}
}
if end {
break
}
log.Println("mmx connected")
if s.autoClosed || time.Now().Hour() > 17 {
s.autoClosed = false
_ = MI.OpenLamp()
}
c, e := s.dev.WatchProperties()
if e != nil {
panic(e)
}
for {
d := <-c
if d.Name == "Connected" && d.Value.(bool) == false && s.dev.Connect() != nil {
if open, e := MI.LampStatus(); e == nil && open {
s.autoClosed = true
}
_ = MI.CloseLamp()
log.Println("mmx disconnected")
s.connLock.Lock()
s.connected = false
s.connLock.Unlock()
break
}
}
}
}