├── gerber.zip ├── DOC ├── 原始编程器固件.bin ├── images │ ├── ha.jpg │ ├── 贴片.jpg │ ├── 接线图.jpg │ ├── 米家智能锁.jpg │ ├── esp8266.png │ ├── nodemcu.jpg │ ├── esp8266供电.jpg │ ├── 小白万能遥控器声控版.jpg │ └── WIFIManager.jpg └── 1d668e297e43a74dfd635def0593bd32_upd_chuangmi.remote.h102c01.bin ├── LOCK2MQTT.ino.nodemcu.bin ├── lock.yaml ├── README.md ├── LICENSE └── LOCK2MQTT.ino /gerber.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/gerber.zip -------------------------------------------------------------------------------- /DOC/原始编程器固件.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/原始编程器固件.bin -------------------------------------------------------------------------------- /DOC/images/ha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/ha.jpg -------------------------------------------------------------------------------- /DOC/images/贴片.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/贴片.jpg -------------------------------------------------------------------------------- /DOC/images/接线图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/接线图.jpg -------------------------------------------------------------------------------- /DOC/images/米家智能锁.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/米家智能锁.jpg -------------------------------------------------------------------------------- /DOC/images/esp8266.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/esp8266.png -------------------------------------------------------------------------------- /DOC/images/nodemcu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/nodemcu.jpg -------------------------------------------------------------------------------- /DOC/images/esp8266供电.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/esp8266供电.jpg -------------------------------------------------------------------------------- /DOC/images/小白万能遥控器声控版.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/小白万能遥控器声控版.jpg -------------------------------------------------------------------------------- /LOCK2MQTT.ino.nodemcu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/LOCK2MQTT.ino.nodemcu.bin -------------------------------------------------------------------------------- /DOC/images/WIFIManager.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/images/WIFIManager.jpg -------------------------------------------------------------------------------- /DOC/1d668e297e43a74dfd635def0593bd32_upd_chuangmi.remote.h102c01.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killadm/LOCK2MQTT/HEAD/DOC/1d668e297e43a74dfd635def0593bd32_upd_chuangmi.remote.h102c01.bin -------------------------------------------------------------------------------- /lock.yaml: -------------------------------------------------------------------------------- 1 | binary_sensor: 2 | - platform: mqtt 3 | name: mijia_lock 4 | state_topic: 'LOCK2MQTT/lock/state' 5 | payload_on: 'unlock' 6 | payload_off: 'lock' 7 | device_class: lock 8 | qos: 1 9 | sensor: 10 | - platform: mqtt 11 | name: lock_state 12 | icon: 'mdi:lock-smart' 13 | state_topic: 'LOCK2MQTT/state' 14 | value_template: '{{value_json.state}}' 15 | - platform: mqtt 16 | name: lock_state_update_time 17 | icon: 'mdi:lock-clock' 18 | state_topic: 'LOCK2MQTT/state' 19 | value_template: '{{value_json.time}}' 20 | - platform: mqtt 21 | name: lock_event 22 | icon: 'mdi:lock-question' 23 | state_topic: 'LOCK2MQTT/event' 24 | value_template: '{{value_json.event}}' 25 | 26 | #可以按照下边的格式匹配操作者 27 | - platform: mqtt 28 | name: lock_user 29 | icon: 'mdi:account-lock' 30 | state_topic: 'LOCK2MQTT/event' 31 | value_template: >- 32 | {% if value_json.key == '00000000' or value_json.key == '01000180' or value_json.key == '02000180' %} 33 | 我 34 | {% elif value_json.key == '03000180' %} 35 | 老婆 36 | {% elif value_json.key == '06000180' %} 37 | 儿子 38 | {% elif value_json.key == 'ffffffff' %} 39 | 未知操作者 40 | {% else %} 41 | 无效操作者 42 | {% endif %} 43 | - platform: mqtt 44 | name: lock_event_update_time 45 | icon: 'mdi:lock-clock' 46 | state_topic: 'LOCK2MQTT/event' 47 | value_template: '{{value_json.time}}' 48 | - platform: mqtt 49 | name: lock_alert 50 | icon: 'mdi:lock-alert' 51 | state_topic: 'LOCK2MQTT/alert' 52 | value_template: '{{value_json.alert}}' 53 | - platform: mqtt 54 | name: lock_alert_update_time 55 | icon: 'mdi:lock-clock' 56 | state_topic: 'LOCK2MQTT/alert' 57 | value_template: '{{value_json.time}}' 58 | - platform: mqtt 59 | name: bolt_state 60 | icon: 'mdi:lock-open-variant' 61 | state_topic: 'LOCK2MQTT/bolt' 62 | value_template: '{{value_json.state}}' 63 | - platform: mqtt 64 | name: lock_battery 65 | state_topic: 'LOCK2MQTT/battery' 66 | value_template: '{{value_json.power}}' 67 | unit_of_measurement: '%' 68 | device_class: battery 69 | 70 | 71 | homeassistant: 72 | customize: 73 | binary_sensor.mijia_lock: 74 | friendly_name: 米家智能锁 75 | sensor.lock_state: 76 | friendly_name: 门锁状态 77 | sensor.lock_state_update_time: 78 | friendly_name: 状态变更时间 79 | sensor.lock_event: 80 | friendly_name: 门锁事件 81 | sensor.lock_user: 82 | friendly_name: 操作者 83 | sensor.lock_event_update_time: 84 | friendly_name: 事件发生时间 85 | sensor.lock_alert: 86 | friendly_name: 警告信息 87 | sensor.lock_alert_update_time: 88 | friendly_name: 警告发生时间 89 | sensor.bolt_state: 90 | friendly_name: 锁舌状态 91 | sensor.lock_battery: 92 | friendly_name: 电池电量 93 | 94 | group: 95 | mijia_lock: 96 | name: 米家智能锁 97 | view: no 98 | entities: 99 | - binary_sensor.mijia_lock 100 | - sensor.lock_state 101 | - sensor.lock_state_update_time 102 | - sensor.lock_event 103 | - sensor.lock_user 104 | - sensor.lock_event_update_time 105 | - sensor.lock_alert 106 | - sensor.lock_alert_update_time 107 | - sensor.bolt_state 108 | - sensor.lock_battery -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LOCK2MQTT 2 | 米家智能门锁接入homeassistant 3 | 4 | ![](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/ha.jpg) 5 | 6 | 7 | 8 | ## 特性 9 | 10 | 11 | 12 | - 门锁状态 13 | 14 | - 开门 15 | - 关门 16 | - 超时未关 17 | - 敲门 18 | - 撬门 19 | - 门卡住 20 | - 锁舌状态 21 | 22 | - 所有锁舌收回 23 | - 斜舌弹出 24 | - 方舌/斜舌弹出 25 | - 呆舌/斜舌弹出 26 | - 所有锁舌弹出 27 | - 开锁事件 28 | 29 | - 方法 30 | 31 | - 蓝牙 32 | - 密码 33 | - 指纹 34 | - 应急钥匙 35 | - 转盘方式 36 | - NFC 37 | - 一次性密码 38 | - 双重验证 39 | - 人工 40 | - 自动 41 | 42 | - 动作 43 | 44 | - 室外开门 45 | - 上提把手锁门 46 | - 反锁 47 | - 解除反锁 48 | - 室内开门 49 | - 门内上锁 50 | - 开启童锁 51 | - 关闭童锁 52 | 53 | - 操作者 54 | - 报警事件 55 | 56 | - 密码多次验证失败 57 | - 指纹多次验证失败 58 | - 密码输入超时 59 | - 撬锁 60 | - 重置按键按下 61 | - 有人撬锁或钥匙未拔 62 | - 钥匙孔异物 63 | - 钥匙未取出 64 | - NFC设备多次验证失败 65 | - 超时未按要求上锁 66 | - 电量事件 67 | - 蓝牙温湿度计接入(主题:LOCK2MQTT/temp/raw,施工中) 68 | - 网关日志格式化后输出(主题:LOCK2MQTT/json) 69 | - WIFIManager配网 70 | - MQTT输出 71 | - OTA在线升级 72 | - 远程调试信息输出 73 | 74 | ## 原理 75 | 76 | 小白万能遥控器的PCB上存在串口测试点,该串口会不停输出运行日志,当它作为米家智能门锁的蓝牙网关时,串口同样会明文输出门锁发送给网关的消息。 77 | 本项目通过ESP8266接收网关的串口日志,提取米家智能门锁相关的消息,经过处理后,序列化为json数据,并通过mqtt协议输出。 78 | 79 | ## 硬件 80 | 81 | - **米家智能门锁** x 1 82 | 83 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/%E7%B1%B3%E5%AE%B6%E6%99%BA%E8%83%BD%E9%94%81.jpg) 84 | 85 | - **小白万能遥控器 声控版** x 1 86 | 87 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/%E5%B0%8F%E7%99%BD%E4%B8%87%E8%83%BD%E9%81%A5%E6%8E%A7%E5%99%A8%E5%A3%B0%E6%8E%A7%E7%89%88.jpg) 88 | 89 | - **ESP8266** x 1 90 | 91 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/nodemcu.jpg) 92 | 93 | ## 编译 94 | 95 | 待填坑 96 | 97 | ## 烧录 98 | 99 | 感谢[astonish921](https://bbs.iobroker.cn/u/astonish921)填坑,这个帖子写的很详细。 100 | 101 | https://bbs.iobroker.cn/t/topic/3364 102 | 103 | ## 接线 104 | 105 | 因网关串口输出非常频繁,为了提高效率,默认使用ESP8266的硬串口。 106 | 调试完成后,可以通过网关PCB上的VBUS触点给ESP8266供电。 107 | 108 | - **网关端** 109 | 110 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/%E6%8E%A5%E7%BA%BF%E5%9B%BE.jpg) 111 | 112 | - **ESP8266端** 113 | 114 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/esp8266.png) 115 | 116 | ## 配网 117 | 118 | 通电后,连接SSID为"LOCK2MQTT_****"的无线,稍后会弹出认证页面(如果没有弹出,手动访问http://192.168.4.1) 119 | 120 | ![图片](https://raw.githubusercontent.com/killadm/LOCK2MQTT/master/DOC/images/WIFIManager.jpg) 121 | 122 | 点击“配置WIFI (扫描)”,选择对应的wifi名称,按照提示输入wifi密码及mqtt服务器信息,确定之后等待几秒,配网成功。 123 | 124 | ## 接入HomeAssistant 125 | 126 | 将项目中的lock.yaml放入packages文件夹,重启ha 127 | 128 | 待填坑 129 | 130 | **如何匹配操作者** 131 | 操作者是通过key来匹配的,key 的后六位都一样(我这是000180,不知道是不是每个人的都一样),正常情况下配置完门锁之后00000000、01000180、02000180分别为管理员、管理员密码、管理员指纹,之后新增的指纹、密码等key的前两位会按顺序递增。 132 | 你也可以通过订阅LOCK2MQTT/event主题来获取相应操作者的key。 133 | 拿到key之后打开lock.yaml,搜索“我”,按下边的格式增加就行了。 134 | 135 | ``` 136 | - platform: mqtt 137 | name: lock_user 138 | icon: 'mdi:account-lock' 139 | state_topic: 'LOCK2MQTT/event' 140 | value_template: >- 141 | {% if value_json.key == '00000000' or value_json.key == '01000180' or value_json.key == '02000180' %} 142 | 我 143 | {% elif value_json.key == '03000180' %} 144 | 老婆 145 | {% elif value_json.key == '新操作者key' %} 146 | 新操作者 147 | {% elif value_json.key == 'ffffffff' %} 148 | 未知操作者 149 | {% else %} 150 | 无效操作者 151 | {% endif %} 152 | ``` 153 | 154 | 155 | ## 进阶-解决ESP8266供电 156 | 157 | 如果你用的是nodemcu开发板,可以通过连接开发板和网关的VIN-VBUS、GND-GND来实现供电。 158 | 159 | 为了能够放入网关外壳,我用了[huex](https://github.com/huexpub/)大佬的IRMQTT板子来做LOCK2MQTT主控板,当然你也可以自己画。 160 | 161 | 下载[gerber](https://github.com/killadm/LOCK2MQTT/raw/master/gerber.zip)文件,去嘉立创薅5块钱5片的羊毛,然后按下图贴好元件(AMS1117是SOT-89,电容电阻是0603),插好排针。 162 | 163 | ![图片](https://github.com/killadm/LOCK2MQTT/raw/master/DOC/images/%E8%B4%B4%E7%89%87.jpg) 164 | 165 | 烧录时需要短接正面(有esp-12f的那面)的Fp触点。 166 | 167 | 烧录成功之后,按下图接线,并把板子卡在相应位置,测试正常之后,就可以把盖子装回去了。 168 | 169 | ![](https://github.com/killadm/LOCK2MQTT/raw/master/DOC/images/esp8266%E4%BE%9B%E7%94%B5.jpg) 170 | 171 | ## 致谢 172 | 173 | - [jstormx](https://bbs.hassbian.com/home.php?mod=space&uid=19155) 在 [小米智能门锁状态信息接入hass的非典型方法](https://bbs.hassbian.com/thread-8444-1-1.html)中提出蓝牙网关会串口输出运行数据,并给出智能锁协议的官方定义 174 | 175 | ## 声明 176 | 177 | - 本项目仅供学习交流技术使用,未经本人同意,不可用于任何商业用途。 178 | - 因使用本项目造成的一切不良后果与作者无关。 179 | - 源代码遵循Mozilla Public License, version 2.0 许可协议发布。 180 | 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /LOCK2MQTT.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define HOSTNAME "LOCK2MQTT" 18 | #define OTA_PASSWORD "password" 19 | #define HTTP_PORT 80 20 | #define WIFI_TIMEOUT 30000 21 | #define TIME_ZONE 8; 22 | 23 | const char* update_path = "/update"; //OTA页面地址 24 | const char* update_username = "admin"; //OTA用户名 25 | const char* update_password = "admin"; //OTA密码 26 | 27 | static unsigned long last_loop; 28 | 29 | long LAST_RECONNECT_ATTEMPT = 0; 30 | 31 | char MQTT_HOST[64] = ""; 32 | char MQTT_PORT[6] = ""; 33 | char MQTT_USER[32] = ""; 34 | char MQTT_PASS[32] = ""; 35 | 36 | String infoBuffer; 37 | 38 | RemoteDebug Debug; 39 | WiFiClient espClient; 40 | ESP8266WebServer httpServer(80); 41 | ESP8266HTTPUpdateServer httpUpdater; 42 | PubSubClient mqtt_client(espClient); 43 | 44 | void mqtt_callback(char* topic, byte* payload, unsigned int length) { 45 | } 46 | 47 | void configModeCallback(WiFiManager *myWiFiManager) { 48 | Serial.println(F("Entered config mode")); 49 | Serial.println(WiFi.softAPIP()); 50 | Serial.println(myWiFiManager->getConfigPortalSSID()); 51 | } 52 | 53 | bool mqtt_reconnect() { 54 | while (!mqtt_client.connected()) { 55 | if (mqtt_client.connect(HOSTNAME, MQTT_USER, MQTT_PASS)) { 56 | Serial.println(F("MQTT connected!")); 57 | mqtt_client.publish("LOCK2MQTT/status", "alive"); 58 | mqtt_client.subscribe("LOCK2MQTT/set"); 59 | } 60 | else { 61 | Serial.print(F("MQTT Connection failed: rc=")); 62 | Serial.println(mqtt_client.state()); 63 | Serial.println(F(" Retrying in 5 seconds")); 64 | delay(5000); 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | String read_eeprom(int offset, int len) { 71 | String res = ""; 72 | for (int i = 0; i < len; ++i) { 73 | res += char(EEPROM.read(i + offset)); 74 | } 75 | Serial.print(F("read_eeprom(): ")); 76 | Serial.println(res.c_str()); 77 | return res; 78 | } 79 | 80 | void write_eeprom(int offset, int len, String value) { 81 | Serial.print(F("write_eeprom(): ")); 82 | Serial.println(value.c_str()); 83 | for (int i = 0; i < len; ++i) { 84 | if ((unsigned)i < value.length()) { 85 | EEPROM.write(i + offset, value[i]); 86 | } 87 | else { 88 | EEPROM.write(i + offset, 0); 89 | } 90 | } 91 | } 92 | 93 | bool shouldSaveConfig = false; 94 | 95 | void save_wifi_config_callback () { 96 | shouldSaveConfig = true; 97 | } 98 | 99 | void setup_ota() { 100 | httpUpdater.setup(&httpServer, update_path, update_username, update_password); 101 | httpServer.begin(); 102 | //Serial.printf("HTTPUpdateServer ready! Open http://%s.local%s in your browser and login with username '%s' and password '%s'\n", host, update_path, update_username, update_password); 103 | } 104 | 105 | void setup_mdns() { 106 | bool mdns_result = MDNS.begin(HOSTNAME); 107 | } 108 | 109 | //获取串口数据中含有method字符的json,基本上涵盖了网关状态、红外收发编码、蓝牙事件、指示灯状态等信息 110 | //我手里没有别的蓝牙设备,没办法抓数据,猜测如果单纯作为蓝牙网关的话只匹配ble_event就够了 111 | String get_json(String data) { 112 | MatchState parse_result; 113 | char match_result[data.length() - 30]; 114 | char buf[data.length() + 1]; 115 | data.toCharArray(buf, data.length() + 1); 116 | parse_result.Target(buf); 117 | if (parse_result.Match("\{.*method.*\}") == REGEXP_MATCHED) { 118 | return String(parse_result.GetMatch(match_result)); 119 | } 120 | else { 121 | return "UNKNOWN"; 122 | } 123 | } 124 | 125 | //十六进制字符串转整数 126 | //switch只支持int类型 127 | int hex_to_int(String paload) { 128 | char* str = const_cast(paload.c_str()); 129 | return (int) strtol(str, 0, 16); 130 | } 131 | 132 | String hex_to_time(String hex_timestamp) { 133 | 134 | //不懂位运算,用字符串简单粗暴解决 135 | String str = hex_timestamp.substring(6, 8) + hex_timestamp.substring(4, 6) + hex_timestamp.substring(2, 4) + hex_timestamp.substring(0, 2); 136 | unsigned long timestamp = hex_to_dec(str) + 3600 * TIME_ZONE; 137 | char time[32]; 138 | 139 | //把时间戳格式化成yyyy-mm-dd hh:mm:ss 140 | sprintf(time, "%02d-%02d-%02d %02d:%02d:%02d", year(timestamp), month(timestamp), day(timestamp), hour(timestamp), minute(timestamp), second(timestamp)); 141 | return time; 142 | } 143 | 144 | //十六进制字符串转十进制 145 | unsigned int hex_to_dec(String hexString) { 146 | unsigned int decValue = 0; 147 | int nextInt; 148 | for (int i = 0; i < hexString.length(); i++) { 149 | nextInt = int(hexString.charAt(i)); 150 | if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9); 151 | if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15); 152 | if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15); 153 | nextInt = constrain(nextInt, 0, 15); 154 | decValue = (decValue * 16) + nextInt; 155 | } 156 | return decValue; 157 | } 158 | 159 | void parse_json(String json) { 160 | 161 | //米家智能锁的json类似这样:{"id":1518998071,"method":"_async.ble_event","params":{"dev":{"did":"1011078646","mac":"AA:BB:CC:DD:EE:FF","pdid":794},"evt":[{"eid":7,"edata":"0036f6e45e"}],"frmCnt":97,"gwts":2362}} 162 | //状态信息在这里:{"eid":7,"edata":"0036f6e45e"} 163 | char buf[json.length() + 1]; 164 | json.toCharArray(buf, json.length() + 1); 165 | StaticJsonDocument<1024> doc; 166 | DeserializationError error = deserializeJson(doc, buf); 167 | JsonObject params = doc["params"]; 168 | if (!error) { 169 | 170 | //提取eid 171 | int eid = params["evt"][0]["eid"]; 172 | 173 | //提取edata 174 | String edata = params["evt"][0]["edata"]; 175 | 176 | //智能锁状态 177 | if (eid == 7) { 178 | String lock_state = edata.substring(0, 2); 179 | String time = hex_to_time(edata.substring(2, 10)); 180 | String state_msg = ""; 181 | switch(hex_to_int(lock_state)) { 182 | case 0: {state_msg.concat("开门");} break; 183 | case 1: {state_msg.concat("关门");} break; 184 | case 2: {state_msg.concat("超时未关");} break; 185 | case 3: {state_msg.concat("敲门");} break; 186 | case 4: {state_msg.concat("撬门");} break; 187 | case 5: {state_msg.concat("门卡住");} break; 188 | default: Debug.println(lock_state); break; 189 | } 190 | String state = "{\"state\":\"" + state_msg + "\",\"time\":\"" + String(time) + "\"}"; 191 | String state_raw = "{\"state\":\"" + lock_state + "\",\"time\":\"" + String(time) + "\"}"; 192 | Debug.println(state); 193 | mqtt_client.publish("LOCK2MQTT/state", state.c_str(), true); 194 | mqtt_client.publish("LOCK2MQTT/state/raw", state_raw.c_str(), true); 195 | //合并门锁状态 用于HA binary_sensor 196 | if (lock_state == "00" || lock_state == "02" || lock_state == "05") { 197 | mqtt_client.publish("LOCK2MQTT/lock/state", "unlock", true); 198 | } 199 | else if (lock_state == "01" || lock_state == "03" || lock_state == "04") { 200 | mqtt_client.publish("LOCK2MQTT/lock/state", "lock", true); 201 | } 202 | } 203 | 204 | //智能锁事件 205 | if (eid == 11) { 206 | String method = edata.substring(0, 1); 207 | String action = edata.substring(1, 2); 208 | String key_id = edata.substring(2, 10); 209 | String time = hex_to_time(edata.substring(10, 18)); 210 | String event_msg,alert_msg,alert = ""; 211 | //判断开锁方式 212 | switch(hex_to_int(method)) { 213 | case 0: {event_msg.concat("蓝牙");} break; 214 | case 1: {event_msg.concat("密码");} break; 215 | case 2: {event_msg.concat("指纹");} break; 216 | case 3: {event_msg.concat("应急钥匙");} break; 217 | case 4: {event_msg.concat("转盘方式");} break; 218 | case 5: {event_msg.concat("NFC");} break; 219 | case 6: {event_msg.concat("一次性密码");} break; 220 | case 7: {event_msg.concat("双重验证");} break; 221 | case 10: {event_msg.concat("");} break; //a 人工 222 | case 11: {event_msg.concat("自动");} break; //b 223 | case 15: {event_msg.concat("异常");} break; //f 224 | default: Debug.println(method); break; 225 | } 226 | //判断门锁动作 227 | switch(hex_to_int(action)) { 228 | case 0: {event_msg.concat("开门");} break; //门外开锁 229 | case 1: {event_msg.concat("上提把手锁门");} break; //门外上锁 230 | case 2: {event_msg.concat("反锁");} break; //门内反锁 231 | case 3: {event_msg.concat("解除反锁");} break; 232 | case 4: {event_msg.concat("室内开门");} break; //门内开锁 233 | case 5: {event_msg.concat("门内上锁");} break; 234 | case 6: {event_msg.concat("开启童锁");} break; 235 | case 7: {event_msg.concat("关闭童锁");} break; 236 | case 15: {event_msg.concat("");} break; //f异常 237 | default: Debug.println(action); break; 238 | } 239 | //判断是否有外部触发的异常 240 | if (key_id.substring(2, 8) == "00dec0") { 241 | alert.concat(key_id.substring(1, 2)); 242 | switch(hex_to_int(alert)) { 243 | case 0: {alert_msg.concat("密码多次验证失败");} break; //密码频繁开锁 244 | case 1: {alert_msg.concat("指纹多次验证失败");} break; //指纹频繁开锁 245 | case 2: {alert_msg.concat("密码输入超时");} break; 246 | case 3: {alert_msg.concat("撬锁");} break; 247 | case 4: {alert_msg.concat("重置按键按下");} break; 248 | case 5: {alert_msg.concat("有人撬锁或钥匙未拔");} break; //钥匙频繁开锁 249 | case 6: {alert_msg.concat("钥匙孔异物");} break; 250 | case 7: {alert_msg.concat("钥匙未取出");} break; 251 | case 8: {alert_msg.concat("NFC设备多次验证失败");} break; //NFC频繁开锁 252 | case 9: {alert_msg.concat("超时未按要求上锁");} break; 253 | default: Debug.println(edata); break; 254 | } 255 | String msg = "{\"alert\":\"" + alert_msg + "\",\"time\":\"" + String(time) + "\"}"; 256 | String msg_raw = "{\"alert\":\"" + alert + "\",\"time\":\"" + String(time) + "\"}"; 257 | Debug.println(msg); 258 | mqtt_client.publish("LOCK2MQTT/alert", msg.c_str(), true); 259 | 260 | //原始数据,玩nodered可能用得到,不需要的可以注释掉 261 | mqtt_client.publish("LOCK2MQTT/alert/raw", msg_raw.c_str(), true); 262 | } 263 | //判断是否有内部触发的异常 264 | else if (key_id.substring(2, 8) == "10dec0") { 265 | alert.concat(key_id.substring(1, 2)); 266 | switch(hex_to_int(alert)) { 267 | case 0: {alert_msg.concat("电量低于10%");} break; 268 | case 1: {alert_msg.concat("电量低于5%");} break; 269 | case 2: {alert_msg.concat("指纹传感器异常");} break; 270 | default: Debug.println(edata); break; 271 | } 272 | String msg = "{\"alert\":\"" + alert_msg + "\",\"time\":\"" + String(time) + "\"}"; 273 | String msg_raw = "{\"alert\":\"" + alert + "\",\"time\":\"" + String(time) + "\"}"; 274 | Debug.println(msg); 275 | mqtt_client.publish("LOCK2MQTT/alert", msg.c_str(), true); 276 | 277 | //原始数据,玩nodered可能用得到,不需要的可以注释掉 278 | mqtt_client.publish("LOCK2MQTT/alert/raw", msg_raw.c_str()); 279 | } 280 | //无异常输出事件消息 281 | else { 282 | 283 | // 作废,改由ha端处理 284 | // 判断事件操作者 285 | // 00000000:锁的管理员 286 | // 01000180:第一个密码 287 | // 02000180:第一个指纹 288 | // if (key_id == "00000000" || key_id == "01000180" || key_id == "02000180") { 289 | // event_msg = "我 " + event_msg; 290 | // } 291 | // if (key_id == "03000180") { 292 | // event_msg = "老婆 " + event_msg; 293 | // } 294 | 295 | String msg = "{\"event\":\"" + event_msg + "\",\"key\":\"" + key_id + "\",\"time\":\"" + String(time) + "\"}"; 296 | String msg_raw = "{\"method\":\"" + method + "\",\"action\":\"" + action + "\",\"key\":\"" + key_id + "\",\"time\":\"" + String(time) + "\"}"; 297 | Debug.println(msg); 298 | mqtt_client.publish("LOCK2MQTT/event", msg.c_str(), true); 299 | 300 | //原始数据,玩nodered可能用得到,不需要的可以注释掉 301 | mqtt_client.publish("LOCK2MQTT/event/raw", msg_raw.c_str(), true); 302 | } 303 | } 304 | 305 | //智能锁电量事件 306 | if (eid == 4106) { 307 | String battery_state = String(hex_to_dec(edata.substring(0, 2))); 308 | String battery_msg = "{\"power\":\"" + battery_state + "\"}"; 309 | mqtt_client.publish("LOCK2MQTT/battery", battery_msg.c_str(), true); 310 | } 311 | 312 | //智能锁锁舌事件 313 | if (eid == 4110) { 314 | String bolt_state = ""; 315 | switch(hex_to_int(edata)) { 316 | case 0: {bolt_state.concat("所有锁舌收回");} break; 317 | case 4: {bolt_state.concat("斜舌弹出");} break; 318 | case 5: {bolt_state.concat("方舌/斜舌弹出");} break; 319 | case 6: {bolt_state.concat("呆舌/斜舌弹出");} break; 320 | case 7: {bolt_state.concat("所有锁舌弹出");} break; 321 | default: Debug.println(edata); break; 322 | } 323 | String bolt_state_msg = "{\"state\":\"" + bolt_state + "\"}"; 324 | String bolt_state_msg_raw = "{\"state\":\"" + edata + "\"}"; 325 | mqtt_client.publish("LOCK2MQTT/bolt", bolt_state_msg.c_str(), true); 326 | 327 | //原始数据,玩nodered可能用得到,不需要的可以注释掉 328 | mqtt_client.publish("LOCK2MQTT/bolt/raw", bolt_state_msg_raw.c_str(), true); 329 | Debug.println(bolt_state_msg.c_str()); 330 | } 331 | 332 | //貌似是温湿度计,一种是圆的一种是方的 333 | //尝试发送一下 334 | if (eid == 4102 || eid == 4109) { 335 | String temp_msg_raw = "{\"eid\":\"" + String(eid) + "\",\"state\":\"" + edata + "\"}"; 336 | 337 | //具体啥格式咱也不清楚,原样发出去给玩nodered的大神们 338 | //看完nodered流后猜测edata前半部分是温度,后半部分是湿度,高低位互换后再hex2dec,如果能确定的话,可以在这里处理 339 | mqtt_client.publish("LOCK2MQTT/temp/raw", temp_msg_raw.c_str(), true); 340 | } 341 | } 342 | else { 343 | Debug.printf("deserializeJson failed: %s\n", error.c_str()); 344 | Debug.printf("Json data: %s\n", buf); 345 | } 346 | } 347 | 348 | void mqtt_publish(String topic, String payload,boolean retained) { 349 | mqtt_client.publish(topic.c_str(), payload.c_str(), retained); 350 | } 351 | 352 | void setup() { 353 | //尝试解决串口乱码 354 | //关闭串口输出 355 | Serial.begin(115200, SERIAL_8N1, SERIAL_RX_ONLY); 356 | Serial.setDebugOutput (false); 357 | //接收缓冲区增大一倍 358 | Serial.setRxBufferSize (512); 359 | //超时时间减半 360 | Serial.setTimeout(500); 361 | 362 | EEPROM.begin(512); 363 | Debug.begin("LOCK2MQTT"); 364 | Debug.setResetCmdEnabled(true); 365 | 366 | String settings_available = read_eeprom(134, 1); 367 | if (settings_available == "1") { 368 | read_eeprom(0, 64).toCharArray(MQTT_HOST, 64); // * 0-63 369 | read_eeprom(64, 6).toCharArray(MQTT_PORT, 6); // * 64-69 370 | read_eeprom(70, 32).toCharArray(MQTT_USER, 32); // * 70-101 371 | read_eeprom(102, 32).toCharArray(MQTT_PASS, 32); // * 102-133 372 | } 373 | WiFiManagerParameter CUSTOM_MQTT_HOST("host", "MQTT服务器","", 64); 374 | WiFiManagerParameter CUSTOM_MQTT_PORT("port", "MQTT端口", "", 6); 375 | WiFiManagerParameter CUSTOM_MQTT_USER("user", "MQTT账号","", 32); 376 | WiFiManagerParameter CUSTOM_MQTT_PASS("pass", "MQTT密码","", 32); 377 | 378 | WiFiManager wifiManager; 379 | 380 | wifiManager.setAPCallback(configModeCallback); 381 | wifiManager.setConfigPortalTimeout(WIFI_TIMEOUT); 382 | wifiManager.setSaveConfigCallback(save_wifi_config_callback); 383 | 384 | wifiManager.addParameter(&CUSTOM_MQTT_HOST); 385 | wifiManager.addParameter(&CUSTOM_MQTT_PORT); 386 | wifiManager.addParameter(&CUSTOM_MQTT_USER); 387 | wifiManager.addParameter(&CUSTOM_MQTT_PASS); 388 | 389 | String ssid = "LOCK2MQTT_" + String(ESP.getChipId()).substring(0, 4); 390 | if (!wifiManager.autoConnect(ssid.c_str(), NULL)) { 391 | Serial.println(F("Failed to connect to WIFI and hit timeout")); 392 | ESP.reset(); 393 | delay(WIFI_TIMEOUT); 394 | } 395 | 396 | if (settings_available != "1") { 397 | strcpy(MQTT_HOST, CUSTOM_MQTT_HOST.getValue()); 398 | strcpy(MQTT_PORT, CUSTOM_MQTT_PORT.getValue()); 399 | strcpy(MQTT_USER, CUSTOM_MQTT_USER.getValue()); 400 | strcpy(MQTT_PASS, CUSTOM_MQTT_PASS.getValue()); 401 | } 402 | 403 | if (shouldSaveConfig) { 404 | Serial.println(F("Saving WiFiManager config")); 405 | write_eeprom(0, 64, MQTT_HOST); // * 0-63 406 | write_eeprom(64, 6, MQTT_PORT); // * 64-69 407 | write_eeprom(70, 32, MQTT_USER); // * 70-101 408 | write_eeprom(102, 32, MQTT_PASS); // * 102-133 409 | write_eeprom(134, 1, "1"); // * 134 --> always "1" 410 | EEPROM.commit(); 411 | } 412 | 413 | setup_ota(); 414 | setup_mdns(); 415 | 416 | mqtt_client.setServer(MQTT_HOST, atoi(MQTT_PORT)); 417 | mqtt_client.setCallback(mqtt_callback); 418 | 419 | Serial.printf("MQTT active: %s:%s\n", MQTT_HOST, MQTT_PORT); 420 | } 421 | 422 | void loop() { 423 | last_loop = millis(); 424 | 425 | Debug.handle(); 426 | httpServer.handleClient(); 427 | 428 | if (!mqtt_client.connected()) { 429 | long now = millis(); 430 | if (now - LAST_RECONNECT_ATTEMPT > 5000) { 431 | LAST_RECONNECT_ATTEMPT = now; 432 | if (mqtt_reconnect()) { 433 | LAST_RECONNECT_ATTEMPT = 0; 434 | } 435 | } 436 | } 437 | else { 438 | mqtt_client.loop(); 439 | } 440 | 441 | while (Serial.available()){ 442 | //只匹配含有method的json 443 | String result = get_json(Serial.readStringUntil('\n')); 444 | if ( result != "UNKNOWN" ){ 445 | 446 | //处理数据 447 | parse_json(result); 448 | 449 | //通过mqtt发送原始json数据,玩nodered的朋友应该用得上 450 | //PubSubClient缓冲区大小有限制,有些数据可以在日志里看到但是发送不出去 451 | //超长的数据基本上没啥用,如果想要的话可以通过修改PubSubClient增大缓冲区解决 452 | mqtt_client.publish("LOCK2MQTT/json", result.c_str(), false); 453 | 454 | //telnet到8266的ip可以查看日志 455 | Debug.println(result); 456 | } 457 | else { 458 | //Debug.println("UNKNOWN"); 459 | } 460 | } 461 | } --------------------------------------------------------------------------------