Akawa

ETY001的博客

本来今天应该是写(二)的,但是昨晚配置的过程中发现了很多问题,比如声音、蓝牙之类的配置,以及最关键的是发现原来 Hass.io 已经不被官方推荐了,现在官方推荐他们自己做的 HassOS,WTF,这都是啥跟啥?!所以今天就是把昨天写的(一)推翻掉,重新写。

先来说说刚才提到的 Hass.ioHassOS

按照目前我的理解,这个项目的演化历程是这样的,最早的 Home Assistant 是一堆脚本构成的,系统是基于 Respbian 的,所以起名叫做 Hassbian,之后基于 ResinOS(一个面向嵌入式系统的docker系统平台),把整个的核心代码封装进了两个镜像中,一个是 **homeassistant/raspberrypi3-homeassistant**,一个是 **homeassistant/armhf-hassio-supervisor**,其余的插件都各自独立成为一个镜像,这样整个系统运行起来就是一个 ResinOS 加上一堆容器,对于整体的稳定性和可维护性提升了不少,这也就是 Hass.io。再之后不知道什么原因,又基于 buildroot 系统(另外一个面向嵌入式的系统平台)做了 HassOS,这款比起上一款来说,感觉加入了一些新的驱动,比如声卡和蓝牙,另外就是宿主机做的更加的封闭了,可能是处于安全考虑,也可能是处于以后的商业考虑。

由于不想覆盖掉昨天的那篇,所以开始这篇作为第一篇,然后开始我们的重做系统之旅吧。。。。。

目前 HassOS 的稳定版本是 1.13,最新版本是 2.3,这个在 HassOS 项目的 Release 页面可以看到,https://github.com/home-assistant/hassos/releases 。我选择了 2.3 这个版本,由于我是树莓派3B,所以下载了 hassos_rpi3-2.3.img.gz 这个包(这里之所以不下载64位的是因为树莓派官方推出64位的系统时间也不长,稳定性有待观察)。

下载后解压得到 img 文件,依旧用 etcher 来写入到 TF 卡中。

这里需要注意,如果你是 Mac 电脑,那么写完后,会提示 TF 卡无法识别,你直接推出就好。这是因为 HassOS 使用的 GPT 分区格式,而 Mac 默认不支持这个格式。

那么这里就有意思了,往常都是 boot 分区是 FAT 格式,然后挂载,放入各种位置文件,比如wifi联网的配置,然后把卡插回树莓派,启动就能用了,但现在 TF 卡的 boot 分区无法挂载,这咋整?

官方给出了方案:https://github.com/home-assistant/hassos/blob/dev/Documentation/configuration.md ,思路就是再找一个U盘,格式化成 FAT32 / EXT4 / NTFS 任意格式,格式化的时候把U盘命名为 CONFIG(全大写)。

之后需要加入的配置文件,按照文档要求的格式放在U盘根目录下即可。启动树莓派的时候,先把U盘插好,系统会自动读取U盘的配置文件并生效。

另外注意就是配置network的时候,文件名必须是my-network。

除了网络配置以外,timesyncd.conf也要配置,否则系统启动起来时间不对,然后更新就会卡住,因为更新访问的是 HTTPS 加密链接,时间不对,签名就有问题,进而就无法访问更新镜像的链接(下面的截图就是这个坑了)。timesyncd.conf中的NTP服务器网上找下亚洲区的就好。

另外建议把 authorized_keys 也配置了吧,这样可以远程直接SSH到宿主机。authorized_keys 文件里的内容其实就是你电脑 ~/.ssh/id_rsa.pub 文件的内容,如果没有这个目录,就执行下 ssh-keygen 生成下。

还有就是一定记住,初始化结束后,要在UI界面上把U盘的配置再导入一次,这个之后会再提到,切记切记啊!!!

以上满满的都是坑!

所有工作做完,启动树莓派,来看看显示器最后输出的内容,默认登陆用户是root,不需要密码。

恩,你没有看错,登陆后是官方实现的一个 cli,用于便捷管理各种服务,屏蔽掉操作系统的存在。虽然现在允许你使用 login 命令进入宿主机,但是我觉得早晚会屏蔽掉这个命令,做的跟路由器似的。

