404E Blog

第 2 页

米家+小爱音箱+巴法云+termux 远程开关机

HomeLab

场景

以前写过一个安卓app可以远程操作家里旧手机给电脑发wake-on-lan数据包触发远程开机,最近用上了米家的智能家居,想把远程开关机集成到米家里,同时又不想买米家的开机卡

碰壁

首先研究了Home Assistant,部署完了对接米家的时候发现只能单向用ha操作米家设备,这不是和我的需求反了吗

然后看了一下小米iot平台开发者账号,发现要企业资质才能申请😓

问gpt,给了一个用脚本模拟米家设备的方案,会顶替掉一个设备😓

今天下班不死心在网上搜,突然看到一个知乎文章,发现还有这种好东西

折腾

巴法云

先注册巴法云账号,然后点按钮切到mqtt设备云并新建一个设备,此处设备命名后缀代表了设备的类型,参考

创建一个设备,该设备的名字代表了后面mqtt协议的频道,然后可以给设备自定义一个昵称,这个名字是米家关联后的设备名字

在该页面左上角可以看到密钥,点击显示复制出来,可以用mosquitto_sub订阅mqtt,接收消息

termux

然后是termux,研究ha的时候就给旧手机装好了,其实不用proot也可以用,但是之前已经装好了那就拿着用了

我用的脚本如下

纯文本
#!/bin/bash

ID="{secret}" # 用自己的替换
TOPIC="{topic}"
HOST="bemfa.com"
PORT=9501

mosquitto_sub -h "$HOST" -p "$PORT" -i "$ID" -t "$TOPIC" | while read payload
do
    ts=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[$ts] $payload" > $TOPIC.log
    if [[ "$payload" == "on" ]]; then
        wakeonlan 01:02:03:04:05:06
    elif [ "$payload" == "off" ]; then
        ssh device "shutdown /s /t 30 /f" # 需要配置 ssh config
    fi
done

运行脚本后会阻塞,此时如果在巴法云管理页面上可以看到订阅者:在线 1,如果在网页上推送消息,则log文件中会打印推送的消息

米家

在米家app中找到 我的 > 添加其他平台 > 巴法

输一下账号密码,然后会看到设备(需要是巴法云上名字符合后缀要求的设备,否则不会同步到米家),看到后就可以退出来了

使用

触发开关机首先可以用语音操作小爱同学打开电脑,这儿的电脑是巴法云里设置的设备昵称,触发之后会给mqtt推送一条消息on

然后可以使用米家操作按钮开关,但是米家里是看不到这个设备的,所以要用到自动化/手动控制去控制小爱音响,设备选择小爱音响,动作选择自定义,然后写打开电脑,就可以触发巴法云的mqtt推送了,延迟基本上在1秒左右

docker 部署 nexus

运维部署

docker 安装 nexus

bash
#!/bin/bash

docker run -d --user root -p 8081:8081 -v /opt/nexus-data:/nexus-data --name nexus3 sonatype/nexus3:latest

数据目录映射到主机的 /opt/nexus-data,容器的/nexus-data

配置签名

本来不知道有这步的,但是偶然点进了system status界面,看到一个红叉和Nexus was not configured with an encryption key and is using the Default key.

签名配置是一个json文件,默认没有生成(起码我没找到),格式如下

json
{
  "active": "key-id",
  "keys": [
    {
      "id": "key-id",
      "key": "base64 key"
    }
  ]
}

这个key可以通过 openssl rand -base64 16 生成

有两种方式指定配置文件路径,一种是使用环境变量,另一种是nexus配置文件(/opt/nexus-data/etc/nexus.properties),我使用的是nexus配置文件,在配置文件添加一行

纯文本
nexus.secrets.file=/nexus-data/secrets.json

关闭central

因为我的nexus搭建在家里云,本身上行带宽就不高,不希望跑太多流量

取消勾选 repository > centralonline

安全

禁用admin用户

因为admin用户名是默认的管理员账号,所以禁用之,然后创建子管理用户,给管理角色,并保存

