米米的博客

做了一点微小的工作

在文章将 Yeelight 烛光氛围灯接入 Apple HomeKit 中,笔者介绍了通过 Homebridge 将设备加入 HomeKit 的方法。此后,笔者也发现,Home Assistant 相比于 Homebridge 功能更加强大,能够将全部智能设备集中管理,创建自动化也非常方便,因此切换到了 Home Assistant。不过,家中还有大量的公牛智家开关没有找到办法接入 Home Assistant,原因是这一型号的智能开关并不像小米等品牌有开放的协议用于控制,网络上也没有找到相关的开源代码。这样就只能通过对公牛智家 App 的抓包和逆向分析,来得到通过 Home Assistant 进行控制的方法了。

经过分析,实现设备操作需要的几个 API 分别是:

  • /v1/auth/form,提交用户名和密码进行登录(如果是在公牛智家 App 中通过手机验证码登录,就需要先设置密码)
    登录成功后,服务器将下发 access_tokenrefresh_tokenopenid
  • /v1/auth/token,提交 refresh_token,换一个新的 access_token
    出于安全原因,access_token 的有效期并不长,因此需要定时刷新
  • /v2/home/devices,提交 access_token,获取设备列表
  • /v1/dc/setDeviceProperty/,提交 access_token 和设备 Id,改变设备开关状态

同时,当用户通过物理按键操作了开关之后,还需要能够在 Home Assistant 中接收到反馈,实时更新开关的状态。在公牛智家 App 中,发现了相关的 MQTT 代码。订阅 MQTT 需要提供 access_tokenopenid,订阅成功后就可以获取到设备的状态通知了。

全部代码已开源至 hass-iotbull,可以直接在 Home Assistant 中使用。

iOS 16 中引入了全新的天文墙纸,可以将地球照片设置为桌面背景,并且能够根据地理位置和时间进行实时地渲染。那么有办法在 macOS 上用上类似的壁纸么?答案是肯定的,我们可以使用 Himawari 8 气象卫星的地球照片作为壁纸。

在网上搜索相关的关键词,我们可以找到很多 Windows 上的实现。而在 macOS 上,更换壁纸的方式有所不同。比较简单的方式是创建一个 Swift 脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env swift

import Cocoa

do {
// get the main (currently active) screen
if let screen = NSScreen.main {
// get path to wallpaper file from first command line argument
let url = URL(fileURLWithPath: CommandLine.arguments[1])
// set the desktop wallpaper
try NSWorkspace.shared.setDesktopImageURL(url, for: screen, options: [:])
}
} catch {
print(error)
}

将其保存为 chwall.swift,然后将其设置为可执行文件:

1
chmod +x chwall.swift

然后,我们使用一个 Python 脚本来下载 Himawari 8 的地球照片,并调用 chwall.swift 将图片设置为壁纸:

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
58
59
60
61
62
63
from PIL import Image
import os
import datetime
import requests
import subprocess
from itertools import product
import traceback
import tempfile

IMAGE_SIZE = 688
IMAGE_ROW = 2
IMAGE_COLUMN = 2

images_dir = os.path.join(os.path.dirname(__file__), 'images')
if not os.path.exists(images_dir):
os.makedirs(images_dir)

def compose_img():
# 获取最新图片的时间,感谢 @Zhmou 的建议
latest_times = requests.get('https://rammb-slider.cira.colostate.edu/data/json/himawari/full_disk/geocolor/latest_times.json')
time = str(latest_times.json()['timestamps_int'][0])
dirpath = f'{time[:4]}/{time[4:6]}/{time[6:8]}'
# 下载的是四张小图,合成一张大图做壁纸
full_image = Image.new(
'RGB', (IMAGE_SIZE * IMAGE_COLUMN, IMAGE_SIZE * IMAGE_ROW))