这时候,你执行 login 进入宿主机,然后执行 docker ps,看看是不是一个 supervisor 的容器,一个 homeassistant/raspberrypi3-homeassistant 生成的容器。

如果没问题,那么在浏览器里访问 http://hassio.local:8123 就能看到创建新用户的界面了(如果你路由器不支持mDNS,那么就只能用 http://树莓派IP:8123 的形式访问了。

登陆之后,就能看到主界面了。去左边栏 Hass.io 中,选择 ADD-ON STORE,然后找到 SSH & Web Terminal,点进去安装,如下图

这个插件其实是让你 SSH 登陆进一个容器,然后把一些配置文件从宿主机映射进容器,这样相对安全一些。

安装完,在下面的配置中,把你的登陆用户名和密码加入进去,另外配置下 Web Terminal 的证书,证书是放在 /root/ssl 目录下的。你可以先关闭 Web Terminal,启动 SSH 成功后,用 SCP 把证书传上去,或者去插件中心安装 Samba Share 插件,用共享的方法传上去。

最关键的一步来了,在 Hass.io 中,选择 SYSTEM ,点击 IMPORT FROM USB 完成配置写入。

OK,这一篇就先到这里,下一篇真的要讲讲其他的配置了。

最近想要在树莓派上折腾下 Home Assistant。

Home Assistant 于 2017年7月26日发布的 Hass.io 集成系统,全可视化安装配置,基于 Docker 和 ResinOS。

Docker 的引入使得 Hass.io 管理功能插件就像你在手机上安装 App 一样简单(事实上 iOS 的底层确实采用了类似机制),再不用通过命令行和代码来管理你的 Home Assistant。同时,通过 Docker 来封装插件,使得插件的稳定性得到了极大提高,用户能够把精力集中在个性化定制 Home Assistant 及自动化上来。

可以预见 Hass.io 是 Home Assistant 的发展方向,如果说它有什么缺点的话,那么也在于它的封闭性上。

下载针对树莓派3的 hass.io 镜像

https://github.com/home-assistant/hassio-build/releases/

使用 etcher 写入镜像到 TF 卡

配置Wifi

如果树莓派采用 WiFi 连接,在烧录完成后使用文本编译器打开 TF 卡目录下 system-connections/resin-sample 文件,修改填写你的 WiFi 信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[connection]
id=resin-wifi
type=wifi

[wifi]
hidden=true
mode=infrastructure
ssid=你的 WiFi SSID

[ipv4]
method=auto

[ipv6]
addr-gen-mode=stable-privacy
method=auto

