18 Commits
v1.2.5 ... main

Author SHA1 Message Date
星光-k
2213433dd8 Add URLs for pisugar power manager and wifi config 2026-02-01 19:38:40 +08:00
星光-k
a5fd08ed3d Update install.sh 2025-07-20 17:57:40 +08:00
星光-k
9268438fd1 Update main.py 2025-05-09 00:45:42 +08:00
星光-k
8c247e2fc6 Update weather.py 2025-05-09 00:44:58 +08:00
星光-k
dd011c3955 Update weather.py 2025-05-09 00:44:32 +08:00
星光-k
e85188dda8 Update weather.py 2025-03-15 11:01:29 +08:00
星光-k
7c73382851 Update README.md 2025-02-08 20:14:55 +08:00
星光-k
2d9959cd77 Add files via upload 2025-02-08 11:58:02 +00:00
星光-k
10f8b8182b Delete bin/vendor/1 2025-02-08 19:57:27 +08:00
星光-k
469aec331a Add files via upload 2025-02-08 11:56:46 +00:00
星光-k
a8247c1932 Create 1 2025-02-08 19:55:48 +08:00
星光-k
f37acd5a20 Update install.sh 2025-02-08 19:20:25 +08:00
星光-k
6836c448be Update README.md 2025-02-08 19:18:32 +08:00
星光-k
2efc2c4f5d Update install.sh 2025-02-08 15:21:57 +08:00
星光-k
01f62f696b Update install.sh 2025-02-08 15:19:52 +08:00
星光-k
e8b3668ed7 更新 weather.py 2025-02-08 01:17:31 +00:00
星光-k
b840257988 更新 main.py 2025-02-08 01:12:39 +00:00
星光-k
3541ecc135 Update start.sh 2025-01-27 21:52:10 +08:00
14 changed files with 352 additions and 135 deletions

View File

