Matrix 首页推荐
Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。
文章代表作者个人观点,少数派仅对标题和排版略作修改。
没有 Siri 的 iOS 不是一个好 OS。
之前提到关于 网络唤醒WoL 这块的内容,也列举了很多唤醒工具,目的是更好地对远端计算机进行有效管控。
这次更进一步,解放双手,尝试让「语音助手」执行唤醒操作。
实现思路
原理亦不复杂,我手头有台 iPad Air,Siri 充当语音助手,调用「捷径」来发送开机指令。
「捷径」本身不支持 WoL 协议,但能发起 HTTP 请求,因此我们需要一个能让不同协议进行沟通/转换的装置/程序,我们简称「协议转换器」,处理 HTTP 请求并发送 WoL 唤醒信号。
我们知道有 Home Assistant 这类平台专门用于构建智能家居交互核心,但对于单一简单需求来说,未免显得过于笨重,所以我决定自己实现一个。如果你已经部署某些类似的物联网核心,可以尝试对其进行扩展或集成。
因此我的总实现流程如下:
- 本地局域网已有一台树莓派 2,作为程序运行的载体
- HTTP 与 WoL 同属网络协议,在此我们使用 Golang 来构建上图的两大功能模块
- HTTP 请求先不设计得非常复杂,够用就行1
http://192.168.1.4:40080/wakeup
- 得益于 HTTP 协议的泛用性,亦可以通过各种浏览器方便地调用,唤醒远程计算机
配置过程
iOS
在「新建捷径 > 获取 URL 内容」中选择 HTTP GET 方法:
接下来开启「互联网、麦克风及语音识别」:
嘿 Siri,「快捷指令名称」:
捷径名称可任意发挥,喊得顺就行。
协议转换器
程序入口 main.go 的具体配置如下:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
func handler_status(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "0")
}
func handler_wakeup(w http.ResponseWriter, r *http.Request) {
sendwol()
fmt.Fprintf(w, "0")
}
func main() {
http.HandleFunc("/", handler)
// Service Status Check
http.HandleFunc("/status", handler_status)
// WOL
http.HandleFunc("/wakeup", handler_wakeup)
// HTTP
err := http.ListenAndServe(":8000", nil)
log.Fatal(err)
}
从 GitHub 扒拉两个函数实现 WoL 协议包 wol.go:
package main
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"net"
)
func sendwol() {
// 目标MAC地址与指定网卡接口
const hw = "e0db55a893e6"
const nic = "eth0"
macHex, err := hex.DecodeString(hw)
if err != nil {
fmt.Printf("MAC: [%s] decode fail.\n", hw)
return
}
// 广播MAC地址 FF:FF:FF:FF:FF:FF
var bcast = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
var buff bytes.Buffer
buff.Write(bcast)
for i := 0; i < 16; i++ {
buff.Write(macHex)
}
// 获得唤醒魔包
mp := buff.Bytes()
if len(mp) != 102 {
fmt.Printf("MAC: [%s] length too short.\n", hw)
return
}
sendMagicPacket(mp, nic)
}
// 向指定网卡发送唤醒魔包
func sendMagicPacket(mp []byte, nic string) {
sender := net.UDPAddr{}
if len(nic) != 0 {
ip, err := interfaceIPv4ByName(nic)
if err != nil {
fmt.Printf("网卡[%s]错误: %s", nic, err)
return
}
sender.IP = ip
}
target := net.UDPAddr{
IP: net.IPv4bcast,
}
conn, err := net.DialUDP("udp", &sender, &target)
if err != nil {
fmt.Printf("创建UDP错误:%v", err)
return
}
defer func() {
_ = conn.Close()
}()
_, err = conn.Write(mp)
if err != nil {
fmt.Printf("魔包发送失败[%s]", err)
} else {
fmt.Printf("魔包发送成功\n")
}
}
// 通过网卡名称获取该网卡绑定的IPv4
func interfaceIPv4ByName(nic string) (net.IP, error) {
inter, err := net.InterfaceByName(nic)
if err != nil {
return nil, err
}
// 检查网卡是否正在工作
if (inter.Flags & net.FlagUp) == 0 {
return nil, errors.New("网卡未工作")
}
addrs, err := inter.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
if ip, ok := addr.(*net.IPNet); ok {
if ipv4 := ip.IP.To4(); ipv4 != nil {
return ipv4, nil
}
}
}
return nil, errors.New("该网卡没有IPv4地址")
}
安装 Golang & 编译 & 试运行
apt-get install golang && go run main.go wol.go
正式运行
程序托管
为了保证程序长时间稳定运行,请个「保姆」,由 Systemd 对「协议转换器」进行托管
vim /etc/systemd/system/assist.service
[Unit]
Description = Assistant Service
[Service]
ExecStart = /usr/local/bin/assist
Restart = always
Type = simple
[Install]
WantedBy = multi-user.target
生成二进制文件 & 存放于合适位置
go build main.go wol.go & cp main /usr/local/bin/assist
启动
systemctl daemon-reload & systemctl start assist
后续还可在此基础上进行扩展,集成「miIO」协议,自由操控物联网设备。
最后来看看效果:
参考链接:
> 下载少数派 客户端 、关注 少数派公众号 ,了解更妙的数字生活 🍃
> 想申请成为少数派作者?冲!