| 层级 | 能力 | 一句话说明 |
|---|---|---|
| 第一层 | 设备接入 API | 设备 / 程序用 device_id + token 即可接入:上报数据、接收指令,无需网页登录。 |
| 第二层 | 规则引擎 | 数据到达后按条件自动做动作(下发指令 / 请求外部网址 / 通知等),无需写业务代码。 |
| 第三层 | 可视化应用工作台 | 拖拽组件绑定设备属性,一键发布为免登录的公开网页,别人打开链接就能看数据、点按钮。 |
本文档所有示例统一使用平台地址:https://www.hjyiot.cn/api.php。遇到任何问题,可通过文末官方客服 QQ 联系我们。
三版内容相互独立,可随时切换;页面最下方附官方客服联系方式。
平台官方提供两套 SDK,把本文档的接入上报 / 指令 / 云盘接口封装成可直接调用的函数,新设备最快 10 分钟完成接入。均为轻量开源实现、无第三方依赖,可直接阅读源码对照下方协议章节。
| SDK | 适用场景 | 包含内容 | 下载 |
|---|---|---|---|
HjyIotESP32 Arduino 库 |
ESP32 开发板(板载 WiFi),Arduino IDE / PlatformIO 均可 | HjyIot.h/.cpp + 官方库描述 library.properties + 示例 device_basic(接入上报)、cloud_disk(云盘写入)、ota_update(OTA 固件下载) |
下载 ZIP |
hjyiotPython 3 模块 |
树莓派 / PC / 网关 / 服务端脚本,仅用 Python 标准库 | hjyiot.py + 示例 device_basic.py(接入上报)、cloud_disk.py(云盘写入)、ota_download.py(OTA 固件下载) |
下载 ZIP |
📖 SDK 使用说明(目录结构 / 函数速查 / 示例对照):SDK使用说明.md;也可直接浏览散文件目录 assets/docs/sdk/。
💡 快速开始:Arduino 用户把 ZIP 解压进 libraries/(库名 HjyIot),菜单「文件 → 示例 → HjyIot」即见三个 demo;Python 用户把 hjyiot.py 放进项目目录后 from hjyiot import HjyIot 即可。接口协议细节对照下方 2.1 鉴权、2.10 云盘接口 与 2.11 OTA 固件下载;示例与接口一对一关系见老手版开头说明。
登录平台后,左侧菜单从上到下依次是这些功能模块(不同角色可见项略有差异):
| 功能模块 | 面向谁 | 能做什么 | 典型场景 |
|---|---|---|---|
| 工作台总览 | 所有登录用户 | 一页看清全局:设备总数、在线设备数、应用数(已发布几个)、启用规则数;提供快速开始向导;展示我的租户信息与平台动态。 | 每天打开先看设备有没有掉线、整体运行是否正常。 |
| 设备管理 | 所有登录用户 | 创建设备、查看设备在线状态;查看属性快照(设备最近上报的数据汇总);查看设备日志(心跳、上报、指令等历史);手动下发指令;查看与重置设备令牌 token。 | 新设备上线登记;排查某台设备为什么没数据;远程手动开关一次设备。 |
| 应用工作台 | 所有登录用户 | 可视化搭网页:7 种组件(文本、指标大数字、仪表盘、折线图、开关、按钮、分隔线),拖进画布、绑定设备属性即可。一键「发布」生成免登录公开链接,可分发给任何人;可「下架」停止访问、可「重置密钥」让旧链接立即失效。 | 给客户做一个大屏展示温湿度;给现场工人做一个手机能点的控制面板。 |
| 规则引擎 | 所有登录用户 | 图形化配置自动化:4 种触发器(设备上报 / 状态变化 / 定时 / Webhook)+ 条件 + 5 种动作(下发指令 / 触发链式规则 / 请求外部 HTTP / 站内通知 / 修改属性),并可查看每条规则的历史执行日志。 | 温度超过 30 度自动开风扇;设备掉线自动通知;每天 8 点自动巡检。 |
| 接入文档(本页) | 所有人 | 分版本说明如何把设备 / 程序接入平台。 | 首次接入先读这里。 |
| 平台管理 | 平台管理员 | 管理成员账号(创建 / 启停 / 重置密码)、管理租户(启停)、维护系统设置(平台名称、客服联系方式、备案信息等)。 | 给同事开账号;改平台显示名称与客服信息。 |
| 设备云盘 | 所有登录用户 | 每个租户 10MB 共享小空间(仅 txt / jpg):建目录、新建 / 编辑 txt、浏览与删除;设备经三个只读接口读取、经属性上报包里的 fs 指令写入文本。 | 设备写运行日志与配置文本,当作轻量数据交换区。 |
| OTA 固件升级 | 所有登录用户 | 设备固件的在线升级闭环:上传 .bin 固件(推荐,免编译)或 .ino 源码 → 平台审查 / 编译 → 推送设备;设备侧用 ota_download 下载固件并校验。固件与云盘共享 10MB 配额。 | 给已部署的设备远程升级功能,不用到现场接线。 |
| 账号与安全能力 | 所有用户 | 注册即自动创建独立开发者空间(租户);支持修改密码;设备接入使用「设备 ID + 令牌」双重凭据,与网页登录账号相互隔离;公开页只暴露你主动绑定并发布的数据。 | 设备令牌泄露只影响单台设备,重置即可,不影响后台账号。 |
平台对外开放能力速览:HTTP 设备接入(心跳 / 属性上报 / 指令拉取)、设备 OTA 固件下载(ota_download,见老手版 2.11)、设备云盘接口(只读三接口 disk_stats / disk_list / disk_read,云盘内容维护随属性上报携带 fs 指令完成,见下方老手版 2.10 云盘接口)、免登录公开应用页(查看 + 点击操作)、公开 JSON 子接口(供自研前端 / 大屏对接)、Webhook 外部联动(第三方系统触发规则)。详见下方对应版本与协议章节。
💡 我的设备 / 程序怎么对接云盘?看下方「设备对接云盘 · 接口速查」,老手版 2.10 云盘接口 还有完整协议与限制说明。要点:云盘不开放整文件上传 / 下载;网页端左侧菜单「云盘」可建目录、建 txt、浏览与删除;设备新增或维护文本统一走属性上报包内的 fs 指令(写入须显式提供目标文件路径),读取走三个只读接口。首次对接请先按零基础版流程创建好设备并拿到 token。
同一租户下的所有设备共享一个云盘(默认 10MB,仅支持 txt / jpg)。⚠️ 这 10MB 是账户级共享配额,与 OTA 固件(见下方「设备 OTA 升级 · 接口速查」)合计计算,固件需在「OTA 升级」页删除任务才能释放。设备接入凭据与设备接口完全一致:HTTP Basic(user=设备ID,pass=token)或 X-Device-ID + X-Device-Token。以下示例统一走平台 API:https://www.hjyiot.cn/api.php
| 动作 | 方法 / 请求 | 返回要点 |
|---|---|---|
| 查看空间 | GET https://www.hjyiot.cn/api.php?action=disk_stats | limit / used / free / percent / read_only / quota_hit / disabled / file_types;写满、封禁时 read_only=1,封禁态拒绝访问并返回原因 |
| 列目录 | GET https://www.hjyiot.cn/api.php?action=disk_list&path=<目录,可省略> | rel / items[](条目含 name、type、ext、size、modified、frozen)、配额与只读状态 |
| 读取文件 | GET https://www.hjyiot.cn/api.php?action=disk_read&path=<文件完整路径> | txt 返回 content(原文);jpg 返回 data(base64) |
| 写入 / 覆盖文本 | POST https://www.hjyiot.cn/api.php?action=report,Body={"props":{},"fs":[{"op":"write","path":"<txt路径>","content":"<文本>","mode":"append|overwrite","create":true|false}]} | fs[] 逐项返回 op/path/ok/bytes/created/mode;追加默认 append;目录或文件不存在时默认报错,显式 create:true 才自动创建 |
| 删除文件 | POST https://www.hjyiot.cn/api.php?action=report,Body={"props":{},"fs":[{"op":"delete","path":"<文件路径>"}]} | fs[] 返回 op/path/ok;仅可删除文件,目录删除请由平台网页端处理 |
| 删除文本片段 | POST https://www.hjyiot.cn/api.php?action=report,Body={"props":{},"fs":[{"op":"delete_content","path":"<txt路径>","needle":"<要删除的片段>","all":true|false}]} | fs[] 返回 op/path/ok/removed/bytes;all=false(默认)仅删首个匹配,all=true 删全部匹配 |
写入示例(Linux / macOS / Windows PowerShell 通用,用 curl;先建目录可省略 create,未建目录需带 "create":true):curl -u 设备ID:token -H "Content-Type: application/json" -X POST "https://www.hjyiot.cn/api.php?action=report" --data '{"props":{},"fs":[{"op":"write","path":"logs/2026-09-08.txt","content":"温度 37.5℃\n","create":true}]}'
读取示例:curl -u 设备ID:token "https://www.hjyiot.cn/api.php?action=disk_read&path=logs/2026-09-08.txt"
⚠️ 云盘不接受整文件上传 / 下载:所有文本写入 / 覆盖 / 内容删除 / 文件删除都通过属性上报包内的 fs 指令完成(见上表 3 条与老手版 2.10 细则);jpg 文件只能经 disk_read 读取内容,其内容写入由平台按业务策略维护,设备无法新增 jpg。
平台提供设备固件的在线升级闭环:上传固件 / 源码 → 审查 / 编译 → 推送设备 → 设备下载并校验。上传与推送在网页端左侧菜单「OTA 升级」完成;设备侧只做一件事:收到 OTA 指令后调用 ota_download 下载固件二进制。固件与云盘共享账户 10MB 配额(disk_stats.used 已含固件占用)。
| 动作 | 方法 / 请求 | 返回要点 |
|---|---|---|
| 下载固件 | GET https://www.hjyiot.cn/api.php?action=ota_download&job_id=<任务ID>(POST 亦可,body {"job_id":"..."}) | 成功返回二进制流(application/octet-stream,不是 JSON),响应头 X-OTA-Version / X-OTA-SHA256 / X-OTA-Size / X-OTA-Job;失败才返回 JSON:1002 功能停用 / 云盘封禁 / 固件冻结,1003 任务不存在,1004 缺 job_id / 未就绪 / 窗口过期,1006 固件文件缺失 |
| 拉取 OTA 指令 | GET https://www.hjyiot.cn/api.php?action=command | 推送后设备会拉到 type=ota 指令,其 payload = {version, job_id, size, sha256, ts} |
下载示例(-OJ 按响应头文件名落盘;下完务必比对 X-OTA-SHA256):curl -u 设备ID:token -OJ "https://www.hjyiot.cn/api.php?action=ota_download&job_id=<任务ID>"
⚠️ 三条硬规则:① 下载窗口 24 小时(自最近一次推送起算),过期需在网页端重新推送;② 任务删除有限制——仅「排队中 / 编译中」不可删,其余状态可删,删除会一并清理 .ino / .bin 并释放配额;③ 固件只能在「OTA 升级」页删除任务释放,云盘侧无法删除。完整状态机、审计口径与 SDK 用法见老手版 2.11 OTA 固件下载。
建议先按本文档自测:零基础版每步都可在网页内完成;出现看不懂的返回码时,把「设备 ID + 页面 + 返回 JSON」一起发给客服,能更快定位。
贺简云IoT 是一站式轻量物联网平台:设备接入、数据可视化、自动化联动、免登录公开页与租户云盘开箱即用。产品由创始人散人独立开发并持续运营,任何建议、反馈与合作,欢迎通过上方 QQ 联系。
如果你:没接触过物联网、看不懂代码、手里可能还没有开发板——就读这一版。它不要求你会编程,只要求你能点鼠标、会复制粘贴。
读完这一版你能做到:① 注册平台并创建你的第一台「设备」;② 让这台设备产生第一条真实数据并在网页上看到它;③ 把数据做成一个网页链接,发给任何人(对方不用登录也能看);④ 明白平台怎么帮你自动干活。
路线图(约 15~30 分钟):注册账号 → 认识几个词 → 创建设备 → 把数据送上来(复制粘贴命令即可)→ 回平台看数据 → 做一个公开网页 → 了解自动联动 → 卡住就看 FAQ。
| 名词 | 它是什么 | 生活比喻 | 在哪看 |
|---|---|---|---|
| 租户 / 开发者空间 | 平台为你单独划分的工作区,你的设备和数据都在里面,别人看不到。 | 你在平台上的「独立办公室」。 | 注册后自动创建;工作台总览可看租户 ID。 |
| 设备 ID(device_id) | 你给设备起的「名字 / 工号」,用来区分每一台设备。 | 员工工牌上的编号。 | 创建时自己填写,之后在设备列表可见。 |
| 令牌(token) | 设备接入平台的「密码」,只有设备自己知道。 | 办公室门禁密码。 | 创建后由平台生成,设备详情可查看 / 重置。 |
| 属性(props) | 设备上报的数据,比如温度、湿度、开关状态。平台会自动汇总成这台设备的「属性快照」。 | 体检报告上的各项指标。 | 设备管理 → 设备详情 → 属性快照。 |
| 指令(command) | 平台想叫设备做的事,例如「打开继电器」。指令先存放在平台队列,设备主动来取并执行。 | 前台给你留的「待办便签」,你路过时取走处理。 | 设备管理 → 下发指令;规则引擎也可自动下发。 |
| 云盘(共享存储) | 每个租户自带的 10MB 共享小空间(只放 txt / jpg)。网页端「云盘」菜单可建文件夹、新建 txt、浏览 / 删除;你的设备也能往里写文本日志、读回数据(见顶部「设备对接云盘 · 接口速查」)。这 10MB 与 OTA 固件共享。 | 公司的公共储物柜:便签和照片往里放,设备也能存取。 | 左侧菜单「云盘」。 |
| 应用 / 公开页 | 把设备数据做成网页给别人看 / 操作,对方不用注册登录。 | 贴在门口的大屏展示牌。 | 应用工作台 → 新建应用 → 发布。 |
| OTA 固件升级 | 给设备里的程序「远程换新版本」:在网页端上传固件或源码,平台审查 / 编译后推送给设备,设备自己下载并重启生效(见顶部「设备 OTA 升级 · 接口速查」)。 | 给手机远程升级系统版本。 | 左侧菜单「OTA 升级」。 |
现在不用背下来,先有个印象;后面每一步都会再次遇到它们。
如果注册后收不到邮件 / 短信验证码,多半是被拦截或延迟,先等 1~2 分钟并检查垃圾箱;仍不行就把注册账号发给我们客服处理。
这里说的「设备」不一定是真实硬件,它只是平台里的一个身份档案。真实硬件 / 程序之后用这个身份来上报数据。
demo-sensor-01。它相当于设备工号,建议全平台内保持唯一,容易记。「设备 ID」和「令牌」的区别:ID 是公开的工号,令牌是私密的密码。别人即使知道你的设备 ID,没有令牌也动不了你的设备。
现在让这台设备产生「第一条真实数据」。需要用到电脑自带的命令行工具:Windows 用 PowerShell,Mac 用「终端」。全程只需要复制粘贴下面两段,然后按回车。
进入「设备管理 → 设备详情」,把 device_id 与 token 两串字符复制到下面命令的对应位置(下面以 demo-sensor-01 为例)。
Windows(打开 PowerShell),把下面整段复制进去回车:
curl -u demo-sensor-01:你的token -X POST "https://www.hjyiot.cn/api.php?action=heartbeat"
Mac(打开「终端」),复制同样内容回车即可。
你会看到类似这样的返回:
{ "code":0, "msg":"成功", "data":{ "online":1, "server_ts":1728000000, "last_seen":1728000000, "rate":null } }
只要看到 "code":0 就是成功;online:1 表示平台已把设备标记为在线。看不懂没关系,记住「code:0 = 成功」即可。
Windows(PowerShell):
curl -u demo-sensor-01:你的token -X POST -H "Content-Type: application/json" -d "{\"ts\":1728000000,\"props\":{\"temperature\":26.5,\"humidity\":58}}" "https://www.hjyiot.cn/api.php?action=report"
Mac(终端):
curl -u demo-sensor-01:你的token -X POST -H 'Content-Type: application/json' -d '{"ts":1728000000,"props":{"temperature":26.5,"humidity":58}}' "https://www.hjyiot.cn/api.php?action=report"
这段的意思是:把「温度 26.5、湿度 58」这两个属性上报给平台。返回 "code":0 即成功。
Windows 的 PowerShell 对引号要求严格,请整段复制、不要手动改动引号;如果提示错误,把返回内容复制给客服即可。Mac / Linux 用单引号版。
temperature: 26.5、humidity: 58。到这里,你已完成一次完整的「设备 → 平台」数据链路。把上面的 demo-sensor-01 和命令替换成你的真实 device_id / token,以后每次想测试就重复 0.5 的命令即可。
这一步不需要写任何代码,全部用鼠标完成:
http://www.hjyiot.cn/app.php?app=应用ID&k=一串密钥)。公开链接等于把数据「公开展示」,请只绑定你愿意公开的属性;想保密的数据不要放进这个页面。
规则引擎用大白话说就是:「如果发生了某件事,就自动做某个动作」。你不需要写程序,在网页里下拉选择就行。
| 例子 | 如果(触发) | 就自动(动作) |
|---|---|---|
| 温度过高自动开风扇 | 设备上报的温度 > 30 | 向风扇设备下发指令「打开」 |
| 设备掉线提醒 | 某设备由在线变为离线 | 站内通知我 |
| 定时巡检 | 每天早上 8:00 | 下发一条指令让设备上报一次数据 |
| 外部系统联动 | 第三方系统调用我规则的专属网址(Webhook) | 请求一个外部接口 / 下发指令 |
现在不用急着配置,先到「规则引擎」页点「新建规则」看一眼界面即可;等你熟悉了数据链路,再回来配置第一条自动化。每条规则跑过之后,都可在「执行日志」里看到它有没有被触发、动作成没成功。
| 问题 | 解答 |
|---|---|
| token 忘了 / 泄露了怎么办? | 设备详情里点「重置令牌」,新 token 会立刻生效,旧 token 立即作废。改完记得同步更新你设备端配置。 |
| 设备一直显示「离线」? | 平台只有在设备最近主动联系过时才算在线。检查设备是否在按节奏发心跳(每 30~60 秒一次比较常见);刚上报完 30 秒内看列表最准。 |
| 上报成功但页面没数据? | 多半是属性名不一致:比如设备上报的是 temperature,而页面组件绑定的是 Temperature(大小写也算不同名字)。让两边逐字一致即可。 |
| 我没有开发板,能继续学吗? | 完全可以。用 0.5 的命令行模拟设备即可;想练「收指令」可以看新手 / 老手版的模拟示例。 |
| Windows 粘贴命令报错? | PowerShell 只接受整段原样粘贴,注意双引号不要被输入法改成中文引号;不行就换 Mac / Linux 试,或直接联系客服。 |
| 公开链接打不开 / 提示失效? | 链接可能被重置或应用被下架。去「应用工作台」重新发布或重新复制最新链接。 |
| 「下发指令」后设备没反应? | 指令是「设备主动来取」的:确认设备端在按节奏调用指令拉取接口(见新手 / 老手版),并确认设备执行后把结果回传。 |
| 设备固件怎么远程升级? | 左侧菜单「OTA 升级」上传 .bin 固件(免编译、免费、即传即用,推荐)或 .ino 源码,通过后点「推送」;设备收到 type=ota 指令后调用 ota_download 下载固件并校验 SHA-256,重启即生效。注意:下载窗口 24 小时内有效,过期要重新推送;固件与云盘共享 10MB 配额,且只能在「OTA 升级」页删除任务释放。 |
| 云盘是什么?设备怎么往云盘写东西? | 每个租户自带 10MB 共享云盘(仅 txt / jpg)。网页端「云盘」菜单可建文件夹 / 新建 txt / 删除;设备经三个只读接口读取、经属性上报包里的 fs 指令写入文本。云盘不开放整文件上传 / 下载,详见页面顶部「设备对接云盘 · 接口速查」与老手版 2.10。 |
如果你:会写一点代码、用过命令行(curl),但第一次接触这个平台——就读这一版。它默认你懂「请求 / 响应、JSON、HTTP」这些基本概念,只讲平台特有的约定。
读完这一版你能做到:用 3 个 HTTP 接口把任意设备接入平台,完成「心跳保活 → 属性上报 → 指令拉取与执行回传」的完整闭环,并知道如何接入规则引擎和可视化应用。
最快的接入路径:注册 → 设备管理创建设备(拿 device_id + token)→ 用下面 curl 样例跑通心跳与上报 → 平台设备详情确认数据 → 接入你自己的程序循环。全程约 10 分钟。
| 概念 | 要点 |
|---|---|
| 租户 / 开发者空间 | 注册后自动创建,数据相互隔离。tenant_id 数字 ID 在工作台可见;设备 API 调用时不需要显式传租户 ID,平台按 device_id + token 自动定位。 |
| 设备 = device_id + token | device_id 唯一标识;token 是接入令牌(设备密码)。两者与网页登录账号体系无关。请求用 HTTP Basic(user=device_id, pass=token)或自定义头,见 1.4。 |
| 属性 props | 设备上报的数据,平台合并成「属性快照」。只接受标量 / null;同键「新增或值变化」才触发规则。属性名大小写敏感。 |
| 指令 command(拉取式) | 后台 / 规则 / 公开页产生的指令先入队,设备主动 GET 拉取;拉取即标记 sent,不代表执行成功,执行后建议回传结果(见 1.7)。 |
| OTA 固件升级 | 网页端上传固件 / 源码 → 平台审查 / 编译 → 推送设备。设备侧只做「收到 type=ota 指令 → 调 ?action=ota_download&job_id=... 下载固件(成功返回二进制流,不是 JSON)→ 校验 X-OTA-SHA256 → 写入 OTA 分区重启」。固件与云盘共享 10MB 配额,下载窗口 24 小时(见 2.11)。 |
| 规则 / 应用 | 规则 = 触发器 + 条件 + 动作(网页配置,无需写代码);应用 = 可视化拖拽出的公开页,可绑定属性展示、绑定按钮下发指令。 |
统一入口:https://www.hjyiot.cn/api.php,用 ?action=xxx 区分接口。
| 接口 | 方法 | 用途 | 请求体 |
|---|---|---|---|
?action=heartbeat | POST / GET | 心跳 / 上线(不处理请求体里的属性数据) | 可省略;如需带 ts 也可 {"ts":...} |
?action=report | POST | 上报属性(合并进属性快照,触发属性类规则) | {"ts":...,"props":{"temperature":23.5}} |
?action=command | GET | 拉取待执行指令(同时视为一次心跳);推送 OTA 后也会从这里拿到 type=ota 指令 | 无 |
?action=ota_download | GET / POST | 按需使用:下载本设备待升级的 OTA 固件(成功返回二进制流,不是 JSON) | job_id(query 或 body,必填) |
curl -u "device_id:token" -X POST "https://www.hjyiot.cn/api.php?action=heartbeat"
# => {"code":0,"msg":"成功","data":{"online":1,"server_ts":1728000000,"last_seen":1728000000,"rate":null}}
curl -u "device_id:token" -X POST -H 'Content-Type: application/json' \
-d '{"ts":1728000000,"props":{"temperature":26.5,"humidity":58}}' \
"https://www.hjyiot.cn/api.php?action=report"
# => {"code":0,"msg":"成功","data":{"accepted":1,"props":{"temperature":26.5,"humidity":58},"server_ts":1728000000,"online":1}}
curl -u "device_id:token" "https://www.hjyiot.cn/api.php?action=command"
# => {"code":0,"msg":"成功","data":{"online":1,"server_ts":...,"commands":[
# {"id":"65f1a2b3c4d5e6f7","type":"command","payload":{"fan_on":1},"source":"rule:8","status":"sent","ts":...,"sent_at":...} ]}}
无待执行指令时 commands 为空数组。已 sent 的指令不会重复出现,请勿反复只拉不执行。
固件下载另见老手版 2.11:需租户 OTA 已开启、云盘未封禁、任务未被冻结且处于「可推送 / 已推送」状态,并且要在最近一次推送后 24 小时内;失败返回 JSON 错误码 1002 / 1003 / 1004 / 1006。
| 方式 | 说明 | 示例 |
|---|---|---|
| HTTP Basic(推荐) | 用户名 = device_id,密码 = token | curl -u "device_id:token" ...,HTTP 客户端大多自动做 base64 |
| 自定义请求头 | 同时携带两个 Header | X-Device-ID: <device_id> + X-Device-Token: <token> |
凭据错误、设备不存在或已停用,统一返回 code=1001(HTTP 401),且提示不区分具体原因——这是平台的安全设计,避免探测有效设备。
Content-Type: application/json。{"code":0,"msg":"成功","data":{...}};code=0 成功,判断结果以业务字段 code 为准(HTTP 状态码仅供参考)。注意字段名是 msg 不是 message。ts(设备侧做好 NTP 校时)。当前版本把 ts 记入设备日志供回溯,尚未强制差值拒收;请保持携带以兼容后续强校验版本。code=1005。DEVICE_ID="your-device-001"
DEVICE_TOKEN="你的设备token"
API="https://www.hjyiot.cn/api.php"
# 1) 心跳上线
curl -u "$DEVICE_ID:$DEVICE_TOKEN" -X POST "$API?action=heartbeat"
# 2) 属性上报
curl -u "$DEVICE_ID:$DEVICE_TOKEN" -X POST -H 'Content-Type: application/json' \
-d '{"ts":'$(date +%s)',"props":{"temperature":26.5,"humidity":58}}' "$API?action=report"
# 3) 拉取待执行指令
curl -u "$DEVICE_ID:$DEVICE_TOKEN" "$API?action=command"
# 4) 执行完成回传结果(示例:last_cmd 记最近指令 ID、cmd_ts 记时间、业务状态 fan_on)
curl -u "$DEVICE_ID:$DEVICE_TOKEN" -X POST -H 'Content-Type: application/json' \
-d '{"ts":'$(date +%s)',"props":{"fan_on":1,"last_cmd":"65f1a2b3c4d5e6f7","cmd_ts":'$(date +%s)'}}' "$API?action=report"
参考节奏(互相独立的三个定时器):心跳 30s / 属性上报 20s / 拉取指令 15s。若设备是电池供电 / 低频场景,可自行放宽,但至少保证周期性心跳维持在线判定。
指令闭环四步:① 平台产生指令入队(手动 / 规则 / 公开页按钮);② 设备 command 拉取(拉取即标记 sent 并返回全部待执行指令);③ 设备逐条执行 type + payload 对应业务动作;④ 执行结果用 report 回写为属性,建议使用 last_cmd(最近指令 ID)+ cmd_ts(执行时间)+ 业务状态键(如 fan_on)。这样规则 / 页面 / 快照都能看到执行结果,形成完整闭环。
指令字段:id(16 位十六进制,用于去重与回传关联)、type(自定义,默认 command)、payload(参数对象)、source(来源:manual / rule:<ID> / public_app:<ID>)。平台保留最近 200 条已发送记录供追溯。
规则引擎:登录后「规则引擎 → 新建规则」,可视化配置即可。触发器 4 类:设备属性上报 / 设备状态变化(上线)/ 定时 cron / Webhook;条件支持 > < >= <= == !=、contains、startswith、endswith、matches;动作 5 类:下发设备指令 / 触发链式规则 / HTTP 请求外部接口 / 站内通知 / 修改设备属性。执行历史在「执行日志」可查。
可视化应用:「应用工作台 → 新建应用」,拖组件(文本 / 指标 / 仪表盘 / 折线图 / 开关 / 按钮 / 分隔线)→ 绑定设备属性或配置指令模板 → 保存发布 → 得到免登录公开链接,可分享、可重置密钥、可下架。开关 / 按钮组件在公开页可直接点击,平台会把指令写入目标设备队列。
如果你在自研前端 / 大屏,想直接读公开页数据或触发按钮,公开页提供 JSON 子接口(&data=1 读快照、&cmd=1 白名单指令下发),细节见 老手版 2.6。
| 现象 / 返回 | 原因与处理 |
|---|---|
code=1001(HTTP 401) | 鉴权失败:device_id / token 不对、设备不存在或已停用。先到设备详情核对并复制 token(注意不要多复制空格),必要时重置 token。 |
code=1004(HTTP 413) | 参数校验失败:请求体为空 / 非对象 / props 不是对象 / 属性含数组对象 / 单包超过 256KB / 未知 action。 |
code=1005 | 请求频率超限:命中了 IP 或设备级限流,稍等再试,并检查是否有死循环在疯狂请求。 |
| 上报成功但规则 / 页面不触发 | 属性名大小写不一致;同值重复上报不会重复触发(平台按「新增或值变化」触发)。 |
| 指令拉不到 / 收不到 | 确认指令已入队(后台下发 / 规则动作 / 公开页按钮);确认设备在用同一个 device_id + token 拉取;拉取间隔不要太长。 |
面向有嵌入式 / 后端经验的开发者。假设你熟悉 HTTP、Basic Auth、JSON 与队列语义。本版只给「规格」和「坑」,不再做科普。
| 端点 | 用途 | 鉴权 |
|---|---|---|
https://www.hjyiot.cn/api.php(?action=heartbeat|report|command) | 设备接入:心跳 / 属性上报 / 指令拉取 | Basic(user=device_id, pass=token)或 X-Device-ID + X-Device-Token;与登录态无关 |
https://www.hjyiot.cn/api.php(?action=disk_stats|disk_list|disk_read) | 设备云盘(只读三接口):空间概览 / 列目录 / 读文件;同一租户所有设备共享。云盘内容的写入、覆盖、删除不设独立 API,统一在 ?action=report 包内携带 fs[] 指令完成 | 同设备通道(Basic 或双 Header) |
http://www.hjyiot.cn/app.php?app=<app_id>&k=<share_key> | 已发布应用的免登录公开页 + JSON 子接口 | URL 携带 share_key(app_id + share_key) |
http://www.hjyiot.cn/webhook.php?tenant_id=<租户ID>&key=<规则密钥>&path=<触发路径> | 第三方 POST 触发规则(Webhook 触发器) | URL 携带 tenant_id + key + path |
https://www.hjyiot.cn/api.php(?action=ota_download&job_id=<任务ID>) | 设备 OTA:下载本设备待升级的固件;成功后返回二进制流(非 JSON),失败返回 JSON 错误包 | 同设备通道(Basic 或双 Header) |
Authorization: Basic base64(device_id:token),或双 Header X-Device-ID + X-Device-Token。code=1001(HTTP 401),不区分原因。app_id + share_key 为凭据;重置 share_key 使旧链接立即失效。统一返回 {"code":0,"msg":"成功","data":{...}};以业务字段 code 判断结果(注意 msg 而非 message)。请求 / 响应 UTF-8 JSON;上报带 Content-Type: application/json。
| action | 方法 | 请求 | 返回 data 要点 |
|---|---|---|---|
heartbeat | POST / GET | 可省略;不处理请求体属性 | online, server_ts, last_seen, rate;离线→在线转换触发「上线」状态类规则 |
report | POST | {"ts":<unix>,"props":{...}} | accepted, props, server_ts, online |
command | GET | 无 | online, server_ts, commands[];拉取即全部标记 sent,并视为一次心跳;OTA 推送后其中含 type=ota 指令 |
ota_download | GET / POST | job_id(query 或 body,必填) | 非 JSON:成功为 application/octet-stream 固件流 + X-OTA-Version / X-OTA-SHA256 / X-OTA-Size / X-OTA-Job 响应头;失败为 {"code":1002|1003|1004|1006,...} |
{"ts":...,"props":{...}};兼容平铺写法(如 {"temperature":23.5}),但平铺时除属性外的字段(含 ts)也会并入快照,不建议混用。code=1004);空体 / 非对象 / props 非对象均 code=1004。id(16 位十六进制,执行去重与回传关联;字段名是 id 不是 command_id)、type(下发方自定义,默认 command)、payload、source(manual / rule:<ID> / public_app:<ID>)、status(返回即 sent)、ts(入队)、sent_at(标记 sent 时间)。report 回传建议键:last_cmd=最近指令 ID、cmd_ts=执行时间、叠加业务状态键。{"code":0,...} 包裹,而是固件字节流;请先判 Content-Type 再决定「按流处理」还是「按 JSON 解析错误」。ready / pushed → 处于推送后 24 小时窗口内 → 固件文件存在。1002(OTA 功能停用 / 云盘封禁 / 固件被冻结)、1003(任务不存在,两种原因同一提示以防跨设备探测)、1004(缺 job_id / 固件未就绪 / 下载窗口过期)、1006(固件文件缺失或读取失败)。Cache-Control: no-store,不做缓存。| 限流层级 | 阈值 | 时机 |
|---|---|---|
| IP 级(设备通道) | 600 次 / 分钟 / IP | 先于鉴权执行,凭据错误也计数 |
| 设备级(设备通道) | 120 次 / 分钟 / 设备 | 鉴权通过后按设备计数 |
| 公开页读(data=1) | 300 次 / 分钟 / IP | 公开 JSON 读接口 |
| 公开页写(cmd=1) | 60 次 / 分钟 / IP | 公开 JSON 写接口 |
| code | 含义 | 典型场景 |
|---|---|---|
| 0 | 成功 | — |
| 1001 | 鉴权失败 | device_id / token 错误、设备不存在或停用、租户停用 |
| 1002 | 权限不足 | 后台管理 API 越权操作等 |
| 1003 | 资源不存在 | 按 ID 查不到设备 / 应用 / 规则 / Webhook;disk_read 读取不存在的云盘文件 |
| 1004 | 参数校验失败 | 缺请求体、props 非对象、属性含非标量、单包超 256KB(HTTP 413)、未知 action |
| 1005 | 请求频率超限 | 命中任一层限流 |
| 1006 | 存储错误 | 平台侧读写异常(极少) |
| 1007 | MQTT 错误 | MQTT 通道相关错误 |
| 1008 | 设备离线 | 需设备在线才能执行的操作遇离线设备 |
规则 = 触发器 + 条件(0..N)+ 动作(最多 10 条顺序执行)。
| 维度 | 可选值 |
|---|---|
| 触发器 | device_prop(属性上报且满足条件)/ device_status(上线 / 掉线:在线状态翻转)/ cron(5 段 cron)/ webhook(外部 POST 专属 URL) |
| 条件 | 组合逻辑 all / any;比较符 > < >= <= == !=、contains、startswith、endswith、matches(正则);Webhook 条件可引用 payload 字段 |
| 动作 | device_command 下发指令 / trigger_rule 触发另一规则(链式,含防环保护)/ http_request 请求外部 URL(POST 默认,可配方法 / 头 / JSON body,超时 3s)/ notify 站内通知(写执行日志与审计)/ set_prop 修改设备属性 |
执行结果(命中与否、每动作成败)见「规则引擎 → 执行日志」。
规则选「Webhook」触发器后,编辑页会生成专属地址(含 tenant_id / key / path)。任意第三方 POST JSON 即可命中,条件可用 payload 字段求值:
curl -X POST -H 'Content-Type: application/json' \
-d '{"event":"alarm","level":2}' \
"http://www.hjyiot.cn/webhook.php?tenant_id=<租户ID>&key=<规则密钥>&path=<触发路径>"
# => { "code":0, "msg":"成功", "data":{ "accepted":1, "server_ts":... } }
| 子接口 | 说明 | 限流 |
|---|---|---|
&data=1(GET) | 实时数据快照:画布绑定设备的 online / last_seen / props 与折线序列 series(历史曲线由设备上报日志还原,最多 60 个点) | 300 / 分钟 / IP |
&cmd=1(POST JSON) | 下发指令:{"component_id":"组件ID","payload":{...}};仅允许触发画布组件已声明的指令模板(白名单防滥用),payload 可覆盖模板参数 | 60 / 分钟 / IP |
# 读数据
curl "http://www.hjyiot.cn/app.php?app=<app_id>&k=<share_key>&data=1"
# 下发指令(component_id 为配置了 command 模板的组件,如 btn_fan)
curl -X POST -H 'Content-Type: application/json' \
-d '{"component_id":"btn_fan","payload":{"fan_on":1}}' \
"http://www.hjyiot.cn/app.php?app=<app_id>&k=<share_key>&cmd=1"
公开数据接口为只读语义;请勿把敏感属性绑定到已发布应用的画布。公开页交互(开关 / 按钮)点击后平台校验 share_key 并把指令写入目标设备队列,设备按 command 拉取节奏收到。
HTTP Basic 鉴权;三独立定时器:心跳 30s / 上报 20s / 拉指令 15s。需安装 ArduinoJson 库。替换 DEVICE_ID / DEVICE_TOKEN / WIFI_SSID / WIFI_PASS。
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "你的WiFi名";
const char* WIFI_PASS = "你的WiFi密码";
const char* DEVICE_ID = "your-device-001";
const char* DEVICE_TOKEN = "你的设备token";
const char* API_BASE = "http://www.hjyiot.cn/api.php";
const unsigned long HEARTBEAT_MS = 30000, REPORT_MS = 20000, PULLCMD_MS = 15000;
unsigned long tH = 0, tR = 0, tP = 0;
bool apiCall(const char* action, const char* body, String& resp) {
HTTPClient http;
http.begin(String(API_BASE) + "?action=" + action);
http.setAuthorization(DEVICE_ID, DEVICE_TOKEN);
http.setTimeout(5000);
int code;
if (body) { http.addHeader("Content-Type", "application/json"); code = http.POST(body); }
else { code = http.GET(); }
if (code == 200) resp = http.getString();
http.end();
return code == 200;
}
void reportProps(JsonDocument& props) {
String body = "{\"ts\":" + String((long)time(nullptr)) + ",\"props\":";
serializeJson(props, body); body += "}";
String r; apiCall("report", body.c_str(), r);
}
void setup() {
Serial.begin(115200);
configTime(0, 0, "pool.ntp.org", "ntp.aliyun.com");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
String r; apiCall("heartbeat", nullptr, r); // 上线
JsonDocument d; d["temperature"] = 26.5; reportProps(d); // 首报
tH = tR = tP = millis();
}
void loop() {
unsigned long now = millis();
if (now - tH >= HEARTBEAT_MS) { tH = now; String r; apiCall("heartbeat", nullptr, r); }
if (now - tR >= REPORT_MS) {
tR = now;
JsonDocument d;
d["temperature"] = 26.0 + random(0, 100) / 10.0;
d["humidity"] = 55 + random(0, 20);
reportProps(d);
}
if (now - tP >= PULLCMD_MS) {
tP = now;
String resp; if (!apiCall("command", nullptr, resp)) return;
JsonDocument doc; deserializeJson(doc, resp);
for (JsonObject c : doc["data"]["commands"].as<JsonArray>()) {
// 按 c["type"] / c["payload"] 执行具体动作
JsonDocument result;
result["last_cmd"] = c["id"] | "";
result["cmd_ts"] = (long)time(nullptr);
result["done"] = 1;
reportProps(result); // 执行结果回传
}
}
delay(100);
}
msg 不是 message;指令字段是 id 不是 command_id。heartbeat 不处理请求体属性;要刷新属性走 report。code 为准,HTTP 状态仅供参考。ts(Unix 秒级),当前版本仅记录,未来版本可能强校验差值,请提前做好 NTP 校时。cmd=1 仅允许白名单指令模板,需在画布组件配置模板后生效。ota_download 成功返回二进制流:按 Content-Type 分支处理,只有失败才是 JSON;不要用「先解析 JSON」的客户端直接读固件。type=ota,固件信息在 payload:version / job_id / size / sha256 / ts。1004,需在网页端重新推送;重复推送会再入队一条 OTA 指令并刷新窗口。disk_stats.used 是「云盘文件 + 固件」合计;固件只能在「OTA 升级」页删除任务释放。每个租户(账户)默认获得 10MB 共享云盘,与租户下全部设备联动共享配额;这 10MB 与 2.11 OTA 固件 合计计算(disk_stats.used = 云盘文件 + 固件),固件需在「OTA 升级」页删除任务释放。当前暂不支持扩容,配额写满后自动进入只读态。允许文件类型:txt(UTF-8 文本)、jpg(图片,仅支持读取 / 预览)。云盘不开放整文件上传与下载;设备对云盘的读取走三个独立只读动作,内容维护一律作为「数据伴随操作」随属性上报包执行。
| action | 方法 | 参数 | 返回 data 要点 |
|---|---|---|---|
disk_stats | GET / POST | 无 | limit, used, free, percent, read_only, quota_hit, disabled, disabled_reason, file_types(另附 limit_text / used_text / free_text 人类可读容量文本,便于直接展示) |
disk_list | GET / POST | path(目录相对路径,可省略=根目录) | rel, items[], used, free, limit, percent, read_only, disabled, quota_hit;items[] 条目含 name / type(dir|file) / ext / size(字节) / modified(Unix秒) / frozen;frozen=1 表示该文件或所在目录已被平台冻结 |
disk_read | GET / POST | path(文件完整相对路径,必填) | txt:name, ext, size, encoding(utf-8), content(原文);jpg:name, ext, size, content_type(image/jpeg), data(base64);冻结 / 封禁的文件读取会被拒绝 |
/),不分层级拼接最多 8 层;名称最长 120 字符,不允许以点号开头,不允许 / \ 与控制字符,杜绝 ../ 穿越。disk_stats / disk_list 返回 code=1002 并附封禁原因(disabled_reason),disk_read 统一返回 code=1002「云盘已被平台封禁」;满配额(quota_hit)不影响读取。发送 report 时在请求体顶层携带 fs 数组,平台会先将 fs 剥离(不落入属性存储),逐项执行后逐项回报;任一项失败不影响其它项,也不影响本次属性上报。单包 fs 最多 20 条;每条 write 的 content 上限 128KB;整体包仍受 256KB 单包限制。所有写操作都会写入平台审计(后台「云盘 → 审计记录」可查)。
POST https://www.hjyiot.cn/api.php?action=report
Authorization: Basic base64(device_id:token)
Content-Type: application/json
{
"ts": 1788861130,
"props": { "temperature": 26.5 },
"fs": [
{ "op": "write", "path": "logs/2026-09-08.txt", "content": "温度 26.5℃\n", "mode": "append", "create": true },
{ "op": "delete_content", "path": "logs/2026-09-08.txt", "needle": "旧行", "all": false }
]
}
# => data.fs[](逐项对应请求顺序):
# [ {"op":"write","path":"/logs/2026-09-08.txt","ok":true,"bytes":24,"created":1,"mode":"append"},
# {"op":"delete_content","path":"/logs/2026-09-08.txt","ok":true,"removed":1,"bytes":19} ]
| op | 参数 | 语义与限制 |
|---|---|---|
write | path(必填,txt 相对路径)、content(必填,UTF-8)、mode(append 追加=默认 / overwrite 整文件覆盖)、create(0/1,默认 0) | 仅支持 txt。目标目录或文件不存在时默认报错 error(不回退为自动创建);显式 create:true 才逐级自动创建目录与文件。返回 op/path/ok/bytes/created/mode;文件超出 8MB 上限或配额满拒绝写入 |
delete | path(必填) | 删除文件;不支持删除目录(目录删除请走平台网页端「云盘」),根目录与冻结文件拒绝。返回 op/path/ok |
delete_content | path(必填,txt 文件)、needle(必填,要删除的文本片段)、all(0/1,默认 0) | 仅支持 txt。按子串删除文本内容:all=false(默认)只删除首个匹配,all=true 删除全部匹配;文件中找不到 needle 时两种模式均返回 error。返回 op/path/ok/removed/bytes(bytes 为处理后文件大小) |
ok:true;失败项不中断批次,置 ok:false 并附 error 中文原因(真实文案如「目录不存在:/logs」「文件不存在:/logs/2026-09-08.txt」「该文件已被平台冻结,禁止修改 / 禁止删除」「云盘已被平台封禁,当前仅可查看」「文件中未找到指定内容:xxx」)。write 拒绝(可先用 disk_stats 判断 quota_hit / read_only)。平台 OTA 升级闭环:网页端上传固件 / 源码 → 审查 / 编译 → 推送设备 → 设备下载并校验。设备侧只需实现「收到 type=ota 指令 → 调 ota_download 下载固件 → 校验 → 写入 OTA 分区重启」。固件与云盘共享账户 10MB 配额。
1003 以防跨设备探测);1002);ready(可推送)或 pushed(已推送);GET https://www.hjyiot.cn/api.php?action=ota_download&job_id=<任务ID>
POST https://www.hjyiot.cn/api.php?action=ota_download Body: {"job_id":"<任务ID>"}
# 成功:HTTP 200 + Content-Type: application/octet-stream(响应体即固件,不是 JSON)
# X-OTA-Version: 1.2.0 (URL 编码,需 decode)
# X-OTA-SHA256: 9f2c... (64 位十六进制,下载后必须校验)
# X-OTA-Size: 512000
# X-OTA-Job: 65f1a2b3c4d5e6f7 (16 位十六进制任务 ID)
# Cache-Control: no-store
# 失败:JSON 错误包 {"code":1002|1003|1004|1006,"msg":"...","data":{...}}
# 一行下载(-OJ 按响应头文件名落盘),下完比对 sha256:
curl -u "device_id:token" -OJ "https://www.hjyiot.cn/api.php?action=ota_download&job_id=<任务ID>"
| code | 场景 |
|---|---|
1002 | 租户 OTA 功能已停用 / 云盘已被平台封禁 / 该固件已被平台冻结 |
1003 | OTA 任务不存在(任务不存在或不属于本设备,提示统一以防探测) |
1004 | 缺少 job_id / 固件尚未就绪 / 下载窗口已过期 |
1006 | 固件文件缺失 / 读取失败 |
推送时平台向设备指令队列写入 type=ota 指令,source 形如 ota:<操作者用户名>,id 为 16 位十六进制指令 ID(与 2.3 的指令结构一致),payload 固定为:
{ "version":"1.2.0", "job_id":"65f1a2b3c4d5e6f7", "size":512000,
"sha256":"9f2c...(64 位十六进制)", "ts":1788600000 }
设备侧推荐流程:① command 拉到 type=ota → ② 从 payload 取 job_id / version / size / sha256 → ③ 与本地版本比对(平台已保证版本递增,设备侧建议再判一次防回退)→ ④ ota_download 下载 → ⑤ 校验大小与 SHA-256 → ⑥ 写入 OTA 分区 → ⑦ 重启 → ⑧ 上线后用 report 回写新版本号,形成闭环。
| 状态 | 页面显示 | 说明 |
|---|---|---|
pending_review | 待审查 | 上传 .ino 源码后等待隔离审查(页面提示约 10 分钟内完成;仅反馈问题代码位置,平台不代修) |
reviewing | 审查中 | 正在审查 |
review_fail | 审查未通过 | 审查未通过(不计编译额度) |
queued | 排队中 | 审查通过,等待编译(页面展示排队位次与预计完成时间) |
need_payment | 待充值 | 编译额度不足,需联系客服充值(3 元 / 次)后继续;此状态仍可直接上传 .bin 免费推送 |
compiling | 编译中 | 正在编译;超过 20 分钟未推进由后台 worker 判为中断,回退 queued 下轮自动重试 |
compile_fail | 编译失败 | 编译失败(返回错误与问题代码片段;编译失败同样计费) |
ready | 可推送 | 编译成功或直传 .bin,可推送、可被 ota_download 下载 |
pushed | 已推送 | 已推送设备(可重复推送;重复推送刷新下载窗口) |
canceled | 已撤销 | 用户撤销 |
frozen | 已冻结 | 平台冻结(阻断编译、推送与下载) |
.ino ≤ 512KB、.bin ≤ 4MB,仅这两种扩展名;版本号须严格大于该设备已发布最高版本(v / V 前缀会被剥离),长度 ≤ 60;ota_quota_reject 审计。queued(排队中)与 compiling(编译中)不可删除:流水线处理中,返回 code=1004,文案「任务当前状态为「排队中/编译中」,流水线处理中不可删除,请等待编译结束后再删除」;pending_review / reviewing / review_fail / need_payment / compile_fail / ready / pushed / canceled / frozen)均可删除;ota_upload / ota_quota_reject / ota_queued / ota_reviewing / ota_review_fail / ota_compile_fail / ota_ready / ota_need_payment / ota_push / ota_delete,以及平台侧 ota_admin_enable / disable / recharge / freeze / unfreeze / delete;每条含操作者与来源(web / admin / device / system)。# Python(hjyiot.py)
from hjyiot import HjyIot
dev = HjyIot("your-device-001", "你的token", base="https://www.hjyiot.cn/api.php")
r = dev.ota_download("65f1a2b3c4d5e6f7", "fw.bin")
# => {"version":"1.2.0","size":512000,"sha256":"9f2c...","sha256_match":true,"path":"fw.bin"}
// ESP32 Arduino(HjyIot.h,流式写入 OTA 分区并边写边算 SHA-256)
HjyIot dev(DEVICE_ID, DEVICE_TOKEN, API_BASE);
String out;
if (dev.otaDownload(jobId, out)) {
Serial.println(out); // 含 version / size / sha256 / match
ESP.restart();
}
两类 SDK 均只做「下载 + 校验 + 落盘 / 落分区」;版本比对与重启时机由业务侧决定。