.net获取米家设备的token通过python-miio来控制小米设备开关

该文章已生成可运行项目,

改写这个代码主要原因是

1、没有.net 相关的代码,只搜到了一个python获取token的代码

2、家里没有网络,你的设备就没办法控制了

3、家里路由器断网,需要重启的话,必须回家插拔一下光猫电源才能恢复网络,有了我这个代码,可以在家里写个.net服务,检测家里断网,通过小米智能开关重启光猫就可以恢复网络了

4、也可以用写一个智能家居服务,自己控制所有的米家设备,自己可以深度控制米家设备。

5、可以通过电脑控制米家设备,无需通过手机打开米家app来操作

注意:小米token有效期一般是90天,不主动去修改一般不会变化;代码里面有安装python-miio的注释网址

 MiioModel miioModel = new MiioModel();

 miioModel.username = "10*****2";//8位数的小米ID,从米家APP里面查看是数字的,可能8位可能11位
 miioModel.password = "你的密码";
 string agent_id = "CCBDEDCCABDDD";//这个随意
 //Android-7.1.1-1.0.0-ONEPLUS A3010-136-CCBDEDCCABDDD APP/xiaomi.smarthome APPV/62830
 miioModel.agent = $"Android-7.1.1-1.0.0-ONEPLUS A3010-136-{agent_id} //这个随意//APP/xiaomi.smarthome APPV/62830";//这个随意
 miioModel.device_id = $"iolupi";//这个随意
 string loginUrl1 = "https://account.xiaomi.com/pass/serviceLogin?sid=xiaomiio&_json=true";
 //0psXfr43eNI0IX6q9Suk3qWbRqU=
 bool IsSuccess = false;
 string cookies = "userId=" + miioModel.username;
 string serviceToken = "";
 //第一步登录,返回sign用来签名的
 string loginText1 = RequestStringUTF_Miio(loginUrl1, ref IsSuccess, miioModel, cookies, ref serviceToken);
 if (loginText1.Contains("_sign"))
 {
     loginText1 = loginText1.Replace("&&&START&&&", "");
     JObject jObject = JsonConvert.DeserializeObject<JObject>(loginText1);
     miioModel.sign = jObject["_sign"].ToString();
 }
 string hashPWD = MD5(miioModel.password).ToUpper();
 string loginUrl2 = "https://account.xiaomi.com/pass/serviceLoginAuth2";
 string callback = "https://sts.api.io.mi.com/sts";
 string qs = "%3Fsid%3Dxiaomiio%26_json%3Dtrue";
 string strParams = $"sid=xiaomiio&hash={hashPWD}&callback={callback}&qs={qs}&user={miioModel.username}&_sign={miioModel.sign}&_json=true";
 //第二部登录,成功返回数据

 string loginText2 = PostRequest_Miio(loginUrl2, ref IsSuccess, strParams, miioModel);
 if (loginText2.Contains("userId"))
 {
     loginText2 = loginText2.Replace("&&&START&&&", "");
     JObject jObject = JsonConvert.DeserializeObject<JObject>(loginText2);
     //miioModel.sign = jObject["_sign"].ToString();
     miioModel.ssecurity = jObject["ssecurity"].ToString();
     miioModel.userId = jObject["userId"].ToString();
     miioModel.cUserId = jObject["cUserId"].ToString();
     miioModel.passToken = jObject["passToken"].ToString();
     miioModel.location = jObject["location"].ToString();
     miioModel.code = jObject["code"].ToString();
 }
 if (miioModel.location == "")
 {
     return;
 }
 //第三步登录,拿到serviceToken
 cookies = "";
 string loginText3 = RequestStringUTF_Miio(miioModel.location, ref IsSuccess, miioModel, cookies, ref serviceToken);
 miioModel.serviceToken = serviceToken.Replace("serviceToken=", "");
 //这里只选择中国就行了cn
 string loginUrl4 = "https://api.io.mi.com/app/home/device_list";
 Dictionary<string, string> parameters = new Dictionary<string, string>
 {
     { "data", "{\"getVirtualModel\":true,\"getHuamiDevices\":1,\"get_split_device\":false,\"support_smart_home\":true}" }
 };

 long millis = DateTimeOffset.Now.ToUnixTimeMilliseconds();
 string _nonce = generate_nonce(millis);
 string signedNonce = signed_nonce(miioModel.ssecurity, _nonce);
 Dictionary<string, string> fields = GenerateEncParams(loginUrl4, "POST", signedNonce, _nonce, parameters, miioModel.ssecurity);

 string httpBuildQuery = HttpBuildQuery(fields);
 loginUrl4 += "?" + httpBuildQuery;
 string loginText4 = PostRequest_MiioDevice(loginUrl4, ref IsSuccess, miioModel);
 string dcyPassword = signed_nonce(miioModel.ssecurity, fields["_nonce"]);
 string abc = DecryptRC4(dcyPassword, loginText4);
 JObject xiaomitoken = JsonConvert.DeserializeObject<JObject>(abc);
 JArray jss = (JArray)JsonConvert.DeserializeObject(xiaomitoken["result"]["list"].ToString());
 //每个设备获取BLE KEY【beaconkey】,暂时不需要
 //string loginUrl5 = "https://cn.api.io.mi.com/v2/device/blt_get_beaconkey";
 // 接下来控制
 // https://github.com/rytilahti/python-miio
 //查看设备信息
 //miiocli device --ip 192.168.31.88 --token f9ac1096f24bc0ea922b4a2eb28d0b89 info

