#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 推送层 —— 调用 Zectrix 极趣云平台 API(仅用标准库 urllib,无第三方依赖)。 文档:显示推送 / 设备管理。 """ import json import uuid import urllib.request import urllib.error import config def _request(method, path, headers=None, body=None): url = f"{config.API_BASE}{path}" h = {"X-API-Key": config.API_KEY} if headers: h.update(headers) req = urllib.request.Request(url, data=body, headers=h, method=method) try: with urllib.request.urlopen(req, timeout=30) as resp: return json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: raise RuntimeError(f"HTTP {e.code}: {e.read().decode('utf-8', 'ignore')}") from e def get_devices(): """获取设备列表。""" data = _request("GET", "/devices") if data.get("code") != 0: raise RuntimeError(f"获取设备失败: {data}") return data.get("data", []) def resolve_device_id(): """返回目标设备 ID:优先用 config.DEVICE_ID,否则取列表第一个。""" if config.DEVICE_ID: return config.DEVICE_ID devices = get_devices() if not devices: raise RuntimeError("账号下没有任何设备") return devices[0]["deviceId"] def _multipart(fields, files): """构造 multipart/form-data 请求体。fields: dict[str,str]; files: list[(name, filename, bytes, mime)]""" boundary = uuid.uuid4().hex crlf = b"\r\n" buf = bytearray() for k, v in fields.items(): buf += b"--" + boundary.encode() + crlf buf += f'Content-Disposition: form-data; name="{k}"'.encode() + crlf + crlf buf += str(v).encode() + crlf for name, filename, content, mime in files: buf += b"--" + boundary.encode() + crlf buf += f'Content-Disposition: form-data; name="{name}"; filename="{filename}"'.encode() + crlf buf += f"Content-Type: {mime}".encode() + crlf + crlf buf += content + crlf buf += b"--" + boundary.encode() + b"--" + crlf return bytes(buf), f"multipart/form-data; boundary={boundary}" def push_image(image_path, device_id=None, page_id=config.PAGE_ID, dither=config.DITHER): """推送图片到设备显示。""" device_id = device_id or resolve_device_id() with open(image_path, "rb") as fp: content = fp.read() fields = {"dither": "true" if dither else "false", "pageId": str(page_id)} files = [("images", "dashboard.png", content, "image/png")] body, content_type = _multipart(fields, files) data = _request("POST", f"/devices/{device_id}/display/image", headers={"Content-Type": content_type}, body=body) if data.get("code") != 0: raise RuntimeError(f"推送失败: {data}") return device_id, data