改写这个代码主要原因是
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);
//}
}
2512

被折叠的 条评论
为什么被折叠?



