介绍
# Nest Device Access
通过 Google 智能设备管理 API (Smart Device Management API) 控制 Nest 设备。
## 设置
### 1. Google Cloud 和设备访问
1. 在 [console.cloud.google.com](https://console.cloud.google.com) 创建一个 Google Cloud 项目 2. 支付 $5 费用并在 [console.nest.google.com/device-access](https://console.nest.google.com/device-access) 创建一个设备访问项目 3. 创建 OAuth 2.0 凭据(Web 应用程序类型) 4. 添加 `https://www.google.com` 作为授权的重定向 URI 5. 将您的 Nest 账户关联到设备访问项目
### 2. 获取刷新令牌
运行 OAuth 流程以获取刷新令牌:
```bash # 1. Open this URL in browser (replace CLIENT_ID and PROJECT_ID): https://nestservices.google.com/partnerconnections/PROJECT_ID/auth?redirect_uri=https://www.google.com&access_type=offline&prompt=consent&client_id=CLIENT_ID&response_type=code&scope=https://www.googleapis.com/auth/sdm.service
# 2. Authorize and copy the 'code' parameter from the redirect URL
# 3. Exchange code for tokens: curl -X POST https://oauth2.googleapis.com/token \ -d "client_id=CLIENT_ID" \ -d "client_secret=CLIENT_SECRET" \ -d "code=AUTH_CODE" \ -d "grant_type=authorization_code" \ -d "redirect_uri=https://www.google.com" ```
### 3. 存储凭据
存储在 1Password 或环境变量中:
**1Password**(推荐): 创建包含以下字段的项目:`project_id`, `client_id`, `client_secret`, `refresh_token`
**环境变量:** ```bash export NEST_PROJECT_ID="your-project-id" export NEST_CLIENT_ID="your-client-id" export NEST_CLIENT_SECRET="your-client-secret" export NEST_REFRESH_TOKEN="your-refresh-token" ```
## 使用方法
### 列出设备 ```bash python3 scripts/nest.py list ```
### 恒温器
```bash # Get status python3 scripts/nest.py get <device_id>
# Set temperature (Celsius) python3 scripts/nest.py set-temp <device_id> 21 --unit c --type heat
# Set temperature (Fahrenheit) python3 scripts/nest.py set-temp <device_id> 70 --unit f --type heat
# Change mode (HEAT, COOL, HEATCOOL, OFF) python3 scripts/nest.py set-mode <device_id> HEAT
# Eco mode python3 scripts/nest.py set-eco <device_id> MANUAL_ECO ```
### 摄像头
```bash # Generate live stream URL (RTSP, valid ~5 min) python3 scripts/nest.py stream <device_id> ```
## Python API
```python from nest import NestClient
client = NestClient()
# List devices devices = client.list_devices()
# Thermostat control client.set_heat_temperature(device_id, 21.0) # Celsius client.set_thermostat_mode(device_id, 'HEAT') client.set_eco_mode(device_id, 'MANUAL_ECO')
# Camera stream result = client.generate_stream(device_id) rtsp_url = result['results']['streamUrls']['rtspUrl'] ```
## 配置
脚本按以下顺序检查凭据:
1. **1Password**:设置 `NEST_OP_VAULT` 和 `NEST_OP_ITEM`(或使用默认值:保险库 "Alfred",项目 "Nest Device Access API") 2. **环境变量**:`NEST_PROJECT_ID`, `NEST_CLIENT_ID`, `NEST_CLIENT_SECRET`, `NEST_REFRESH_TOKEN`
## 温度参考
| 设置 | 摄氏度 | 华氏度 | |---------|---------|------------| | Eco (外出) | 15-17°C | 59-63°F | | 舒适 | 19-21°C | 66-70°F | | 温暖 | 22-23°C | 72-73°F | | 夜间 | 17-18°C | 63-65°F |
---
## 实时事件(门铃、移动等)
要在有人按门铃或检测到移动时即时收到警报,您需要设置 Google Cloud Pub/Sub 和 Webhook。
### 先决条件
- 已安装并完成身份验证的 Google Cloud CLI (`gcloud`) - 用于隧道的 Cloudflare 账户(免费层级即可) - 在配置中启用了 Clawdbot hooks
### 1. 启用 Clawdbot Hooks
添加到您的 `clawdbot.json`:
```json { "hooks": { "enabled": true, "token": "your-secret-token-here" } } ```
生成一个令牌:`openssl rand -hex 24`
### 2. 创建 Pub/Sub 主题
```bash gcloud config set project YOUR_GCP_PROJECT_ID
# Create topic gcloud pubsub topics create nest-events
# Grant SDM permission to publish (both the service account and publisher group) gcloud pubsub topics add-iam-policy-binding nest-events \ --member="serviceAccount:[email protected]" \ --role="roles/pubsub.publisher"
gcloud pubsub topics add-iam-policy-binding nest-events \ --member="group:[email protected]" \ --role="roles/pubsub.publisher" ```
### 3. 将主题关联到设备访问
前往 [console.nest.google.com/device-access](https://console.nest.google.com/device-access) → 您的项目 → 编辑 → 将 Pub/Sub 主题设置为:
``` projects/YOUR_GCP_PROJECT_ID/topics/nest-events ```
### 4. 设置 Cloudflare 隧道
```bash # Install cloudflared curl -L -o ~/.local/bin/cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 chmod +x ~/.local/bin/cloudflared
# Authenticate (opens browser) ~/.local/bin/cloudflared tunnel login
# Create named tunnel ~/.local/bin/cloudflared tunnel create nest-webhook
# Note the Tunnel ID (UUID) from output ```
创建 `~/.cloudflared/config.yml`:
```yaml tunnel: nest-webhook credentials-file: /home/YOUR_USER/.cloudflared/TUNNEL_ID.json
ingress: - hostname: nest.yourdomain.com service: http://localhost:8420 - service: http_status:404 ```
创建 DNS 路由:
```bash ~/.local/bin/cloudflared tunnel route dns nest-webhook nest.yourdomain.com ```
### 5. 创建 Systemd 服务
**Webhook 服务器** (`/etc/systemd/system/nest-webhook.service`):
```ini [Unit] Description=Nest Pub/Sub Webhook Server After=network.target
[Service] Type=simple User=YOUR_USER Environment=CLAWDBOT_GATEWAY_URL=http://localhost:18789 Environment=CLAWDBOT_HOOKS_TOKEN=your-hooks-token-here ExecStart=/usr/bin/python3 /path/to/skills/nest-devices/scripts/nest-webhook.py Restart=always RestartSec=5
[Install] WantedBy=multi-user.target ```
**Cloudflare 隧道** (`/etc/systemd/system/cloudflared-nest.service`):
```ini [Unit] Description=Cloudflare Tunnel for Nest Webhook After=network-online.target Wants=network-online.target
[Service] Type=simple User=YOUR_USER ExecStart=/home/YOUR_USER/.local/bin/cloudflared tunnel run nest-webhook Restart=always RestartSec=5
[Install] WantedBy=multi-user.target ```
启用并启动:
```bash sudo systemctl daemon-reload sudo systemctl enable --now nest-webhook cloudflared-nest ```
### 6. 创建 Pub/Sub 推送订阅
```bash gcloud pubsub subscriptions create nest-events-sub \ --topic=nest-events \ --push-endpoint="https://nest.yourdomain.com/nest/events" \ --ack-deadline=30 ```
### 7. 测试
```bash # Test webhook endpoint curl https://nest.yourdomain.com/health
# Simulate doorbell event curl -X POST http://localhost:8420/nest/events \ -H "Content-Type: application/json" \ -d '{"message":{"data":"eyJyZXNvdXJjZVVwZGF0ZSI6eyJuYW1lIjoiZW50ZXJwcmlzZXMvdGVzdC9kZXZpY2VzL0RPT1JCRUxMLTAxIiwiZXZlbnRzIjp7InNkbS5kZXZpY2VzLmV2ZW50cy5Eb29yYmVsbENoaW1lLkNoaW1lIjp7ImV2ZW50SWQiOiJ0ZXN0In19fX0="}}' ```
### 支持的事件
| 事件 | 行为 | |-------|-----------| | `DoorbellChime.Chime` | 🔔 **警报** — 发送照片到 Telegram | | `CameraPerson.Person` | 🚶 **警报** — 发送照片到 Telegram | | `CameraMotion.Motion` | 📹 仅记录(无警报) | | `CameraSound.Sound` | 🔊 仅记录(无警报) | | `CameraClipPreview.ClipPreview` | 🎬 仅记录(无警报) |
> **陈旧过滤器:** 超过 5 分钟的事件会被记录,但不会触发警报。这可以防止在 Pub/Sub 消息队列延迟投递时造成通知轰炸。
### 图像捕获
当门铃或人员事件触发警报时:
1. **主要方式:** SDM `GenerateImage` API — 快速,事件特定快照 2. **回退方式:** 通过 `ffmpeg` 捕获 RTSP 实时流帧(需要安装 `ffmpeg`)
### 环境变量
| 变量 | 是否必需 | 描述 | |----------|----------|-------------| | `CLAWDBOT_GATEWAY_URL` | 否 | 网关 URL(默认:`http://localhost:18789`) | | `CLAWDBOT_HOOKS_TOKEN` | 是 | 用于感知通知的网关 hooks 令牌 | | `OP_SVC_ACCT_TOKEN` | 是 | 用于 Nest API 凭据的 1Password 服务账户令牌 | | `TELEGRAM_BOT_TOKEN` | 是 | 用于发送警报的 Telegram 机器人令牌 | | `TELEGRAM_CHAT_ID` | 是 | 接收警报的 Telegram 聊天 ID | | `PORT` | 否 | Webhook 服务器端口(默认:`8420`) |
### 重要设置说明
- **验证设备访问控制台中的完整 Pub/Sub 主题路径**是否与您的 GCP 项目完全匹配:`projects/YOUR_GCP_PROJECT_ID/topics/nest-events` - **使用推送订阅,而非拉取订阅** — Webhook 期望通过 HTTP POST 接收数据 - **设置后进行端到端测试**:按一下门铃并确认收到照片。不要仅依赖模拟的 POST 请求。
---
## 限制
- 摄像头事件图像约在 5 分钟后过期(RTSP 回退方式将捕获当前帧) - 实时事件需要设置 Pub/Sub(见上文) - 快速隧道(不带 Cloudflare 账户)不保证在线时间 - 某些较旧的 Nest 设备可能不支持所有功能 - 移动和声音事件故意不设警报,以避免通知疲劳