First submission

master
kunmeng 7 months ago
commit fa5cd5e337

@ -0,0 +1,142 @@
package api
import (
"encoding/json"
"strconv"
)
type MiJia struct {
Certificate Authorize
HomeData Home
}
func NewMiJia(Certificate Authorize) MiJia {
return MiJia{
Certificate: Certificate,
}
}
// 获取设备列表
func (mi *MiJia) Devices() {
data := map[string]interface{}{
"getVirtualModel": false,
"getHuamiDevices": 0,
}
PostData("/home/device_list", data, mi.Certificate)
}
func (mi *MiJia) Rooms() {
data := map[string]interface{}{
"fg": false,
"fetch_share": true,
"fetch_share_dev": true,
"limit": 300,
"app_ver": 7,
}
jsonData := PostData("/v2/homeroom/gethome", data, mi.Certificate)
json.Unmarshal(jsonData, &mi.HomeData)
}
// 获取场景列表
func (mi *MiJia) Scenes(roomIdx int) {
HomeId := mi.HomeData.Result.Homelist[roomIdx].Id
data := map[string]interface{}{
"home_id": HomeId,
}
PostData("/appgateway/miot/appsceneservice/AppSceneService/GetSceneList", data, mi.Certificate)
}
// 根据房间获取耗材列表
func (mi *MiJia) Consumables(roomIdx int) {
HomeId := mi.HomeData.Result.Homelist[roomIdx].Id
Homei, err := strconv.Atoi(HomeId)
if err != nil {
return
}
data := map[string]interface{}{
"home_id": Homei,
"owner_id": mi.Certificate.UserId,
}
PostData("/v2/home/standard_consumable_items", data, mi.Certificate)
}
func (mi *MiJia) RunScene(SceneId string) {
data := map[string]interface{}{
"scene_id": SceneId,
"trigger_key": "user.click",
}
PostData("/appgateway/miot/appsceneservice/AppSceneService/RunScene", data, mi.Certificate)
}
type HomeList struct {
Id string `json:"id"`
Name string `json:"name"`
Bssid string `json:"bssid"`
Dids []string `json:"dids"`
TempDids interface{} `json:"temp_dids"`
Icon string `json:"icon"`
Shareflag int `json:"shareflag"`
PermitLevel int `json:"permit_level"`
Status int `json:"status"`
Background string `json:"background"`
SmartRoomBackground string `json:"smart_room_background"`
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
CityId int `json:"city_id"`
Address string `json:"address"`
CreateTime int `json:"create_time"`
Roomlist []struct {
Id string `json:"id"`
Name string `json:"name"`
Bssid string `json:"bssid"`
Parentid string `json:"parentid"`
Dids []string `json:"dids"`
Icon string `json:"icon"`
Background string `json:"background"`
Shareflag int `json:"shareflag"`
CreateTime int `json:"create_time"`
}
Uid int64 `json:"uid"`
AppearHomeList interface{} `json:"appear_home_list"`
PopupFlag int `json:"popup_flag"`
PopupTimeStamp int `json:"popup_time_stamp"`
CarDid string `json:"car_did"`
}
type Home struct {
Code int `json:"code"`
Message string `json:"message"`
Result struct {
Homelist []HomeList `json:"homelist"`
HasMore bool `json:"has_more"`
MaxId string `json:"max_id"`
} `json:"result"`
}
func (mi *MiJia) GetDeviceVar(devs []map[string]interface{}) {
//参数说明
//did: 设备ID
//siid: 功能分类ID
//piid: 设备属性ID
//aiid: 设备方法ID
//从下述网站查询
//米家产品库网站不稳定不知道siid和piid无法使用`getDevAtt`和`setDevAtt`
//一个取巧的办法是使用在米家APP手动设置批量控制然后使用`runScene`
//https://home.miot-spec.com/
//
//获取全部设备列表函数`getDevices`返回结果说明
//返回结果说明
//name: 设备名称
//did: 设备ID
//isOnline: 设备是否在线
//model: 设备产品型号, 根据这个去米家产品库查该产品相关的信息
//
//获取设备属性,一次可以请求多个
//Atts = getDevAtt([{"did":"111111111","siid":2,"piid":1},{"did":"111111111","siid":2,"piid":2},
// {"did":"111111111","siid":2,"piid":3},{"did":"111111111","siid":2,"piid":4}])
//print(Atts)
data := map[string]interface{}{
"params": devs,
}
PostData("/miotspec/prop/get", data, mi.Certificate)
}

@ -0,0 +1,180 @@
package api
import (
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
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)
}
type Authorize struct {
CUserId string `json:"cUserId"`
Code int `json:"code"`
DeviceId string `json:"deviceId"`
Message string `json:"message"`
SecurityToken string `json:"securityToken"`
ServiceToken string `json:"serviceToken"`
Sid string `json:"sid"`
UserId int64 `json:"userId"`
}
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 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{})
userAgent := "APP/com.xiaomi.mihome APPV/6.0.103 iosPassportSDK/3.9.0 iOS/14.4 miHSTS"
client := &http.Client{}
req, _ := http.NewRequest("GET", msgURL, nil)
req.Header.Set("User-Agent", userAgent)
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)
userAgent := "APP/com.xiaomi.mihome APPV/6.0.103 iosPassportSDK/3.9.0 iOS/14.4 miHSTS"
client := &http.Client{}
req, _ := http.NewRequest("POST", "https://api.io.mi.com/app"+uri, strings.NewReader(body.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", userAgent)
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
}

@ -0,0 +1,3 @@
module mijia-go-sdk
go 1.21

@ -0,0 +1,46 @@
package main
import (
"encoding/json"
"fmt"
"mijia-go-sdk/api"
)
func main() {
user := "15542100924"
pwd := "1XAII@wsk"
result, _ := api.Login(user, pwd)
marshal, err := json.Marshal(result)
if err != nil {
return
}
Mi := api.NewMiJia(result)
//Mi.Rooms()
//Mi.Devices()
//Mi.Scenes(0)
//Mi.RunScene("1784216758920822784")
var data []map[string]interface{}
data = append(data, map[string]interface{}{
"did": "538261193",
"siid": 2,
"piid": 1,
})
data = append(data, map[string]interface{}{
"did": "538261193",
"siid": 2,
"piid": 2,
})
data = append(data, map[string]interface{}{
"did": "538261193",
"siid": 2,
"piid": 3,
})
Mi.GetDeviceVar(data)
//Mi.Consumables(0)
//Mi.Scenes(0)
println(marshal)
fmt.Println(result)
}
Loading…
Cancel
Save