for i, j in product([0, 1], [0, 1]):
imgname = f'00{i}_00{j}.png'
# 图片地址样式:https://rammb-slider.cira.colostate.edu/data/imagery/2022/10/12/himawari---full_disk/geocolor/20221012123000/00/000_000.png
url = 'https://rammb-slider.cira.colostate.edu/data/imagery/' + \
dirpath + '/himawari---full_disk/geocolor/' + time + '/01/' + imgname
print(f'图片 {url} 正在下载…')
# 设置 `stream=True` 后可以直接用 PIL 打开
response = requests.get(url, stream=True)
image_path = os.path.join(images_dir, imgname)
img = Image.open(response.raw)
width, height = img.size
# 检查图片大小是否正确
assert width == height == IMAGE_SIZE
img.save(image_path)
print(f'图片 {imgname} 下载成功!')
# 合成壁纸图片
full_image.paste(img, (j * IMAGE_SIZE, i * IMAGE_SIZE))
full_image.save(os.path.join(images_dir, 'full.png'))
# 计算生成的壁纸尺寸
height = int(IMAGE_SIZE * IMAGE_ROW / 0.618)
width = int(height / 9 * 16)
image = Image.new('RGB', (width, height))
image.paste(
full_image, (int(width / 2 - IMAGE_SIZE * IMAGE_ROW / 2), int(height * 0.382 / 2)))
# 创建一个临时文件存储壁纸
image_path = tempfile.NamedTemporaryFile(suffix='.png').name
image.save(image_path)
print(f'图片 {image_path} 保存成功!')
return image_path

try:
# 设置桌面壁纸
subprocess.run(['./chwall.swift', compose_img()])
print('壁纸设置成功!')
except Exception as e:
print('壁纸设置失败!')
traceback.print_exc()

最后,使用 Launchd 来定时运行这个脚本,即可实现自动更换壁纸。

需要注意,macOS 会缓存使用过的壁纸,相关的目录包括

  • ~/Library/Application Support/Dock/desktoppicture.db
  • /private/var/folders/**/**/com.apple.desktoppicture

这些缓存不会自动清除,经常更换壁纸的话缓存占用的空间会增加。


参考文章:
How can I programmatically change the background in Mac OS X?
OS X: com.apple.desktoppicture folder grows when changing the wallpaper

有关 GitHub 仓库:
SpaceEye

在全新安装的 Windows 10 和 11 上,安装程序会自动在磁盘上创建好 ESP、MSR、系统分区和 Windows Recovery 分区。而如果系统是从较旧的版本升级而来,可能会一直保持最初安装系统时的 MBR 分区表。虽然这并不影响使用,但让强迫症患者非常难受。本文将介绍一种无需重装系统的,将 Windows 分区表由 MBR 改为 GPT 的方法。

准备工作

虽然不用重装,但本文中的一个步骤需要在 Windows PE 环境下进行,因此需要准备一个 Windows PE 启动盘。笔者推荐使用 WePE 或者优启通。

转换分区表

这一步可以使用硬盘分区软件 DiskGenius。打开 DiskGenius,默认会有系统保留和系统分区两个分区。

MBR分区表

选中系统盘,在 DiskGenius 的「磁盘」菜单下,选择「转换分区表类型为 GUID 格式」。然后,选中「系统保留」分区,点击「删除分区」。

删除「系统保留」分区

随后,在「分区」菜单下,选择「建立 ESP/MSR 分区」,参数使用默认值即可。

新建的 ESP/MSR 分区会占据原先「系统保留」分区的位置。如果「系统保留」分区空间很小,ESP 分区可能会放不下。在这种情况下,就需要先压缩一下系统分区的大小,从系统分区前面腾出大约 300M 空间即可。

建立ESP/MSR分区

完成后,点击「保存更改」。这时硬盘的分区表就是 GPT 了。

保存GPT分区表

不过仅仅转换分区表还是不够的,此时系统还不能引导,如果重启就进不了系统了。

重建 Windows 引导

重启后,使用 Windows PE 启动盘进入 Windows PE 环境。我们需要把 Windows Boot Manager 放到新建的 ESP 分区里面。需要用到的命令是 bcdboot。先给 ESP 分区分配一个盘符,例如 X:;而系统盘盘符默认是 C:(如果不是则对应修改);那么打开命令行,执行以下命令

1
bcdboot C:\Windows /s X: /f UEFI

完成后,再次重启就可以正常进入 Windows 了。

