13 Commits

Author SHA1 Message Date
星光-k
1ac9ca91ef Update weather.py 2025-05-09 00:46:43 +08:00
星光-k
5c8d24f43c Update main.py 2025-05-09 00:46:14 +08:00
星光-k
13adf24df4 Update weather.py 2025-03-15 11:01:52 +08:00
星光-k
09c6c3c7ac Update README.md 2025-02-08 20:15:29 +08:00
星光-k
caf0228277 Update README.md 2025-02-08 19:17:44 +08:00
星光-k
54b76b4d26 Update install.sh 2025-02-08 18:56:22 +08:00
星光-k
f25f0d8dcf Update install.sh 2025-02-08 15:01:20 +08:00
星光-k
8f0fd2e6d6 Delete bin/vendor/1 2025-02-08 14:59:05 +08:00
星光-k
7350d120ad Add files via upload 2025-02-08 06:58:26 +00:00
星光-k
db8c4344ba Create 1 2025-02-08 14:57:23 +08:00
星光-k
7759cabcf0 Update install.sh 2025-02-08 13:56:42 +08:00
星光-k
a56b337975 Rename requirements.txt to bin/requirements.txt 2025-02-08 13:50:06 +08:00
星光-k
7cd6136696 Add files via upload 2025-02-08 05:49:03 +00:00
13 changed files with 257 additions and 117 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)
@@ -66,6 +66,7 @@ sudo reboot
--zh 设置系统语言为zh_CN,UTF-8
--cn 替换apt镜像源为中国镜像源
--gitcn 克隆中国仓库
--pip-offline pip依赖离线安装
--pisugar-wifi-conf 安装pisugar-wifi-conf
--pisugar-power-manager 安装pisugar-power-manager
--version <tag> 版本号(使用方法 --version + 仓库标签,格式例如 v1.x.x ,可以是主仓库main)
@@ -73,22 +74,29 @@ sudo reboot
```
###
```Bash
#中国源默认设置
#中国源默认设置(不加参数)
curl -sSL https://gitee.com/xingguangk/2.13-Ink-screen-clock/raw/main/bin/install.sh | sudo bash
```
```Bash
#中国源参数设置(不使用--debug参数)
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>
#中国源参数设置(不使用--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 --version <tag>
```
```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
```
```Bash
#默认源全参数设置(不使用--debug参数)
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>
#默认源默认设置(不使用--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 --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

View File