@@ -1,6 +1,6 @@
# 墨水屏展示当前时间及天气数据 # 墨水屏展示当前时间及天气数据
本项目复刻自[Seek-Huang](https://github.com/Seek-Huang/2.13-Ink-screen-clock)的代码仓库 本项目复刻自[Seek-Huang](https://github.com/Seek-Huang)的[代码仓库](https://github.com/Seek-Huang/2.13-Ink-screen-clock)
并在此基础上进行改进 并在此基础上进行改进
## 本仓库已添加[一键安装部署脚本](https://github.com/kxgx/2.13-Ink-screen-clock#%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC%E7%9B%B4%E6%8E%A5%E5%AE%89%E8%A3%85%E6%8E%A8%E8%8D%90) ## 本仓库已添加[一键安装部署脚本](https://github.com/kxgx/2.13-Ink-screen-clock#%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC%E7%9B%B4%E6%8E%A5%E5%AE%89%E8%A3%85%E6%8E%A8%E8%8D%90)
@@ -66,6 +66,7 @@ sudo reboot
--zh 设置系统语言为zh_CN,UTF-8 --zh 设置系统语言为zh_CN,UTF-8
--cn 替换apt镜像源为中国镜像源 --cn 替换apt镜像源为中国镜像源
--gitcn 克隆中国仓库 --gitcn 克隆中国仓库
--pip-offline pip依赖离线安装
--pisugar-wifi-conf 安装pisugar-wifi-conf --pisugar-wifi-conf 安装pisugar-wifi-conf
--pisugar-power-manager 安装pisugar-power-manager --pisugar-power-manager 安装pisugar-power-manager
--version <tag> 版本号(使用方法 --version + 仓库标签,格式例如 v1.x.x ,可以是主仓库main) --version <tag> 版本号(使用方法 --version + 仓库标签,格式例如 v1.x.x ,可以是主仓库main)
@@ -73,22 +74,29 @@ sudo reboot
``` ```
### ###
```Bash ```Bash
#中国源默认设置 #中国源默认设置(不加参数)
curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash
``` ```
```Bash ```Bash
#中国源参数设置(不使用--debug参数) #中国源参数设置(不使用--debug参数,替换cn镜像源,pip在线安装,不安装pisugar)
curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --pisugar-power-manager --pisugar-wifi-conf --version <tag> curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --version <tag>
``` ```
```Bash ```Bash
#默认源默认设置 #中国源参数设置(不使用--debug参数,替换cn镜像源,pip依赖离线安装,不安装pisugar)
curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --pip-offline --version <tag>
```
```Bash
#默认源默认设置(不加参数)
curl -sSL https://github.com/kxgx/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash curl -sSL https://github.com/kxgx/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash
``` ```
```Bash ```Bash
#默认源全参数设置(不使用--debug参数) #默认源默认设置(不使用--debug参数,替换cn镜像源,pip在线安装,不安装pisugar)
curl -sSL https://github.com/kxgx/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --pisugar-power-manager --pisugar-wifi-conf --version <tag> curl -sSL https://github.com/kxgx/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --version <tag>
```
```Bash
#默认源默认设置(不使用--debug参数,替换cn镜像源,pip依赖离线安装,不安装pisugar)
curl -sSL https://github.com/kxgx/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash -s -- --zh --cn --gitcn --pip-offline --version <tag>
``` ```
## 需要安装的软件和依赖: ## 需要安装的软件和依赖:
参考 参考
微雪电子 https://www.waveshare.net/wiki/2.13inch_e-Paper_HAT+#Raspberry_Pi 微雪电子 https://www.waveshare.net/wiki/2.13inch_e-Paper_HAT+#Raspberry_Pi

View File

@@ -18,6 +18,8 @@ USE_CN_GIT=false
USE_PISUGAR_WIFI_CONF=false USE_PISUGAR_WIFI_CONF=false
# 检查是否安装pisugar-power-manager # 检查是否安装pisugar-power-manager
USE_PISUGAR_POWER_MANAGER=false USE_PISUGAR_POWER_MANAGER=false
# 检查是否使用离线安装pip依赖
USE_OFFLINE_PIP=false
# 解析命令行参数 # 解析命令行参数
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
@@ -31,6 +33,9 @@ while [ "$#" -gt 0 ]; do
--gitcn) --gitcn)
USE_CN_GIT=true USE_CN_GIT=true
;; ;;
--pip-offline)
USE_OFFLINE_PIP=true
;;
--pisugar-wifi-conf) --pisugar-wifi-conf)
USE_PISUGAR_WIFI_CONF=true USE_PISUGAR_WIFI_CONF=true
;; ;;
@@ -203,23 +208,7 @@ install_packages() {
fi fi
} }
# 安装pip包函数 install_Ink-screen-clock() {
install_pip_packages() {
echo "正在安装pip软件包"
if ! sudo pip3 install -i "$PIPY_MIRROR" spidev borax pillow requests Flask; then
echo "pip软件包安装失败如果是最新版系统或是非lite系统" >&2
echo "请手动运行sudo pip3 install -i "$PIPY_MIRROR" spidev borax pillow requests --break-system-packages" >&2
exit 1
fi
}
# 复制服务文件并设置为开机启动
setup_service() {
local service_path="raspi_e-Paper.service"
local service1_path="e-Paper_clean.service"
local service_file_path="$HOME/2.13-Ink-screen-clock/bin/$service_path"
local service1_file_path="$HOME/2.13-Ink-screen-clock/bin/$service1_path"
# 检查墨水屏时钟仓库是否存在 # 检查墨水屏时钟仓库是否存在
if [ ! -d "$HOME/2.13-Ink-screen-clock" ]; then if [ ! -d "$HOME/2.13-Ink-screen-clock" ]; then
echo "正在克隆仓库" echo "正在克隆仓库"
@@ -234,6 +223,78 @@ setup_service() {
else else
echo "仓库文件夹已存在,跳过克隆" echo "仓库文件夹已存在,跳过克隆"
fi fi
}
# 检查pip包是否已安装
check_pip_packages_installed() {
local requirements_file="$HOME/2.13-Ink-screen-clock/bin/requirements.txt"
if [ ! -f "$requirements_file" ]; then
echo "requirements.txt 文件不存在" >&2
return 1
fi
# 读取requirements.txt中的包名忽略版本号
local packages=()
while IFS= read -r line; do
# 去除注释和空行
if [[ -n "$line" && ! "$line" =~ ^\s*# ]]; then
# 提取包名(忽略版本号)
local package_name=$(echo "$line" | cut -d'=' -f1 | cut -d'>' -f1 | cut -d'<' -f1)
packages+=("$package_name")
fi
done < "$requirements_file"
# 检查每个包是否已安装
for package in "${packages[@]}"; do
if ! pip3 show "$package" &> /dev/null; then
echo "$package 未安装"
return 1
fi
done
echo "所有pip包已安装"
return 0
}
# 安装pip包函数
install_oline_pip_packages() {
# 首先检查是否所有pip包都已安装
if check_pip_packages_installed; then
echo "所有pip包已安装跳过安装"
return 0
fi
# ... 继续安装 ...
echo "正在安装pip软件包"
if ! sudo pip3 install -i "$PIPY_MIRROR" -r "$HOME/2.13-Ink-screen-clock/bin/requirements.txt"; then
echo "pip软件包安装失败如果是最新版系统或是非lite系统" >&2
echo "请手动运行sudo pip3 install -i "$PIPY_MIRROR" -r "$HOME/2.13-Ink-screen-clock/bin/requirements.txt" --break-system-packages" >&2
exit 1
fi
}
install_offline_pip_packages() {
echo "正在安装pip软件包"
if ! sudo pip3 install --no-index --find-links="$HOME/2.13-Ink-screen-clock/bin/vendor" -r "$HOME/2.13-Ink-screen-clock/bin/requirements.txt"; then
echo "pip软件包安装失败如果是最新版系统或是非lite系统" >&2
echo "请手动运行sudo pip3 install --no-index --find-links="$HOME/2.13-Ink-screen-clock/bin/vendor" -r "$HOME/2.13-Ink-screen-clock/bin/requirements.txt" --break-system-packages" >&2
exit 1
fi
}
install_pip_packages() {
if [ "$USE_OFFLINE_PIP" = true ]; then
install_offline_pip_packages
else
install_oline_pip_packages
fi
}
# 复制服务文件并设置为开机启动
setup_service() {
local service_path="raspi_e-Paper.service"
local service1_path="e-Paper_clean.service"
local service_file_path="$HOME/2.13-Ink-screen-clock/bin/$service_path"
local service1_file_path="$HOME/2.13-Ink-screen-clock/bin/$service1_path"
# 检查服务文件是否存在 # 检查服务文件是否存在
if [ -f "$service_file_path" ] && [ -f "$service1_file_path" ]; then if [ -f "$service_file_path" ] && [ -f "$service1_file_path" ]; then
@@ -260,7 +321,7 @@ setup_service() {
exit 1 exit 1
fi fi
} }
PISUGAR_POWER_MANAGER_URL=https://cdn.pisugar.com/release/pisugar-power-manager.sh
# 安装pisugar-power-manager函数 # 安装pisugar-power-manager函数
install_pisugar-power-manager() { install_pisugar-power-manager() {
if [ "$USE_PISUGAR_POWER_MANAGER" = true ]; then if [ "$USE_PISUGAR_POWER_MANAGER" = true ]; then
@@ -272,7 +333,7 @@ install_pisugar-power-manager() {
fi fi
fi fi
} }
PISUGAR_WIFI_CONF_URL=https://cdn.pisugar.com/PiSugar-wificonfig/script/install.sh
# 安装pisugar-wifi-conf函数 # 安装pisugar-wifi-conf函数
install_pisugar-wifi-conf() { install_pisugar-wifi-conf() {
if [ "$USE_PISUGAR_WIFI_CONF" = true ]; then if [ "$USE_PISUGAR_WIFI_CONF" = true ]; then
@@ -306,6 +367,7 @@ if [ -f /etc/debian_version ]; then
echo "执行Debian 11 (Bullseye) 相关操作" echo "执行Debian 11 (Bullseye) 相关操作"
update_sources_list "bullseye" update_sources_list "bullseye"
install_packages install_packages
install_Ink-screen-clock
install_pip_packages install_pip_packages
setup_service setup_service
#install_webui #install_webui
@@ -316,6 +378,7 @@ if [ -f /etc/debian_version ]; then
echo "执行Debian 12 (Bookworm) 相关操作" echo "执行Debian 12 (Bookworm) 相关操作"
update_sources_list "bookworm" update_sources_list "bookworm"
install_packages install_packages
install_Ink-screen-clock
install_pip_packages install_pip_packages
setup_service setup_service
#install_webui #install_webui

View File

@@ -55,28 +55,28 @@ def set_system_time_from_hwclock(utc=True):
hwclock_args = ['sudo', 'hwclock', '--hctosys'] hwclock_args = ['sudo', 'hwclock', '--hctosys']
if not utc: if not utc:
hwclock_args.append('--localtime') hwclock_args.append('--localtime')
logging.debug(f"Executing hwclock command: {' '.join(hwclock_args)}") logging.debug(f"Executing hwclock command: {' '.join(hwclock_args)}")
# 使用 subprocess.run 执行 hwclock --hctosys 并捕获输出 # 使用 subprocess.run 执行 hwclock --hctosys 并捕获输出
result = subprocess.run(hwclock_args, result = subprocess.run(hwclock_args,
check=True, # 如果命令失败,则抛出 CalledProcessError 异常 check=True, # 如果命令失败,则抛出 CalledProcessError 异常
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
text=True) text=True)
# 等待一小段时间以确保时间更新完成 # 等待一小段时间以确保时间更新完成
time.sleep(0.1) # 根据需要调整 time.sleep(0.1) # 根据需要调整
# 记录调用 hwclock 后的时间 # 记录调用 hwclock 后的时间
after_time = datetime.datetime.now() after_time = datetime.datetime.now()
logging.debug("System time successfully set from hardware clock.") logging.debug("System time successfully set from hardware clock.")
logging.debug(f"System time after hwclock call: {after_time}") logging.debug(f"System time after hwclock call: {after_time}")
# 检查时间是否发生了倒退 # 检查时间是否发生了倒退
if after_time < before_time: if after_time < before_time:
logging.warning(f"Time went backwards after hwclock call: before={before_time}, after={after_time}") logging.warning(f"Time went backwards after hwclock call: before={before_time}, after={after_time}")
return True return True
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
logging.error(f"Failed to set system time from hardware clock: {e.stderr}") logging.error(f"Failed to set system time from hardware clock: {e.stderr}")
@@ -87,18 +87,17 @@ def set_system_time_from_hwclock(utc=True):
def get_time(): def get_time():
global has_set_system_time global has_set_system_time
if not has_set_system_time: if not has_set_system_time:
# 尝试从硬件时钟设置系统时间,默认假设 RTC 是 UTC # 尝试从硬件时钟设置系统时间,默认假设 RTC 是 UTC
success = set_system_time_from_hwclock(utc=True) success = set_system_time_from_hwclock(utc=True)
# 无论成功与否,都更新标志变量以避免重复尝试 # 无论成功与否,都更新标志变量以避免重复尝试
has_set_system_time = True has_set_system_time = True
# 获取并返回当前时间,格式为 HH:MM 大写 # 获取并返回当前时间,格式为 HH:MM 大写
current_time = time.strftime('%H:%M').upper() current_time = time.strftime('%H:%M').upper()
logging.debug(f"Returning current time: {current_time}") logging.debug(f"Returning current time: {current_time}")
return current_time return current_time
def Get_ipv4_address(): # 获取当前的IP地址 def Get_ipv4_address(): # 获取当前的IP地址
try: try:
ip_output = subprocess.check_output( ip_output = subprocess.check_output(
@@ -108,14 +107,36 @@ def Get_ipv4_address(): # 获取当前的IP地址
return filtered_ips[0] if filtered_ips else "地址获取失败" return filtered_ips[0] if filtered_ips else "地址获取失败"
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return "获取失败" return "获取失败"
# 全局变量声明
last_power = None # 用于存储上一次获取的电量值
last_power_time = 0 # 用于存储上一次获取电量的时间戳
def power_battery(): # 获取当前电池电量 def power_battery(): # 获取当前电池电量
return str(int(subprocess.check_output( global last_power, last_power_time
u"echo \"get battery\" | nc -q 0 127.0.0.1 8423|awk -F':' '{print int($2)}'", current_time = time.time() # 获取当前时间戳
shell=True).decode('gbk'))) + u'%'
# 每3分钟或首次获取时更新
if (current_time - last_power_time >= 180) or (last_power is None):
try:
# 执行命令获取电池电量
result = subprocess.check_output(
u"echo \"get battery\" | nc -q 0 127.0.0.1 8423 | awk -F':' '{print int($2)}'",
shell=True, stderr=subprocess.STDOUT
).decode('gbk').strip()
new_power = f"{int(result)}%" # 格式化电量值
# 仅在电量变化或首次时更新显示
if new_power != last_power or last_power is None:
logging.info(f"电池电量更新: {new_power}")
last_power = new_power # 更新缓存值
last_power_time = current_time # 更新获取时间
except Exception as e:
logging.error(f"电量获取失败: {str(e)}")
new_power = last_power if last_power else "0%" # 失败时使用缓存值或默认值
else:
new_power = last_power # 未到更新时间,使用缓存值
return new_power
def Bottom_edge(): # 在图片中添加底边内容 def Bottom_edge(): # 在图片中添加底边内容
draw.rectangle((0, 105, 250, 122), 'black', 'black') draw.rectangle((0, 105, 250, 122), 'black', 'black')
'''电池图标画图''' '''电池图标画图'''
@@ -138,26 +159,39 @@ def Bottom_edge(): # 在图片中添加底边内容
draw.text((10, 107), f"IP:{local_addr}", font=font05, fill=255) # 显示当前IP地址 draw.text((10, 107), f"IP:{local_addr}", font=font05, fill=255) # 显示当前IP地址
def Weather(): # 在图片中添加天气内容 def Weather():
with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as file: try:
weather_data = json.load(file) with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as Weather_json:
global Weather_position, temperature, weather, wind_direction, weather_update, weather_date, humidity Weather_data = Weather_json.read()
Weather_position = weather_data['cityname'] # 定位位置 if not Weather_data.strip(): # 检查文件是否为空
temperature = weather_data['temp'] + u'°C' # 温度 logging.error("天气数据文件为空")
weather = weather_data['weather'] # 天气情况 return
wind_direction = weather_data['WD'] # 风向
weather_update = weather_data['time'] # 天气更新时间 Weather_text = json.loads(Weather_data)
weather_date = weather_data['date'] # 日期 global Weather_position, temperature, weather, wind_direction, weather_update, weather_date, humidity
humidity = weather_data['SD'] # 湿度
draw.text((150, 25), "天气:", font=font06, fill=0) # 显示当前天气前缀 Weather_position = Weather_text.get('cityname', '未知') # 使用get方法提供默认值
draw.text((150, 45), "温度:", font=font06, fill=0) # 显示当前温度前缀 temperature = f"{Weather_text.get('temp', '--')}°C"
draw.text((150, 65), "湿度:", font=font06, fill=0) # 显示当前湿度前缀 weather = Weather_text.get('weather', '未知')
draw.text((150, 85), "城市:", font=font06, fill=0) # 显示当前城市前缀 wind_direction = Weather_text.get('WD', '未知')
draw.text((191, 25), weather, font=font06, fill=0) weather_update = Weather_text.get('time', '未知')
draw.text((191, 45), temperature, font=font06, fill=0) weather_date = Weather_text.get('date', '未知')
draw.text((191, 65), humidity, font=font06, fill=0) humidity = Weather_text.get('SD', '未知')
draw.text((191, 85), Weather_position, font=font06, fill=0) draw.text((150,25),"天气:",font = font06,fill =0)#显示当前天气前缀
draw.text((211, 107), weather_update, font=font05, fill=255) # 显示天气更新时间 draw.text((150,45),"温度:",font = font06,fill =0)#显示当前温度前缀
draw.text((150,65),"湿度:",font = font06,fill =0)#显示当前湿度前缀
draw.text((150,85),"城市:",font = font06,fill =0)#显示当前城市前缀
draw.text((191,25),weather,font = font06,fill =0)
draw.text((191,45),temperature,font = font06,fill =0)
draw.text((191,65),humidity,font = font06,fill =0)
draw.text((191,85),Weather_position,font = font06,fill =0)
draw.text((211,107),weather_update,font = font05,fill =255) #显示天气更新时间
except FileNotFoundError:
logging.error("天气数据文件未找到")
except json.JSONDecodeError as e:
logging.error(f"天气数据解析失败: {str(e)}")
except Exception as e:
logging.error(f"获取天气信息时发生错误: {str(e)}")
def Basic_refresh(): # 全刷函数 def Basic_refresh(): # 全刷函数
@@ -288,4 +322,4 @@ epd.init()
epd.Clear(0xFF) # 清除屏幕内容 epd.Clear(0xFF) # 清除屏幕内容
epd.sleep() # 使屏幕进入休眠状态 epd.sleep() # 使屏幕进入休眠状态
epd2in13_V4.epdconfig.module_exit() # 清理资源 epd2in13_V4.epdconfig.module_exit() # 清理资源
exit() exit()

8
bin/requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
borax==4.1.2
certifi==2025.1.31
charset-normalizer==3.4.1
idna==3.10
pillow==11.1.0
requests==2.32.3
spidev==3.6
urllib3==2.3.0

View File

@@ -11,4 +11,4 @@ do
kill -9 $id kill -9 $id
done done
nohup /usr/bin/python3 -u $dir/$f_name > $logdir/info.log 2>&1 & nohup /usr/bin/python3 -u $dir/$f_name > $logdir/info.log 2>&1 &
nohup /usr/bin/python3 -u $dir/$f1_name > $logdir/info-wenter.log 2>&1 & nohup /usr/bin/python3 -u $dir/$f1_name > $logdir/info-weather.log 2>&1 &

BIN
bin/vendor/borax-4.1.2-py3-none-any.whl vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
bin/vendor/idna-3.10-py3-none-any.whl vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/vendor/spidev-3.6.tar.gz vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -5,85 +5,189 @@ import json
import time import time
import logging import logging
import requests import requests
import random
import socket
from functools import wraps
from threading import Timer from threading import Timer
white = 255 # 颜色 logging.basicConfig(
black = 0 level=logging.INFO,
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') # 设置日志级别 format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def check_network_connection():
"""检查网络连接状态"""
try:
# 尝试连接一个可靠的公共DNS服务器
socket.create_connection(("223.5.5.5", 53), timeout=5)
return True
except OSError:
return False
def get_ip():
"""改进的IP获取函数"""
if not check_network_connection():
logging.warning("网络连接不可用")
return None
services = [
{"url": "https://api.ipify.org?format=json", "field": "ip"},
{"url": "https://ipinfo.io/json", "field": "ip"},
{"url": "https://ifconfig.me/all.json", "field": "ip_addr"}
]
for service in services:
try:
resp = requests.get(service["url"], timeout=10)
data = resp.json()
return data.get(service["field"])
except Exception:
continue
logging.error("所有IP服务尝试失败")
return None
def get_ip():
"""从ip.cn获取当前IP地址"""
url = "https://ip.cn/api/index?ip=&type=0"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
try:
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
data = resp.json()
if data.get('code') == 'Success':
return data.get('ip', '')
logging.error("获取IP失败: %s", data.get('msg', '未知错误'))
except Exception as e:
logging.error("获取IP异常: %s", str(e))
return None
def get_current_city():
"""通过IP地址获取当前定位城市并去除''后缀"""
ip = get_ip()
if not ip:
return None
url = f"http://ip-api.com/json/{ip}?fields=city&lang=zh-CN"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
while True:
try:
resp = requests.get(url, headers=headers, timeout=10)
data = resp.json()
if data.get('status') == 'success':
return data.get('city', '').replace('', '')
logging.error("定位失败: %s", data.get('message', '未知错误'))
except Exception as e:
logging.error("定位异常: %s", str(e))
time.sleep(180)
def schedule_getWeath():
"""改进的定时任务调度"""
retry_count = 0
max_retries = 3
while retry_count < max_retries:
try:
getWeath()
break
except Exception as e:
retry_count += 1
wait_time = min(300, retry_count * 60) # 指数退避
logging.error(f"天气更新失败({retry_count}/{max_retries}): {str(e)}")
time.sleep(wait_time)
# 无论成功与否,都安排下一次执行
Timer(1800, schedule_getWeath).start() # 30分钟间隔
def getWeath(default_city='101060101'):
"""改进的天气获取函数"""
# 1. 检查网络连接
if not check_network_connection():
logging.error("网络不可用,跳过天气更新")
return
# 2. 获取城市信息
city_name = None
try:
city_name = get_current_city()
except Exception as e:
logging.error(f"获取城市失败: {str(e)}")
# 3. 确定区域ID
area_id = default_city
if city_name:
try:
found_id = get_area_id(city_name)
if found_id:
area_id = found_id
logging.info(f"使用城市区域ID: {area_id} ({city_name})")
except Exception as e:
logging.error(f"获取区域ID失败: {str(e)}")
# 4. 获取天气数据
weather_apis = [
f'https://d1.weather.com.cn/sk_2d/{area_id}.html',
f'https://www.weather.com.cn/weather1d/{area_id}.shtml'
]
for api_url in weather_apis:
try:
resp = requests.get(
api_url,
headers={'User-Agent': 'Mozilla/5.0'},
timeout=15
)
resp.raise_for_status()
# 处理不同API的响应格式
if 'sk_2d' in api_url:
weather_data = resp.content[11:].decode('utf-8')
else:
weather_data = parse_html_weather(resp.text)
# 验证数据有效性
if validate_weather_data(weather_data):
with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'w') as f:
json.dump(weather_data, f)
logging.info("天气数据更新成功")
return
except Exception as e:
logging.warning(f"天气API {api_url} 失败: {str(e)}")
continue
logging.error("所有天气API尝试失败")
def get_area_id(city_name): def get_area_id(city_name):
"""从city.js中检索AREAID无限重试直到成功""" """从city.js中检索AREAID无限重试直到成功"""
url = "https://j.i8tq.com/weather2020/search/city.js" url = "https://j.i8tq.com/weather2020/search/city.js"
while True:
try:
response = requests.get(url)
response.raise_for_status() # 检查请求是否成功
city_data_text = response.text.strip().split('var city_data = ')[-1].rstrip(';')
if city_data_text:
city_data = json.loads(city_data_text)
for province, cities in city_data.items():
for city, districts in cities.items():
for district, info in districts.items():
if info['NAMECN'] == city_name:
return info['AREAID']
logging.error("城市名称 '%s' 在城市数据中未找到", city_name)
else:
logging.error("从city.js接收到的数据为空")
except (requests.RequestException, json.JSONDecodeError) as e:
logging.error("检索或解析城市数据时发生错误: %s", e)
time.sleep(180) # 重试前等待
def get_current_city():
"""获取当前城市名称,无限重试直到成功"""
url = "http://ip-api.com/json/?lang=zh-CN"
while True:
try:
response = requests.get(url)
response.raise_for_status() # 检查请求是否成功
data = response.json()
if data['status'] == 'success':
return data['city']
else:
logging.error("获取当前城市失败: %s", data['message'])
except (requests.RequestException, json.JSONDecodeError) as e:
logging.error("检索或解析当前城市时出现错误: %s", e)
time.sleep(180) # 重试前等待
def schedule_getWeath():
"""调度 getWeath 函数每3分钟执行一次"""
getWeath()
Timer(180, schedule_getWeath).start() # 重新设置定时器
def getWeath(city='101060101'):
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
'Referer': 'https://www.weather.com.cn/'
} }
current_city = get_current_city() while True:
if current_city: try:
area_id = get_area_id(current_city) resp = requests.get(url, headers=headers, timeout=10)
if area_id: resp.encoding = 'utf-8'
city = area_id city_data = json.loads(resp.text.split('=', 1)[1].rstrip(';'))
for province in city_data.values():
for city in province.values():
for district, info in city.items():
if info['NAMECN'] == city_name:
return info['AREAID']
logging.error("未找到城市: %s", city_name)
except Exception as e:
logging.error("获取城市ID失败: %s", str(e))
time.sleep(180)
if __name__ == "__main__":
try: try:
response = requests.get(f'https://d1.weather.com.cn/sk_2d/{city}.html', headers=headers) schedule_getWeath()
response.raise_for_status() # 检查请求是否成功 while True:
response.encoding = 'utf-8' time.sleep(1)
weather_data = response.text[11:] except KeyboardInterrupt:
with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'w') as fileHandle: logging.info("程序已终止")
fileHandle.write(weather_data)
print("天气文件更新")
except requests.RequestException as e:
logging.error("获取天气数据时出现网络错误: %s", e)
except Exception as e:
logging.error("发生了意外错误: %s", e)
try:
schedule_getWeath() # 开始调度天气获取函数
except KeyboardInterrupt:
logging.info("检测到键盘中断,正在退出")
except Exception as e:
logging.error("发生了意外错误: %s", e)
exit()
# 脚本正常结束后的清理操作
exit()