package em3395ty import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "log" "net/http" "rain_monitor/models" "strconv" "strings" "time" "github.com/google/uuid" ) // 涂鸦API配置 type TuyaConfig struct { ClientID string Secret string BaseURL string } var config TuyaConfig // InitConfig 初始化涂鸦API配置 func InitConfig(clientID, secret, baseURL string) { config = TuyaConfig{ ClientID: clientID, Secret: secret, BaseURL: baseURL, } log.Printf("EM3395TY API配置已初始化,ClientID: %s, BaseURL: %s", clientID, baseURL) } // 计算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)) } // GetAccessToken 获取访问令牌 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 := config.ClientID + t + nonce + stringToSign // 使用HMAC-SHA256计算签名 signature := calculateSignature(config.Secret, strToHash) signatureUpper := strings.ToUpper(signature) // 构建请求头 req, err := http.NewRequest("GET", config.BaseURL+url, nil) if err != nil { return "", err } req.Header.Set("client_id", config.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.EM3395TYTokenResponse 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 } // GetDeviceInfo 获取设备信息 func GetDeviceInfo(accessToken, deviceID string) (*models.EM3395TYDeviceInfo, 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 := config.ClientID + accessToken + t + nonce + stringToSign // 使用HMAC-SHA256计算签名 signature := calculateSignature(config.Secret, strToHash) signatureUpper := strings.ToUpper(signature) // 构建请求头 req, err := http.NewRequest("GET", config.BaseURL+url, nil) if err != nil { return nil, err } req.Header.Set("client_id", config.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.EM3395TYDeviceInfo if err := json.Unmarshal(body, &deviceInfo); err != nil { return nil, err } return &deviceInfo, nil } // GetDeviceStatus 获取设备状态 func GetDeviceStatus(accessToken, deviceID string) (*models.EM3395TYDeviceStatus, 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 := config.ClientID + accessToken + t + nonce + stringToSign // 使用HMAC-SHA256计算签名 signature := calculateSignature(config.Secret, strToHash) signatureUpper := strings.ToUpper(signature) // 构建请求头 req, err := http.NewRequest("GET", config.BaseURL+url, nil) if err != nil { return nil, err } req.Header.Set("client_id", config.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.EM3395TYStatusItem `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.EM3395TYDeviceStatus{ Success: tempResponse.Success, T: tempResponse.T, TID: tempResponse.TID, } // 将临时结构中的数据转换为我们的结构化数据 statusData := models.EM3395TYStatusData{} // 遍历状态项并填充结构体 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 }