创建ci用户并分配权限

准备后续使用ci推送构件到nexus,所以专门分配一个ci用户

给了如下权限

  • nx-repository-view-maven2-maven-central-add
  • nx-repository-view-maven2-maven-central-browse
  • nx-repository-view-maven2-maven-central-edit
  • nx-repository-view-maven2-maven-central-read
  • nx-repository-view-maven2-maven-public-add
  • nx-repository-view-maven2-maven-public-browse
  • nx-repository-view-maven2-maven-public-edit
  • nx-repository-view-maven2-maven-public-read
  • nx-repository-view-maven2-maven-releases-add
  • nx-repository-view-maven2-maven-releases-browse
  • nx-repository-view-maven2-maven-releases-edit
  • nx-repository-view-maven2-maven-releases-read
  • nx-repository-view-maven2-maven-snapshots-add
  • nx-repository-view-maven2-maven-snapshots-browse
  • nx-repository-view-maven2-maven-snapshots-edit
  • nx-repository-view-maven2-maven-snapshots-read

尝试推送

招了一个gradle项目,配置publish

纯文本
publishing {
    repositories {
        maven {
            name = "snapshot"
            url = uri("http://localhost:8081/repository/maven-snapshots/")
            isAllowInsecureProtocol = true
            credentials {
                username = "homo"
                password = "114514"
            }
        }
    }
}

注意此处推送的是snapshot,需要在项目version后添加后缀-SNAPSHOT,否则推送响应400

总结