[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk=你的 WiFi 密码

启动初始化

将 TF 卡插入树莓派中,上电,几分钟后,在浏览器(推荐 Chrome)地址栏输入 http://hassio.local:8123 即可看到如下界面了

由于没有国内的镜像站,所以且等着就好了,需要很久,除非你在路由上开了科学上网。

完成后,大致是这样

接下来

在下一篇将会继续安装相关的插件和配置,敬请期待。

跑路前还发个公告

重要公告
首先感谢大中华地区的BlockCDN爱好者和参与者,在这一年多来对BCDN项目的支持和鼓励。伴随BCDN走过了融资到退币,从94到成长。没有您们就没有BCDN的成长!为了更好的让分享型CDN能尽快的落地造福大众,区块链全球发展基金会(Blockchain Global Development Foundation)拟定同全球CDN巨头合作技术研发和加快BCDN的全球应用落地。具体洽谈合作商包括:Akamai 、 Amazon 、CloudFront、EdgeCast、CloudFlare。介于大中华地区对CDN的准入条件和对区块链行业的政策性风险,基金会艰难的决定暂停中国地区的BCDN业务和停止同原中国合作商(成都区块链公司)的开发和大中华区的推广合作。并于2018年12月1日起暂停原大中华区的BCDN挖矿,快照等一切活动。大中华区业务的从新开启时间待定。原大中华区平台内的BCDN资产请于12月15日前提现到个人ETH 钱包,官方将于12月15日24:00后将停止所有大中华区平台内BCDN的提现、维护等活动,未提现的按自愿放弃其所有权。望相互告知。再次感谢您的支持!

区块链全球发展基金会(Blockchain Global Development Foundation)
2018/11/22

之前写过一篇用 Portainer 图形界面工具来搭建 Docker 化的LNMP。但是每次开一台新的VPS,按照那个流程走下来都要累死,于是按照那个思路,整理了一份命令行版本的部署方案。

创建数据目录

1
mkdir -p /data/wwwroot && mkdir -p /data/logs && mkdir -p /data/mysql

这三个目录分别用来存储网站文件,网站日志,数据库文件。

在 CentOS 下安装 Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
&& yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 \
&& yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo \
&& yum install -y docker-ce \
&& systemctl enable docker \
&& systemctl start docker

以上是 Docker 官方给出的在 CentOS 下安装 Docker 的文档,Ubuntu 可以参考这里: https://docs.docker.com/install/linux/docker-ce/ubuntu/ 。在边栏还可以看到其他系统的安装方法。

创建子网络

1
docker network create --gateway "172.20.0.1" --subnet "172.20.0.0/16" lnmp

把 LNMP 放在独立的网络里更易管理。

部署 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -itd --name nginx nginx \
&& \
docker cp nginx:/etc/nginx /etc \
&& \
docker stop nginx && docker rm nginx \
&& \
docker run -itd --name nginx \
-v /etc/nginx:/etc/nginx \
-v /data/wwwroot:/data/wwwroot \
-v /data/logs/nginx:/var/log/nginx \
-p 80:80 -p 443:443 \
--restart always \
-l "ink.akawa.nginx" \
-v /tmp:/tmp \
--network lnmp --ip "172.20.0.2" nginx

前三行的作用就是启动一个临时的 Nginx 容器,目的是为了把其中的配置文件复制出来,最后再删除掉这个临时容器。最后一行就是真正启动 Nginx 容器的命令了。

部署 MySQL

1
read -s mysql_pass

输入MySQL管理员密码后,继续执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker run -itd --name mysql mariadb \
&& \
docker cp mysql:/etc/mysql /etc \
&& \
docker stop mysql && docker rm mysql \
&& \
docker run -itd --name mysql \
-p 3306:3306 \
-v /etc/mysql:/etc/mysql \
-v /data/mysql:/var/lib/mysql \
-v /tmp:/tmp \
-v /data/logs/mysql:/var/log/mysql \
--restart always \
--network lnmp \
--ip "172.20.0.3" \
-e MYSQL_ROOT_PASSWORD=$mysql_pass \
mariadb

前三行依旧是为了复制配置文件出来,第四行会要求你输入 MySQL 的 root 密码。第五行启动 MySQL 容器。

部署 PHP7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -itd --name php7 ety001/php:7.2.14 \
&& \
docker cp php7:/etc/php7 /etc \
&& \
docker stop php7 && docker rm php7 \
&& \
docker run -itd --name php7 \
-v /etc/php7:/etc/php7 \
-v /data/wwwroot:/data/wwwroot \
-v /tmp:/tmp \
-v /data/logs/php7:/var/log/php7 \
--restart always \
--network lnmp \
--ip "172.20.0.4" \
ety001/php:7.2.14

前三行复制配置文件出来,第四行启动 PHP 容器。其中这里使用的镜像是我自己封装的,镜像里面写死了数据目录,如果你更改了数据目录,请自行修改 /etc/php7/php-fpm.d/www.conf 中的 chdir 参数值。

修改 Nginx 配置文件并进行测试

打开 /etc/nginx/conf.d/default.conf 文件,改成下面的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

location / {
root /data/wwwroot/default; # <------ 修改这里为自己的目录
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /data/wwwroot/default; # <------ 修改这里为自己的目录
fastcgi_pass 172.20.0.4:9000; # <------ 修改这里为正确的PHP解析地址
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # <------ 修改这里的路径
include fastcgi_params;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

修改好以后,执行 docker exec nginx nginx -s reload 重载 nginx 配置以使其生效。这时可以在 /data/wwwroot/default 目录下创建一个 index.html 的文件,然后浏览器访问下看看是否可以正常显示。再创建一个含有 <?php phpinfo();?> 代码的 PHP 文件测试下 PHP 是否可用。

后续要添加网站

后续如果要增加新的网站,只需要在 /data/wwwroot/ 中新建个文件夹存储网站文件,然后在 /etc/nginx/conf.d/ 中增加新网站的配置文件,最后执行 docker exec nginx nginx -s reload 重载 nginx 配置即可生效。

如果要增加 Let’s encrypted 的 SSL 的话,建议使用 acme.sh 中的 DNS 方法来注册证书,并把证书安装到 /etc/nginx/ssl 目录里,最后使用 docker restart nginx 来重启 Nginx 容器生效。这个后续可以再开篇文章写一下。

完工!

再来一篇,这篇代码是获取指定用户当前的 Voting Power,不废话直接上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
function postData($data) {
$url = 'https://api.steemit.com';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemTools/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result;
}

function getAccountVotingPower($username) {
$data = postData('{"jsonrpc":"2.0", "method":"condenser_api.get_accounts", "params":[["'.trim($username).'"]], "id":1}');
$user = $data[0];
$last_vote_time = $user['last_vote_time'];
var_dump(strtotime($last_vote_time));
$voting_power = $user['voting_power'];
var_dump($voting_power);
$vpow = $voting_power + (10000 * (time() - strtotime($last_vote_time)) / 432000);
return number_format(min($vpow, 10000) / 100, 2);
}
var_dump(getAccountVotingPower('ety001'));

完工!

由于最近开发需要计算指定账号的赞的价值,因此研究了下。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php
function postData($data) {
$url = 'https://api.steemit.com';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemTools/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result;
}

function getRewardFund() {
$result = postData('{"id":0,"jsonrpc":"2.0","method":"call","params":["database_api","get_reward_fund",["post"]]}');
if (isset($result['reward_balance'])) {
$result['reward_balance'] = str_replace(' STEEM', '', $result['reward_balance']);
}
return $result;
}

function getAccountVests($username) {
$result = postData('{"jsonrpc":"2.0", "method":"condenser_api.get_accounts", "params":[["'.trim($username).'"]], "id":1}');
return str_replace(' VESTS', '', $result[0]['vesting_shares']) + str_replace(' VESTS', '', $result[0]['received_vesting_shares']) - str_replace(' VESTS', '', $result[0]['delegated_vesting_shares']);
}

function getCurrentMedianHistoryPrice() {
$result = postData('{"id":1,"jsonrpc":"2.0","method":"call","params":["database_api","get_current_median_history_price",[]]}');
return str_replace(' SBD', '', $result['base']) / str_replace(' STEEM', '', $result['quote']);
}

function getAccountUpvoteValue($username, $vp, $weight) {
$power = (100*$vp * 100*$weight / 1e4 + 49) / 50;
$total_vests = getAccountVests($username);
$final_vests = $total_vests * 1e6;
$rshares = $power * $final_vests / 1e4;
$rewards = getRewardFund();
$sbd_median_price = getCurrentMedianHistoryPrice();
$estimate = $rshares / $rewards['recent_claims'] * $rewards['reward_balance'] * $sbd_median_price;
return $estimate;
}

// var_dump(getRewardFund());
// var_dump(getAccountVests('ety001'));
// var_dump(getCurrentMedianHistoryPrice());
var_dump(getAccountUpvoteValue('ety001', 100, 100));

官网的公式已经很明确了,其中遇到的问题就是 voting powerweight 的精度问题了。由于在官网公式上并没有指明,所以很容易被忽略。

在我的代码中,很清楚的看到我传入的 voting power90weight100,由于 Steem 在百分比上的精度是下面这样子的:

1
100.00%

而存储在区块上的是整数 10000。因此官方给出的代码中,power = (voting_power * weight / 10000) / 50 要看你传入是什么精度的数据了。按照我的传参,我必须要给传入的值再乘以100才是正确结果,也就是我的代码是 $power = (100*$vp * 100*$weight / 1e4 + 49) / 50;。其中的 +49 是挺高精度的一种方式,可以去掉。

完工!

最近有个项目需要使用 eth 的智能合约,虽然我不看好eth,但是为了挣钱也不得不花些时间去学习下了。之所以选择 Docker 是因为我不喜欢删除的时候删不干净,毕竟不熟悉工具会安装哪些东西。使用 Docker 的话,基本上都被封装在容器里,不用了删掉容器和映射出来的目录数据就ok了。

去 eth 的 github 看了下,是有提供 Docker 镜像的,那么我们直接拉取官方做好的镜像包就好了

1
docker pull ethereum/client-go

由于我们是本地开发测试,所以我们启动测试节点

1
docker run -it --rm -v $(pwd)/eth:/root/.ethereum ethereum/client-go console --dev

其中 $(pwd)/eth 是用来存储区块数据的宿主机目录,设置成为自己的一个目录就好。
执行后,就会看到类似下面的样子

最后会出现一个 > 的控制符,这时候就可以输入命令了,比如查看下目前的账户

1
2
> eth.accounts
["0xeeffae8858bee06d1b4f57d7a2fbf3544a593f12"]

获取指定用户的 balance

1
2
> eth.getBalance(eth.accounts[0])
1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77

创建个密码是“ety001”的新用户来专门测试开发

1
2
> personal.newAccount('ety001')
"0x6ed051356a14c1354a95f3352856f05d622b1658"

转账给新账户,用于测试

1
> eth.sendTransaction({from: '0xb0ebe17ef0e96b5c525709c0a1ede347c66bd391', to: '0xf280facfd60d61f6fd3f88c9dee4fb90d0e11dfc', value: web3.toWei(1, "ether")})

从网上找一段合约代码试试创建合约,把下面的代码复制到 Remix 里,编译器选择 0.4.18 编译,之后点击“detail”

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.18;
contract hello {
string greeting;

function hello(string _greeting) public {
greeting = _greeting;
}

function say() constant public returns (string) {
return greeting;
}
}

点击“detail”打开一个浮层,里面找到 WEB3DEPLOY 的代码,复制出来,修改一下第一行,比如

1
var _greeting = 'hi, ety001'

修改完后解锁钱包,把代码复制进 console 里,回车,即可创建新的合约了

1
> personal.unlockAccount(eth.accounts[1],"ety001");

console 里输入 hello.say() 回车后,就能看到输出了 hi, ety001 了,这样一个智能合约的开发环境就搭建好了。

完工!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
function steem_per_mvests() {
$url = 'https://api.steemit.com';

$data = '{"jsonrpc":"2.0", "method":"database_api.get_dynamic_global_properties", "id":1}';
$options = array(
'http' =>
array(
'header' => "Content-Type: application/json\r\n".
"Content-Length: ".strlen($data)."\r\n".
"User-Agent:SteemMention/1.0\r\n",
'method' => 'POST',
'content' => $data,
)
);
$context = stream_context_create($options);
try {
$result = file_get_contents($url, false, $context);
$r = json_decode($result, true);
$result = $r['result'];
} catch (\Exception $e) {
return false;
}
return $result['total_vesting_fund_steem']['amount'] / ($result['total_vesting_shares']['amount'] / 1e6);
}

function vests_to_sp($vests) {
$steem_per_mvests = steem_per_mvests();
if ($steem_per_mvests) {
return $vests / 1e6 * $steem_per_mvests;
} else {
return false;
}
}

昨天刷机结束,成功root后,还没有来得及安装谷歌四件套,毕竟很多软件不敢从国内应用商店安装,有些软件则是国内应用商店没有的。

今天使用老方法,去 GoPlayCN 下载回App,然后安装,结果一直在 Google Play 服务那一步安装失败,于是又从网上搜了半天,最后发现正确的安装姿势应该是去 Open Gapps 下载卡刷包,然后进第三方Recovery 直接卡刷。在 Open Gapps 上只要选好自己的CPU是啥架构的,安卓版本是啥,选择什么体型的包,然后下载就ok啦~

最近为了研究 xposed 模块开发,我拿我的一块手机跟我妈换了下,把她手里的小米4C换过来,准备用这个作为开发机使用。折腾了一整天,终于是搞定了。

准备工作

  • 解锁BL
  • 线刷包 libra_images_6.1.7_20151221.0000.11_5.1_cn_b09dac70a0.tgz
  • cofface_twrp_recovery.img
  • SuperSU-v2.79-SR2-20170103_automode_by_kane.apk
  • XposedInstaller_3.0_alpha4.apk
  • xposed-v86-sdk22-arm64-MIUI-edition-by-Solar_-20160710.zip

下载所需的资源

准备工作中列出的各种工具,都已放到百度云了,链接: https://pan.baidu.com/s/1GPumHzCjc89o-jZlKh1V3g 提取码: 25xf。其中 cofface_twrp_recovery.img小米4C刷第三方recovery工具2.8.7.3.zip 中。

解锁BL

先去这里 http://www.miui.com/unlock/index.html 申请解锁手机BL的资格。BL 就是 BootLoader,解锁后才能修改 BootLoader。

获得解锁资格后如下图所示

然后下载解锁工具。解锁工具只有 Windows 版,因此如果你用的是非 Windows 系统,请自行准备 Windows 系统 或者虚拟机。启动解锁工具,会提示登陆

登陆后,将手机关机进入Fastboot模式(同时按电源键及音量减键),手机上会出现米兔在维修机器人的图标,

然后这时用数据线连接电脑和手机,然后在电脑上点击“解锁”

大约一分钟左右即可完成解锁,然后系统会自动重启。

刷入低版本系统

目前已知 6.1.7 及以下的版本是没有任何锁的,之后的版本都对 /system 分区加锁了,因此我们需要先刷入低版本的系统。从 http://www.miui.com/shuaji-393.html 这里可以找到通用刷机工具,按照这个网页中的教程操作即可。

刷完后,手机自动重启,第一次重启会很长时间,耐心等待直到进入系统。

做完系统初始化配置后,进入到设置里,找到“关于手机”,一直点击 MIUI版本上 直到开启“开发者选项”,切回设置主菜单,“其他高级设置”里面找到“开发者选项”,打开“USB调试”。

刷入第三方Recovery

再次进入 Fastboot 模式,手机线连接手机和电脑,在电脑上解压 小米4C刷第三方recovery工具2.8.7.3.zip,如果是 windows 系统,直接执行里面的一个 bat 脚本即可完成第三方 Recovery 刷入。

由于我 Mac 下有 ADBFastboot 工具,因此我是手动刷入并引导进入 Recovery 的,如下图:

对系统进行ROOT

刷入第三方 Recovery 后,会自动重启进入 Recovery,界面如下图

这时在我的电脑里应该能找到手机的SD卡的那个盘,把 SuperSU-v2.79-SR2-20170103_automode_by_kane.apk 拷贝进去,然后回到手机,点击“安装刷机包”,找到 SuperSU-v2.79-SR2-20170103_automode_by_kane.apk,并滑动滑块开始安装,等提示完成后,即可重启手机。进入系统后就能在桌面看到新安装的“超级授权”应用了。

安装 xposed

从我的电脑里,把 XposedInstaller_3.0_alpha4.apkxposed-v86-sdk22-arm64-MIUI-edition-by-Solar_-20160710.zip 复制进手机SD卡。在手机上安装 XposedInstaller_3.0_alpha4.apk

安装后,进入 Recovery 模式,即一直按住电源键和音量增键,直至出现开机Logo的时候,松开电源键,再等几秒后,进入 Recovery,这时松开音量增键。

点击进入“安装刷机包”,选择 xposed-v86-sdk22-arm64-MIUI-edition-by-Solar_-20160710.zip,滑动进行确认后开始安装,完成后重启手机。进入系统后,打开 xposed installer 点击“框架”,就能看到如下的字样

说明我们的 xposed 已经安装好了。

为了测试下是否安装正确,我安装了一个微信机器人的模块,并且把微信机器人放到了群里测试了下,效果如下:

至此,完成!

0%