@@ -18,6 +18,8 @@ USE_CN_GIT=false
USE_PISUGAR_WIFI_CONF=false
# 检查是否安装pisugar-power-manager
USE_PISUGAR_POWER_MANAGER=false
# 检查是否使用离线安装pip依赖
USE_OFFLINE_PIP=false
# 解析命令行参数
while [ "$#" -gt 0 ]; do
@@ -31,6 +33,9 @@ while [ "$#" -gt 0 ]; do
--gitcn)
USE_CN_GIT=true
;;
--pip-offline)
USE_OFFLINE_PIP=true
;;
--pisugar-wifi-conf)
USE_PISUGAR_WIFI_CONF=true
;;
@@ -203,23 +208,7 @@ install_packages() {
fi
}
# 安装pip包函数
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"
install_Ink-screen-clock() {
# 检查墨水屏时钟仓库是否存在
if [ ! -d "$HOME/2.13-Ink-screen-clock" ]; then
echo "正在克隆仓库"
@@ -234,6 +223,41 @@ setup_service() {
else
echo "仓库文件夹已存在,跳过克隆"
fi
}
# 安装pip包函数
install_oline_pip_packages() {
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
@@ -306,6 +330,7 @@ if [ -f /etc/debian_version ]; then
echo "执行Debian 11 (Bullseye) 相关操作"
update_sources_list "bullseye"
install_packages
install_Ink-screen-clock
install_pip_packages
setup_service
#install_webui
@@ -316,6 +341,7 @@ if [ -f /etc/debian_version ]; then
echo "执行Debian 12 (Bookworm) 相关操作"
update_sources_list "bookworm"
install_packages
install_Ink-screen-clock
install_pip_packages
setup_service
#install_webui

View File

@@ -161,35 +161,39 @@ def Bottom_edge(): #在图片中添加底边内容
global local_addr #获取当前IP地址
local_addr= Get_ipv4_address() #获取当前IP地址
draw.text((10,107),"IP:"+local_addr,font = font05,fill =255)#显示当前IP地址
def Weather(): #在图片中添加天气内容
Weather_json = open('/root/2.13-Ink-screen-clock/bin/weather.json','r')
Weather_data = Weather_json.read()
Weather_json.close()
Weather_text=json.loads(Weather_data)
global Weather_position
global temperature
global weather
global wind_direction
global weather_update
global weather_date
global humidity
Weather_position = Weather_text['cityname'] #定位位置
temperature=Weather_text['temp']+u'°C' #温度
weather = Weather_text['weather'] #天气情况
wind_direction = Weather_text['WD'] #风向
weather_update = Weather_text['time'] #天气更新时间
weather_date = Weather_text['date'] #日期
humidity = Weather_text['SD'] #湿度
draw.text((150,25),"天气:",font = font06,fill =0)#显示当前天气前缀
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) #显示天气更新时间
def Weather():
try:
with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'r') as Weather_json:
Weather_data = Weather_json.read()
if not Weather_data.strip(): # 检查文件是否为空
logging.error("天气数据文件为空")
return
Weather_text = json.loads(Weather_data)
global Weather_position, temperature, weather, wind_direction, weather_update, weather_date, humidity
Weather_position = Weather_text.get('cityname', '未知') # 使用get方法提供默认值
temperature = f"{Weather_text.get('temp', '--')}°C"
weather = Weather_text.get('weather', '未知')
wind_direction = Weather_text.get('WD', '未知')
weather_update = Weather_text.get('time', '未知')
weather_date = Weather_text.get('date', '未知')
humidity = Weather_text.get('SD', '未知')
draw.text((150,25),"天气:",font = font06,fill =0)#显示当前天气前缀
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(): #全刷函数
logging.info("在启动画布之前,刷新并准备基本内容")#开始画布前刷新准备基础内容
global get_date_var
@@ -326,4 +330,4 @@ epd.init()
epd.Clear(0xFF) # 清除屏幕内容
epd.sleep() # 使屏幕进入休眠状态
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

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,6 +5,9 @@ import json
import time
import logging
import requests
import random
import socket
from functools import wraps
from threading import Timer
logging.basicConfig(
@@ -13,13 +16,162 @@ logging.basicConfig(
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):
"""从city.js中检索AREAID无限重试直到成功"""
url = "https://j.i8tq.com/weather2020/search/city.js"
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, timeout=10)
resp.encoding = 'utf-8' # 显式设置编码
resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = 'utf-8'
city_data = json.loads(resp.text.split('=', 1)[1].rstrip(';'))
for province in city_data.values():
for city in province.values():
@@ -31,68 +183,10 @@ def get_area_id(city_name):
logging.error("获取城市ID失败: %s", str(e))
time.sleep(180)
def get_current_city():
"""获取当前定位城市并去除''后缀"""
url = "http://ip-api.com/json/?lang=zh-CN"
while True:
try:
resp = requests.get(url, timeout=10)
resp.encoding = 'utf-8' # 显式设置编码
data = resp.json()
if data['status'] == 'success':
return data['city'].rstrip('')
logging.error("定位失败: %s", data.get('message'))
except Exception as e:
logging.error("定位异常: %s", str(e))
time.sleep(180)
def schedule_getWeath():
"""定时任务调度"""
try:
getWeath()
finally:
Timer(180, schedule_getWeath).start()
def getWeath(default_city='101060101'):
"""获取天气数据核心函数"""
city_name = get_current_city()
area_id = default_city # 设置默认值
if city_name:
try:
area_id = get_area_id(city_name) or default_city
except Exception as e:
logging.error("获取区域ID失败: %s", str(e))
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',
'Referer': 'https://www.weather.com.cn/'
}
try:
resp = requests.get(
f'https://d1.weather.com.cn/sk_2d/{area_id}.html',
headers=headers,
timeout=15
)
resp.encoding = 'utf-8' # 关键修改:强制设置响应编码
resp.raise_for_status()
# 直接处理原始字节数据
weather_data = resp.content[11:].decode('utf-8')
# 使用utf-8编码写入文件
with open('/root/2.13-Ink-screen-clock/bin/weather.json', 'w', encoding='utf-8') as f:
f.write(weather_data)
logging.info("天气数据更新成功")
except Exception as e:
logging.error("天气更新失败: %s", str(e))
if __name__ == "__main__":
try:
schedule_getWeath()
while True:
time.sleep(1) # 保持主线程存活
time.sleep(1)
except KeyboardInterrupt:
logging.info("程序已终止")
logging.info("程序已终止")