还没有使用所以没有总结(咕咕咕

kotlin script

工程实践

kotlin是我最喜欢用的语言,语法简洁功能丰富,但是项目管理略嫌麻烦,那有什么办法可以跳过麻烦的项目管理,同时又直接使用jvm庞大生态的依赖呢

答案就是 kotlin script

使用

首先需要一个最新的idea,旧版本的idea对这类新特性的支持并不太好

kotlin脚本有好几种,临时文件里创建的scratch,gradle项目管理的build.gradle.kts,以及本文的主题 main.kts

用idea在任意位置创建一个 test.main.kts ,随便写一段代码

纯文本
println("hello kotlin script")

虽然文件行号上没有任何标记,但是我们可以用 ctrl + shilt + f10 运行该文件

当然也可以直接新建一个运行项

引入依赖

引入依赖的方式如下 @file:DependsOn("com.google.code.gson:gson:2.11.0")

也可以用 @file:Repository("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") 指定maven仓库

修改代码如下,然后点一下代码编辑器右上角的加载脚本依赖项按钮

kotlin
@file:DependsOn("com.google.code.gson:gson:2.11.0")

import com.google.gson.JsonElement
import com.google.gson.JsonParser

val json: JsonElement = JsonParser.parseString("""{ "name": "kotlin script", "age": 1 }""")

println(json)

此时就已经可以使用gson的类了

当然在没有科学上网的情况下有时候是无法正常下载依赖的,对于这一点我修改了maven镜像源,以便在不开tun的情况下正常下载依赖

序列化

虽然我一直都很喜欢kotlinx.serialization的序列化,但是该序列化依赖gradle插件,我还没有研究明白怎么在 kts 脚本里使用这种插件

所以为了在脚本里使用序列化,我会选择gson作为序列化实现

提供服务

我有一些简单的http服务需要实现,这个时候我会选择ktor,他和kotlin相性极佳(毕竟都是一家的项目)

kotlin
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2")

@file:DependsOn("io.ktor:ktor-server-content-negotiation:2.3.13")
@file:DependsOn("io.ktor:ktor-server-compression-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-serialization-kotlinx-json:2.3.13")
@file:DependsOn("io.ktor:ktor-server-core-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-server-netty-jvm:2.3.13")
@file:DependsOn("io.ktor:ktor-server-call-logging-jvm:2.3.13")

@file:DependsOn("com.google.code.gson:gson:2.11.0")

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.compression.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.event.Level
import kotlin.system.exitProcess

val logger: Logger = LoggerFactory.getLogger("App")
val scope = CoroutineScope(Dispatchers.IO) + CoroutineExceptionHandler { coroutineContext, throwable ->
    logger.warn("", throwable)
}
val gson = Gson()
val json = """
    |{
    |  "kotlin": 1,
    |  "java": 2
    |}
""".trimMargin()

// 信号处理
run {
    Runtime.getRuntime().addShutdownHook(Thread {
        logger.info("exit...")
        scope.cancel()
        server.stop(0, 0)
    })
}

fun Route.configureRouting() {
    get("/{id}/status") {
        val id = call.parameters["id"]!!
        val status = gson.fromJson<Map<String, Int>>(json, object : TypeToken<Map<String, Int>>() {}.type)[id]
        call.respondText(status.toString())
    }
}

// http服务
val server = embeddedServer(Netty, 5753) {
    install(CallLogging) {
        level = Level.INFO
    }
    install(Compression) {
        gzip {
            priority = 1.0
            matchContentType(ContentType.Text.Any)
        }
        deflate {
            priority = 10.0
            minimumSize(1024)
        }
    }
    routing {
        configureRouting()
    }
}
server.start(true)
exitProcess(0)

部署

在idea里面我们已经可以运行main.kts脚本了,但是如果要部署,那怎么办呢

这儿需要到 kotlinrelease 下载 kotlin-compiler

纯文本
#!/bin/bash

kotlinc/bin/kotlin -Dfile.encoding=utf8 your_file.main.kts

然而添加jvm参数的部分在win上用不了,虽然我提了issue但是并没有解决

优势

很多时候我会写很多测试代码,调研某个库的使用方法和功能实现,但是这些代码如果集中在一个项目,会让项目变得很大,加载很慢,如果分开会不方便统一管理

这个时候使用kotlin script就可以很方便的管理

小问题

当我有如下代码

kotlin
@file:DependsOn("com.google.code.gson:gson:2.11.0")

import com.google.gson.JsonElement
import com.google.gson.JsonParser

val json: JsonElement = JsonParser.parseString("""{ "name": "kotlin script", "age": 1 }""")

object MyService {
    fun someFunction(): String {
        return json.toString()
    }
}

println(MyService.someFunction())

那么运行之后会出现

虽然这是合理的是可能的,但是这是合理的是不太可能的

另外idea在重载kts脚本依赖的时候会阻塞ui线程导致idea无响应

再另外,对于低版本的idea使用kts脚本引用外部依赖,会在打开反编译代码的时候放一个索引按钮,但是点了压根没用(新版本修了

总结

对于一些简单项目,使用 kotlin script非常爽,不管是修改还是部署,都很方便

总之好用爱用多用😋

kotlin notebook 使用体验

工程实践

最近遇到一些数据分析的需求,正好之前看到又新又好的[kotlin notebook](https://book.kotlincn.net/text/d-notebook-get-started.html),就拿来玩了一下

前置条件

首先按照教程,需要安装插件,此处由于公司电脑的idea版本较旧(不是不想更,是更新了jrebel启动慢一倍),所以一开始新建了文件也没有代码高亮,最终装个两个版本的idea,旧版本idea专门跑jrebel项目

然后按照教程需要一个项目,此处测试了项目类型,intellij和gradle都是可以的

使用

在项目文件夹内任意位置新建一个kotlin notebook文件,即可使用

引入依赖

纯文本
// 使用最新版本的依赖
%useLatestDescriptors

// 引入 Kotlin DataFrame 依赖
%use dataframe

// 引入 Gson 依赖 (引入方式和 main.kts 一样)
@file:DependsOn("com.google.code.gson:gson:2.11.0")

高亮

在运行notebook中的代码之后,关闭项目并重新打开,会发现代码高亮没了,此时需要重新完整运行notebook的代码,才能重置高亮(另外代码块不支持折叠很坏

小问题

可以在块1定义class块2引用,但是直接在块2引用块1定义的变量,代码高亮会出现错误

总结

虽然体验上任有改进空间,但是实际使用是没有大问题的,好用爱用多用😋

安装docker

运维部署

2025-04-01ubuntu-24.04.1 上安装 docker

参考自 https://www.sysgeek.cn/install-docker-ubuntu/

安装前置

bash
sudo apt update
sudo apt install apt-transport-https curl

添加gpg密钥

这一步遇到问题,可能失败,失败后会在 /etc/apt/keyrings/docker.gpg 写一个空文件,需要删除后重试

bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

添加仓库

虽然格式和apt自带的不一致,但是能用

bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

更新软件包列表

bash
sudo apt update

安装

这一步下载很慢,选择给apt设置代理(http_proxy环境变量居然无效) 创建文件 /etc/apt/apt.conf.d/proxy.conf 并写入

ini
Acquire::http::Proxy "http://192.168.1.1:7890/";
Acquire::https::Proxy "http://192.168.1.1:7890/";
bash
# 安装
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

检查docker状态

bash
sudo systemctl is-active docker

hello world

下不动哈哈哈
https://cloud.tencent.com/developer/article/2485043 找到了可用的两个镜像源

编辑文件 /etc/docker/daemon.json

json
{
  "registry-mirrors": [
    "https://docker.xuanyuan.me",
    "https://docker.1ms.run"
  ]
}

配置完成后

bash
sudo systemctl daemon-reload
sudo systemctl restart docker

github student developer pack

开发工具

申请时间 2024.03.26

准备github账号

  1. 注册并登录github
  2. 右上角头像 -> 设置
  3. 左边选择 billing and plans 下的 payment infomation
  4. 填写账单地址,主要名字必须是真名,和学籍信息一致的名字,firstname长度不够要求可以在后面加空格

绑定edu邮箱

  1. 链接:https://github.com/settings/emails
  2. 绑定邮箱并通过验证,这儿我绑定的是edu.cn的邮箱

开始申请

  1. 链接:https://education.github.com/discount_requests/application
  2. 在这之前需要确定你的定位在学校,手机可以使用fake location(要root),pc可以打开开发者工具,点右上角三个点 -> 更多工具 -> 传感器,在打开的界面中位置选择其他,然后输入经纬度
  3. 修改好定位后刷新网页,并确保接下来的步骤中不要使用梯子
  4. 在下面的邮箱列表中选择 edu.cn 邮箱
  5. 输入学校全名,此处会有补全,但是速度可能较慢,最终提交时应该是全英文的名字
  6. 点击下方的 continue(ps:此处我一开始用手机还是pc都点continue无效,最后用的f12修改传感器解决)
  7. 第二个界面要求拍照上传凭证,pc无法完成,但是申请的进度是可以保留的,因此我们用手机打开申请链接,可以继续完成第二步;手机上传照片,网上很多推荐用学籍报告之类的,实际上手写也是可以的,github应该是ocr识别的;此处我上传先是submit怎么点都不成功,回到原界面,最后用开发者工具打开手机chrome的调试才发现是payload too large了(点名批评gh开发者),最终使用前置摄像头拍了一张很糊的照片(因为很糊,所以上面只有关键信息,没有把二维码和标题之类的拍进去)
  8. 提交

tmux

运维部署

在后台启动tmux tmux new -d -s name

给tmux输入发送指令 tmux send-keys -t name:0 "echo hello" C-m

通过局域网在安卓设备和PC之间传输文件

HomeLab

PC操作

配置共享文件夹

  1. 首先需要一个文件夹用于存放传输的文件,建议在空间比较大的盘下创建,比如D盘
  2. 右键文件夹点击属性 -> 共享 -> 网络文件和文件夹共享 -> 共享
  3. 下拉框中选择用户,此处若要允许匿名访问则选择下拉框中的EveryOne
  4. 在下方添加的用户中配置权限,若要允许修改则点击读取修改为读取/写入
  5. 点击共享
  6. 点击完成

匿名共享

  1. 若要允许匿名共享,则需要点击属性界面中最下方 密码保护 -> 网络和共享中心
  2. 在跳转的高级共享设置界面选择所有网络
  3. 最下方密码保护的共享选择无密码保护的共享

tips

同一个共享文件夹中可以针对子文件夹配置不同的访问权限,使匿名访问无法查看某些文件夹

安卓操作

下载文件管理器+

连接SMB共享

  1. 打开 文件管理器+ 并选择远程存储,点击添加远程存储
  2. 选择SMB(SMB是windows的局域网文件共享协议)
  3. 在弹出的窗口中填写主机名,用户名和密码(或者匿名)
    此处主机名可以尝试填写对应pc的机器名,但是有可能因为路由器原因导致失败
    若失败请填入机器的ip地址,win+r 打开 cmd ,在 cmd 中输入 ipconfig 显示自己的ip地址
    在其中选择 和要连接的安卓设备在同一个网络下的网络连接(注意选择有IPv4 地址的网卡)
    比如pc通过网线连接的路由器,那么此时选择以太网的网络地址
    比如pc通过wifi连接的路由器,那么此时选择无线局域网适配器
  4. 填写用户名和密码(若匿名则勾选匿名)
  5. 连接

tips

可以将远程存储中的文件夹放入收藏夹从而快捷访问

unidbg自行支持其他版本的协议

编程笔记

两个so文件

首先从自带的文件中可以看出,需要获取两个so文件

这两个so文件只要下载对应版本的qqapk,使用解压软件打开就可以获得

config.json中的协议信息

这儿使用jadx来反编译qqapk

通过自带的版本信息可以知道,qua是V1_AND_SQ开头的,我们在jadx中搜索

可以看到,前缀匹配上了,但是后缀不一样,所以这儿我们只复制版本信息以及code(版本号后面的四位数字应该就是需要的code)

dtconfig

一样在jadx中搜索dtconfig,可以看到搜出来很多选项,但是我们一个一个看过去

可以看到,在第5个搜索出来的文件里面,看到了一串硬编码的二进制数据

有些常量可以ctrl+左键跳转,将反编译的内容复制出来然后替换掉常量并修改格式,这样就获取到了dtconfig

至此所有需要的内容就都从apk中获取到了

Windows部署MongoDB

运维部署

下载

MongoDB 6 的zip中只包含mongod即服务端,需要额外下载mongosh作为客户端

mongo下载:https://www.mongodb.com/try/download/community

mongosh下载:https://www.mongodb.com/try/download/shell

两者的下载都需要选择平台和架构

mongod安装服务

下载的MongoDB解压到目标位置,然后在其中新建一个文件mongod.yml(名字和后缀名不重要,随便起)

这儿的两个path,都换成自己的,目录需要自己手动创建,比如我这儿就是在MongoDB文件夹下新建的data文件夹和logs文件夹

yaml
systemLog:
    destination: file
    path: D:\App\MongoDB\logs\mongod.log
storage:
    dbPath: D:\App\MongoDB\data

在命令行中执行以下指令注册服务(这儿的serviceName是不可缺少的)

bash
# 安装mongo服务
mongod --config "D:\App\MongoDB\mongo.yml" --install --serviceName "MongoDB"
# 启动mongo服务
net start MongoDB

此时mongodb的服务就已经安装并启动了,可以用mongosh连接了(添加path不再赘述)

创建用户

纯文本
// 参考 https://www.mongodb.com/docs/manual/tutorial/create-users/#create-additional-users-for-your-deployment
use test
db.createUser(
  {
    user: "用户名",
    // passwordPrompt()会要求在控制台中输入密码, 避免密码出现在日志中, 也可以直接使用密码
    pwd:  passwordPrompt(),
    // 权限参考 https://www.mongodb.com/docs/manual/reference/built-in-roles/#database-user-roles
    roles: [ { role: "readWrite", db: "test" },
             { role: "read", db: "reporting" } ]
  }
)

mongosh登录

纯文本
mongosh "mongodb://admin:123456@localhost:27017/test?authSource=admin"