//安装python-miio
 //https://blog.youkuaiyun.com/linzhiji/article/details/118194526
 //https://www.cnblogs.com/ff888/p/16977114.html
 //# 获取插座状态
 //miiocli -d device --ip 192.168.31.88 --token f9ac1096f24bc0ea922b4a2eb28d0b89 raw_command get_properties "[{'did': 'MYDID', 'siid': 2, 'piid': 1 }]"

 //# 开
 //miiocli -d device --ip 192.168.31.88 --token f9ac1096f24bc0ea922b4a2eb28d0b89 raw_command set_properties "[{'did': 'MYDID', 'siid': 2, 'piid': 1, 'value':True}]"

 //# 关
 //miiocli -d device --ip 192.168.31.88 --token f9ac1096f24bc0ea922b4a2eb28d0b89 raw_command set_properties "[{'did': 'MYDID', 'siid': 2, 'piid': 1, 'value':False}]"
public class MiioModel
{
    string _username = "";
    public string username
    {
        set { _username = value; }
        get { return _username; }
    }
    string _password = "";
    public string password
    {
        set { _password = value; }
        get { return _password; }
    }
    string _agent = "";
    public string agent
    {
        set { _agent = value; }
        get { return _agent; }
    }
    string _device_id = "";
    public string device_id
    {
        set { _device_id  = value; }
        get { return _device_id; }
    }
    string _sign = "";
    public string sign
    {
        set { _sign = value; }
        get { return _sign; }
    }
    string _ssecurity = "";
    public string ssecurity
    {
        set { _ssecurity = value; }
        get { return _ssecurity; }
    }
     string _userId = "";
    public string userId
    {
        set { _userId = value; }
        get { return _userId; }
    }
    string _cUserId = "";
    public string cUserId
    {
        set { _cUserId = value; }
        get { return _cUserId; }
    }
   string _passToken = "";
    public string passToken
    {
        set { _passToken = value; }
        get { return _passToken; }
    }
    string _location = "";
    public string location
    {
        set { _location = value; }
        get { return _location; }
    }
    string _code = "";
    public string code
    {
        set { _code = value; }
        get { return _code; }
    }
    string _serviceToken = "";
    public string serviceToken
    {
        set { _serviceToken = value; }
        get { return _serviceToken; }
    }
}  

        static Dictionary<string, string> GenerateEncParams(string url, string method, string signedNonce, string nonce, Dictionary<string, string> parameters, string ssecurity)
        {
            parameters["rc4_hash__"] = GenerateEncSignature(url, method, signedNonce, parameters);
            //for (int i = 0; i < parameters.Count; i++)
            parameters["data"] = EncryptRC4(signedNonce, parameters["data"]);
            //{
            parameters["rc4_hash__"] = EncryptRC4(signedNonce, parameters["rc4_hash__"]);
            // }
            //foreach (var kvp in parameters)
            //{
            //    parameters[kvp.Key] = EncryptRC4(signedNonce, kvp.Value);
            //}

            parameters["signature"] = GenerateEncSignature(url, method, signedNonce, parameters);
            parameters["ssecurity"] = ssecurity;
            parameters["_nonce"] = nonce;

            return parameters;
        }
        static string GenerateEncSignature(string url, string method, string signedNonce, Dictionary<string, string> parameters)
        {
            List<string> signatureParams = new List<string>
            {
                method.ToUpper(),
                Regex.Split(url, "com")[1].Replace("/app/", "/")
            };

            foreach (var kvp in parameters)
            {
                signatureParams.Add($"{kvp.Key}={kvp.Value}");
            }

            signatureParams.Add(signedNonce);

            string signatureString = string.Join("&", signatureParams);

            using (SHA1 sha1 = SHA1.Create())
            {
                byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(signatureString));
                return Convert.ToBase64String(hashBytes);
            }
        }
        static string EncryptRC4(string password, string payload)
        {
            //https://blog.youkuaiyun.com/mzl87/article/details/125377501
            using (ARC4CryptoTransform rc4 = new ARC4CryptoTransform(Convert.FromBase64String(password)))
            {
                byte[] data = Encoding.UTF8.GetBytes(payload);
                rc4.TransformFinalBlock(new byte[1024], 0, 1024);
                byte[] encryptedBytes = rc4.TransformFinalBlock(data, 0, data.Length);
                return Convert.ToBase64String(encryptedBytes);
            }
        }
        static string DecryptRC4(string password, string payload)
        {
            using (ARC4CryptoTransform rc4 = new ARC4CryptoTransform(Convert.FromBase64String(password)))
            {
                byte[] data = Convert.FromBase64String(payload);
                rc4.TransformFinalBlock(new byte[1024], 0, 1024);
                //注意这里必须传递byte的length,不要直接用payload的length
                byte[] decryptedBytes = rc4.TransformFinalBlock(data, 0, data.Length);
                return Encoding.UTF8.GetString(decryptedBytes);
            }
        }
        public static string PostRequest_MiioDevice(string url, ref bool IsSuccess, MiioModel miioModel)
        {
            string responseText = string.Empty;
            try
            {
                System.GC.Collect();
                System.Net.ServicePointManager.DefaultConnectionLimit = 1000;//最好不要超过1024
                //byte[] data = Encoding.UTF8.GetBytes(param);
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
                //request.Proxy = new WebProxy("127.0.0.1:8888", true);
                request.Timeout = 1000 * 10;// 1分钟 //默认值为 100,000 毫秒(100 秒)。
                request.Method = "POST";
                request.Headers["Accept-Encoding"] = "identity";
                request.UserAgent = miioModel.agent;
                request.ContentType = "application/x-www-form-urlencoded";
                request.Headers["MIOT-ENCRYPT-ALGORITHM"] = "ENCRYPT-RC4";
                request.Headers["x-xiaomi-protocal-flag-cli"] = "PROTOCAL-HTTP2";

                request.Headers["Cookie"] = $"userId={miioModel.userId};yetAnotherServiceToken={miioModel.serviceToken};serviceToken={miioModel.serviceToken};locale=en_GB;timezone=GMT+02:00;is_daylight=1;dst_offset=3600000;channel=MI_APP_STORE";

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode.ToString() != "OK")
                {
                    IsSuccess = false;
                    return "";
                }
                responseText = WebServerTool.Common.GetResponseContext(response, Encoding.UTF8);

                //StreamReader myreader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                //responseText = myreader.ReadToEnd();
                IsSuccess = true;
            }
            catch (WebException webEx)
            {
                if (webEx.Status == WebExceptionStatus.Timeout)
                {//超时
                    Logger.LogError("", webEx.Message + "\r\n uriString=" + url, "请求超时_" + DateTime.Today.ToString("yyyy_MM_dd"));
                    IsSuccess = false;
                    return "14";//返回超时状态码
                }
                else
                {
                    Logger.LogError("", webEx.Message + "\r\n uriString=" + url);
                    IsSuccess = false;
                    return "";
                }
            }


            return responseText;
        }
        public static string PostRequest_Miio(string url, ref bool IsSuccess, string param, MiioModel miioModel)
        {
            string responseText = string.Empty;
            try
            {
                System.GC.Collect();
                System.Net.ServicePointManager.DefaultConnectionLimit = 1000;//最好不要超过1024
                //byte[] data = Encoding.UTF8.GetBytes(param);
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url + "?" + param);
                // request.Proxy = new WebProxy("127.0.0.1:8888", true);
                request.Timeout = 1000 * 10;// 1分钟 //默认值为 100,000 毫秒(100 秒)。
                request.Method = "POST";
                request.ContentType = "Application/x-www-form-urlencode";
                request.UserAgent = miioModel.agent;
                // request.KeepAlive = true;
                //Stream sm = request.GetRequestStream();
                //sm.Write(data, 0, data.Length);
                //sm.Close();

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode.ToString() != "OK")
                {
                    IsSuccess = false;
                    return "";
                }
                responseText = WebServerTool.Common.GetResponseContext(response, Encoding.UTF8);

                //StreamReader myreader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                //responseText = myreader.ReadToEnd();
                IsSuccess = true;
            }
            catch (WebException webEx)
            {
                if (webEx.Status == WebExceptionStatus.Timeout)
                {//超时
                    Logger.LogError("", webEx.Message + "\r\n uriString=" + url, "请求超时_" + DateTime.Today.ToString("yyyy_MM_dd"));
                    IsSuccess = false;
                    return "14";//返回超时状态码
                }
                else
                {
                    Logger.LogError("", webEx.Message + "\r\n uriString=" + url);
                    IsSuccess = false;
                    return "";
                }
            }


            return responseText;
        }
        /// <summary>
        /// 将GB2312转化成UTF8格式
        /// </summary>
        /// <param name="uriString"></param>
        /// <returns></returns>
        public static string RequestStringUTF_Miio(string uriString, ref bool IsSuccess, MiioModel miioModel, string cookies, ref string serviceToken)
        {
            System.GC.Collect();
            System.Net.ServicePointManager.DefaultConnectionLimit = 1200;//最好不要超过1024
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
            //request.Proxy = new WebProxy("127.0.0.1:8888", true);
            try
            {

                request.Timeout = 1000 * 25;// 1分钟 //默认值为 100,000 毫秒(100 秒)。
                request.ReadWriteTimeout = 1000 * 2;
                request.Method = "Get";
                request.ContentType = "Application/x-www-form-urlencode";
                request.UserAgent = miioModel.agent;
                if (cookies != "")
                {
                    request.Headers["Cookie"] = cookies;
                }


                //request.Headers["Forwarding"] = "x-forwarded-for";
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    string cookie = response.Headers["Set-Cookie"];
                    string[] strs_cookie = cookie.Split(';');
                    for (int i = 0; i < strs_cookie.Length; i++)
                    {
                        if (strs_cookie[i].Contains("serviceToken"))
                        {

                            string[] gtoom_cookie = strs_cookie[i].Split(',');
                            for (int j = 0; j < gtoom_cookie.Length; j++)
                            {
                                if (gtoom_cookie[j].Contains("serviceToken"))
                                {
                                    serviceToken = gtoom_cookie[j];
                                    break;
                                }

                            }

                        }
                    }
                    //response采用"gb2312"进行编码的
                    using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")))
                    {

                        string data = reader.ReadToEnd();
                        if (!string.IsNullOrEmpty(data))
                        {
                            //  Logger.LogError("打印data:", data );
                            IsSuccess = true;
                            response.Close();
                            return data;
                        }
                    }
                }

                return "";
            }
            catch (Exception ex)
            {
                Logger.LogError("RequestString()方法异常:", ex.Message + "\r\n uriString=" + uriString);
                IsSuccess = false;
                return "";
            }
            finally
            {
                if (request != null)
                {
                    request.Abort();

                }
            }
        }

        public static byte[] addBytes(byte[] data1, byte[] data2)
        {
            byte[] data3 = new byte[data1.Length + data2.Length];
            data1.CopyTo(data3, 0);
            data2.CopyTo(data3, data1.Length);
            return data3;
        }
        static string ComputeSha256Hash(string rawData)
        {
            // Create a SHA256
            using (SHA256 sha256Hash = SHA256.Create())
            {
                // ComputeHash - returns byte array
                byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));

                // Convert byte array to a string
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                {
                    builder.Append(bytes[i].ToString("x2"));
                }
                return builder.ToString();
            }
        }

        static string generate_nonce(long millis)
        {
            //byte[] nonceBytes = BitConverter.GetBytes((int)(millis / 60000));
            //Array.Reverse(nonceBytes); // Convert to big-endian
            //string base64Nonce = Convert.ToBase64String(nonceBytes);
            //return base64Nonce;

            byte[] randomBytes = new byte[8];
            using (var rng = new RNGCryptoServiceProvider())
            {
                rng.GetBytes(randomBytes);
            }

            byte[] millisBytes = BitConverter.GetBytes((int)(millis / 60000));
            Array.Reverse(millisBytes); // Convert to big-endian

            byte[] nonceBytes = new byte[randomBytes.Length + millisBytes.Length];
            Buffer.BlockCopy(randomBytes, 0, nonceBytes, 0, randomBytes.Length);
            Buffer.BlockCopy(millisBytes, 0, nonceBytes, randomBytes.Length, millisBytes.Length);

            string base64Nonce = Convert.ToBase64String(nonceBytes);
            return base64Nonce;



        }
        static string signed_nonce(string _ssecurity, string nonce)
        {
            byte[] ssecurityBytes = Convert.FromBase64String(_ssecurity);
            byte[] nonceBytes = Convert.FromBase64String(nonce);

            using (var sha256 = new SHA256Managed())
            {
                byte[] hashBytes = sha256.ComputeHash(CombineArrays(ssecurityBytes, nonceBytes));
                return Convert.ToBase64String(hashBytes);
            }
        }
        static byte[] CombineArrays(byte[] array1, byte[] array2)
        {
            byte[] combined = new byte[array1.Length + array2.Length];
            Buffer.BlockCopy(array1, 0, combined, 0, array1.Length);
            Buffer.BlockCopy(array2, 0, combined, array1.Length, array2.Length);
            return combined;
        }

        static string EncryptRC42(string password, string payload)
        {

            using (ARC4CryptoTransform rc4 = new ARC4CryptoTransform(Convert.FromBase64String(password)))
            {
                byte[] data = Encoding.UTF8.GetBytes(payload);
                rc4.TransformFinalBlock(new byte[1024], 0, 1024);
                byte[] encryptedBytes = rc4.TransformFinalBlock(data, 0, data.Length);
                return Convert.ToBase64String(encryptedBytes);
            }
        }
        static string DecryptRC42(string password, string payload)
        {
            //https://blog.youkuaiyun.com/mzl87/article/details/125377501
            //using (var arc4 = ARC4.Create(password))
            //{
            //    using (var transform = arc4.CreateEncryptor()) // Encryption.
            //    {
            //        encrypted = transform.TransformFinalBlock(data, 0, data.Length);
            //    }
            //}
            using (ARC4CryptoTransform rc4 = new ARC4CryptoTransform(Convert.FromBase64String(password)))
            {
                byte[] data = Convert.FromBase64String(payload);
                rc4.TransformFinalBlock(new byte[1024], 0, 1024);
                byte[] decryptedBytes = rc4.TransformFinalBlock(data, 0, data.Length);
                return Encoding.UTF8.GetString(decryptedBytes);
            }

            //byte[] data = Encoding.UTF8.GetBytes(payload);

            //using (var transform = ARC4.Create(password).CreateDecryptor())
            //{
            //    byte[] decryptedBytes = transform.TransformFinalBlock(data, 0, data.Length);
            //    return Encoding.UTF8.GetString(decryptedBytes);
            //}

        }

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值