You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
5.3 KiB
Go

package mijia_go_sdk
import (
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
// 生成随机设备ID
func generateRandomDeviceId(size int) string {
tempStr := "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
deviceId := make([]byte, size)
_, err := rand.Read(deviceId)
if err != nil {
panic(err)
}
for i := range deviceId {
deviceId[i] = tempStr[deviceId[i]%byte(len(tempStr))]
}
return string(deviceId)
}
func mapToStruct(data map[string]interface{}) (Authorize, error) {
jsonStr, err := json.Marshal(data)
if err != nil {
return Authorize{}, err
}
var myStruct Authorize
err = json.Unmarshal(jsonStr, &myStruct)
if err != nil {
return Authorize{}, err
}
return myStruct, nil
}
func SetReqHeader(req *http.Request) {
req.Header.Set("User-Agent", "APP/com.xiaomi.mihome APPV/6.0.103 iosPassportSDK/3.9.0 iOS/14.4 miHSTS")
}
func Login(user, pwd string) (Authorize, error) {
msgURL := fmt.Sprintf("https://account.xiaomi.com/pass/serviceLogin?sid=xiaomiio&_json=true")
loginURL := "https://account.xiaomi.com/pass/serviceLoginAuth2"
deviceID := generateRandomDeviceId(16)
authorize := make(map[string]interface{})
client := &http.Client{}
req, _ := http.NewRequest("GET", msgURL, nil)
SetReqHeader(req)
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Language", "zh-tw")
req.Header.Set("Cookie", fmt.Sprintf("deviceId=%s; sdkVersion=3.4.1", deviceID))
resp, _ := client.Do(req)
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(bodyBytes[11:], &result)
body := url.Values{}
body.Set("qs", result["qs"].(string))
body.Set("sid", result["sid"].(string))
body.Set("_sign", result["_sign"].(string))
body.Set("callback", result["callback"].(string))
body.Set("user", user)
pwdHash := md5.Sum([]byte(pwd))
pwdHashStr := strings.ToUpper(hex.EncodeToString(pwdHash[:]))
body.Set("hash", pwdHashStr)
body.Set("_json", "true")
loginReq, _ := http.NewRequest("POST", loginURL, strings.NewReader(body.Encode()))
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginResp, _ := client.Do(loginReq)
defer loginResp.Body.Close()
loginBodyBytes, _ := io.ReadAll(loginResp.Body)
json.Unmarshal(loginBodyBytes[11:], &result)
if result["code"].(float64) != 0 {
authorize["code"] = result["code"]
authorize["message"] = result["desc"]
//return authorize
}
redirectURL := result["location"].(string)
redirectReq, _ := http.NewRequest("GET", redirectURL, nil)
redirectResp, _ := client.Do(redirectReq)
defer redirectResp.Body.Close()
cookies := redirectResp.Header["Set-Cookie"]
for _, cookie := range cookies {
cookieParts := strings.Split(strings.Split(cookie, "; ")[0], "=")
authorize[cookieParts[0]] = cookieParts[1]
}
authorize["code"] = 0
authorize["sid"] = "xiaomiio"
authorize["userId"] = result["userId"]
authorize["securityToken"] = result["ssecurity"]
authorize["deviceId"] = deviceID
authorize["message"] = "成功"
return mapToStruct(authorize)
}
func generateSignedNonce(secret, nonce string) string {
hash := sha256.New()
decodeString, _ := base64.StdEncoding.DecodeString(secret)
hash.Write(decodeString)
V, _ := base64.StdEncoding.DecodeString(nonce)
hash.Write(V)
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}
func generateSignature(uri, signedNonce, nonce, data string) string {
sign := uri + "&" + signedNonce + "&" + nonce + "&data=" + data
decodeString, _ := base64.StdEncoding.DecodeString(signedNonce)
mac := hmac.New(sha256.New, decodeString)
mac.Write([]byte(sign))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
func mapToJSON(data map[string]interface{}) (string, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", err
}
return string(jsonBytes), nil
}
func PostData(uri string, data map[string]interface{}, Certificate Authorize) []byte {
Certificate.UserId = 2251648609
Certificate.ServiceToken = "x2K8nowPRvFEaeEKt2aj35xTgEZLTspxURrRIbbETcLJ5WLNdzxFrI1DfI3M3+/tvxJyfQMdaYu+8EfedWgSfB81T0P8R5zofX6GUd/jE2KlslBLyzn3vabqhHIL93ahoPUog1Q8pKBJJTRqOrL2pypDy1ODWkQ07jUC6fWqf+k="
Certificate.Code = 0
Certificate.Sid = "xiaomiio"
Certificate.SecurityToken = "btZAHcCqLvQZp0eot8IlOQ=="
Certificate.DeviceId = "3P5v99CdV4vwkpsS"
dataStr, err := mapToJSON(data)
if err != nil {
return nil
}
nonce := generateRandomDeviceId(16)
nonce = "H9qOd9J9wRRMTXj0"
signedNonce := generateSignedNonce(Certificate.SecurityToken, nonce)
signature := generateSignature(uri, signedNonce, nonce, dataStr)
body := url.Values{}
body.Set("_nonce", nonce)
body.Set("data", dataStr)
body.Set("signature", signature)
client := &http.Client{}
req, _ := http.NewRequest("POST", "https://api.io.mi.com/app"+uri, strings.NewReader(body.Encode()))
SetReqHeader(req)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("x-xiaomi-protocal-flag-cli", "PROTOCAL-HTTP2")
req.Header.Set("Cookie", fmt.Sprintf("PassportDeviceId=%s;userId=%v;serviceToken=%s;", Certificate.DeviceId, Certificate.UserId, Certificate.ServiceToken))
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
return respBody
}