411 lines
12 KiB
Go
411 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"rain_monitor/models"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
// 涂鸦开发者账号中获取的信息
|
|
clientID = "nwmdye9c8ejymu9ge5kf" // 授权密钥对 key
|
|
secret = "658733ea78624cd4b63bae6083cd3fae" // 授权密钥对 value
|
|
baseURL = "https://openapi.tuyacn.com"
|
|
deviceID = "6cbbf72843839b6157wfb2"
|
|
)
|
|
|
|
// 计算HMAC-SHA256签名
|
|
func calculateSignature(secret, stringToSign string) string {
|
|
h := hmac.New(sha256.New, []byte(secret))
|
|
h.Write([]byte(stringToSign))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
// 获取访问令牌
|
|
func getAccessToken() (string, error) {
|
|
t := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) // 13位时间戳
|
|
nonce := uuid.New().String() // 生成随机UUID
|
|
url := "/v1.0/token?grant_type=1"
|
|
|
|
// 构建stringToSign
|
|
contentSHA256 := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // 空body的SHA256值
|
|
stringToSign := fmt.Sprintf("GET\n%s\n\n%s", contentSHA256, url)
|
|
|
|
// 构建待签名字符串
|
|
strToHash := clientID + t + nonce + stringToSign
|
|
|
|
// 使用HMAC-SHA256计算签名
|
|
signature := calculateSignature(secret, strToHash)
|
|
signatureUpper := fmt.Sprintf("%s", signature)
|
|
signatureUpper = strings.ToUpper(signatureUpper)
|
|
|
|
// 构建请求头
|
|
req, err := http.NewRequest("GET", baseURL+url, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
req.Header.Set("client_id", clientID)
|
|
req.Header.Set("sign", signatureUpper)
|
|
req.Header.Set("sign_method", "HMAC-SHA256")
|
|
req.Header.Set("t", t)
|
|
req.Header.Set("nonce", nonce)
|
|
|
|
// 发起GET请求
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// 解析响应
|
|
var tokenResp models.PWS01TokenResponse
|
|
if err := json.Unmarshal(body, &tokenResp); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !tokenResp.Success {
|
|
return "", fmt.Errorf("获取token失败: %s", string(body))
|
|
}
|
|
|
|
return tokenResp.Result.AccessToken, nil
|
|
}
|
|
|
|
// 获取设备信息
|
|
func getDeviceInfo(accessToken string) (*models.PWS01DeviceInfo, error) {
|
|
t := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) // 13位时间戳
|
|
nonce := uuid.New().String() // 生成随机UUID
|
|
url := fmt.Sprintf("/v2.0/cloud/thing/%s", deviceID)
|
|
|
|
// 构建stringToSign
|
|
contentSHA256 := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // 空body的SHA256值
|
|
stringToSign := fmt.Sprintf("GET\n%s\n\n%s", contentSHA256, url)
|
|
|
|
// 构建待签名字符串
|
|
strToHash := clientID + accessToken + t + nonce + stringToSign
|
|
|
|
// 使用HMAC-SHA256计算签名
|
|
signature := calculateSignature(secret, strToHash)
|
|
signatureUpper := fmt.Sprintf("%s", signature)
|
|
signatureUpper = strings.ToUpper(signatureUpper)
|
|
|
|
// 构建请求头
|
|
req, err := http.NewRequest("GET", baseURL+url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("client_id", clientID)
|
|
req.Header.Set("access_token", accessToken)
|
|
req.Header.Set("sign", signatureUpper)
|
|
req.Header.Set("sign_method", "HMAC-SHA256")
|
|
req.Header.Set("t", t)
|
|
req.Header.Set("nonce", nonce)
|
|
|
|
// 发起GET请求
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 解析响应
|
|
var deviceInfo models.PWS01DeviceInfo
|
|
if err := json.Unmarshal(body, &deviceInfo); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &deviceInfo, nil
|
|
}
|
|
|
|
// 获取设备状态
|
|
func getDeviceStatus(accessToken string) (*models.PWS01DeviceStatus, error) {
|
|
t := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) // 13位时间戳
|
|
nonce := uuid.New().String() // 生成随机UUID
|
|
url := fmt.Sprintf("/v1.0/iot-03/devices/%s/status", deviceID)
|
|
|
|
// 构建stringToSign
|
|
contentSHA256 := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // 空body的SHA256值
|
|
stringToSign := fmt.Sprintf("GET\n%s\n\n%s", contentSHA256, url)
|
|
|
|
// 构建待签名字符串
|
|
strToHash := clientID + accessToken + t + nonce + stringToSign
|
|
|
|
// 使用HMAC-SHA256计算签名
|
|
signature := calculateSignature(secret, strToHash)
|
|
signatureUpper := fmt.Sprintf("%s", signature)
|
|
signatureUpper = strings.ToUpper(signatureUpper)
|
|
|
|
// 构建请求头
|
|
req, err := http.NewRequest("GET", baseURL+url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("client_id", clientID)
|
|
req.Header.Set("access_token", accessToken)
|
|
req.Header.Set("sign", signatureUpper)
|
|
req.Header.Set("sign_method", "HMAC-SHA256")
|
|
req.Header.Set("t", t)
|
|
req.Header.Set("nonce", nonce)
|
|
|
|
// 发起GET请求
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 首先解析为临时结构
|
|
var tempResponse struct {
|
|
Result []models.PWS01StatusItem `json:"result"`
|
|
Success bool `json:"success"`
|
|
T int64 `json:"t"`
|
|
TID string `json:"tid"`
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &tempResponse); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 创建最终响应
|
|
deviceStatus := &models.PWS01DeviceStatus{
|
|
Success: tempResponse.Success,
|
|
T: tempResponse.T,
|
|
TID: tempResponse.TID,
|
|
}
|
|
|
|
// 将临时结构中的数据转换为我们的结构化数据
|
|
statusData := models.PWS01StatusData{}
|
|
|
|
// 遍历状态项并填充结构体
|
|
for _, item := range tempResponse.Result {
|
|
switch item.Code {
|
|
case "temp_current":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.TempCurrent = int(val)
|
|
}
|
|
case "humidity_value":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HumidityValue = int(val)
|
|
}
|
|
case "battery_percentage":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.BatteryPercentage = int(val)
|
|
}
|
|
case "temp_unit_convert":
|
|
if val, ok := item.Value.(string); ok {
|
|
statusData.TempUnitConvert = val
|
|
}
|
|
case "windspeed_unit_convert":
|
|
if val, ok := item.Value.(string); ok {
|
|
statusData.WindspeedUnitConvert = val
|
|
}
|
|
case "pressure_unit_convert":
|
|
if val, ok := item.Value.(string); ok {
|
|
statusData.PressureUnitConvert = val
|
|
}
|
|
case "rain_unit_convert":
|
|
if val, ok := item.Value.(string); ok {
|
|
statusData.RainUnitConvert = val
|
|
}
|
|
case "bright_unit_convert":
|
|
if val, ok := item.Value.(string); ok {
|
|
statusData.BrightUnitConvert = val
|
|
}
|
|
case "temp_current_external":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.TempCurrentExternal = int(val)
|
|
}
|
|
case "humidity_outdoor":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HumidityOutdoor = int(val)
|
|
}
|
|
case "temp_current_external_1":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.TempCurrentExternal1 = int(val)
|
|
}
|
|
case "humidity_outdoor_1":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HumidityOutdoor1 = int(val)
|
|
}
|
|
case "temp_current_external_2":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.TempCurrentExternal2 = int(val)
|
|
}
|
|
case "humidity_outdoor_2":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HumidityOutdoor2 = int(val)
|
|
}
|
|
case "temp_current_external_3":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.TempCurrentExternal3 = int(val)
|
|
}
|
|
case "humidity_outdoor_3":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HumidityOutdoor3 = int(val)
|
|
}
|
|
case "atmospheric_pressture":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.AtmosphericPressure = int(val)
|
|
}
|
|
case "pressure_drop":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.PressureDrop = int(val)
|
|
}
|
|
case "windspeed_avg":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.WindspeedAvg = int(val)
|
|
}
|
|
case "windspeed_gust":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.WindspeedGust = int(val)
|
|
}
|
|
case "rain_1h":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.Rain1h = int(val)
|
|
}
|
|
case "rain_24h":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.Rain24h = int(val)
|
|
}
|
|
case "rain_rate":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.RainRate = int(val)
|
|
}
|
|
case "uv_index":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.UVIndex = int(val)
|
|
}
|
|
case "dew_point_temp":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.DewPointTemp = int(val)
|
|
}
|
|
case "feellike_temp":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.FeellikeTemp = int(val)
|
|
}
|
|
case "heat_index":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.HeatIndex = int(val)
|
|
}
|
|
case "windchill_index":
|
|
if val, ok := item.Value.(float64); ok {
|
|
statusData.WindchillIndex = int(val)
|
|
}
|
|
}
|
|
}
|
|
|
|
deviceStatus.Result = statusData
|
|
return deviceStatus, nil
|
|
}
|
|
|
|
// 打印设备信息
|
|
func printDeviceInfo(info *models.PWS01DeviceInfo) {
|
|
fmt.Println("========== 设备信息 ==========")
|
|
fmt.Printf("设备ID: %s\n", info.Result.ID)
|
|
fmt.Printf("设备名称: %s\n", info.Result.Name)
|
|
fmt.Printf("设备型号: %s\n", info.Result.Model)
|
|
fmt.Printf("IP地址: %s\n", info.Result.IP)
|
|
fmt.Printf("在线状态: %v\n", info.Result.IsOnline)
|
|
fmt.Printf("经度: %s\n", info.Result.Lon)
|
|
fmt.Printf("纬度: %s\n", info.Result.Lat)
|
|
fmt.Printf("时区: %s\n", info.Result.TimeZone)
|
|
fmt.Printf("激活时间: %d\n", info.Result.ActiveTime)
|
|
fmt.Printf("更新时间: %d\n", info.Result.UpdateTime)
|
|
fmt.Println("============================")
|
|
}
|
|
|
|
// 打印设备状态
|
|
func printDeviceStatus(status *models.PWS01DeviceStatus) {
|
|
fmt.Println("========== 设备状态 ==========")
|
|
fmt.Printf("室内温度: %.1f℃\n", float64(status.Result.TempCurrent)/10.0)
|
|
fmt.Printf("室内湿度: %d%%\n", status.Result.HumidityValue)
|
|
fmt.Printf("电池电量: %d%%\n", status.Result.BatteryPercentage)
|
|
fmt.Printf("温度单位: %s\n", status.Result.TempUnitConvert)
|
|
fmt.Printf("风速单位: %s\n", status.Result.WindspeedUnitConvert)
|
|
fmt.Printf("气压单位: %s\n", status.Result.PressureUnitConvert)
|
|
fmt.Printf("雨量单位: %s\n", status.Result.RainUnitConvert)
|
|
fmt.Printf("亮度单位: %s\n", status.Result.BrightUnitConvert)
|
|
fmt.Printf("室外温度: %.1f℃\n", float64(status.Result.TempCurrentExternal)/10.0)
|
|
fmt.Printf("室外湿度: %d%%\n", status.Result.HumidityOutdoor)
|
|
fmt.Printf("大气压力: %d hPa\n", status.Result.AtmosphericPressure)
|
|
fmt.Printf("压降: %d hPa\n", status.Result.PressureDrop)
|
|
fmt.Printf("平均风速: %.1f m/s\n", float64(status.Result.WindspeedAvg)/10.0)
|
|
fmt.Printf("阵风风速: %.1f m/s\n", float64(status.Result.WindspeedGust)/10.0)
|
|
fmt.Printf("一小时降雨量: %.1f mm\n", float64(status.Result.Rain1h)/10.0)
|
|
fmt.Printf("24小时降雨量: %.1f mm\n", float64(status.Result.Rain24h)/10.0)
|
|
fmt.Printf("雨率: %.1f mm\n", float64(status.Result.RainRate)/10.0)
|
|
fmt.Printf("紫外线指数: %d\n", status.Result.UVIndex)
|
|
fmt.Printf("露点温度: %.1f℃\n", float64(status.Result.DewPointTemp)/10.0)
|
|
fmt.Printf("体感温度: %.1f℃\n", float64(status.Result.FeellikeTemp)/10.0)
|
|
fmt.Printf("酷热指数: %.1f℃\n", float64(status.Result.HeatIndex)/10.0)
|
|
fmt.Printf("风寒指数: %.1f℃\n", float64(status.Result.WindchillIndex)/10.0)
|
|
fmt.Println("============================")
|
|
}
|
|
|
|
func main() {
|
|
// 获取访问令牌
|
|
fmt.Println("正在获取访问令牌...")
|
|
accessToken, err := getAccessToken()
|
|
if err != nil {
|
|
fmt.Printf("获取访问令牌失败: %v\n", err)
|
|
return
|
|
}
|
|
fmt.Printf("成功获取访问令牌: %s\n", accessToken)
|
|
|
|
// 获取设备信息
|
|
fmt.Println("\n正在获取设备信息...")
|
|
deviceInfo, err := getDeviceInfo(accessToken)
|
|
if err != nil {
|
|
fmt.Printf("获取设备信息失败: %v\n", err)
|
|
return
|
|
}
|
|
printDeviceInfo(deviceInfo)
|
|
|
|
// 获取设备状态
|
|
fmt.Println("\n正在获取设备状态...")
|
|
deviceStatus, err := getDeviceStatus(accessToken)
|
|
if err != nil {
|
|
fmt.Printf("获取设备状态失败: %v\n", err)
|
|
return
|
|
}
|
|
printDeviceStatus(deviceStatus)
|
|
|
|
// 打印原始JSON数据
|
|
fmt.Println("\n========== 原始设备信息JSON ==========")
|
|
infoJSON, _ := json.MarshalIndent(deviceInfo, "", " ")
|
|
fmt.Println(string(infoJSON))
|
|
|
|
fmt.Println("\n========== 原始设备状态JSON ==========")
|
|
statusJSON, _ := json.MarshalIndent(deviceStatus, "", " ")
|
|
fmt.Println(string(statusJSON))
|
|
} |