#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 推送层 —— 直连局域网墨水屏设备(喵喵固件,仅用标准库 urllib,无第三方依赖)。 固件开源:https://gitee.com/gxp666111/miaomiao(web/ 为设备自带管理页,可在浏览器打开 http:/// 调试同一套接口)。 """ import json import time import urllib.parse import urllib.request import urllib.error import config # 局域网 mDNS(.local)域名解析基于组播 UDP,偶发瞬时失败正常,重试即可恢复。 RETRY_TIMES = 2 RETRY_DELAY = 2 def _request(method, path, headers=None, body=None): url = f"http://{config.EPD_HOST}{path}" req = urllib.request.Request(url, data=body, headers=headers or {}, method=method) for attempt in range(RETRY_TIMES + 1): try: with urllib.request.urlopen(req, timeout=30) as resp: return resp.read().decode("utf-8", "ignore") except urllib.error.HTTPError as e: raise RuntimeError(f"HTTP {e.code}: {e.read().decode('utf-8', 'ignore')}") from e except urllib.error.URLError: if attempt == RETRY_TIMES: raise time.sleep(RETRY_DELAY) def _list_gallery_names(): text = _request("GET", "/images") return [item["name"] for item in json.loads(text).get("items", [])] def _clear_gallery(): """设备画廊按追加保存,用完即删,使其始终只留最新一张。 清理是锦上添花,不是推送的必要条件——拉列表或单张删除失败都不能阻塞后续上传。""" try: names = _list_gallery_names() except Exception as e: print(f"[推送] 拉取画廊列表失败,跳过本次清理:{e}") return for name in names: try: _request("POST", f"/delete_image?name={urllib.parse.quote(name)}") except Exception as e: print(f"[推送] 清理画廊旧图 {name} 失败:{e}") def push_image(image_path): """推送图片到局域网墨水屏设备:先清空画廊旧图,再上传新图触发显示刷新。""" _clear_gallery() with open(image_path, "rb") as fp: content = fp.read() resp = _request("POST", "/upload", headers={"Content-Type": "image/png"}, body=content) return config.EPD_HOST, resp