重建 Windows Recovery 分区

按照前面的方法转换分区表后,还并不会有 Windows Recovery 分区。这时,启动恢复模式所需的文件会存放在 C 盘的 Recovery 目录下。下面介绍重建 Windows Recovery 分区的方法。

我们仍然使用 Disk Genius 软件。Windows Recovery 分区可以放在系统分区的后面,如果系统分区后面没有空间了,我们就先使用分区工具调整系统分区的大小,将该分区后面的一部分空间压缩出来,腾出大约 800M 就行。

然后,在 Disk Genius 软件中创建一个新分区,文件系统类型选择「Windows recovery partition」。

以管理员权限打开 PowerShell,执行

1
reagentc /enable

即可激活 Windows Recovery 分区。如果这一步失败,可能原因是给 Windows Recovery 分区预留的空间大小不够,可以尝试进一步压缩系统分区的空间并重新操作。


参考文章:Rebuild the Recovery partition

PL2303 是一个被广泛使用的 USB 转 RS232 串口芯片。其中一些型号,例如 PL2303HXA,虽然早已停产,但还在市场上流通,被使用在一些单片机上。在较新的 Windows 10 和 Windows 11 系统中,首次通过 USB 连接 PL2303 时,系统默认安装的驱动是不能使用的,只有一句提示 PL2303 已停产的信息:

PL2303驱动无效

要解决这个问题,需要手动下载安装旧版的驱动:PL2303_Prolific_GPS_1013_20090319.exe

安装驱动后,在设备管理器中点开 PL2303 的属性,然后选择更新驱动程序。

更新驱动程序

这里选择较旧的驱动即可。

最后,为了避免系统自动安装的新驱动造成问题,我们使用 pnputil 将其卸载。用管理员权限打开 Powershell,执行 pnputil -e,在里面找到 PL2303 新驱动所对应的发布名称,例如 oemxx.inf。然后执行 pnputil -d oemxx.inf 即可将其卸载。

卸载不需要的驱动

在文章鱼眼镜头与小行星特效中,笔者介绍了摄影师 Stephane Vetter 创作的一幅非常震撼的星轨照片。通过后期堆栈叠加的方式,将许多张连续拍摄的星空照片进行合成,即可得到星轨。这样的拍摄方式相比于 B 门曝光具有一些优势,例如可以避免拍摄过程中出现问题而前功尽弃,并且也有更多的数据用于后期处理。后期处理的软件也有很多选择,例如 Photoshop 的堆栈功能,或者著名的 Startrails.exe。笔者也用 Python 和 OpenCV 写了一段星轨的后期处理代码,但此前一直没有机会实测一下效果。

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
#!/usr/bin/env python3

import os
import cv2
import numpy as np

# 放置原始图片的路径
base = '/path/to/pictures'
names = sorted(os.listdir(base))

out_img = 'star-trails.jpg' # 保存的图片文件名
out_video = 'star-trails.mp4' # 保存的视频文件名
fps = 24 # 保存视频的 FPS,可以适当调整
frameSize = (3840, 2160) # 视频的尺寸

# 需要先装 ffmepg: sudo apt install ffmepg
fourcc = cv2.VideoWriter_fourcc(*'avc1')
videoWriter = cv2.VideoWriter(
out_video, fourcc, fps, frameSize)
frame = None

for name in names:
if os.path.splitext(name)[1].lower() == '.jpg':
print(name)
path = os.path.join(base, name)
curr = cv2.imread(path)
if frame is None:
frame = curr
else:
frame = np.maximum(frame, curr)
# 图片需要缩放到与视频尺寸一致
videoWriter.write(cv2.resize(frame, frameSize,
interpolation=cv2.INTER_AREA))
# 保存中间结果
cv2.imwrite(out_img, frame)

videoWriter.release()

最近几天天气都很不错,笔者终于可以填坑了。下图使用的拍摄参数是:焦距 17mm,光圈 f/4,ISO 1250,单张曝光时间 30s,连续拍摄了 240 张进行合成。

摄于北京大学


参考文章:star-stacker

拓展阅读:叠加法星轨的拍摄及后期方法

0%