├── LICENSE.txt ├── README.md ├── TIMERS.md ├── blynklib.py ├── blynklib_mp.py ├── blynktimer.py ├── certificate └── blynk-cloud.com.crt ├── examples ├── 01_write_virtual_pin.py ├── 02_read_virtual_pin.py ├── 03_connect_disconnect.py ├── 04_email.py ├── 05_set_property_notify.py ├── 06_terminal_widget.py ├── 07_tweet_and_logging.py ├── 08_blynk_timer.py ├── 09_sync_virtual_pin.py ├── 10_rtc_sync.py ├── 11_ssl_socket.py ├── 12_app_connect_disconnect.py ├── esp32 │ ├── 01_touch_button.py │ ├── 02_terminal_cli.py │ ├── 03_temperature_humidity_dht22.py │ └── README.md ├── esp8266 │ ├── 01_potentiometer.py │ └── README.md └── raspberry │ ├── 01_weather_station_pi3b.py │ └── README.md ├── setup.cfg ├── setup.py └── test ├── test_blynk_connection.py ├── test_blynk_main.py └── test_blynk_protocol.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Anton Morozenko 4 | Copyright (c) 2015-2019 Volodymyr Shymanskyy 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > **This project is still available for exploration, but is no longer actively maintained or updated.** 3 | > We recommend switching to the Blynk MQTT API for a robust and future-proof experience. 4 | > Support for this project will be phased out over time. 5 | > You can explore some [useful MQTT examples here](https://github.com/Blynk-Technologies/Blynk-MQTT-Samples). 6 | 7 | # Blynk Python Library 8 | This library provides API to connect IoT hardware that supports Micropython/Python to Blynk Cloud and communiate with Blynk apps (iOS and Android). You can send raw and processed sensor data and remotely control anything that is connected to your hardware (relays, motors, servos) from anywhere in the world. 9 | 10 | [![GitHub version](https://img.shields.io/github/release/blynkkk/lib-python.svg)][lib-release] 11 | [![GitHub download](https://img.shields.io/github/downloads/blynkkk/lib-python/total.svg)][lib-release] 12 | [![GitHub stars](https://img.shields.io/github/stars/blynkkk/lib-python.svg)][lib-stars] 13 | [![GitHub issues](https://img.shields.io/github/issues/blynkkk/lib-python.svg)][lib-issues] 14 | [![Build Status](https://img.shields.io/travis/blynkkk/lib-python.svg)][lib-travis] 15 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)][lib-licence] 16 | 17 | If you like **Blynk** - give it a star, or fork it and contribute! 18 | [![GitHub stars](https://img.shields.io/github/stars/blynkkk/lib-python.svg?style=social&label=Star)][lib-stars] 19 | [![GitHub forks](https://img.shields.io/github/forks/blynkkk/lib-python.svg?style=social&label=Fork)][lib-network] 20 | 21 | 22 | ![Blynk Banner][blynk-banner] 23 | ### Blynk is **the most popular Internet of Things platform** for connecting hardware to the cloud, designing apps to control them, and managing your deployed devices at scale. 24 | 25 | - With Blynk Library you can connect **over 400 hardware models** (including ESP8266, ESP32, NodeMCU, all Arduinos, Raspberry Pi, Particle, Texas Instruments, etc.)to the Blynk Cloud. 26 | Full list of supported hardware can be found [here][blynk-hw]. 27 | 28 | - With Blynk apps for **iOS** and **Android** apps you can easily build graphic interfaces for all of your projects by simply dragging and dropping widgets on your smartphone. It's a purely WYSIWG experience: no coding on iOS or Android required. 29 | 30 | - Hardware can connect to Blynk Cloud (open-source server) over the Internet using hardware connectivity on board, or with the use of various shields (Ethernet, WiFi, GSM, LTE, etc). Blynk Cloud is available for every user of Blynk **for free**. 31 | 32 | 33 | 34 | 35 | ## Installation of Blynk Python Library 36 | 37 | #### Installation via python pip 38 | - Check python availability in your system. 39 | ```commandline 40 | python --version 41 | ``` 42 | To exclude compatibility issue preferable versions are Python 2.7.9 (or greater) or Python 3.4 (or greater) 43 | If python not present you can download and install it from [here][python-org]. 44 | 45 | **NOTE:** To run python in "sandbox" you can try **virtualenv** module. Check [this document][virtual-env] how to do it. 46 | 47 | - If you’re using preferable versions of python mentioned above, then **pip** comes installed with Python by default. 48 | Check pip availability: 49 | ```commandline 50 | pip --version 51 | ``` 52 | - Install blynk library 53 | ```commandline 54 | sudo pip install blynklib 55 | ``` 56 | 57 | #### Manual installation 58 | Library can be installed locally from git sources: 59 | 60 | ```commandline 61 | git clone https://github.com/blynkkk/lib-python.git 62 | cd lib-python 63 | pip install --user -e . 64 | 65 | # sudo pip install -e . # if installation needed not for current but for all users 66 | ``` 67 | 68 | #### Testing 69 | You can run unit tests for cPython version of library (blynklib.py) using the command: 70 | 71 | python setup.py test 72 | 73 | **NOTE** Blynklib version <0.2.6 should use pytest-mock<1.11.2. In version 1.11.2 were added restrictions for context manager usage 74 | 75 | **NOTE:** Unit tests for Micropython ENV are not available yet. 76 | 77 | #### Micropython installation 78 | Some hardware platforms can use **[Micropython][micropython-org]** package. 79 | This is helpful for preliminary testing and debugging of your code outside of real hardware. Supported platforms 80 | and related installation docs can be found [here][micropython-pkg]. 81 | 82 | 83 | ## Features 84 | This library supports Python2, Python3 (blynklib.py) , and Micropython (blynklib_mp.py). 85 | 86 | - Communication with public or local [Blynk Server][blynk-server]. 87 | - Exchange any data between your hardware and app 88 | - Tested to work with: Raspberry Pi (any), ESP32, ESP8266 89 | 90 | ##### List of available operations: 91 | - Subscribe to connect/disconnect events (ssl connection supported only by cPython lib) 92 | - Subscribe to read/write events of [virtual pins][blynk-vpins] 93 | - [Virtual Pin][blynk-vpins] write 94 | - [Virtual Pin][blynk-vpins] sync 95 | - Send mobile app push notifications 96 | - Send email notifications 97 | - Send twitter notifications 98 | - Change widget GUI parameters in Blynk app based on hardware input 99 | 100 | 101 | ## Quickstart 102 | 1. Install Blynk python library as described above 103 | 2. Install Blynk App: 104 | [ Google Play][blynk-app-android] | 105 | [ App Store][blynk-app-ios] 106 | 107 | - Create new account in Blynk app using your email address 108 | - Create a new Project in Blynk app 109 | - You will get Auth Token delivered to your email account. 110 | - Put this Auth Token within your python script to authenticate your device on [public][blynk-server-public] or [local][blynk-server] 111 | 112 | ```python 113 | BLYNK_AUTH = '' #insert your Auth Token here 114 | ``` 115 | 116 | #### Usage example 117 | ```python 118 | import blynklib 119 | # import blynklib_mp as blynklib # micropython import 120 | 121 | BLYNK_AUTH = '' #insert your Auth Token here 122 | # base lib init 123 | blynk = blynklib.Blynk(BLYNK_AUTH) 124 | 125 | # advanced options of lib init 126 | # from __future__ import print_function 127 | # blynk = blynklib.Blynk(BLYNK_AUTH, server='blynk-cloud.com', port=80, ssl_cert=None, 128 | # heartbeat=10, rcv_buffer=1024, log=print) 129 | 130 | # Lib init with SSL socket connection 131 | # blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl_cert='') 132 | # current blynk-cloud.com certificate stored in project as 133 | # https://github.com/blynkkk/lib-python/blob/master/certificate/blynk-cloud.com.crt 134 | # Note! ssl feature supported only by cPython 135 | 136 | # register handler for Virtual Pin V22 reading by Blynk App. 137 | # when a widget in Blynk App asks Virtual Pin data from server within given configurable interval (1,2,5,10 sec etc) 138 | # server automatically sends notification about read virtual pin event to hardware 139 | # this notification captured by current handler 140 | @blynk.handle_event('read V22') 141 | def read_virtual_pin_handler(pin): 142 | 143 | # your code goes here 144 | # ... 145 | # Example: get sensor value, perform calculations, etc 146 | sensor_data = '' 147 | critilcal_data_value = '' 148 | 149 | # send value to Virtual Pin and store it in Blynk Cloud 150 | blynk.virtual_write(pin, sensor_data) 151 | 152 | # you can define if needed any other pin 153 | # example: blynk.virtual_write(24, sensor_data) 154 | 155 | # you can perform actions if value reaches a threshold (e.g. some critical value) 156 | if sensor_data >= critilcal_data_value 157 | 158 | blynk.set_property(pin, 'color', '#FF0000') # set red color for the widget UI element 159 | blynk.notify('Warning critical value') # send push notification to Blynk App 160 | blynk.email(, 'Email Subject', 'Email Body') # send email to specified address 161 | 162 | # main loop that starts program and handles registered events 163 | while True: 164 | blynk.run() 165 | ``` 166 | ## Other Examples 167 | 168 | Examples can be found **[here][blynk-py-examples]** Check them all to get familiar with main Blynk API features. 169 | 170 | ##### Core operations: 171 | - [01_write_virtual_pin.py](https://github.com/blynkkk/lib-python/blob/master/examples/01_write_virtual_pin.py): How to read incoming data from Blynk app to Virtual Pin and use it in your code 172 | - [02_read_virtual_pin.py](https://github.com/blynkkk/lib-python/blob/master/examples/02_read_virtual_pin.py): How to update value on Virtual Pin 173 | - [03_connect_disconnect.py](https://github.com/blynkkk/lib-python/blob/master/examples/03_connect_disconnect.py): Managing connection with Blynk Cloud 174 | - [04_email.py](https://github.com/blynkkk/lib-python/blob/master/examples/04_email.py): How to send send email and push notifications from your hardware 175 | - [05_set_property_notify.py](https://github.com/blynkkk/lib-python/blob/master/examples/05_set_property_notify.py): How to change some of widget UI properties like colors, labels, etc 176 | - [06_terminal_widget.py](https://github.com/blynkkk/lib-python/blob/master/examples/06_terminal_widget.py): Communication between hardware and app through Terminal widget) 177 | - [07_tweet_and_logging.py](https://github.com/blynkkk/lib-python/blob/master/examples/07_tweet_and_logging.py): How to post to Twitter and log events from your hardware 178 | - [08_blynk_timer.py](https://github.com/blynkkk/lib-python/blob/master/examples/08_blynk_timer.py): How send data periodically from hardware by using **[Blynk Timer][blynktimer-doc]** 179 | - [09_sync_virtual_pin.py](https://github.com/blynkkk/lib-python/blob/master/examples/09_sync_virtual_pin.py): How to sync virtual pin states and properties 180 | - [10_rtc_sync.py](https://github.com/blynkkk/lib-python/blob/master/examples/10_rtc_sync.py): How to perform RTC sync with blynk server 181 | - [11_ssl_socket.py](https://github.com/blynkkk/lib-python/blob/master/examples/11_ssl_socket.py): SSL server connection. Feature supported only by cPython library. 182 | - [12_app_connect_disconnect.py](https://github.com/blynkkk/lib-python/blob/master/examples/12_app_connect_disconnect.py): Managing APP connect/disconnect events with Blynk Cloud. 183 | 184 | 185 | ##### Raspberry Pi (any): 186 | Read [Raspberry Pi guide](https://github.com/blynkkk/lib-python/tree/master/examples/raspberry) first. 187 | 188 | - [01_weather_station_pi3b.py](https://github.com/blynkkk/lib-python/blob/master/examples/raspberry/01_weather_station_pi3b.py) Connect DHT22; BMP180 sensors and send data to Blynk app 189 | 190 | ##### ESP32 191 | Read [ESP32 guide](https://github.com/blynkkk/lib-python/tree/master/examples/esp32) first. 192 | - [01_touch_button.py](https://github.com/blynkkk/lib-python/blob/master/examples/esp32/01_touch_button.py) Connect TTP223B touch sensor to ESP32 and react to touch 193 | - [02_terminal_cli.py](https://github.com/blynkkk/lib-python/blob/master/examples/esp32/02_terminal_cli.py) Communication between ESP32 hardware and app through Terminal widget 194 | - [03_temperature_humidity_dht22.py](https://github.com/blynkkk/lib-python/blob/master/examples/esp32/03_temperature_humidity_dht22.py) Connect DHT22 sensor to ESP32 and send data to Blynk app 195 | 196 | ##### ESP8266 197 | Read [ESP8266 guide](https://github.com/blynkkk/lib-python/tree/master/examples/esp8266) first. 198 | - [01_potentiometer.py](https://github.com/blynkkk/lib-python/blob/master/examples/esp8266/01_potentiometer.py) Cconnect potentiometer to ESP8266 and send resistance value to the app 199 | 200 | 201 | 202 | ### Memory size limitations 203 | For hardware with limited memory size (ex. ESP8266) you can use ***frozen modules*** or ***frozen bytecode*** approaches 204 | to load **blynklib** or any other library to hardware. 205 | 206 | Read [this document][esp8266-readme] to get more information. 207 | 208 | ## Documentation and other helpful links 209 | 210 | [Full Blynk Documentation](https://docs.blynk.io) - a complete guide on Blynk features 211 | 212 | [Community (Forum)](https://community.blynk.cc) - join a 1'000'000 Blynk community to ask questions and share ideas 213 | 214 | [Official Website](https://blynk.io) 215 | 216 | **Social Media:** 217 | 218 | [Facebook](https://www.fb.com/blynkapp) [Twitter](https://twitter.com/blynk_app) [Youtube](https://www.youtube.com/blynk) 219 | 220 | [Instagram](https://www.instagram.com/blynk.iot/) [LinkedIn](https://www.linkedin.com/company/b-l-y-n-k/) 221 | 222 | 223 | ## Blynk libraries for other platforms 224 | * [C++](https://github.com/blynkkk/blynk-library) 225 | * [Node.js, Espruino, Browsers](https://github.com/vshymanskyy/blynk-library-js) 226 | * [Python](https://github.com/vshymanskyy/blynk-library-python) (by Volodymyr Shymanskyy) 227 | * [Particle](https://github.com/vshymanskyy/blynk-library-spark) 228 | * [Lua, OpenWrt, NodeMCU](https://github.com/vshymanskyy/blynk-library-lua) 229 | * [OpenWrt packages](https://github.com/vshymanskyy/blynk-library-openwrt) 230 | * [MBED](https://developer.mbed.org/users/vshymanskyy/code/Blynk/) 231 | * [Node-RED for Blynk IoT](https://flows.nodered.org/node/node-red-contrib-blynk-iot) 232 | * [LabVIEW](https://github.com/juncaofish/NI-LabVIEWInterfaceforBlynk) 233 | * [C#](https://github.com/sverrefroy/BlynkLibrary) 234 | 235 | ## Contributing 236 | You are very welcome to contribute: stability bugfixes, new hardware support, or any other improvements. Please. 237 | 238 | 239 | ### License 240 | This project is released under The MIT License (MIT) 241 | 242 | 243 | [lib-release]: https://github.com/blynkkk/lib-python/releases/latest 244 | [lib-licence]: https://github.com/blynkkk/lib-python/blob/master/LICENSE 245 | [lib-travis]: https://travis-ci.org/blynkkk/lib-python 246 | [lib-issues]: https://github.com/blynkkk/lib-python/issues 247 | [lib-stars]: https://github.com/blynkkk/lib-python/stargazers 248 | [lib-network]: https://github.com/blynkkk/lib-python/network 249 | [blynk-io]: https://github.com/blynkkk/blynkkk.github.io 250 | [blynk-hw]: https://github.com/blynkkk/blynkkk.github.io/blob/master/SupportedHardware.md 251 | [blynk-architecture]: https://github.com/blynkkk/blynkkk.github.io/blob/master/images/architecture.png 252 | [blynk-banner]: https://github.com/blynkkk/blynkkk.github.io/blob/master/images/GithubBanner.jpg 253 | [blynk-server]: https://github.com/blynkkk/blynk-server 254 | [blynk-server-public]: http://blynk-cloud.com 255 | [blynk-docs]: https://docs.blynk.cc/ 256 | [blynk-py-examples]: https://github.com/blynkkk/lib-python/blob/master/examples 257 | [blynk-app-android]: https://play.google.com/store/apps/details?id=cloud.blynk 258 | [blynk-app-ios]: https://apps.apple.com/us/app/blynk-iot/id1559317868 259 | [blynk-vpins]: http://help.blynk.cc/getting-started-library-auth-token-code-examples/blynk-basics/what-is-virtual-pins 260 | [python-org]: https://www.python.org/downloads/ 261 | [micropython-org]: https://micropython.org/ 262 | [micropython-pkg]: https://github.com/micropython/micropython/wiki/Getting-Started 263 | [virtual-env]: https://virtualenv.pypa.io/en/latest/installation/ 264 | [esp8266-readme]: https://github.com/blynkkk/lib-python/blob/master/examples/esp8266/README.md 265 | [blynktimer-doc]: https://github.com/blynkkk/lib-python/blob/master/TIMERS.md 266 | -------------------------------------------------------------------------------- /TIMERS.md: -------------------------------------------------------------------------------- 1 | # Blynk Timers 2 | There are two options of setting polling timers in **blynk** 3 | 4 | - create timers for your functions on hardware side 5 | - create timers on Blynk App side. 6 | 7 | ### Hardware timers 8 | Existing core library solutions may be helpful for hardware timers creation. 9 | 10 | For example: 11 | - micropython provides [machine.Timer][micropython-timer] 12 | - for cPython [threading.Timer][threading-timer] can be used 13 | - etc 14 | 15 | Unfortunately mentioned above solutions may be not so lightweight and clear as expected. 16 | For Quickstart we provide separate [timer module][blynktimer] that allows execute functions periodically or run them once. 17 | 18 | ##### Basic usage examples 19 | ```python 20 | from blynktimer import Timer 21 | blynk_timer = Timer() 22 | 23 | # run once timer that will fire after 1 sec 24 | @blynk_timer.register(interval=1, run_once=True) 25 | def your_run_once_function(): 26 | print('Hello, World!') 27 | 28 | # periodical timer that will fire each 5 sec 29 | # run_once flag by default is False 30 | @blynk_timer.register(interval=5) 31 | def your_periodical_function(): 32 | print('Hello, Blynkers!') 33 | 34 | while True: 35 | blynk_timer.run() 36 | 37 | ``` 38 | 39 | ##### Advanced usage examples 40 | ```python 41 | import time 42 | from blynktimer import Timer 43 | 44 | # disable exception raise if all all timers were stopped 45 | blynk_timer = Timer(no_timers_err=False) 46 | 47 | 48 | # register two timers for single function with different function parameters 49 | @blynk_timer.register('p1', 'p2', c=1, interval=2, run_once=True) 50 | @blynk_timer.register('fp1', 'fp2', interval=3, run_once=False) 51 | def function1(a, b, c=2): 52 | time.sleep(c) 53 | print('Function params: {} {} {}'.format(a, b, c)) 54 | 55 | 56 | # simple function registration for further stop 57 | # interval default = 10 sec 58 | # run_once default is False 59 | @blynk_timer.register() 60 | def function2(): 61 | print('Function2') 62 | 63 | 64 | # list available timers 65 | print(blynk_timer.get_timers()) 66 | 67 | # switch timer state to stopped by timer id 68 | # id = order_num + '_' + function_name 69 | # OR: on ports with low memory (such as the esp8266) 70 | # id = order_num + '_' + 'timer' 71 | blynk_timer.stop('2_function2') 72 | 73 | 74 | while True: 75 | intervals = blynk_timer.run() 76 | # print real passed time for timer fired events 77 | # maybe needed for debug 78 | if any(intervals): 79 | print(intervals) 80 | ``` 81 | 82 | To get more accuracy for timers intervals it is possible to decrease library WAIT_SEC parameter. Default value = 0.05 sec 83 | 84 | ### Blynk App timers 85 | Some Blynk app widgets have timer setting where yoy can define (1,2,5,10 etc) seconds intervals for reading 86 | virtual pin values. 87 | 88 | Flow: 89 | - each N seconds Blynk app widget will do Virtual Pin reading operation. 90 | - Blynk Server for App read request will return current pin value 91 | - Additionally Blynk server will fire read virtual pin event and send it to hardware 92 | - If read pin event was registered on hardware certain handler will be executed 93 | 94 | [micropython-timer]: https://docs.micropython.org/en/latest/library/machine.Timer.html 95 | [threading-timer]:https://docs.python.org/3/library/threading.html#threading.Timer 96 | [blynktimer]: https://github.com/blynkkk/lib-python/blob/master/blynktimer.py -------------------------------------------------------------------------------- /blynklib.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020 Anton Morozenko 2 | # Copyright (c) 2015-2019 Volodymyr Shymanskyy. 3 | # See the file LICENSE for copying permission. 4 | 5 | __version__ = '0.2.6' 6 | 7 | import socket 8 | import ssl 9 | import struct 10 | import time 11 | 12 | LOGO = """ 13 | ___ __ __ 14 | / _ )/ /_ _____ / /__ 15 | / _ / / // / _ \\/ '_/ 16 | /____/_/\\_, /_//_/_/\\_\\ 17 | /___/ for Python v{}\n""".format(__version__) 18 | 19 | 20 | def stub_log(*args): 21 | pass 22 | 23 | 24 | def ticks_ms(): 25 | return int(time.time() * 1000) 26 | 27 | 28 | def sleep_ms(ms): 29 | time.sleep(ms // 1000) 30 | 31 | 32 | class BlynkError(Exception): 33 | pass 34 | 35 | 36 | class RedirectError(Exception): 37 | def __init__(self, server, port): 38 | self.server = server 39 | self.port = port 40 | 41 | 42 | class Protocol(object): 43 | MSG_RSP = 0 44 | MSG_LOGIN = 2 45 | MSG_PING = 6 46 | MSG_TWEET = 12 47 | MSG_EMAIL = 13 48 | MSG_NOTIFY = 14 49 | MSG_BRIDGE = 15 50 | MSG_HW_SYNC = 16 51 | MSG_INTERNAL = 17 52 | MSG_PROPERTY = 19 53 | MSG_HW = 20 54 | MSG_REDIRECT = 41 55 | MSG_HEAD_LEN = 5 56 | 57 | STATUS_INVALID_TOKEN = 9 58 | STATUS_NO_DATA = 17 59 | STATUS_OK = 200 60 | VPIN_MAX_NUM = 32 61 | 62 | _msg_id = 0 63 | 64 | def _get_msg_id(self, **kwargs): 65 | if 'msg_id' in kwargs: 66 | return kwargs['msg_id'] 67 | self._msg_id += 1 68 | return self._msg_id if self._msg_id <= 0xFFFF else 1 69 | 70 | def _pack_msg(self, msg_type, *args, **kwargs): 71 | data = ('\0'.join([str(curr_arg) for curr_arg in args])).encode('utf-8') 72 | return struct.pack('!BHH', msg_type, self._get_msg_id(**kwargs), len(data)) + data 73 | 74 | def parse_response(self, rsp_data, msg_buffer): 75 | msg_args = [] 76 | try: 77 | msg_type, msg_id, h_data = struct.unpack('!BHH', rsp_data[:self.MSG_HEAD_LEN]) 78 | except Exception as p_err: 79 | raise BlynkError('Message parse error: {}'.format(p_err)) 80 | if msg_id == 0: 81 | raise BlynkError('invalid msg_id == 0') 82 | elif h_data >= msg_buffer: 83 | raise BlynkError('Command too long. Length = {}'.format(h_data)) 84 | elif msg_type in (self.MSG_RSP, self.MSG_PING): 85 | pass 86 | elif msg_type in (self.MSG_HW, self.MSG_BRIDGE, self.MSG_INTERNAL, self.MSG_REDIRECT): 87 | msg_body = rsp_data[self.MSG_HEAD_LEN: self.MSG_HEAD_LEN + h_data] 88 | msg_args = [itm.decode('utf-8') for itm in msg_body.split(b'\0')] 89 | else: 90 | raise BlynkError("Unknown message type: '{}'".format(msg_type)) 91 | return msg_type, msg_id, h_data, msg_args 92 | 93 | def heartbeat_msg(self, heartbeat, rcv_buffer): 94 | return self._pack_msg(self.MSG_INTERNAL, 'ver', __version__, 'buff-in', rcv_buffer, 'h-beat', heartbeat, 95 | 'dev', 'python') 96 | 97 | def login_msg(self, token): 98 | return self._pack_msg(self.MSG_LOGIN, token) 99 | 100 | def ping_msg(self): 101 | return self._pack_msg(self.MSG_PING) 102 | 103 | def response_msg(self, *args, **kwargs): 104 | return self._pack_msg(self.MSG_RSP, *args, **kwargs) 105 | 106 | def virtual_write_msg(self, v_pin, *val): 107 | return self._pack_msg(self.MSG_HW, 'vw', v_pin, *val) 108 | 109 | def virtual_sync_msg(self, *pins): 110 | return self._pack_msg(self.MSG_HW_SYNC, 'vr', *pins) 111 | 112 | def email_msg(self, to, subject, body): 113 | return self._pack_msg(self.MSG_EMAIL, to, subject, body) 114 | 115 | def tweet_msg(self, msg): 116 | return self._pack_msg(self.MSG_TWEET, msg) 117 | 118 | def notify_msg(self, msg): 119 | return self._pack_msg(self.MSG_NOTIFY, msg) 120 | 121 | def set_property_msg(self, pin, prop, *val): 122 | return self._pack_msg(self.MSG_PROPERTY, pin, prop, *val) 123 | 124 | def internal_msg(self, *args): 125 | return self._pack_msg(self.MSG_INTERNAL, *args) 126 | 127 | 128 | class Connection(Protocol): 129 | SOCK_MAX_TIMEOUT = 5 130 | SOCK_TIMEOUT = 0.05 131 | SOCK_SSL_TIMEOUT = 1 132 | EAGAIN = 11 133 | ETIMEDOUT = 60 134 | RETRIES_TX_DELAY = 2 135 | RETRIES_TX_MAX_NUM = 3 136 | RECONNECT_SLEEP = 1 137 | TASK_PERIOD_RES = 50 138 | DISCONNECTED = 0 139 | CONNECTING = 1 140 | AUTHENTICATING = 2 141 | AUTHENTICATED = 3 142 | 143 | _state = None 144 | _socket = None 145 | _last_rcv_time = 0 146 | _last_ping_time = 0 147 | _last_send_time = 0 148 | 149 | def __init__(self, token, server='blynk-cloud.com', port=80, ssl_cert=None, heartbeat=10, rcv_buffer=1024, 150 | log=stub_log): 151 | self.token = token 152 | self.server = server 153 | self.port = port 154 | self.heartbeat = heartbeat 155 | self.rcv_buffer = rcv_buffer 156 | self.log = log 157 | self.ssl_cert = ssl_cert 158 | 159 | def send(self, data): 160 | retries = self.RETRIES_TX_MAX_NUM 161 | while retries > 0: 162 | try: 163 | retries -= 1 164 | self._last_send_time = ticks_ms() 165 | return self._socket.send(data) 166 | except (IOError, OSError): 167 | sleep_ms(self.RETRIES_TX_DELAY) 168 | 169 | def receive(self, length, timeout): 170 | d_buff = b'' 171 | try: 172 | self._socket.settimeout(timeout) 173 | d_buff += self._socket.recv(length) 174 | if len(d_buff) >= length: 175 | d_buff = d_buff[:length] 176 | return d_buff 177 | except (IOError, OSError) as err: 178 | if 'timed out' in str(err): 179 | return b'' 180 | if str(self.EAGAIN) in str(err) or str(self.ETIMEDOUT) in str(err): 181 | return b'' 182 | raise 183 | 184 | def is_server_alive(self): 185 | now = ticks_ms() 186 | h_beat_ms = self.heartbeat * 1000 187 | rcv_delta = now - self._last_rcv_time 188 | ping_delta = now - self._last_ping_time 189 | send_delta = now - self._last_send_time 190 | if rcv_delta > h_beat_ms + (h_beat_ms // 2): 191 | return False 192 | if (ping_delta > h_beat_ms // 10) and (send_delta > h_beat_ms or rcv_delta > h_beat_ms): 193 | self.send(self.ping_msg()) 194 | self.log('Heartbeat time: {}'.format(now)) 195 | self._last_ping_time = now 196 | return True 197 | 198 | def _get_socket(self): 199 | try: 200 | self._state = self.CONNECTING 201 | self._socket = socket.socket() 202 | self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][4]) 203 | self._socket.settimeout(self.SOCK_TIMEOUT) 204 | if self.ssl_cert: 205 | # system default CA certificates case 206 | if self.ssl_cert == "default": 207 | self.ssl_cert = None 208 | self.log('Using SSL socket...') 209 | ssl_context = ssl.create_default_context(cafile=self.ssl_cert) 210 | ssl_context.verify_mode = ssl.CERT_REQUIRED 211 | self._socket.settimeout(self.SOCK_SSL_TIMEOUT) 212 | self._socket = ssl_context.wrap_socket(sock=self._socket, server_hostname=self.server) 213 | self.log('Connected to blynk server') 214 | except Exception as g_exc: 215 | raise BlynkError('Connection with the Blynk server failed: {}'.format(g_exc)) 216 | 217 | def _authenticate(self): 218 | self.log('Authenticating device...') 219 | self._state = self.AUTHENTICATING 220 | self.send(self.login_msg(self.token)) 221 | rsp_data = self.receive(self.rcv_buffer, self.SOCK_MAX_TIMEOUT) 222 | if not rsp_data: 223 | raise BlynkError('Auth stage timeout') 224 | msg_type, _, status, args = self.parse_response(rsp_data, self.rcv_buffer) 225 | if status != self.STATUS_OK: 226 | if status == self.STATUS_INVALID_TOKEN: 227 | raise BlynkError('Invalid Auth Token') 228 | if msg_type == self.MSG_REDIRECT: 229 | raise RedirectError(*args) 230 | raise BlynkError('Auth stage failed. Status={}'.format(status)) 231 | self._state = self.AUTHENTICATED 232 | self.log('Access granted') 233 | 234 | def _set_heartbeat(self): 235 | self.send(self.heartbeat_msg(self.heartbeat, self.rcv_buffer)) 236 | rcv_data = self.receive(self.rcv_buffer, self.SOCK_MAX_TIMEOUT) 237 | if not rcv_data: 238 | raise BlynkError('Heartbeat stage timeout') 239 | _, _, status, _ = self.parse_response(rcv_data, self.rcv_buffer) 240 | if status != self.STATUS_OK: 241 | raise BlynkError('Set heartbeat returned code={}'.format(status)) 242 | self.log('Heartbeat = {} sec. MaxCmdBuffer = {} bytes'.format(self.heartbeat, self.rcv_buffer)) 243 | 244 | def connected(self): 245 | return True if self._state == self.AUTHENTICATED else False 246 | 247 | 248 | class Blynk(Connection): 249 | _CONNECT_TIMEOUT = 30 # 30sec 250 | _VPIN_WILDCARD = '*' 251 | _VPIN_READ = 'read v' 252 | _VPIN_WRITE = 'write v' 253 | _INTERNAL = 'internal_' 254 | _CONNECT = 'connect' 255 | _DISCONNECT = 'disconnect' 256 | _VPIN_READ_ALL = '{}{}'.format(_VPIN_READ, _VPIN_WILDCARD) 257 | _VPIN_WRITE_ALL = '{}{}'.format(_VPIN_WRITE, _VPIN_WILDCARD) 258 | _events = {} 259 | 260 | def __init__(self, token, **kwargs): 261 | Connection.__init__(self, token, **kwargs) 262 | self._start_time = ticks_ms() 263 | self._last_rcv_time = ticks_ms() 264 | self._last_send_time = ticks_ms() 265 | self._last_ping_time = ticks_ms() 266 | self._state = self.DISCONNECTED 267 | print(LOGO) 268 | 269 | def connect(self, timeout=_CONNECT_TIMEOUT): 270 | end_time = time.time() + timeout 271 | while not self.connected(): 272 | if self._state == self.DISCONNECTED: 273 | try: 274 | self._get_socket() 275 | self._authenticate() 276 | self._set_heartbeat() 277 | self._last_rcv_time = ticks_ms() 278 | self.log('Registered events: {}\n'.format(list(self._events.keys()))) 279 | self.call_handler(self._CONNECT) 280 | return True 281 | except BlynkError as b_err: 282 | self.disconnect(b_err) 283 | sleep_ms(self.TASK_PERIOD_RES) 284 | except RedirectError as r_err: 285 | self.disconnect() 286 | self.server = r_err.server 287 | self.port = r_err.port 288 | sleep_ms(self.TASK_PERIOD_RES) 289 | if time.time() >= end_time: 290 | return False 291 | 292 | def disconnect(self, err_msg=None): 293 | self.call_handler(self._DISCONNECT) 294 | if self._socket: 295 | self._socket.close() 296 | self._state = self.DISCONNECTED 297 | if err_msg: 298 | self.log('[ERROR]: {}\nConnection closed'.format(err_msg)) 299 | self._msg_id = 0 300 | time.sleep(self.RECONNECT_SLEEP) 301 | 302 | def virtual_write(self, v_pin, *val): 303 | return self.send(self.virtual_write_msg(v_pin, *val)) 304 | 305 | def virtual_sync(self, *v_pin): 306 | return self.send(self.virtual_sync_msg(*v_pin)) 307 | 308 | def email(self, to, subject, body): 309 | return self.send(self.email_msg(to, subject, body)) 310 | 311 | def tweet(self, msg): 312 | return self.send(self.tweet_msg(msg)) 313 | 314 | def notify(self, msg): 315 | return self.send(self.notify_msg(msg)) 316 | 317 | def set_property(self, v_pin, property_name, *val): 318 | return self.send(self.set_property_msg(v_pin, property_name, *val)) 319 | 320 | def internal(self, *args): 321 | return self.send(self.internal_msg(*args)) 322 | 323 | def handle_event(blynk, event_name): 324 | class Deco(object): 325 | def __init__(self, func): 326 | self.func = func 327 | # wildcard 'read V*' and 'write V*' events handling 328 | if str(event_name).lower() in (blynk._VPIN_READ_ALL, blynk._VPIN_WRITE_ALL): 329 | event_base_name = str(event_name).split(blynk._VPIN_WILDCARD)[0] 330 | for i in range(blynk.VPIN_MAX_NUM + 1): 331 | blynk._events['{}{}'.format(event_base_name.lower(), i)] = func 332 | else: 333 | blynk._events[str(event_name).lower()] = func 334 | 335 | def __call__(self): 336 | return self.func() 337 | 338 | return Deco 339 | 340 | def call_handler(self, event, *args, **kwargs): 341 | if event in self._events.keys(): 342 | self.log("Event: ['{}'] -> {}".format(event, args)) 343 | self._events[event](*args, **kwargs) 344 | 345 | def process(self, msg_type, msg_id, msg_len, msg_args): 346 | if msg_type == self.MSG_RSP: 347 | self.log('Response status: {}'.format(msg_len)) 348 | elif msg_type == self.MSG_PING: 349 | self.send(self.response_msg(self.STATUS_OK, msg_id=msg_id)) 350 | elif msg_type in (self.MSG_HW, self.MSG_BRIDGE, self.MSG_INTERNAL): 351 | if msg_type == self.MSG_INTERNAL: 352 | self.call_handler("{}{}".format(self._INTERNAL, msg_args[0]), msg_args[1:]) 353 | elif len(msg_args) >= 3 and msg_args[0] == 'vw': 354 | self.call_handler("{}{}".format(self._VPIN_WRITE, msg_args[1]), int(msg_args[1]), msg_args[2:]) 355 | elif len(msg_args) == 2 and msg_args[0] == 'vr': 356 | self.call_handler("{}{}".format(self._VPIN_READ, msg_args[1]), int(msg_args[1])) 357 | 358 | def read_response(self, timeout=0.5): 359 | end_time = time.time() + timeout 360 | while time.time() <= end_time: 361 | rsp_data = self.receive(self.rcv_buffer, self.SOCK_TIMEOUT) 362 | if rsp_data: 363 | self._last_rcv_time = ticks_ms() 364 | msg_type, msg_id, h_data, msg_args = self.parse_response(rsp_data, self.rcv_buffer) 365 | self.process(msg_type, msg_id, h_data, msg_args) 366 | 367 | def run(self): 368 | if not self.connected(): 369 | self.connect() 370 | else: 371 | try: 372 | self.read_response(timeout=self.SOCK_TIMEOUT) 373 | if not self.is_server_alive(): 374 | self.disconnect('Blynk server is offline') 375 | except KeyboardInterrupt: 376 | raise 377 | except BlynkError as b_err: 378 | self.log(b_err) 379 | self.disconnect() 380 | except Exception as g_exc: 381 | self.log(g_exc) 382 | -------------------------------------------------------------------------------- /blynklib_mp.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020 Anton Morozenko 2 | # Copyright (c) 2015-2019 Volodymyr Shymanskyy. 3 | # See the file LICENSE for copying permission. 4 | 5 | __version__ = '0.2.6' 6 | 7 | import usocket as socket 8 | import utime as time 9 | import ustruct as struct 10 | import uselect as select 11 | from micropython import const 12 | 13 | ticks_ms = time.ticks_ms 14 | sleep_ms = time.sleep_ms 15 | 16 | IOError = OSError 17 | 18 | LOGO = """ 19 | ___ __ __ 20 | / _ )/ /_ _____ / /__ 21 | / _ / / // / _ \\/ '_/ 22 | /____/_/\\_, /_//_/_/\\_\\ 23 | /___/ for Python v{}\n""".format(__version__) 24 | 25 | 26 | def stub_log(*args): 27 | pass 28 | 29 | 30 | class BlynkError(Exception): 31 | pass 32 | 33 | 34 | class RedirectError(Exception): 35 | def __init__(self, server, port): 36 | self.server = server 37 | self.port = port 38 | 39 | 40 | class Protocol(object): 41 | MSG_RSP = const(0) 42 | MSG_LOGIN = const(2) 43 | MSG_PING = const(6) 44 | MSG_TWEET = const(12) 45 | MSG_EMAIL = const(13) 46 | MSG_NOTIFY = const(14) 47 | MSG_BRIDGE = const(15) 48 | MSG_HW_SYNC = const(16) 49 | MSG_INTERNAL = const(17) 50 | MSG_PROPERTY = const(19) 51 | MSG_HW = const(20) 52 | MSG_REDIRECT = const(41) 53 | MSG_HEAD_LEN = const(5) 54 | 55 | STATUS_INVALID_TOKEN = const(9) 56 | STATUS_OK = const(200) 57 | VPIN_MAX_NUM = const(32) 58 | 59 | _msg_id = 1 60 | 61 | def _get_msg_id(self, **kwargs): 62 | if 'msg_id' in kwargs: 63 | return kwargs['msg_id'] 64 | self._msg_id += const(1) 65 | return self._msg_id if self._msg_id <= const(0xFFFF) else const(1) 66 | 67 | def _pack_msg(self, msg_type, *args, **kwargs): 68 | data = ('\0'.join([str(curr_arg) for curr_arg in args])).encode('utf-8') 69 | return struct.pack('!BHH', msg_type, self._get_msg_id(**kwargs), len(data)) + data 70 | 71 | def parse_response(self, rsp_data, msg_buffer): 72 | msg_args = [] 73 | msg_len = 0 74 | try: 75 | msg_type, msg_id, h_data = struct.unpack('!BHH', rsp_data[:self.MSG_HEAD_LEN]) 76 | msg_len = self.MSG_HEAD_LEN + h_data 77 | except Exception as p_err: 78 | raise BlynkError('Message parse error: {}'.format(p_err)) 79 | if msg_id == 0: 80 | raise BlynkError('invalid msg_id == 0') 81 | elif h_data >= msg_buffer: 82 | raise BlynkError('Command too long. Length = {}'.format(h_data)) 83 | elif msg_type in (self.MSG_RSP, self.MSG_PING): 84 | pass 85 | elif msg_type in (self.MSG_HW, self.MSG_BRIDGE, self.MSG_INTERNAL, self.MSG_REDIRECT): 86 | msg_body = rsp_data[self.MSG_HEAD_LEN: msg_len] 87 | msg_args = [itm.decode('utf-8') for itm in msg_body.split(b'\0')] 88 | else: 89 | raise BlynkError("Unknown message type: '{}'".format(msg_type)) 90 | return msg_type, msg_id, h_data, msg_args, msg_len 91 | 92 | def heartbeat_msg(self, heartbeat, rcv_buffer): 93 | return self._pack_msg(self.MSG_INTERNAL, 'ver', __version__, 'buff-in', rcv_buffer, 'h-beat', heartbeat, 94 | 'dev', 'mpython') 95 | 96 | def login_msg(self, token): 97 | return self._pack_msg(self.MSG_LOGIN, token) 98 | 99 | def ping_msg(self): 100 | return self._pack_msg(self.MSG_PING) 101 | 102 | def response_msg(self, *args, **kwargs): 103 | return self._pack_msg(self.MSG_RSP, *args, **kwargs) 104 | 105 | def virtual_write_msg(self, v_pin, *val): 106 | return self._pack_msg(self.MSG_HW, 'vw', v_pin, *val) 107 | 108 | def virtual_sync_msg(self, *pins): 109 | return self._pack_msg(self.MSG_HW_SYNC, 'vr', *pins) 110 | 111 | def email_msg(self, to, subject, body): 112 | return self._pack_msg(self.MSG_EMAIL, to, subject, body) 113 | 114 | def tweet_msg(self, msg): 115 | return self._pack_msg(self.MSG_TWEET, msg) 116 | 117 | def notify_msg(self, msg): 118 | return self._pack_msg(self.MSG_NOTIFY, msg) 119 | 120 | def set_property_msg(self, pin, prop, *val): 121 | return self._pack_msg(self.MSG_PROPERTY, pin, prop, *val) 122 | 123 | def internal_msg(self, *args): 124 | return self._pack_msg(self.MSG_INTERNAL, *args) 125 | 126 | 127 | class Connection(Protocol): 128 | SOCK_MAX_TIMEOUT = const(5) 129 | SOCK_TIMEOUT = 0.05 130 | EAGAIN = const(11) 131 | ETIMEDOUT = const(60) 132 | RETRIES_TX_DELAY = const(2) 133 | RETRIES_TX_MAX_NUM = const(3) 134 | RECONNECT_SLEEP = const(1) 135 | TASK_PERIOD_RES = const(50) 136 | DISCONNECTED = const(0) 137 | CONNECTING = const(1) 138 | AUTHENTICATING = const(2) 139 | AUTHENTICATED = const(3) 140 | 141 | _state = None 142 | _socket = None 143 | _last_rcv_time = 0 144 | _last_ping_time = 0 145 | _last_send_time = 0 146 | 147 | def __init__(self, token, server='blynk-cloud.com', port=80, heartbeat=10, rcv_buffer=1024, log=stub_log): 148 | self.token = token 149 | self.server = server 150 | self.port = port 151 | self.heartbeat = heartbeat 152 | self.rcv_buffer = rcv_buffer 153 | self.log = log 154 | 155 | def _set_socket_timeout(self, timeout): 156 | if getattr(self._socket, 'settimeout', None): 157 | self._socket.settimeout(timeout) 158 | else: 159 | p = select.poll() 160 | p.register(self._socket) 161 | p.poll(int(timeout * const(1000))) 162 | 163 | def send(self, data): 164 | retries = self.RETRIES_TX_MAX_NUM 165 | while retries > 0: 166 | try: 167 | retries -= 1 168 | self._last_send_time = ticks_ms() 169 | return self._socket.send(data) 170 | except (IOError, OSError): 171 | sleep_ms(self.RETRIES_TX_DELAY) 172 | 173 | def receive(self, length, timeout): 174 | d_buff = b'' 175 | try: 176 | self._set_socket_timeout(timeout) 177 | d_buff += self._socket.recv(length) 178 | if len(d_buff) >= length: 179 | d_buff = d_buff[:length] 180 | return d_buff 181 | except (IOError, OSError) as err: 182 | if str(err) == 'timed out': 183 | return b'' 184 | if str(self.EAGAIN) in str(err) or str(self.ETIMEDOUT) in str(err): 185 | return b'' 186 | raise 187 | 188 | def is_server_alive(self): 189 | now = ticks_ms() 190 | h_beat_ms = self.heartbeat * const(1000) 191 | rcv_delta = time.ticks_diff(now, self._last_rcv_time) 192 | ping_delta = time.ticks_diff(now, self._last_ping_time) 193 | send_delta = time.ticks_diff(now, self._last_send_time) 194 | if rcv_delta > h_beat_ms + (h_beat_ms // const(2)): 195 | return False 196 | if (ping_delta > h_beat_ms // const(10)) and (send_delta > h_beat_ms or rcv_delta > h_beat_ms): 197 | self.send(self.ping_msg()) 198 | self.log('Heartbeat time: {}'.format(now)) 199 | self._last_ping_time = now 200 | return True 201 | 202 | def _get_socket(self): 203 | try: 204 | self._state = self.CONNECTING 205 | self._socket = socket.socket() 206 | self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][-1]) 207 | self._set_socket_timeout(self.SOCK_TIMEOUT) 208 | self.log('Connected to server') 209 | except Exception as g_exc: 210 | raise BlynkError('Server connection failed: {}'.format(g_exc)) 211 | 212 | def _authenticate(self): 213 | self.log('Authenticating device...') 214 | self._state = self.AUTHENTICATING 215 | self.send(self.login_msg(self.token)) 216 | rsp_data = self.receive(self.rcv_buffer, self.SOCK_MAX_TIMEOUT) 217 | if not rsp_data: 218 | raise BlynkError('Auth stage timeout') 219 | msg_type, _, status, args, _ = self.parse_response(rsp_data, self.rcv_buffer) 220 | if status != self.STATUS_OK: 221 | if status == self.STATUS_INVALID_TOKEN: 222 | raise BlynkError('Invalid Auth Token') 223 | if msg_type == self.MSG_REDIRECT: 224 | raise RedirectError(*args) 225 | raise BlynkError('Auth stage failed. Status={}'.format(status)) 226 | self._state = self.AUTHENTICATED 227 | self.log('Access granted') 228 | 229 | def _set_heartbeat(self): 230 | self.send(self.heartbeat_msg(self.heartbeat, self.rcv_buffer)) 231 | rcv_data = self.receive(self.rcv_buffer, self.SOCK_MAX_TIMEOUT) 232 | if not rcv_data: 233 | raise BlynkError('Heartbeat stage timeout') 234 | _, _, status, _, _ = self.parse_response(rcv_data, self.rcv_buffer) 235 | if status != self.STATUS_OK: 236 | raise BlynkError('Set heartbeat returned code={}'.format(status)) 237 | self.log('Heartbeat = {} sec. MaxCmdBuffer = {} bytes'.format(self.heartbeat, self.rcv_buffer)) 238 | 239 | def connected(self): 240 | return True if self._state == self.AUTHENTICATED else False 241 | 242 | 243 | class Blynk(Connection): 244 | _CONNECT_TIMEOUT = const(30) # 30sec 245 | _VPIN_WILDCARD = '*' 246 | _VPIN_READ = 'read v' 247 | _VPIN_WRITE = 'write v' 248 | _INTERNAL = 'internal_' 249 | _CONNECT = 'connect' 250 | _DISCONNECT = 'disconnect' 251 | _VPIN_READ_ALL = '{}{}'.format(_VPIN_READ, _VPIN_WILDCARD) 252 | _VPIN_WRITE_ALL = '{}{}'.format(_VPIN_WRITE, _VPIN_WILDCARD) 253 | _events = {} 254 | 255 | def __init__(self, token, **kwargs): 256 | Connection.__init__(self, token, **kwargs) 257 | self._start_time = ticks_ms() 258 | self._last_rcv_time = ticks_ms() 259 | self._last_send_time = ticks_ms() 260 | self._last_ping_time = ticks_ms() 261 | self._state = self.DISCONNECTED 262 | print(LOGO) 263 | 264 | def connect(self, timeout=_CONNECT_TIMEOUT): 265 | end_time = time.time() + timeout 266 | while not self.connected(): 267 | if self._state == self.DISCONNECTED: 268 | try: 269 | self._get_socket() 270 | self._authenticate() 271 | self._set_heartbeat() 272 | self._last_rcv_time = ticks_ms() 273 | self.log('Registered events: {}\n'.format(list(self._events.keys()))) 274 | self.call_handler(self._CONNECT) 275 | return True 276 | except BlynkError as b_err: 277 | self.disconnect(b_err) 278 | sleep_ms(self.TASK_PERIOD_RES) 279 | except RedirectError as r_err: 280 | self.disconnect() 281 | self.server = r_err.server 282 | self.port = r_err.port 283 | sleep_ms(self.TASK_PERIOD_RES) 284 | if time.time() >= end_time: 285 | return False 286 | 287 | def disconnect(self, err_msg=None): 288 | self.call_handler(self._DISCONNECT) 289 | if self._socket: 290 | self._socket.close() 291 | self._state = self.DISCONNECTED 292 | if err_msg: 293 | self.log('[ERROR]: {}\nConnection closed'.format(err_msg)) 294 | time.sleep(self.RECONNECT_SLEEP) 295 | 296 | def virtual_write(self, v_pin, *val): 297 | return self.send(self.virtual_write_msg(v_pin, *val)) 298 | 299 | def virtual_sync(self, *v_pin): 300 | return self.send(self.virtual_sync_msg(*v_pin)) 301 | 302 | def email(self, to, subject, body): 303 | return self.send(self.email_msg(to, subject, body)) 304 | 305 | def tweet(self, msg): 306 | return self.send(self.tweet_msg(msg)) 307 | 308 | def notify(self, msg): 309 | return self.send(self.notify_msg(msg)) 310 | 311 | def set_property(self, v_pin, property_name, *val): 312 | return self.send(self.set_property_msg(v_pin, property_name, *val)) 313 | 314 | def internal(self, *args): 315 | return self.send(self.internal_msg(*args)) 316 | 317 | def handle_event(blynk, event_name): 318 | class Deco(object): 319 | def __init__(self, func): 320 | self.func = func 321 | # wildcard 'read V*' and 'write V*' events handling 322 | if str(event_name).lower() in (blynk._VPIN_READ_ALL, blynk._VPIN_WRITE_ALL): 323 | event_base_name = str(event_name).split(blynk._VPIN_WILDCARD)[0] 324 | for i in range(blynk.VPIN_MAX_NUM + 1): 325 | blynk._events['{}{}'.format(event_base_name.lower(), i)] = func 326 | else: 327 | blynk._events[str(event_name).lower()] = func 328 | 329 | def __call__(self): 330 | return self.func() 331 | 332 | return Deco 333 | 334 | def call_handler(self, event, *args, **kwargs): 335 | if event in self._events.keys(): 336 | self.log("Event: ['{}'] -> {}".format(event, args)) 337 | self._events[event](*args, **kwargs) 338 | 339 | def process(self, msg_type, msg_id, msg_len, msg_args): 340 | if msg_type == self.MSG_RSP: 341 | self.log('Response status: {}'.format(msg_len)) 342 | elif msg_type == self.MSG_PING: 343 | self.send(self.response_msg(self.STATUS_OK, msg_id=msg_id)) 344 | elif msg_type in (self.MSG_HW, self.MSG_BRIDGE, self.MSG_INTERNAL): 345 | if msg_type == self.MSG_INTERNAL: 346 | self.call_handler("{}{}".format(self._INTERNAL, msg_args[0]), msg_args[1:]) 347 | elif len(msg_args) >= const(3) and msg_args[0] == 'vw': 348 | self.call_handler("{}{}".format(self._VPIN_WRITE, msg_args[1]), int(msg_args[1]), msg_args[2:]) 349 | elif len(msg_args) == const(2) and msg_args[0] == 'vr': 350 | self.call_handler("{}{}".format(self._VPIN_READ, msg_args[1]), int(msg_args[1])) 351 | 352 | def read_response(self, timeout=0.5): 353 | end_time = time.ticks_ms() + int(timeout * const(1000)) 354 | while time.ticks_diff(end_time, time.ticks_ms()) > 0: 355 | rsp_data = self.receive(self.rcv_buffer, self.SOCK_TIMEOUT) 356 | if rsp_data: 357 | self._last_rcv_time = ticks_ms() 358 | while rsp_data: 359 | msg_type, msg_id, h_data, msg_args, msg_len = self.parse_response(rsp_data, self.rcv_buffer) 360 | self.process(msg_type, msg_id, h_data, msg_args) 361 | rsp_data = rsp_data[msg_len:] 362 | 363 | def run(self): 364 | if not self.connected(): 365 | self.connect() 366 | else: 367 | try: 368 | self.read_response(timeout=self.SOCK_TIMEOUT) 369 | if not self.is_server_alive(): 370 | self.disconnect('Server is offline') 371 | except KeyboardInterrupt: 372 | raise 373 | except BlynkError as b_err: 374 | self.log(b_err) 375 | self.disconnect() 376 | except Exception as g_exc: 377 | self.log(g_exc) 378 | -------------------------------------------------------------------------------- /blynktimer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020 Anton Morozenko 2 | """ 3 | Polling timers for functions. 4 | Registers timers and performs run once or periodical function execution after defined time intervals. 5 | """ 6 | # select.select call used as polling waiter where it is possible 7 | # cause time.sleep sometimes may load CPU up to 100% with small polling wait interval 8 | try: 9 | # cpython 10 | import time 11 | import select 12 | 13 | polling_wait = lambda x: select.select([], [], [], x) 14 | polling_wait(0.01) 15 | except OSError: 16 | # windows case where select.select call fails 17 | polling_wait = lambda x: time.sleep(x) 18 | 19 | except ImportError: 20 | # micropython 21 | import utime as time 22 | 23 | try: 24 | from uselect import select as s_select 25 | 26 | polling_wait = lambda x: s_select([], [], [], x) 27 | except ImportError: 28 | # case when micropython port does not support select.select 29 | polling_wait = lambda x: time.sleep(x) 30 | 31 | WAIT_SEC = 0.05 32 | MAX_TIMERS = 16 33 | DEFAULT_INTERVAL = 10 34 | 35 | 36 | class TimerError(Exception): 37 | pass 38 | 39 | 40 | class Timer(object): 41 | timers = {} 42 | 43 | def __init__(self, no_timers_err=True): 44 | self.no_timers_err = no_timers_err 45 | 46 | def _get_func_name(self, obj): 47 | """retrieves a suitable name for a function""" 48 | if hasattr(obj, 'func'): 49 | # handles nested decorators 50 | return self._get_func_name(obj.func) 51 | # simply returns 'timer' if on port without function attrs 52 | return getattr(obj, '__name__', 'timer') 53 | 54 | def register(blynk, *args, **kwargs): 55 | # kwargs with defaults are used cause PEP 3102 no supported by Python2 56 | interval = kwargs.pop('interval', DEFAULT_INTERVAL) 57 | run_once = kwargs.pop('run_once', False) 58 | stopped = kwargs.pop('stopped', False) 59 | 60 | class Deco(object): 61 | def __init__(self, func): 62 | self.func = func 63 | if len(list(Timer.timers.keys())) >= MAX_TIMERS: 64 | raise TimerError('Max allowed timers num={}'.format(MAX_TIMERS)) 65 | _timer = _Timer(interval, func, run_once, stopped, *args, **kwargs) 66 | Timer.timers['{}_{}'.format(len(list(Timer.timers.keys())), blynk._get_func_name(func))] = _timer 67 | 68 | def __call__(self, *f_args, **f_kwargs): 69 | return self.func(*f_args, **f_kwargs) 70 | 71 | return Deco 72 | 73 | @staticmethod 74 | def stop(t_id): 75 | timer = Timer.timers.get(t_id, None) 76 | if timer is None: 77 | raise TimerError('Timer id={} not found'.format(t_id)) 78 | Timer.timers[t_id].stopped = True 79 | 80 | @staticmethod 81 | def start(t_id): 82 | timer = Timer.timers.get(t_id, None) 83 | if timer is None: 84 | raise TimerError('Timer id={} not found'.format(t_id)) 85 | Timer.timers[t_id].stopped = False 86 | Timer.timers[t_id].fire_time = None 87 | Timer.timers[t_id].fire_time_prev = None 88 | 89 | @staticmethod 90 | def is_stopped(t_id): 91 | timer = Timer.timers.get(t_id, None) 92 | if timer is None: 93 | raise TimerError('Timer id={} not found'.format(t_id)) 94 | return timer.stopped 95 | 96 | def get_timers(self): 97 | states = {True: 'Stopped', False: 'Running'} 98 | return {k: states[v.stopped] for k, v in self.timers.items()} 99 | 100 | def run(self): 101 | polling_wait(WAIT_SEC) 102 | timers_intervals = [curr_timer.run() for curr_timer in Timer.timers.values() if not curr_timer.stopped] 103 | if not timers_intervals and self.no_timers_err: 104 | raise TimerError('Running timers not found') 105 | return timers_intervals 106 | 107 | 108 | class _Timer(object): 109 | def __init__(self, interval, deco, run_once, stopped, *args, **kwargs): 110 | self.interval = interval 111 | self.deco = deco 112 | self.args = args 113 | self.run_once = run_once 114 | self.kwargs = kwargs 115 | self.fire_time = None 116 | self.fire_time_prev = None 117 | self.stopped = stopped 118 | 119 | def run(self): 120 | timer_real_interval = 0 121 | if self.fire_time is None: 122 | self.fire_time = time.time() + self.interval 123 | if self.fire_time_prev is None: 124 | self.fire_time_prev = time.time() 125 | curr_time = time.time() 126 | if curr_time >= self.fire_time: 127 | self.deco(*self.args, **self.kwargs) 128 | if self.run_once: 129 | self.stopped = True 130 | timer_real_interval = curr_time - self.fire_time_prev 131 | self.fire_time_prev = self.fire_time 132 | self.fire_time = curr_time + self.interval 133 | return timer_real_interval 134 | -------------------------------------------------------------------------------- /certificate/blynk-cloud.com.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID5TCCAs2gAwIBAgIJAIHSnb+cv4ECMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD 3 | VQQGEwJVQTENMAsGA1UECAwES3lpdjENMAsGA1UEBwwES3lpdjELMAkGA1UECgwC 4 | SVQxEzARBgNVBAsMCkJseW5rIEluYy4xGDAWBgNVBAMMD2JseW5rLWNsb3VkLmNv 5 | bTEfMB0GCSqGSIb3DQEJARYQZG1pdHJpeUBibHluay5jYzAeFw0xNjAzMTcxMTU4 6 | MDdaFw0yMTAzMTYxMTU4MDdaMIGIMQswCQYDVQQGEwJVQTENMAsGA1UECAwES3lp 7 | djENMAsGA1UEBwwES3lpdjELMAkGA1UECgwCSVQxEzARBgNVBAsMCkJseW5rIElu 8 | Yy4xGDAWBgNVBAMMD2JseW5rLWNsb3VkLmNvbTEfMB0GCSqGSIb3DQEJARYQZG1p 9 | dHJpeUBibHluay5jYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALso 10 | bhbXQuNlzYBFa9h9pd69n43yrGTL4Ba6k5Q1zDwY9HQbMdfC5ZfnCkqT7Zf+R5MO 11 | RW0Q9nLsFNLJkwKnluRCYGyUES8NAmDLQBbZoVc8mv9K3mIgAQvGyY2LmKak5GSI 12 | V0PC3x+iN03xU2774+Zi7DaQd7vTl/9RGk8McyHe/s5Ikbe14bzWcY9ZV4PKgCck 13 | p1chbmLhSfGbT3v64sL8ZbIppQk57/JgsZMrVpjExvxQPZuJfWbtoypPfpYO+O8l 14 | 1szaMlTEPIZVMoYi9uE+DnOlhzJFn6Ac4FMrDzJXzMmCweSX3IxguvXALeKhUHQJ 15 | +VP3G6Q3pkZRVKz+5XsCAwEAAaNQME4wHQYDVR0OBBYEFJtqtI62Io66cZgiTR5L 16 | A5Tl5m+xMB8GA1UdIwQYMBaAFJtqtI62Io66cZgiTR5LA5Tl5m+xMAwGA1UdEwQF 17 | MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKphjtEOGs7oC3S87+AUgIw4gFNOuv+L 18 | C98/l47OD6WtsqJKvCZ1lmKxY5aIro9FBPk8ktCOsbwEjE+nyr5wul+6CLFr+rnv 19 | 7OHYGwLpjoz+rZgYJiQ61E1m0AZ4y9Fyd+D90HW6247vrBXyEiUXOhN/oDDVfDQA 20 | eqmNBx1OqWel81D3tA7zPMA7vUItyWcFIXNjOCP+POy7TMxZuhuPMh5bVu+/cthl 21 | /Q9u/Z2lKl4CWV0Ivt2BtlN6iefva0e2AP/As+gfwjxrb0t11zSILLNJ+nxRIwg+ 22 | k4MGb1zihKbIXUzsjslONK4FY5rlQUSwKJgEAVF0ClxB4g6dECm0ckc= 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /examples/01_write_virtual_pin.py: -------------------------------------------------------------------------------- 1 | """ 2 | [WRITE VIRTUAL PIN EXAMPLE] ======================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Slider" widget, 7 | - bind it to Virtual Pin V4, 8 | - set values range 0-255 9 | - add "LED" widget and assign Virtual Pin V4 to it 10 | - Run the App (green triangle in the upper right corner). 11 | - define your auth token for current example and run it 12 | 13 | 14 | This started program will periodically call and execute event handler "write_virtual_pin_handler". 15 | In app you can move slider that will cause LED brightness change and will send virtual write event 16 | to current running example. Handler will print pin number and it's updated value. 17 | 18 | Schema: 19 | ==================================================================================================== 20 | +-----------+ +--------------+ +--------------+ 21 | | | | | | | 22 | | blynk lib | | blynk server | | blynk app | 23 | | | | virtual pin | | | 24 | | | | | | | 25 | +-----+-----+ +------+-------+ +-------+------+ 26 | | | | 27 | | | write event from "Slider" widget | 28 | | | | 29 | | +<-----------------------------------+ 30 | | | | 31 | | | | 32 | | | | 33 | | | | 34 | event handler | write event to hw from server | | 35 | (user function) | | | 36 | +-----------<------------------------------------+ | 37 | | | | | 38 | | | | | 39 | +--------->+ | | 40 | | | | 41 | | | | 42 | | | | 43 | + + + 44 | ==================================================================================================== 45 | Additional blynk info you can find by examining such resources: 46 | 47 | Downloads, docs, tutorials: https://blynk.io 48 | Sketch generator: http://examples.blynk.cc 49 | Blynk community: http://community.blynk.cc 50 | Social networks: http://www.fb.com/blynkapp 51 | http://twitter.com/blynk_app 52 | ==================================================================================================== 53 | """ 54 | 55 | import blynklib 56 | 57 | BLYNK_AUTH = 'YourAuthToken' 58 | 59 | # initialize Blynk 60 | blynk = blynklib.Blynk(BLYNK_AUTH) 61 | 62 | WRITE_EVENT_PRINT_MSG = "[WRITE_VIRTUAL_PIN_EVENT] Pin: V{} Value: '{}'" 63 | 64 | 65 | # register handler for virtual pin V4 write event 66 | @blynk.handle_event('write V4') 67 | def write_virtual_pin_handler(pin, value): 68 | print(WRITE_EVENT_PRINT_MSG.format(pin, value)) 69 | 70 | 71 | ########################################################### 72 | # infinite loop that waits for event 73 | ########################################################### 74 | while True: 75 | blynk.run() 76 | -------------------------------------------------------------------------------- /examples/02_read_virtual_pin.py: -------------------------------------------------------------------------------- 1 | """ 2 | [READ VIRTUAL PIN EXAMPLE] ======================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Value Display" widget, 7 | - bind it to Virtual Pin V11, 8 | - set values range 0-255 9 | - set the read frequency to 5 second. 10 | - optionally to have more visibility you can add "LED" widget and assign Virtual Pin V11 to it 11 | - Run the App (green triangle in the upper right corner). 12 | - define your auth token for current example and run it 13 | 14 | 15 | This started program will periodically call and execute event handler "read_virtual_pin_handler". 16 | Calling virtual_write operation inside handler updates widget value. 17 | In app you can see updated values and optionally LED brightness change. 18 | 19 | Schema: 20 | ===================================================================================================== 21 | +-----------+ +--------------+ +--------------+ 22 | | | | | | | 23 | | blynk lib | | blynk server | | blynk app | 24 | | | | virtual pin | | | 25 | | | | | | | 26 | +-----------+ +--------------+ +--------------+ 27 | | | | 28 | | | | 29 | | | widget read frequency = 5 sec | 30 | | +<-----------------------------------+ 31 | | | | 32 | | | | 33 | | | send virtual pin value to widget | 34 | | | | 35 | event handler | read event to hw from server +----------------------------------->+ 36 | (user function) | | | 37 | +-----------<------------------------------------+ | 38 | | | | | 39 | | | write random 0-255 value to pin | | 40 | +--------->------------------------------------->+ next widget read event | 41 | | | | 42 | | +<-----------------------------------+ 43 | | | | 44 | | | | 45 | + + + 46 | 47 | ==================================================================================================== 48 | Additional info about blynk you can find by examining such resources: 49 | 50 | Downloads, docs, tutorials: https://blynk.io 51 | Sketch generator: http://examples.blynk.cc 52 | Blynk community: http://community.blynk.cc 53 | Social networks: http://www.fb.com/blynkapp 54 | http://twitter.com/blynk_app 55 | ===================================================================================================== 56 | """ 57 | 58 | import blynklib 59 | import random 60 | 61 | BLYNK_AUTH = 'YourAuthToken' 62 | 63 | # initialize blynk 64 | blynk = blynklib.Blynk(BLYNK_AUTH) 65 | 66 | READ_PRINT_MSG = "[READ_VIRTUAL_PIN_EVENT] Pin: V{}" 67 | 68 | 69 | # register handler for virtual pin V11 reading 70 | @blynk.handle_event('read V11') 71 | def read_virtual_pin_handler(pin): 72 | print(READ_PRINT_MSG.format(pin)) 73 | blynk.virtual_write(pin, random.randint(0, 255)) 74 | 75 | 76 | ########################################################### 77 | # infinite loop that waits for event 78 | ########################################################### 79 | while True: 80 | blynk.run() 81 | -------------------------------------------------------------------------------- /examples/03_connect_disconnect.py: -------------------------------------------------------------------------------- 1 | """ 2 | [CONNECT/DISCONNECT EVENTS EXAMPLE] ================================================================= 3 | 4 | Environment prepare: 5 | - define your auth token for current example and run it 6 | 7 | 8 | This started program after successful connect operation will call and execute "connect event handler" 9 | Within handler after short sleep delay blynk disconnect call will be performed that will trigger 10 | "disconnect event handler" execution. 11 | 12 | Schema: 13 | ===================================================================================================== 14 | +-----------+ +--------------+ 15 | | | | | 16 | | blynk lib | | blynk server | 17 | | | | virtual pin | 18 | | | | | 19 | +-----+-----+ +------+-------+ 20 | | | 21 | | connect/authenticate request | 22 | +------------------------------------>+ 23 | connect handler | | 24 | (user function) | connected successfully | 25 | +-----------<------------------------------------+ 26 | | | | 27 | | | disconnect request | 28 | +--------->------------------------------------->+ 29 | | | 30 | | | 31 | disconnect handler | | 32 | (user function) | disconnected successfully | 33 | +-----------<------------------------------------+ 34 | | | | 35 | | | | 36 | +--------->+ | 37 | | reconnect request | 38 | | performed by lib automatically | 39 | +------------------------------------>+ 40 | + + 41 | 42 | 43 | ==================================================================================================== 44 | Additional info about blynk you can find by examining such resources: 45 | 46 | Downloads, docs, tutorials: https://blynk.io 47 | Sketch generator: http://examples.blynk.cc 48 | Blynk community: http://community.blynk.cc 49 | Social networks: http://www.fb.com/blynkapp 50 | http://twitter.com/blynk_app 51 | ===================================================================================================== 52 | """ 53 | 54 | import blynklib 55 | import time 56 | 57 | BLYNK_AUTH = 'YourAuthToken' 58 | 59 | blynk = blynklib.Blynk(BLYNK_AUTH) 60 | 61 | CONNECT_PRINT_MSG = '[CONNECT_EVENT]' 62 | DISCONNECT_PRINT_MSG = '[DISCONNECT_EVENT]' 63 | 64 | 65 | @blynk.handle_event("connect") 66 | def connect_handler(): 67 | print(CONNECT_PRINT_MSG) 68 | print('Sleeping 2 sec in connect handler...') 69 | time.sleep(2) 70 | blynk.disconnect() 71 | 72 | 73 | @blynk.handle_event("disconnect") 74 | def disconnect_handler(): 75 | print(DISCONNECT_PRINT_MSG) 76 | print('Sleeping 4 sec in disconnect handler...') 77 | time.sleep(4) 78 | 79 | 80 | ########################################################### 81 | # infinite loop that waits for event 82 | ########################################################### 83 | while True: 84 | blynk.run() 85 | -------------------------------------------------------------------------------- /examples/04_email.py: -------------------------------------------------------------------------------- 1 | """ 2 | [EMAIL ON CONNECT EXAMPLE] ========================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Email" widget, 7 | - Run the App (green triangle in the upper right corner). 8 | - define for current example your target email. 9 | - define your auth token for current example and run it 10 | 11 | 12 | This started program will operate with "connect_handler". 13 | Within handler after short sleep delay email send operation will be performed. 14 | 15 | Schema: 16 | ===================================================================================================================== 17 | +-----------+ +--------------+ +--------------+ +-------+ 18 | | | | | | | | | 19 | | blynk lib | | blynk server | | blynk app | | email | 20 | | | | virtual pin | | | | box | 21 | | | | | | | | | 22 | +-----+-----+ +------+-------+ +-------+------+ +---+---- 23 | | connect request | | | 24 | +------------------------------------>+ | | 25 | connect handler | | | | 26 | (user function) | connected successfully | | | 27 | +-----------<------------------------------------+ | | 28 | | | | | | 29 | | | send email | email widget present? | | 30 | +--------->------------------------------------->+ (is user allowed to send emails) | | 31 | | +----------------------------------->+ | 32 | | | | | 33 | | | email sending allowed | | 34 | | +<-----------------------------------+ | 35 | | | | | 36 | | | | | 37 | | | send email to defined address | | 38 | | +----------------------------------------------------->+ 39 | | | | | 40 | | | | | 41 | + + + + 42 | 43 | 44 | ===================================================================================================================== 45 | Additional info about blynk you can find by examining such resources: 46 | 47 | Downloads, docs, tutorials: https://blynk.io 48 | Sketch generator: http://examples.blynk.cc 49 | Blynk community: http://community.blynk.cc 50 | Social networks: http://www.fb.com/blynkapp 51 | http://twitter.com/blynk_app 52 | ===================================================================================================================== 53 | """ 54 | 55 | import blynklib 56 | import time 57 | 58 | BLYNK_AUTH = 'YourAuthToken' 59 | TARGET_EMAIL = 'YourTargetEmail' 60 | 61 | blynk = blynklib.Blynk(BLYNK_AUTH) 62 | EMAIL_PRINT_MSG = "[EMAIL WAS SENT to '{}']".format(TARGET_EMAIL) 63 | 64 | 65 | @blynk.handle_event("connect") 66 | def connect_handler(): 67 | print('Sleeping 2 sec before sending email...') 68 | time.sleep(2) 69 | blynk.email(TARGET_EMAIL, 'BLYNK-HW-TEST-EMAIL', 'Connected!') 70 | print(EMAIL_PRINT_MSG) 71 | 72 | 73 | ########################################################### 74 | # infinite loop that waits for event 75 | ########################################################### 76 | while True: 77 | blynk.run() 78 | -------------------------------------------------------------------------------- /examples/05_set_property_notify.py: -------------------------------------------------------------------------------- 1 | """ 2 | [SET_PROPERTY/NOTIFY EXAMPLE] ========================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Slider" widget, 7 | - bind it to Virtual Pin V5, 8 | - set values range 0-255 9 | - add "LED" widget and assign Virtual Pin V5 to it 10 | - add "Notification" widget to be allowed receive notifications in App 11 | - Run the App (green triangle in the upper right corner). 12 | - define your auth token for current example and run it 13 | 14 | 15 | This started program will periodically call and execute event handler "write_virtual_pin_handler". 16 | In app you can move slider that will cause LED brightness change and will send virtual write event 17 | to current running example. Handler will set random color for virtual pin and will send notification 18 | event to App. Virtual pin property 'color' change will cause color changes for "Slider" and "LED" widgets 19 | In App user will get notifications about color change event. 20 | 21 | Schema: 22 | ===================================================================================================================== 23 | +-----------+ +--------------+ +--------------+ 24 | | | | | | | 25 | | blynk lib | | blynk server | | blynk app | 26 | | | | virtual pin | | | 27 | | | | | | | 28 | +-----+-----+ +------+-------+ +-------+------+ 29 | | | | 30 | | | write event from "Slider" widget | 31 | | | | 32 | | +<-----------------------------------+ 33 | | | | 34 | | | | 35 | | | | 36 | | | | 37 | event handler | write event to hw from server | | 38 | (user function) | | | 39 | +-----------<------------------------------------+ | 40 | | | | | 41 | | | pin set property | pin property changed msg | 42 | +-----+--->------------------------------------->------------------------------------>+ 43 | | | | | 44 | | | send notification | notification widget present? | 45 | +--->------------------------------------->------------------------------------>+ 46 | | | | 47 | | | yes | 48 | | +<-----------------------------------+ 49 | | | | 50 | | | | 51 | | +----------------------------------->+ 52 | + + notification delivery + 53 | 54 | ===================================================================================================================== 55 | Additional info about blynk you can find by examining such resources: 56 | 57 | Downloads, docs, tutorials: https://blynk.io 58 | Sketch generator: http://examples.blynk.cc 59 | Blynk community: http://community.blynk.cc 60 | Social networks: http://www.fb.com/blynkapp 61 | http://twitter.com/blynk_app 62 | ===================================================================================================================== 63 | """ 64 | 65 | import blynklib 66 | import random 67 | 68 | BLYNK_AUTH = 'YourAuthToken' 69 | blynk = blynklib.Blynk(BLYNK_AUTH) 70 | 71 | NOTIFY_MSG = "['COLOR' = '{}']" 72 | colors = {'#FF00FF': 'Magenta', '#00FF00': 'Lime'} 73 | 74 | 75 | @blynk.handle_event('write V5') 76 | def write_handler(pin, value): 77 | current_color = random.choice(list(colors.keys())) 78 | blynk.set_property(pin, 'color', current_color) 79 | blynk.notify(NOTIFY_MSG.format(colors[current_color])) 80 | print(NOTIFY_MSG.format(colors[current_color])) 81 | 82 | 83 | ########################################################### 84 | # infinite loop that waits for event 85 | ########################################################### 86 | while True: 87 | blynk.run() 88 | -------------------------------------------------------------------------------- /examples/06_terminal_widget.py: -------------------------------------------------------------------------------- 1 | """ 2 | [TERMINAL WIDGET EXAMPLE] ========================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Terminal" widget, 7 | - bind it to Virtual Pin V6, 8 | - add input line option in widget settings 9 | - Run the App (green triangle in the upper right corner). 10 | - define your auth token for current example 11 | - optionally you can define your own "ALLOWED_COMMANDS_LIST" 12 | - run current example 13 | 14 | 15 | This started program will periodically call and execute event handler "write_virtual_pin_handler". 16 | In App Terminal widget you can type commands. If command present in allowed list, script will try 17 | to execute it in current running environment and will send back to terminal execution results. 18 | Additionally can be used 'help' command to get in terminal list of available commands. 19 | 20 | Schema: 21 | ===================================================================================================================== 22 | +-----+ +-----------+ +--------------+ +--------------+ 23 | | | | | | | | | 24 | | env | | blynk lib | | blynk server | | blynk app | 25 | | | | | | virtual pin | | | 26 | | | | | | | | | 27 | +--+--+ +-----+-----+ +------+-------+ +-------+------+ 28 | | | | | 29 | | | | write command from terminal widget | 30 | | | | | 31 | | | +<-----------------------------------+ 32 | | event handler | write event to hw from server | | 33 | | (user function) | | | 34 | | exec +-----------<------------------------------------+ | 35 | +<----------+ | | | 36 | | | | write cmd out back to pin | | 37 | +---------->+--------->------------------------------------->+ | 38 | | | | was pin updated? | 39 | | | +<-----------------------------------+ 40 | | | | | 41 | | | | | 42 | | | | take vpin data to widget output | 43 | | | | | 44 | | | +----------------------------------->+ 45 | | | | | 46 | + + + + 47 | 48 | ===================================================================================================================== 49 | Additional info about blynk you can find by examining such resources: 50 | 51 | Downloads, docs, tutorials: https://blynk.io 52 | Sketch generator: http://examples.blynk.cc 53 | Blynk community: http://community.blynk.cc 54 | Social networks: http://www.fb.com/blynkapp 55 | http://twitter.com/blynk_app 56 | ===================================================================================================================== 57 | """ 58 | 59 | import blynklib 60 | import subprocess 61 | 62 | BLYNK_AUTH = 'YourAuthToken' 63 | 64 | # last command in example - just to show error handling 65 | # for certain HW can be added specific commands. 'gpio readall' on PI3b for example 66 | ALLOWED_COMMANDS_LIST = ['ls', 'lsusb', 'ip a', 'ip abc'] 67 | 68 | blynk = blynklib.Blynk(BLYNK_AUTH) 69 | 70 | 71 | @blynk.handle_event('write V6') 72 | def write_handler(pin, values): 73 | header = '' 74 | result = '' 75 | delimiter = '{}\n'.format('=' * 30) 76 | if values and values[0] in ALLOWED_COMMANDS_LIST: 77 | cmd_params = values[0].split(' ') 78 | try: 79 | result = subprocess.check_output(cmd_params).decode('utf-8') 80 | header = '[output]\n' 81 | except subprocess.CalledProcessError as exe_err: 82 | header = '[error]\n' 83 | result = 'Return Code: {}\n'.format(exe_err.returncode) 84 | except Exception as g_err: 85 | print("Command caused '{}'".format(g_err)) 86 | elif values and values[0] == 'help': 87 | header = '[help -> allowed commands]\n' 88 | result = '{}\n'.format('\n'.join(ALLOWED_COMMANDS_LIST)) 89 | 90 | # communicate with terminal if help or some allowed command 91 | if result: 92 | output = '{}{}{}{}'.format(header, delimiter, result, delimiter) 93 | print(output) 94 | blynk.virtual_write(pin, output) 95 | blynk.virtual_write(pin, '\n') 96 | 97 | 98 | ########################################################### 99 | # infinite loop that waits for event 100 | ########################################################### 101 | while True: 102 | blynk.run() 103 | -------------------------------------------------------------------------------- /examples/07_tweet_and_logging.py: -------------------------------------------------------------------------------- 1 | """ 2 | [TWEET AND LOGGING EXAMPLE] ========================================================================================= 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Slider" widget, 7 | - bind it to Virtual Pin V7, 8 | - set values range 0-255 9 | - add "Twitter Settings" widget 10 | - connect via this widget to your twitter account. 11 | - Run the App (green triangle in the upper right corner). 12 | - define your auth token for current example and run it 13 | 14 | 15 | This started program will periodically call and execute event handler "write_virtual_pin_handler". 16 | In App you can move slider that send virtual write event to current running example. 17 | Handler will log to console this event and additionally will send tweet message with pin number and it's updated value. 18 | 19 | Schema: 20 | ===================================================================================================================== 21 | +-----------+ +--------------+ +--------------+ +---------+ 22 | | | | | | | | | 23 | | blynk lib | | blynk server | | blynk app | | twitter | 24 | | | | virtual pin | | | | | 25 | | | | | | | | | 26 | +-----+-----+ +------+-------+ +-------+------+ +----+----+ 27 | | | | tweet widget | 28 | | | | auth | 29 | | | +--------------->+ 30 | | | | ok | 31 | | | write event from "Slider" widget +<---------------+ 32 | | | | | 33 | | +<-----------------------------------+ | 34 | | | | | 35 | event handler | write event to hw from server | | | 36 | (user function) | | | | 37 | +-----------<------------------------------------+ | | 38 | | | | | | 39 | | | msg for tweet widget | | | 40 | +--------->-------------------------------------------------------------------------->+ | 41 | | | | real tweet msg | 42 | | | +--------------->+ 43 | | | | | 44 | + + + + 45 | 46 | ===================================================================================================================== 47 | Additional blynk info you can find by examining such resources: 48 | 49 | Downloads, docs, tutorials: https://blynk.io 50 | Sketch generator: http://examples.blynk.cc 51 | Blynk community: http://community.blynk.cc 52 | Social networks: http://www.fb.com/blynkapp 53 | http://twitter.com/blynk_app 54 | ===================================================================================================================== 55 | """ 56 | # uncomment line below if simple printing will be used instead of logging 57 | # from __future__ import print_function 58 | import blynklib 59 | import logging 60 | 61 | # tune console logging 62 | _log = logging.getLogger('BlynkLog') 63 | logFormatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") 64 | consoleHandler = logging.StreamHandler() 65 | consoleHandler.setFormatter(logFormatter) 66 | _log.addHandler(consoleHandler) 67 | _log.setLevel(logging.DEBUG) 68 | 69 | # uncomment line below if simple printing will be used instead of logging 70 | # _log.info = print 71 | 72 | BLYNK_AUTH = 'YourAuthToken' 73 | blynk = blynklib.Blynk(BLYNK_AUTH, log=_log.info) 74 | 75 | # uncomment line below if simple printing will be used instead of logging 76 | # blynk = blynklib.Blynk(BLYNK_AUTH, log=print) 77 | 78 | WRITE_EVENT_PRINT_MSG = "[WRITE_VIRTUAL_PIN_EVENT] Pin: V{} Value: '{}'" 79 | TWEET_MSG = "New value='{}' on VPIN({})" 80 | 81 | 82 | # register handler for all virtual pins (including V7). 83 | # So in one block here we can handle evens for vpins 0-32 (32 - Max allowed VPIN number in lib) 84 | @blynk.handle_event('write V*') 85 | def write_virtual_pin_handler(pin, value): 86 | _log.info(WRITE_EVENT_PRINT_MSG.format(pin, value)) 87 | blynk.tweet(TWEET_MSG.format(pin, value)) 88 | 89 | 90 | ########################################################### 91 | # infinite loop that waits for event 92 | ########################################################### 93 | while True: 94 | blynk.run() 95 | -------------------------------------------------------------------------------- /examples/08_blynk_timer.py: -------------------------------------------------------------------------------- 1 | """ 2 | [BLYNK TIMER EXAMPLE] ============================================================================================ 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "SuperChart" widget 7 | - add two data streams for it ( stream #1 - Virtual Pin 8, stream #2 - Virtual Pin 9) 8 | - set different colors for data stream (ex. red and orange) 9 | - Run the App (green triangle in the upper right corner). 10 | - define your auth token for current example and run it 11 | 12 | 13 | This started program will register two timers that will update Virtual Pins data after defined intervals. 14 | In app you can see on graph both pins data change. Additionally timers will print new pin values info to stdout. 15 | 16 | Schema: 17 | ================================================================================================================= 18 | +-----------+ +--------------+ +--------------+ 19 | | | | | | | 20 | | blynk lib | | blynk server | | blynk app | 21 | | | | virtual pin | | | 22 | | | | | | | 23 | +-----+-----+ +------+-------+ +-------+------+ 24 | | | | 25 | | | | 26 | | | | 27 | | | | 28 | +------------+ | | 29 | | +---------+ | | 30 | | | | update virtual pin 8 value | | 31 | | | | | notify app about vpin 8 update | 32 | | +-------->------------------------------------->+ | 33 | | | +----------------------------------->+ 34 | +----------->------------------------------------->+ | 35 | | +----------------------------------->+ 36 | | update virtual pin 9 value | | 37 | | | notify app about vpin 9 update | 38 | | | | 39 | | | | 40 | | | | 41 | + + + 42 | 43 | ================================================================================================================ 44 | Additional blynk info you can find by examining such resources: 45 | 46 | Downloads, docs, tutorials: https://blynk.io 47 | Sketch generator: http://examples.blynk.cc 48 | Blynk community: http://community.blynk.cc 49 | Social networks: http://www.fb.com/blynkapp 50 | http://twitter.com/blynk_app 51 | ==================================================================================================== 52 | """ 53 | 54 | import blynklib 55 | import blynktimer 56 | import random 57 | 58 | BLYNK_AUTH = 'YourAuthToken' # insert your Auth Token here 59 | blynk = blynklib.Blynk(BLYNK_AUTH) 60 | 61 | # create timers dispatcher instance 62 | timer = blynktimer.Timer() 63 | 64 | WRITE_EVENT_PRINT_MSG = "[WRITE_VIRTUAL_WRITE] Pin: V{} Value: '{}'" 65 | 66 | 67 | # Code below: register two timers for different pins with different intervals 68 | # run_once flag allows to run timers once or periodically 69 | @timer.register(vpin_num=8, interval=4, run_once=False) 70 | @timer.register(vpin_num=9, interval=7, run_once=False) 71 | def write_to_virtual_pin(vpin_num=1): 72 | value = random.randint(0, 20) 73 | print(WRITE_EVENT_PRINT_MSG.format(vpin_num, value)) 74 | blynk.virtual_write(vpin_num, value) 75 | 76 | 77 | while True: 78 | blynk.run() 79 | timer.run() 80 | -------------------------------------------------------------------------------- /examples/09_sync_virtual_pin.py: -------------------------------------------------------------------------------- 1 | """ 2 | [VIRTUAL PIN SYNC EXAMPLE] ========================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add 3 "Button" widgets, 7 | - bind then to Virtual Pins V0, V1, v2 8 | - set mode "SWITCH" for all of them 9 | - Run the App (green triangle in the upper right corner). 10 | - define your auth token for current example and run it 11 | 12 | 13 | This started program will restore on connect write_virtual_pin states and colors. 14 | Buttons states and colors can be modified during script run. 15 | If script was interrupted (KeyboardInterrupt) buttons colors will be changed to red. 16 | During next connect event ( script re-run) previous buttons states and colors will be restored. 17 | 18 | Schema: 19 | ===================================================================================================================== 20 | +-----------+ +--------------+ +--------------+ 21 | | | | | | | 22 | | blynk lib | | blynk server | | blynk app | 23 | | | | virtual pin | | | 24 | | | | | | | 25 | +-----+-----+ +------+-------+ +-------+------+ 26 | connect handler | | | 27 | +--------+ | | 28 | | +------------------------------------>+ | 29 | | | virtual pin sync | | 30 | | +<------------------------------------+----------------------------------->+ 31 | | | virtual pin write stored value | send pin value to app | 32 | | +<------------------------------------+----------------------------------->+ 33 | | | virtual pin apply stored properties | send pin properties to app | 34 | +------->+ | | 35 | write handler | | | 36 | +--------+ +<-----------------------------------+ 37 | | +<------------------------------------+ write event from button | 38 | +>------>+ write event form server | | 39 | | | | 40 | | | | 41 | disconnect | | | 42 | handler | | | 43 | +-------+ | | 44 | | +------------------------------------>+ | 45 | +------>+ set new virtual pin property | | 46 | | | | 47 | | | | 48 | | | | 49 | + + + 50 | 51 | ===================================================================================================================== 52 | Additional info about blynk you can find by examining such resources: 53 | 54 | Downloads, docs, tutorials: https://blynk.io 55 | Sketch generator: http://examples.blynk.cc 56 | Blynk community: http://community.blynk.cc 57 | Social networks: http://www.fb.com/blynkapp 58 | http://twitter.com/blynk_app 59 | ===================================================================================================================== 60 | """ 61 | import logging 62 | import blynklib 63 | 64 | # tune console logging 65 | _log = logging.getLogger('BlynkLog') 66 | logFormatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") 67 | consoleHandler = logging.StreamHandler() 68 | consoleHandler.setFormatter(logFormatter) 69 | _log.addHandler(consoleHandler) 70 | _log.setLevel(logging.DEBUG) 71 | 72 | colors = {'1': '#FFC300', '0': '#CCCCCC', 'OFFLINE': '#FF0000'} 73 | 74 | BLYNK_AUTH = 'YourAuthToken' 75 | blynk = blynklib.Blynk(BLYNK_AUTH, log=_log.info) 76 | 77 | 78 | @blynk.handle_event("connect") 79 | def connect_handler(): 80 | _log.info('SCRIPT_START') 81 | for pin in range(3): 82 | _log.info('Syncing virtual pin {}'.format(pin)) 83 | blynk.virtual_sync(pin) 84 | 85 | # within connect handler after each server send operation forced socket reading is required cause: 86 | # - we are not in script listening state yet 87 | # - without forced reading some portion of blynk server messages can be not delivered to HW 88 | blynk.read_response(timeout=0.5) 89 | 90 | 91 | @blynk.handle_event('write V*') 92 | def write_handler(pin, value): 93 | button_state = value[0] 94 | blynk.set_property(pin, 'color', colors[button_state]) 95 | 96 | 97 | @blynk.handle_event("disconnect") 98 | def connect_handler(): 99 | for pin in range(3): 100 | _log.info("Set 'OFFLINE' color for pin {}".format(pin)) 101 | blynk.set_property(pin, 'color', colors['OFFLINE']) 102 | 103 | 104 | ########################################################### 105 | # infinite loop that waits for event 106 | ########################################################### 107 | try: 108 | while True: 109 | blynk.run() 110 | except KeyboardInterrupt: 111 | blynk.disconnect() 112 | _log.info('SCRIPT WAS INTERRUPTED') 113 | -------------------------------------------------------------------------------- /examples/10_rtc_sync.py: -------------------------------------------------------------------------------- 1 | """ 2 | [RTC EXAMPLE] ======================================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "RTC" widget, 7 | - set required TimeZone, 8 | - Run the App (green triangle in the upper right corner). 9 | - define your auth token for current example and run it 10 | 11 | 12 | This started program on connect will send rtc_sync call to server. 13 | RTC reply will be captured by "internal_rtc" handler 14 | UTC time with required timezone correction will be printed 15 | 16 | Schema: 17 | ===================================================================================================================== 18 | +-----------+ +--------------+ +--------------+ 19 | | | | | | | 20 | | blynk lib | | blynk server | | blynk app | 21 | | | | | | | 22 | | | | | | | 23 | +-----+-----+ +------+-------+ +-------+------+ 24 | | | | 25 | connect handler | | | 26 | +-------+ | | 27 | | | | | 28 | | | rtc sync | | 29 | +------>------------------------------------->+ rtc widget present in app? | 30 | | +----------------------------------->+ 31 | | | | 32 | | | yes rtc widget found | 33 | | rtc with timezone correction +<-----------------------------------+ 34 | internal_rtc | | | 35 | handler +--------<------------------------------------+ | 36 | | | | | 37 | | | | | 38 | +------>+ | | 39 | | | | 40 | | | | 41 | + + + 42 | ===================================================================================================================== 43 | Additional blynk info you can find by examining such resources: 44 | 45 | Downloads, docs, tutorials: https://blynk.io 46 | Sketch generator: http://examples.blynk.cc 47 | Blynk community: http://community.blynk.cc 48 | Social networks: http://www.fb.com/blynkapp 49 | http://twitter.com/blynk_app 50 | ===================================================================================================================== 51 | """ 52 | 53 | import blynklib 54 | from datetime import datetime 55 | 56 | BLYNK_AUTH = 'YourAuthToken' 57 | blynk = blynklib.Blynk(BLYNK_AUTH) 58 | 59 | 60 | @blynk.handle_event("connect") 61 | def connect_handler(): 62 | blynk.internal("rtc", "sync") 63 | print("RTC sync request was sent") 64 | 65 | 66 | @blynk.handle_event('internal_rtc') 67 | def rtc_handler(rtc_data_list): 68 | hr_rtc_value = datetime.utcfromtimestamp(int(rtc_data_list[0])).strftime('%Y-%m-%d %H:%M:%S') 69 | print('Raw RTC value from server: {}'.format(rtc_data_list[0])) 70 | print('Human readable RTC value: {}'.format(hr_rtc_value)) 71 | 72 | 73 | ########################################################### 74 | # infinite loop that waits for event 75 | ########################################################### 76 | while True: 77 | blynk.run() 78 | -------------------------------------------------------------------------------- /examples/11_ssl_socket.py: -------------------------------------------------------------------------------- 1 | """ 2 | [SSL CONNECT/DISCONNECT EVENTS EXAMPLE] ================================================================= 3 | NOTE! 4 | This example works correctly only fo cPython version of library (blynklib.py) 5 | For micropython present limitation that keyword arguments of wrap_socket may be not supported by certain ports 6 | 7 | 8 | Environment prepare: 9 | - define your auth token for current example and run it 10 | 11 | 12 | This started program after successful connect operation will call and execute "connect event handler" 13 | Within handler after short sleep delay blynk disconnect call will be performed that will trigger 14 | "disconnect event handler" execution. 15 | 16 | Schema: 17 | ===================================================================================================== 18 | +-----------+ +--------------+ 19 | | | | | 20 | | blynk lib | | blynk server | 21 | | | | virtual pin | 22 | | | | | 23 | +-----+-----+ +------+-------+ 24 | | | 25 | | connect/authenticate request | 26 | +------------------------------------>+ 27 | connect handler | | 28 | (user function) | connected successfully | 29 | +-----------<------------------------------------+ 30 | | | | 31 | | | disconnect request | 32 | +--------->------------------------------------->+ 33 | | | 34 | | | 35 | disconnect handler | | 36 | (user function) | disconnected successfully | 37 | +-----------<------------------------------------+ 38 | | | | 39 | | | | 40 | +--------->+ | 41 | | reconnect request | 42 | | performed by lib automatically | 43 | +------------------------------------>+ 44 | + + 45 | 46 | 47 | ==================================================================================================== 48 | Additional info about blynk you can find by examining such resources: 49 | 50 | Downloads, docs, tutorials: https://blynk.io 51 | Sketch generator: http://examples.blynk.cc 52 | Blynk community: http://community.blynk.cc 53 | Social networks: http://www.fb.com/blynkapp 54 | http://twitter.com/blynk_app 55 | ===================================================================================================== 56 | """ 57 | 58 | import blynklib 59 | import time 60 | import logging 61 | 62 | # tune console logging 63 | _log = logging.getLogger('BlynkLog') 64 | logFormatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") 65 | consoleHandler = logging.StreamHandler() 66 | consoleHandler.setFormatter(logFormatter) 67 | _log.addHandler(consoleHandler) 68 | _log.setLevel(logging.DEBUG) 69 | 70 | BLYNK_AUTH = 'YourAuthToken' 71 | 72 | blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl_cert='../certificate/blynk-cloud.com.crt', log=_log.info) 73 | 74 | CONNECT_PRINT_MSG = '[CONNECT_EVENT]' 75 | DISCONNECT_PRINT_MSG = '[DISCONNECT_EVENT]' 76 | 77 | 78 | @blynk.handle_event("connect") 79 | def connect_handler(): 80 | print(CONNECT_PRINT_MSG) 81 | print('Sleeping 4 sec in SSL connect handler...') 82 | time.sleep(4) 83 | blynk.disconnect() 84 | 85 | 86 | @blynk.handle_event("disconnect") 87 | def disconnect_handler(): 88 | print(DISCONNECT_PRINT_MSG) 89 | print('Sleeping 3 sec in SSL disconnect handler...') 90 | time.sleep(5) 91 | 92 | 93 | ########################################################### 94 | # infinite loop that waits for event 95 | ########################################################### 96 | while True: 97 | blynk.run() 98 | -------------------------------------------------------------------------------- /examples/12_app_connect_disconnect.py: -------------------------------------------------------------------------------- 1 | """ 2 | [APP CONNECT DISCONNECT EVENTS EXAMPLE] ============================================================ 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - in Project Settings enable flag "Notify devices when APP connected" 7 | - define your auth token for current example and run it 8 | - Run the App (green triangle in the upper right corner). 9 | 10 | This started program will call handlers and print messages for APP_CONNECT or APP_DISCONNECT events. 11 | 12 | Schema: 13 | ==================================================================================================== 14 | +-----------+ +--------------+ +--------------+ 15 | | | | | | | 16 | | blynk lib | | blynk server | | blynk app | 17 | | | | virtual pin | | | 18 | | | | | | | 19 | +-----+-----+ +------+-------+ +-------+------+ 20 | | | app connected or disconnected | 21 | | | from server | 22 | | | 23 | event handler | app connect/disconnect event +<-----------------------------------+ 24 | (user function) | | | 25 | +------------<-----------------------------------+ | 26 | | | | | 27 | | | | | 28 | +--------->+ | | 29 | | | | 30 | | | | 31 | | | | 32 | | | | 33 | | | | 34 | | | | 35 | | | | 36 | | | | 37 | + + + 38 | ==================================================================================================== 39 | Additional blynk info you can find by examining such resources: 40 | 41 | Downloads, docs, tutorials: https://blynk.io 42 | Sketch generator: http://examples.blynk.cc 43 | Blynk community: http://community.blynk.cc 44 | Social networks: http://www.fb.com/blynkapp 45 | http://twitter.com/blynk_app 46 | ==================================================================================================== 47 | """ 48 | 49 | import blynklib 50 | 51 | BLYNK_AUTH = 'YourAuthToken' 52 | 53 | # initialize Blynk 54 | blynk = blynklib.Blynk(BLYNK_AUTH) 55 | 56 | APP_CONNECT_PRINT_MSG = '[APP_CONNECT_EVENT]' 57 | APP_DISCONNECT_PRINT_MSG = '[APP_DISCONNECT_EVENT]' 58 | 59 | 60 | @blynk.handle_event('internal_acon') 61 | def app_connect_handler(*args): 62 | print(APP_CONNECT_PRINT_MSG) 63 | 64 | 65 | @blynk.handle_event('internal_adis') 66 | def app_disconnect_handler(*args): 67 | print(APP_DISCONNECT_PRINT_MSG) 68 | 69 | 70 | ########################################################### 71 | # infinite loop that waits for event 72 | ########################################################### 73 | while True: 74 | blynk.run() 75 | -------------------------------------------------------------------------------- /examples/esp32/01_touch_button.py: -------------------------------------------------------------------------------- 1 | """ 2 | [TOUCH BUTTON (TTP223B sensors) ESP32 TTGO T8 V1.7] ================================================================= 3 | 4 | ESP32 pins schema: 5 | https://images-na.ssl-images-amazon.com/images/I/81%2Bjpo9-D8L._SX522_.jpg 6 | 7 | TTP223B sensor: 8 | - connect VCC sensor line to "+3.3v" board pin 9 | - connect GND sensor line to "ground" board pin 10 | - connect DATA sensor line to board GPIO2 11 | 12 | Environment prepare: 13 | In your Blynk App project: 14 | - add "Value Display" widget, 15 | - bind it to Virtual Pin V10, 16 | - set the read frequency to 5 second. 17 | - Run the App (green triangle in the upper right corner). 18 | - define SSID and WiFi password that will be used by ESP32 board 19 | - optionally change HW GPIO pin that will be used by touch button data line 20 | - define your auth token for current example and run it 21 | 22 | This started program will create system IRQ with callback for "button touched" event. 23 | Callback will store touch attempt number and touch time. 24 | Current program will periodically call and execute event handler "read_virtual_pin_handler". 25 | Handler reads stored last touch num and touch time values and by calling virtual_write 26 | operation updates widget value. In app you can see touch event updated info. Ex "#10 touch at 604705274". 27 | ===================================================================================================================== 28 | Additional info about blynk you can find by examining such resources: 29 | 30 | Downloads, docs, tutorials: https://blynk.io 31 | Sketch generator: http://examples.blynk.cc 32 | Blynk community: http://community.blynk.cc 33 | Social networks: http://www.fb.com/blynkapp 34 | http://twitter.com/blynk_app 35 | ===================================================================================================================== 36 | """ 37 | import blynklib_mp as blynklib 38 | import network 39 | import utime as time 40 | from machine import Pin 41 | 42 | 43 | class Touch: 44 | num = 0 45 | time = 0 46 | 47 | 48 | def callback(pin): 49 | print('Touch event on {}'.format(pin)) 50 | Touch.num += 1 51 | Touch.time = time.time() 52 | 53 | 54 | WIFI_SSID = 'YourWifiSSID' 55 | WIFI_PASS = 'YourWifiPassword' 56 | BLYNK_AUTH = 'YourAuthToken' 57 | GPIO_PIN = 2 58 | 59 | print("Connecting to WiFi network '{}'".format(WIFI_SSID)) 60 | wifi = network.WLAN(network.STA_IF) 61 | wifi.active(True) 62 | wifi.connect(WIFI_SSID, WIFI_PASS) 63 | while not wifi.isconnected(): 64 | time.sleep(1) 65 | print('WiFi connect retry ...') 66 | print('WiFi IP:', wifi.ifconfig()[0]) 67 | 68 | print("Connecting to Blynk server...") 69 | blynk = blynklib.Blynk(BLYNK_AUTH) 70 | 71 | hw_pin = Pin(GPIO_PIN, Pin.IN) 72 | hw_pin.irq(trigger=Pin.IRQ_RISING, handler=callback) 73 | 74 | 75 | @blynk.handle_event('read V10') 76 | def read_virtual_pin_handler(vpin): 77 | print('Read event on vpin {}'.format(vpin)) 78 | blynk.virtual_write(vpin, "#{} touch at {} ".format(Touch.num, Touch.time)) 79 | 80 | 81 | while True: 82 | blynk.run() 83 | -------------------------------------------------------------------------------- /examples/esp32/02_terminal_cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | [TERMINAL WIDGET ESP32 EXAMPLE] ===================================================================================== 3 | 4 | Environment prepare: 5 | In your Blynk App project: 6 | - add "Terminal" widget, 7 | - bind it to Virtual Pin V2, 8 | - add input line option in widget settings 9 | - Run the App (green triangle in the upper right corner). 10 | - define your auth token for current example 11 | - define SSID and WiFi password that will be used by ESP32 board 12 | - run current example 13 | 14 | 15 | This started program will call and execute event handler "write_virtual_pin_handler" if new 16 | event comes from terminal app widget. 17 | In App Terminal widget you can type commands. 18 | 'help' - info about available commnds 19 | 'logo' - prints blynk library ascii logo 20 | 'version' - prints blynk library version 21 | 'sysinfo' - prints board system info 22 | 'ls' - list target board dir. Example: 'ls /lib'. 23 | root dir will be listed if no arguments provided 24 | 25 | For any other terminal inputs will be printed error info that provided command is not supported. 26 | ===================================================================================================================== 27 | Additional info about blynk you can find by examining such resources: 28 | 29 | Downloads, docs, tutorials: https://blynk.io 30 | Sketch generator: http://examples.blynk.cc 31 | Blynk community: http://community.blynk.cc 32 | Social networks: http://www.fb.com/blynkapp 33 | http://twitter.com/blynk_app 34 | ===================================================================================================================== 35 | """ 36 | import blynklib_mp as blynklib 37 | import network 38 | import uos 39 | import utime as time 40 | 41 | WIFI_SSID = 'YourWifiSSID' 42 | WIFI_PASS = 'YourWifiPassword' 43 | BLYNK_AUTH = 'YourAuthToken' 44 | 45 | print("Connecting to WiFi network '{}'".format(WIFI_SSID)) 46 | wifi = network.WLAN(network.STA_IF) 47 | wifi.active(True) 48 | wifi.connect(WIFI_SSID, WIFI_PASS) 49 | while not wifi.isconnected(): 50 | time.sleep(1) 51 | print('WiFi connect retry ...') 52 | print('WiFi IP:', wifi.ifconfig()[0]) 53 | 54 | print("Connecting to Blynk server...") 55 | blynk = blynklib.Blynk(BLYNK_AUTH) 56 | 57 | CMD_LIST = ['logo', 'version', 'sysinfo', 'ls'] 58 | 59 | 60 | @blynk.handle_event('write V2') 61 | def write_handler(pin, values): 62 | if values: 63 | in_args = values[0].split(' ') 64 | cmd = in_args[0] 65 | cmd_args = in_args[1:] 66 | 67 | if cmd == 'help': 68 | output = ' '.join(CMD_LIST) 69 | elif cmd == CMD_LIST[0]: 70 | output = blynklib.LOGO 71 | elif cmd == CMD_LIST[1]: 72 | output = blynklib.__version__ 73 | elif cmd == CMD_LIST[2]: 74 | output = uos.uname() 75 | elif cmd == CMD_LIST[3]: 76 | arg = cmd_args[0] if cmd_args else '' 77 | output = uos.listdir(arg) 78 | else: 79 | output = "[ERR]: Not supported command '{}'".format(values[0]) 80 | 81 | blynk.virtual_write(pin, output) 82 | blynk.virtual_write(pin, '\n') 83 | 84 | 85 | while True: 86 | blynk.run() 87 | -------------------------------------------------------------------------------- /examples/esp32/03_temperature_humidity_dht22.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | [TEMPERATURE/HUMIDITY SENSOR (DHT22) ESP32 TTGO T8 V1.7] ============================================================ 4 | 5 | ESP32 pins schema: 6 | https://images-na.ssl-images-amazon.com/images/I/81%2Bjpo9-D8L._SX522_.jpg 7 | 8 | DHT22 sensor: 9 | - connect VCC sensor line to "+5" board pin 10 | - connect GND sensor line to "ground" board pin 11 | - connect DATA sensor line to board GPIO 4 12 | 13 | DHT22 datasheet that can be helpful: 14 | https://www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.pdf 15 | 16 | Environment prepare: 17 | In your Blynk App project: 18 | - add "Gauge" widget, 19 | - bind it to Virtual Pin V3 20 | - set name "Temperature" 21 | - set label /pin.##/°C 22 | - set update time=15 sec and assign 0-100 values range to it 23 | 24 | - add "Gauge" widget, 25 | - bind it to Virtual Pin V4 26 | - set name "Humidity" 27 | - set label /pin.##/ % 28 | - set update time=15 sec and assign 0-100 values range to it 29 | 30 | 31 | - Run the App (green triangle in the upper right corner). 32 | - define SSID and WiFi password that will be used by ESP32 board 33 | - optionally change HW GPIO pin that will be used by touch button data line 34 | - define your auth token for current example and run it 35 | 36 | This started program will periodically call and execute event handler "read_virtual_pin_handler". 37 | that will try to get data from sensor and write them to related virtual pins. 38 | Within App widget values will be updated each 15 sec. 39 | If during cycle there was error in get data operation - widget colors will be changed to grey (aka disabled state) 40 | and restored on next successful read sensor data operation. 41 | ===================================================================================================================== 42 | Additional info about blynk you can find by examining such resources: 43 | 44 | Downloads, docs, tutorials: https://blynk.io 45 | Sketch generator: http://examples.blynk.cc 46 | Blynk community: http://community.blynk.cc 47 | Social networks: http://www.fb.com/blynkapp 48 | http://twitter.com/blynk_app 49 | ===================================================================================================================== 50 | """ 51 | import blynklib_mp as blynklib 52 | import network 53 | import utime as time 54 | from machine import Pin 55 | 56 | # DHT driver already included into Micropython software as core module 57 | # so we just import it 58 | import dht 59 | 60 | WIFI_SSID = 'YourWifiSSID' 61 | WIFI_PASS = 'YourWifiPassword' 62 | BLYNK_AUTH = 'YourAuthToken' 63 | GPIO_DHT22_PIN = 4 64 | 65 | print("Connecting to WiFi network '{}'".format(WIFI_SSID)) 66 | wifi = network.WLAN(network.STA_IF) 67 | wifi.active(True) 68 | wifi.connect(WIFI_SSID, WIFI_PASS) 69 | while not wifi.isconnected(): 70 | time.sleep(1) 71 | print('WiFi connect retry ...') 72 | print('WiFi IP:', wifi.ifconfig()[0]) 73 | 74 | print("Connecting to Blynk server...") 75 | blynk = blynklib.Blynk(BLYNK_AUTH) 76 | 77 | T_COLOR = '#f5b041' 78 | H_COLOR = '#85c1e9' 79 | ERR_COLOR = '#444444' 80 | 81 | T_VPIN = 3 82 | H_VPIN = 4 83 | 84 | dht22 = dht.DHT22(Pin(4, Pin.IN, Pin.PULL_UP)) 85 | 86 | 87 | @blynk.handle_event('read V{}'.format(T_VPIN)) 88 | def read_handler(vpin): 89 | temperature = 0.0 90 | humidity = 0.0 91 | 92 | # read sensor data 93 | try: 94 | dht22.measure() 95 | temperature = dht22.temperature() 96 | humidity = dht22.humidity() 97 | except OSError as o_err: 98 | print("Unable to get DHT22 sensor data: '{}'".format(o_err)) 99 | 100 | # change widget values and colors according read results 101 | if temperature != 0.0 and humidity != 0.0: 102 | blynk.set_property(T_VPIN, 'color', T_COLOR) 103 | blynk.set_property(H_VPIN, 'color', H_COLOR) 104 | blynk.virtual_write(T_VPIN, temperature) 105 | blynk.virtual_write(H_VPIN, humidity) 106 | else: 107 | # show widgets aka 'disabled' that mean we had errors during read sensor operation 108 | blynk.set_property(T_VPIN, 'color', ERR_COLOR) 109 | blynk.set_property(H_VPIN, 'color', ERR_COLOR) 110 | 111 | 112 | while True: 113 | blynk.run() 114 | -------------------------------------------------------------------------------- /examples/esp32/README.md: -------------------------------------------------------------------------------- 1 | # [ESP32 Micropython][esp32] 2 | 3 | ![esp32][esp32-banner] 4 | ## Board preparation 5 | 6 | - download latest micropython [firmware][micropython-download] for your board 7 | - on your host system install python3 8 | - install **[esptool][micropython-esptool]**, **[rshell][micropython-rshell]**, **[ampy][micropython-ampy]** on your host to communicate with esp32 board 9 | ```bash 10 | # Example on Linux 11 | sudo pip3 install esptool 12 | sudo pip3 install rshell 13 | sudo pip3 install adafruit-ampy 14 | ``` 15 | 16 | - connect board via USB. Run command: 17 | ```bash 18 | dmesg | grep tty 19 | ``` 20 | that will help to find ttyUSB connection port.This port will be used later for all communication operations 21 | 22 | - check board flash status. (In this example and below we assume that port=ttyUSB0) 23 | ```bash 24 | esptool.py --port /dev/ttyUSB0 flash_id 25 | ``` 26 | 27 | - erase board flash before new firmware uploading 28 | ```bash 29 | esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash 30 | ``` 31 | 32 | - burn new firmware 33 | ```bash 34 | esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 [your esp32 firmware .bin] 35 | ``` 36 | 37 | ## Board CLI 38 | 39 | - For board CLI access [rshell][micropython-rshell] can be used: 40 | ```bash 41 | rshell --buffer-size=30 -p /dev/ttyUSB0 42 | ``` 43 | after board prompt appears ">" you will have access to some board commands: 44 | ```text 45 | args cat connect echo exit filetype ls repl rsync 46 | boards cd cp edit filesize help mkdir rm shell 47 | ``` 48 | - **[repl][micropython-repl]** command will start micropython interactive shell. 49 | 50 | Also this shell can be used for board soft reboots(Ctrl+D). 51 | 52 | Hard reboots can be done by board "RST" button. 53 | 54 | 55 | ## Board files/directories operations 56 | 57 | - **[ampy][micropython-ampy]** utility can be used for storing/deleting files, 58 | directories creation/removing and scripts run 59 | ```bash 60 | export AMPY_PORT=/dev/ttyUSB0 61 | ampy mkdir /lib 62 | ampy put blynklib_mp.py /lib/blynklib_mp.py 63 | ampy put test.py test.py 64 | ampy run test.py 65 | ``` 66 | 67 | 68 | ## Micropython libraries compiltation 69 | 70 | Micropython provides ability to compile source code into **.mpy** frozen module file. 71 | Main advantage of this that **.mpy** files will consume less RAM compared 72 | to raw Python .py source files 73 | 74 | For **.mpy** file compilation you need: 75 | - get **[mpy-cross][micropython-mpy-cross]** tool 76 | ```bash 77 | git clone https://github.com/micropython/micropython.git 78 | cd micropython/mpy-cross 79 | make 80 | ``` 81 | - optionally get heapsize of your board via **[repl][micropython-repl]**. 82 | ```text 83 | >>> import gc 84 | >>> gc.collect() 85 | >>> gc.mem_free() 86 | 2812256 87 | ``` 88 | - compile source code and get .mpy file 89 | ```bash 90 | ./mpy-cross -X heapsize=2812256 blynklib_mp.py 91 | ``` 92 | - .mpy files in the same manner can be placed to board libs with **[ampy][micropython-ampy]** 93 | as usual .py file 94 | ```bash 95 | ampy put blynklib_mp.mpy /lib/blynklib_mp.mpy 96 | ``` 97 | and then imported within user scripts as usual .py lib 98 | ```python 99 | # start of user script 100 | import blynklib_mp 101 | ``` 102 | 103 | ## Wifi Connection 104 | Micropython allows to use core ***network*** module for WiFi connection setup. 105 | 106 | In script just place: 107 | ```python 108 | import network 109 | 110 | WIFI_SSID = 'YourWifiSSID' 111 | WIFI_PASS = 'YourWifiPassword' 112 | 113 | wifi = network.WLAN(network.STA_IF) 114 | wifi.active(True) 115 | wifi.connect(WIFI_SSID, WIFI_PASS) 116 | 117 | # check if board connected 118 | connect_status = wifi.isconnected() 119 | 120 | ``` 121 | 122 | 123 | [esp32]: http://esp32.net 124 | [esp32-banner]: https://i.ytimg.com/vi/30f1n9h3aSc/maxresdefault.jpg 125 | [micropython-download]: http://micropython.org/download#esp32 126 | [micropython-repl]: https://docs.micropython.org/en/latest/esp8266/tutorial/repl.html 127 | [micropython-ampy]: https://github.com/pycampers/ampy 128 | [micropython-rshell]: https://github.com/dhylands/rshell 129 | [micropython-esptool]: https://github.com/espressif/esptool 130 | [micropython-mpy-cross]: https://pypi.org/project/mpy-cross/ -------------------------------------------------------------------------------- /examples/esp8266/01_potentiometer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | [Slide Potentiometer NodeMcu ESP8266] =============================================================================== 4 | 5 | ESP8266 pinout: 6 | https://i.imgur.com/HgVTxCh.png 7 | 8 | Slide Potentiometer: 9 | - connect VCC sensor line to "+3.3v" board pin 10 | - connect GND sensor line to "ground" board pin 11 | - connect DATA sensor line to board A0 pib (ADC - analog to digital conversion on dedicated pin) 12 | 13 | Environment prepare: 14 | In your Blynk App project: 15 | - add "Gauge" widget, 16 | - bind it to Virtual Pin V1 17 | - set name "Potentiometer" 18 | - set label /pin.##/ 19 | - set update time=1 sec and assign 0-1024 values range to it 20 | 21 | 22 | - Run the App (green triangle in the upper right corner). 23 | - define SSID and WiFi password that will be used by ESP32 board 24 | - define your auth token for current example and run it 25 | 26 | This started program will periodically call and execute event handler "read_virtual_pin_handler". 27 | that will try to get data from potentiometer and write it to related virtual pin. 28 | Within App widget gauge value will be updated each 1 sec. 29 | ===================================================================================================================== 30 | Additional info about blynk you can find by examining such resources: 31 | 32 | Downloads, docs, tutorials: http://www.blynk.cc 33 | Sketch generator: http://examples.blynk.cc 34 | Blynk community: http://community.blynk.cc 35 | Social networks: http://www.fb.com/blynkapp 36 | http://twitter.com/blynk_app 37 | ===================================================================================================================== 38 | """ 39 | import blynklib_mp as blynklib 40 | import network 41 | import utime as time 42 | import machine 43 | 44 | WIFI_SSID = 'YourWifiSSID' 45 | WIFI_PASS = 'YourWifiPassword' 46 | BLYNK_AUTH = 'YourAuthToken' 47 | 48 | print("Connecting to WiFi network '{}'".format(WIFI_SSID)) 49 | wifi = network.WLAN(network.STA_IF) 50 | wifi.active(True) 51 | wifi.connect(WIFI_SSID, WIFI_PASS) 52 | while not wifi.isconnected(): 53 | time.sleep(1) 54 | print('WiFi connect retry ...') 55 | print('WiFi IP:', wifi.ifconfig()[0]) 56 | 57 | print("Connecting to Blynk server...") 58 | blynk = blynklib.Blynk(BLYNK_AUTH) 59 | adc = machine.ADC(0) 60 | 61 | 62 | @blynk.handle_event('read V1') 63 | def read_handler(vpin): 64 | p_value = adc.read() 65 | print('Current potentiometer value={}'.format(p_value)) 66 | blynk.virtual_write(vpin, p_value) 67 | 68 | 69 | while True: 70 | blynk.run() 71 | -------------------------------------------------------------------------------- /examples/esp8266/README.md: -------------------------------------------------------------------------------- 1 | # [ESP8266 Micropython][esp8266] 2 | 3 | ![esp8266][esp8266-banner] 4 | ## Board firmware installation 5 | 6 | - download latest micropython [esp8266 firmware][micropython-download] for your board 7 | - on your host system install python3 8 | - install **[esptool][micropython-esptool]**, **[rshell][micropython-rshell]**, **[ampy][micropython-ampy]** on your host to communicate with esp32 board 9 | ```bash 10 | # Example on Linux 11 | sudo pip3 install esptool 12 | sudo pip3 install rshell 13 | sudo pip3 install adafruit-ampy 14 | ``` 15 | 16 | - connect board via USB. Run command: 17 | ```bash 18 | dmesg | grep tty 19 | ``` 20 | that will help to find ttyUSB connection port.This port will be used later for all communication operations 21 | 22 | - check board flash status. (In this example and below we assume that port=ttyUSB0) 23 | ```bash 24 | esptool.py --port /dev/ttyUSB0 flash_id 25 | ``` 26 | 27 | - erase board flash before new firmware uploading 28 | ```bash 29 | esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash 30 | ``` 31 | 32 | - burn new firmware 33 | ```bash 34 | esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 [your esp32 firmware .bin] 35 | ``` 36 | 37 | ## Board CLI 38 | 39 | - For board CLI access [rshell][micropython-rshell] can be used: 40 | ```bash 41 | rshell --buffer-size=30 -p /dev/ttyUSB0 42 | ``` 43 | after board prompt appears ">" you will have access to some board commands: 44 | ```text 45 | args cat connect echo exit filetype ls repl rsync 46 | boards cd cp edit filesize help mkdir rm shell 47 | ``` 48 | - **[repl][micropython-repl]** command will start micropython interactive shell. 49 | 50 | Also this shell can be used for board soft reboots(Ctrl+D). 51 | 52 | Hard reboots can be done by board "RST" button. 53 | 54 | 55 | ## Board files/directories operations 56 | 57 | - **[ampy][micropython-ampy]** utility can be used for storing/deleting files, 58 | directories creation/removing and scripts run 59 | ```bash 60 | export AMPY_PORT=/dev/ttyUSB0 61 | ampy mkdir /lib 62 | ampy put blynklib_mp.py /lib/blynklib_mp.py 63 | ampy put test.py test.py 64 | ampy run test.py 65 | ``` 66 | 67 | 68 | ## Libraries importing under ESP8266 Micropython 69 | 70 | The firmware including the MicroPython subsystem is stored in the onboard flash. 71 | The remaining capacity is available for use. For reasons connected with the physical architecture 72 | of the flash memory part of this capacity may be inaccessible as a filesystem. In such cases this space 73 | may be employed by incorporating user modules into a firmware build which is then flashed to the device. 74 | 75 | There are two ways to achieve this: 76 | - frozen modules 77 | - frozen bytecode. 78 | 79 | 80 | 81 | ### Frozen module inside firmware 82 | Frozen modules store the Python source with the firmware. 83 | 84 | For custom esp8266 firmware build creation: 85 | - install docker to your host system. Steps how to do it under different OS described [here][docker-install] 86 | - clone with git esp8266 docker repository. Virtual environment will be needed for firmware build operation. 87 | ```bash 88 | git clone https://github.com/enqack/docker-esp8266-micropython.git 89 | cd ./docker-esp8266-micropython 90 | ``` 91 | - place your library module to **docker-esp8266-micropython** root 92 | - modify Dockerfile. You need place your library module to esp8266 frozen modules directory. 93 | Thus **Copy** instruction in Dockerfile should be placed after **'RUN apt-get update'** and before **'USER micropython'** 94 | ```text 95 | RUN apt-get update ... 96 | ... 97 | COPY blynklib_mp.py /micropython/ports/esp8266/modules/blynklib_mp.py 98 | USER micropython 99 | ... 100 | ``` 101 | - follow **[this][esp8266-build-docker]** instructions to build and copy custom esp8266 firmware. 102 | 103 | Build process can take some time ~ 15-40 minutes. 104 | 105 | - after firmware created and copied locally - you can try to burn it with **esptool** to your ESP8266 board. 106 | - connect to board CLI with **rshell** and test **blynklib_mp** availability within **repl** 107 | ```python 108 | import blynklib_mp 109 | print(blynklib_mp.LOGO) 110 | ``` 111 | 112 | 113 | ### Frozen bytecode 114 | Frozen bytecode approach uses the cross compiler to convert the source to bytecode which is then stored with the firmware. 115 | 116 | Examine [this document][blynk-esp32-readme] to get more details how to compile *.py files into *.mpy bytecode 117 | 118 | After *.mpy files can be placed to **/lib** directory of esp8266 board with **ampy** tool. Libraries *.mpy can be simply imported 119 | in the same manner as standard *.py library 120 | ```python 121 | import blynklib_mp 122 | ``` 123 | 124 | ***Note!!*** During custom firmware creation your libraries will be converted and adopted to esp8266 environment 125 | automatically. So you can create custom build and then just copy *.mpy files from docker system to local 126 | ```bash 127 | docker cp micropython:/micropython/ports/esp8266/build/frozen_mpy/blynklib_mp.mpy blynklib_mp.mpy 128 | ``` 129 | 130 | 131 | ## Wifi Connection 132 | Micropython allows to use core ***network*** module for WiFi connection setup. 133 | 134 | In script just place: 135 | ```python 136 | import network 137 | 138 | WIFI_SSID = 'YourWifiSSID' 139 | WIFI_PASS = 'YourWifiPassword' 140 | 141 | wifi = network.WLAN(network.STA_IF) 142 | wifi.active(True) 143 | wifi.connect(WIFI_SSID, WIFI_PASS) 144 | 145 | # check if board connected 146 | connect_status = wifi.isconnected() 147 | ``` 148 | 149 | 150 | [esp8266]: https://en.wikipedia.org/wiki/ESP8266 151 | [esp8266-banner]: http://arduino.ua/products_pictures/large_AOC400-1.jpg 152 | [micropython-download]: http://micropython.org/download#esp8266 153 | [micropython-repl]: https://docs.micropython.org/en/latest/esp8266/tutorial/repl.html 154 | [micropython-ampy]: https://github.com/pycampers/ampy 155 | [micropython-rshell]: https://github.com/dhylands/rshell 156 | [micropython-esptool]: https://github.com/espressif/esptool 157 | [micropython-mpy-cross]: https://pypi.org/project/mpy-cross/ 158 | [esp8266-build-docker]: https://github.com/enqack/docker-esp8266-micropython 159 | [docker-install]: https://docs.docker.com/install/linux/docker-ce/ubuntu/ 160 | [blynk-esp32-readme]: https://github.com/blynkkk/lib-python/blob/master/examples/esp32/README.md -------------------------------------------------------------------------------- /examples/raspberry/01_weather_station_pi3b.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | [WEATHER STATION EXAMPLE (DHT22; BMP180 sensors) PI3b+] ============================================================= 4 | 5 | PI3b+ pins schema: 6 | https://www.theengineeringprojects.com/wp-content/uploads/2018/07/introduction-to-raspberry-pi-3-b-plus-2.png 7 | 8 | DHT22 sensor: 9 | - connect VCC sensor line to "+5v" board pin 10 | - connect GND sensor line to "ground" board pin 11 | - connect DATA sensor line to board GPIO17 12 | - pip install Adafruit-DHT 13 | 14 | DHT22 datasheet that can be helpful: 15 | https://www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.pdf 16 | 17 | 18 | BMP180 sensor: 19 | - connect VIN sensor line to "+3.3v" board pin 20 | - connect GND sensor line to "ground" board pin 21 | - connect SCL sensor line to board GPIO3 (SCL) 22 | - connect SDA sensor line to board GPIO2 (SDA) 23 | 24 | - ensure that i2c-tools package installed in your system 'aptitude show i2c-tools' 25 | - if not installed run 'sudo apt-get install i2c-tools' 26 | - ensure that i2c interface was enabled in your system 27 | 'sudo raspi-config' -> Interfacing Options -> I2C enable -> Confirm -> then board reboot 28 | - run 'sudo i2cdetect -y 1' that will detect i2c devices. 29 | If sensor was connected correctly within output table you will see "77" 30 | - to install BPM python lib you will need get BMP180 archived project and install lib from local files: 31 | git clone https://github.com/adafruit/Adafruit_Python_BMP 32 | cd Adafruit_Python_BMP 33 | sudo python setup.py install 34 | 35 | BMP180 datasheet that can be helpful: 36 | https://arduino.ua/docs/BMP085_DataSheet_Rev.1.0_01July2008.pdf 37 | 38 | 39 | Environment prepare: 40 | In your Blynk App project: 41 | - add "Notification" widget 42 | 43 | - add "Gauge" widget, 44 | - bind it to Virtual Pin V7 45 | - set name "Temperature" 46 | - set label /pin.##/°C 47 | - set update time=10 sec and assign 0-100 values range to it 48 | 49 | - add "Gauge" widget, 50 | - bind it to Virtual Pin V8 51 | - set name "Humidity" 52 | - set label /pin.##/ % 53 | - set update time=10 sec and assign 0-100 values range to it 54 | 55 | - add "Gauge" widget, 56 | - bind it to Virtual Pin V9 57 | - set name "Pressure" 58 | - set label /pin/ Hg 59 | - set update time=10 sec and assign 0-800 values range to it 60 | 61 | - add "Gauge" widget, 62 | - bind it to Virtual Pin V10 63 | - set name "Altitude" 64 | - set label /pin.##/ m 65 | - set update time=10 sec and assign 0-1000 values range to it 66 | 67 | - Run the App (green triangle in the upper right corner). 68 | - define your auth token for current example 69 | - optionally change default critical temperature value to your own 70 | - run current example 71 | 72 | This started program will periodically call and execute event handler "read_virtual_pin_handler". 73 | that will try to get data from sensors and write them to related virtual pins. 74 | Within App widget values will be updated each 10 sec. 75 | If during cycle there was error in get data operation - widget colors will be changed to grey (aka disabled state) 76 | and restored on next successful read sensor data operation. Additionally if temperature will be less than 77 | critical value (default = 20°C) handler will send warning notification to on App. 78 | 79 | Schema for temperature parameter: 80 | ===================================================================================================================== 81 | +-----------+ +--------------+ +--------------+ 82 | | | | | | | 83 | | blynk lib | | blynk server | | blynk app | 84 | | | | virtual pin | | | 85 | | | | | | | 86 | +-----------+ +--------------+ +--------------+ 87 | | | | 88 | | | | 89 | | | widget read frequency = 10 sec | 90 | | +<-----------------------------------+ 91 | | | | 92 | | | | 93 | | | send virtual pin value to widget | 94 | | | | 95 | event handler | read event to hw from server +----------------------------------->+ 96 | (user function) | | | 97 | +-----------<------------------------------------+ | 98 | | | | | 99 | | | write temperature value to pin | | 100 | +--------->------------------------------------->+ next widget read event | 101 | | optionally: | | 102 | | - set widget property +<-----------------------------------+ 103 | | - send notification | | 104 | | | | 105 | + + + 106 | ===================================================================================================================== 107 | Additional info about blynk you can find by examining such resources: 108 | 109 | Downloads, docs, tutorials: https://blynk.io 110 | Sketch generator: http://examples.blynk.cc 111 | Blynk community: http://community.blynk.cc 112 | Social networks: http://www.fb.com/blynkapp 113 | http://twitter.com/blynk_app 114 | ===================================================================================================================== 115 | """ 116 | 117 | import blynklib 118 | import Adafruit_DHT 119 | import Adafruit_BMP.BMP085 as BMP085 120 | 121 | 122 | class Counter: 123 | cycle = 0 124 | 125 | 126 | BLYNK_AUTH = 'YourAuthToken' 127 | blynk = blynklib.Blynk(BLYNK_AUTH, heartbeat=15, max_msg_buffer=512) 128 | 129 | T_CRI_VALUE = 20.0 # 20.0°C 130 | T_CRI_MSG = 'Low TEMP!!!' 131 | T_CRI_COLOR = '#c0392b' 132 | 133 | T_COLOR = '#f5b041' 134 | H_COLOR = '#85c1e9' 135 | P_COLOR = '#a2d9ce' 136 | A_COLOR = '#58d68d' 137 | ERR_COLOR = '#444444' 138 | 139 | T_VPIN = 7 140 | H_VPIN = 8 141 | P_VPIN = 9 142 | A_VPIN = 10 143 | GPIO_DHT22_PIN = 17 144 | 145 | 146 | @blynk.handle_event('read V{}'.format(T_VPIN)) 147 | def read_handler(vpin): 148 | # DHT22 149 | dht22_sensor = Adafruit_DHT.DHT22 # possible sensor modifications .DHT11 .DHT22 .AM2302. Also DHT21 === DHT22 150 | humidity, temperature = Adafruit_DHT.read_retry(dht22_sensor, GPIO_DHT22_PIN, retries=5, delay_seconds=1) 151 | Counter.cycle += 1 152 | # check that values are not False (mean not None) 153 | if all([humidity, temperature]): 154 | print('temperature={} humidity={}'.format(temperature, humidity)) 155 | if temperature <= T_CRI_VALUE: 156 | blynk.set_property(T_VPIN, 'color', T_CRI_COLOR) 157 | # send notifications not each time but once a minute (6*10 sec) 158 | if Counter.cycle % 6 == 0: 159 | blynk.notify(T_CRI_MSG) 160 | Counter.cycle = 0 161 | else: 162 | blynk.set_property(T_VPIN, 'color', T_COLOR) 163 | blynk.set_property(H_VPIN, 'color', H_COLOR) 164 | blynk.virtual_write(T_VPIN, temperature) 165 | blynk.virtual_write(H_VPIN, humidity) 166 | else: 167 | print('[ERROR] reading DHT22 sensor data') 168 | blynk.set_property(T_VPIN, 'color', ERR_COLOR) # show aka 'disabled' that mean we errors on data read 169 | blynk.set_property(H_VPIN, 'color', ERR_COLOR) 170 | 171 | # BMP180 172 | bmp180_sensor = BMP085.BMP085(busnum=1) 173 | pressure = bmp180_sensor.read_pressure() 174 | altitude = bmp180_sensor.read_altitude() 175 | # check that values are not False (mean not None) 176 | if all([pressure, altitude]): 177 | print('pressure={} altitude={}'.format(pressure, altitude)) 178 | blynk.set_property(P_VPIN, 'color', P_COLOR) 179 | blynk.set_property(A_VPIN, 'color', A_COLOR) 180 | blynk.virtual_write(P_VPIN, pressure / 133.322) # mmHg 1mmHg = 133.322 Pa 181 | blynk.virtual_write(A_VPIN, altitude) 182 | else: 183 | print('[ERROR] reading BMP180 sensor data') 184 | blynk.set_property(P_VPIN, 'color', ERR_COLOR) # show aka 'disabled' that mean we errors on data read 185 | blynk.set_property(A_VPIN, 'color', ERR_COLOR) 186 | 187 | 188 | ########################################################### 189 | # infinite loop that waits for event 190 | ########################################################### 191 | while True: 192 | blynk.run() 193 | -------------------------------------------------------------------------------- /examples/raspberry/README.md: -------------------------------------------------------------------------------- 1 | # [Raspberry Pi][raspberry] 2 | A small and affordable computer that you can use to learn programming 3 | 4 | ![Blynk Banner][raspberry-banner] 5 | 6 | ## Board preparation 7 | 8 | - Micro Sd card will be needed with capacity >= 8 Gb (for installation without GUI 4Gb card can be used) 9 | - Download latest **[raspbian os][raspbian]** (debian based os for raspberry devices) 10 | - Examine these instructions **[windows setup][install-windows]**, **[linux setup][install-linux]**, 11 | **[headless-setup][install-headless]**, **[remote vnc][install-vnc]**, **[wifi][install-wifi]**. 12 | 13 | - For GPIO interface communication **[wiring Pi][wiring-pi]** should be installed 14 | This will allow you to communicate with GPIO pins directly via board CLI. 15 | 16 | For example: 17 | ```bash 18 | # prints pin diagram appropriate to your Pi 19 | gpio readall 20 | ``` 21 | - install ***rpi.gpio*** module to communicate with GPIO pins from Python scripts 22 | ```bash 23 | sudo apt-get update 24 | sudo apt-get -y install python-rpi.gpio 25 | 26 | # sudo apt-get -y install python3-rpi.gpio # for Python3 27 | ``` 28 | ## Security 29 | 30 | Protect your device and to avoid situations when it can be used without your permission. 31 | 32 | Read this [guide][raspberry-security] to understand how to impove rasberry Pi security. 33 | 34 | 35 | 36 | 37 | [raspberry]: https://www.raspberrypi.org/ 38 | [raspberry-banner]: https://www.raspberrypi.org/app/uploads/2018/03/770A5842-1612x1080.jpg 39 | [raspbian]: https://www.raspberrypi.org/downloads/raspbian/ 40 | [install-windows]: https://howtoraspberrypi.com/create-raspbian-sd-card-raspberry-pi-windows/ 41 | [install-headless]: https://howtoraspberrypi.com/how-to-raspberry-pi-headless-setup/ 42 | [install-linux]: https://howtoraspberrypi.com/create-sd-card-raspberry-pi-command-line-linux/ 43 | [install-vnc]:https://howtoraspberrypi.com/raspberry-pi-vnc/ 44 | [install-wifi]: https://howtoraspberrypi.com/connect-wifi-raspberry-pi-3-others/ 45 | [wiring-pi]: http://wiringpi.com/download-and-install/ 46 | [raspberry-security]: https://www.raspberrypi.org/documentation/configuration/security.md -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [metadata] 5 | description-file = README.md 6 | 7 | [tool:pytest] 8 | addopts = --verbose 9 | python_files = test/*.py -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name='blynklib', 8 | version='0.2.6', 9 | description='Blynk Python/Micropython library', 10 | long_description=long_description, 11 | long_description_content_type="text/markdown", 12 | url='https://github.com/blynkkk/lib-python', 13 | license='MIT', 14 | author='Anton Morozenko', 15 | author_email='antoha.ua@gmail.com', 16 | setup_requires=['pytest-runner', ], 17 | tests_require=['pytest', 'pytest-mock>=1.11.2', ], 18 | py_modules=['blynklib', 'blynktimer', 'blynklib_mp'], 19 | classifiers=[ 20 | "Programming Language :: Python :: 2.7", 21 | "Programming Language :: Python :: 3", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /test/test_blynk_connection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | import time 4 | import pytest 5 | import socket 6 | from blynklib import Connection, BlynkError, RedirectError 7 | 8 | 9 | class TestBlynkConnection: 10 | @pytest.fixture 11 | def cb(self): 12 | connection = Connection('1234', log=print) 13 | yield connection 14 | 15 | def test_send(self, cb, mocker): 16 | cb._socket = socket.socket() 17 | mocker.patch('socket.socket.send', return_value=5) 18 | result = cb.send('1234') 19 | assert result == 5 20 | 21 | def test_send_ioerror(self, cb, mocker): 22 | cb._socket = socket.socket() 23 | mocker.patch('socket.socket.send', side_effect=IOError('IO')) 24 | result = cb.send('1234') 25 | assert result is None 26 | 27 | def test_send_oserror(self, cb, mocker): 28 | cb._socket = socket.socket() 29 | mocker.patch('socket.socket.send', side_effect=OSError('OS')) 30 | result = cb.send('1234') 31 | assert result is None 32 | 33 | def test_send_socket_timeout(self, cb, mocker): 34 | cb._socket = socket.socket() 35 | mocker.patch('socket.socket.send', side_effect=socket.timeout()) 36 | result = cb.send('1234') 37 | assert result is None 38 | 39 | def test_send_error_retry_count(self, cb, mocker): 40 | cb._socket = socket.socket() 41 | mocker.patch('socket.socket.send', side_effect=OSError('OS')) 42 | mocker.spy(time, 'sleep') 43 | cb.send('1234') 44 | assert cb._socket.send.call_count == 3 45 | 46 | def test_receive(self, cb, mocker): 47 | cb._socket = socket.socket() 48 | mocker.patch('socket.socket.recv', return_value=b'12345') 49 | result = cb.receive(10, 1) 50 | assert result == b'12345' 51 | 52 | def test_receive_timeout(self, cb, mocker): 53 | cb._socket = socket.socket() 54 | mocker.patch('socket.socket.recv', side_effect=OSError('timed out')) 55 | result = cb.receive(10, 1) 56 | assert result == b'' 57 | 58 | def test_receive_timeout_2(self, cb, mocker): 59 | cb._socket = socket.socket() 60 | mocker.patch('socket.socket.recv', side_effect=socket.timeout('timed out')) 61 | result = cb.receive(10, 1) 62 | assert result == b'' 63 | 64 | def test_receive_eagain(self, cb, mocker): 65 | cb._socket = socket.socket() 66 | mocker.patch('socket.socket.recv', side_effect=IOError('[Errno 11]')) 67 | result = cb.receive(10, 1) 68 | assert result == b'' 69 | 70 | def test_receive_etimeout(self, cb, mocker): 71 | cb._socket = socket.socket() 72 | mocker.patch('socket.socket.recv', side_effect=OSError('[Errno 60]')) 73 | result = cb.receive(10, 1) 74 | assert result == b'' 75 | 76 | def test_receive_raise_other_oserror(self, cb, mocker): 77 | cb._socket = socket.socket() 78 | mocker.patch('socket.socket.recv', side_effect=OSError('[Errno 13]')) 79 | with pytest.raises(OSError) as os_err: 80 | cb.receive(10, 1) 81 | assert '[Errno 13]' in str(os_err.value) 82 | 83 | def test_is_server_alive_negative(self, cb): 84 | result = cb.is_server_alive() 85 | assert result is False 86 | 87 | def test_is_server_alive_positive_ping(self, cb, mocker): 88 | cb._last_rcv_time = int(time.time() * 1000) 89 | mocker.patch.object(cb, 'send', return_value=None) 90 | result = cb.is_server_alive() 91 | assert result is True 92 | 93 | def test_is_server_alive_positive_no_ping_1(self, cb): 94 | cb._last_rcv_time = int(time.time() * 1000) 95 | cb._last_ping_time = int(time.time() * 1000) 96 | result = cb.is_server_alive() 97 | assert result is True 98 | 99 | def test_is_server_alive_positive_no_ping_2(self, cb): 100 | cb._last_rcv_time = int(time.time() * 1000) 101 | cb._last_send_time = int(time.time() * 1000) 102 | result = cb.is_server_alive() 103 | assert result is True 104 | 105 | def test_get_socket(self, cb, mocker): 106 | mocker.patch('socket.socket') 107 | mocker.patch('socket.getaddrinfo') 108 | cb._get_socket() 109 | assert cb._state == cb.CONNECTING 110 | 111 | def test_get_socket_exception(self, cb, mocker): 112 | mocker.patch('socket.socket') 113 | mocker.patch('socket.getaddrinfo', side_effect=BlynkError('BE')) 114 | with pytest.raises(BlynkError) as b_err: 115 | cb._get_socket() 116 | assert 'Connection with the Blynk server failed: BE' in str(b_err.value) 117 | 118 | def test_authenticate(self, cb, mocker): 119 | mocker.patch.object(cb, 'send', return_value=None) 120 | mocker.patch.object(cb, 'receive', return_value=b'\x00\x00\x02\x00\xc8') 121 | cb._authenticate() 122 | assert cb._state == cb.AUTHENTICATED 123 | 124 | def test_authenticate_invalid_auth_token(self, cb, mocker): 125 | mocker.patch.object(cb, 'send', return_value=None) 126 | mocker.patch.object(cb, 'receive', return_value=b'\x00\x00\x02\x00\x09') 127 | with pytest.raises(BlynkError) as b_err: 128 | cb._authenticate() 129 | assert 'Invalid Auth Token' in str(b_err.value) 130 | 131 | def test_authenticate_redirect_message(self, cb, mocker): 132 | mocker.patch.object(cb, 'send', return_value=None) 133 | mocker.patch.object(cb, 'receive', return_value=b'\x29\x00\x02\x00\x11127.0.0.1\x004444') 134 | with pytest.raises(RedirectError) as r_err: 135 | cb._authenticate() 136 | # pytest exception wraps real exception - so we need access value field first 137 | assert '127.0.0.1' in r_err.value.server 138 | assert '4444' in r_err.value.port 139 | 140 | def test_authenticate_not_ok_status(self, cb, mocker): 141 | mocker.patch.object(cb, 'send', return_value=None) 142 | mocker.patch.object(cb, 'receive', return_value=b'\x00\x00\x02\x00\x19') 143 | with pytest.raises(BlynkError) as b_err: 144 | cb._authenticate() 145 | assert 'Auth stage failed. Status=25' in str(b_err.value) 146 | 147 | def test_authenticate_timeout(self, cb, mocker): 148 | mocker.patch.object(cb, 'send', return_value=None) 149 | mocker.patch.object(cb, 'receive', return_value=None) 150 | with pytest.raises(BlynkError) as b_err: 151 | cb._authenticate() 152 | assert 'Auth stage timeout' in str(b_err.value) 153 | 154 | def test_set_heartbeat_timeout(self, cb, mocker): 155 | mocker.patch.object(cb, 'send', return_value=None) 156 | mocker.patch.object(cb, 'receive', return_value=None) 157 | with pytest.raises(BlynkError) as b_err: 158 | cb._set_heartbeat() 159 | assert 'Heartbeat stage timeout' in str(b_err.value) 160 | 161 | def test_set_heartbeat_error_status(self, cb, mocker): 162 | mocker.patch.object(cb, 'send', return_value=None) 163 | mocker.patch.object(cb, 'receive', return_value=b'\x00\x00\x02\x00\x0e') 164 | with pytest.raises(BlynkError) as b_err: 165 | cb._set_heartbeat() 166 | assert 'Set heartbeat returned code=14' in str(b_err.value) 167 | 168 | def test_set_heartbeat_positive(self, cb, mocker): 169 | mocker.patch.object(cb, 'send', return_value=None) 170 | mocker.patch.object(cb, 'receive', return_value=b'\x00\x00\x02\x00\xc8') 171 | cb._set_heartbeat() 172 | 173 | def test_connected_false(self, cb): 174 | result = cb.connected() 175 | assert result is False 176 | 177 | def test_connected_true(self, cb): 178 | cb._state = cb.AUTHENTICATED 179 | result = cb.connected() 180 | assert result is True 181 | -------------------------------------------------------------------------------- /test/test_blynk_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | import socket 4 | import pytest 5 | import blynklib 6 | 7 | 8 | class TestBlynk: 9 | @pytest.fixture 10 | def bl(self): 11 | blynk = blynklib.Blynk('1234', log=print) 12 | yield blynk 13 | 14 | def test_connect(self, bl, mocker): 15 | mocker.patch.object(bl, 'connected', return_value=False) 16 | mocker.patch.object(bl, '_get_socket', return_value=None) 17 | mocker.patch.object(bl, '_authenticate', return_value=None) 18 | mocker.patch.object(bl, '_set_heartbeat', return_value=None) 19 | mocker.patch.object(bl, 'call_handler', return_value=None) 20 | mocker.patch.object(blynklib, 'ticks_ms', return_value=42) 21 | result = bl.connect() 22 | assert result is True 23 | assert bl._last_rcv_time == 42 24 | 25 | def test_connect_exception(self, bl, mocker): 26 | mocker.patch.object(bl, 'connected', return_value=False) 27 | mocker.patch.object(bl, '_get_socket', return_value=None) 28 | mocker.patch.object(bl, '_authenticate', side_effect=blynklib.BlynkError()) 29 | mocker.patch.object(bl, 'disconnect', return_value=None) 30 | mocker.patch('time.sleep', return_value=None) 31 | mocker.spy(bl, 'disconnect') 32 | result = bl.connect(0.001) 33 | assert result is False 34 | assert bl.disconnect.call_count > 1 35 | 36 | def test_connect_redirect_exception(self, bl, mocker): 37 | mocker.patch.object(bl, 'connected', return_value=False) 38 | mocker.patch.object(bl, '_get_socket', return_value=None) 39 | mocker.patch.object(bl, '_authenticate', side_effect=blynklib.RedirectError('127.0.0.1', 4444)) 40 | mocker.patch.object(bl, 'disconnect', return_value=None) 41 | mocker.patch('time.sleep', return_value=None) 42 | mocker.spy(bl, 'disconnect') 43 | result = bl.connect(0.001) 44 | assert result is False 45 | assert bl.disconnect.call_count > 1 46 | assert bl.server == '127.0.0.1' 47 | assert bl.port == 4444 48 | 49 | def test_connect_timeout(self, bl, mocker): 50 | bl._state = bl.CONNECTING 51 | mocker.patch.object(bl, 'connected', return_value=False) 52 | result = bl.connect(0.001) 53 | assert result is False 54 | 55 | def test_disconnect(self, bl, mocker): 56 | bl._socket = socket.socket() 57 | mocker.patch('time.sleep', return_value=None) 58 | bl.disconnect('123') 59 | 60 | def test_virtual_write(self, bl, mocker): 61 | mocker.patch.object(bl, 'send', return_value=10) 62 | result = bl.virtual_write(20, 'va1', 'val2') 63 | assert result == 10 64 | 65 | def test_virtual_sync(self, bl, mocker): 66 | mocker.patch.object(bl, 'send', return_value=20) 67 | result = bl.virtual_sync(20, 22) 68 | assert result == 20 69 | 70 | def test_email(self, bl, mocker): 71 | mocker.patch.object(bl, 'send', return_value=30) 72 | result = bl.email('1', '2', '3') 73 | assert result == 30 74 | 75 | def test_tweet(self, bl, mocker): 76 | mocker.patch.object(bl, 'send', return_value=40) 77 | result = bl.tweet('123') 78 | assert result == 40 79 | 80 | def test_notify(self, bl, mocker): 81 | mocker.patch.object(bl, 'send', return_value=50) 82 | result = bl.notify('123') 83 | assert result == 50 84 | 85 | def test_set_property(self, bl, mocker): 86 | mocker.patch.object(bl, 'send', return_value=60) 87 | result = bl.set_property(1, '2', '3') 88 | assert result == 60 89 | 90 | def test_internal(self, bl, mocker): 91 | mocker.patch.object(bl, 'send', return_value=70) 92 | result = bl.internal('rtc', 'sync') 93 | assert result == 70 94 | 95 | def test_hadle_event(self, bl): 96 | bl._events = {} 97 | 98 | @bl.handle_event('connect') 99 | def connect_handler(): 100 | pass 101 | 102 | @bl.handle_event('disconnect') 103 | def disconnect_handler(): 104 | pass 105 | 106 | assert 'connect' in bl._events.keys() 107 | assert 'disconnect' in bl._events.keys() 108 | 109 | def test_read_wildcard_event(self, bl): 110 | bl._events = {} 111 | 112 | @bl.handle_event('read v*') 113 | def read_pin_handler(): 114 | pass 115 | 116 | assert 'read v10' in bl._events.keys() 117 | assert len(bl._events.keys()) == bl.VPIN_MAX_NUM + 1 118 | 119 | def test_write_wildcard_event(self, bl): 120 | bl._events = {} 121 | 122 | @bl.handle_event('write v*') 123 | def write_pin_handler(): 124 | pass 125 | 126 | assert 'write v5' in bl._events.keys() 127 | assert len(bl._events.keys()) == bl.VPIN_MAX_NUM + 1 128 | 129 | def test_call_handler(self, bl): 130 | bl._events = {} 131 | 132 | @bl.handle_event('write v2') 133 | def write_pin_handler(arg1, kwarg1=None): 134 | bl._state = 'TEST{}{}'.format(arg1, kwarg1) 135 | 136 | bl.call_handler('write v2', 12, kwarg1='34') 137 | assert bl._state == 'TEST1234' 138 | 139 | def test_process_rsp(self, bl, mocker): 140 | mocker.spy(bl, 'log') 141 | bl.process(bl.MSG_RSP, 100, 200, []) 142 | assert bl.log.call_count == 1 143 | 144 | def test_process_ping(self, bl, mocker): 145 | mocker.patch.object(bl, 'send', return_value=None) 146 | mocker.spy(bl, 'send') 147 | bl.process(bl.MSG_PING, 100, 200, []) 148 | assert bl.send.call_count == 1 149 | 150 | def test_process_internal(self, bl, mocker): 151 | bl._events = {} 152 | 153 | @bl.handle_event('internal_xyz') 154 | def internal_handler(*args): 155 | bl._status = 'INTERNAL TEST {}'.format(*args) 156 | 157 | mocker.patch.object(bl, 'send', return_value=None) 158 | bl.process(bl.MSG_INTERNAL, 100, 20, ['xyz', 'add', 2]) 159 | assert bl._status == "INTERNAL TEST ['add', 2]" 160 | 161 | def test_process_write(self, bl, mocker): 162 | bl._events = {} 163 | 164 | @bl.handle_event('write V4') 165 | def write_handler(pin, *values): 166 | bl._status = 'WRITE TEST{}'.format(*values) 167 | 168 | mocker.patch.object(bl, 'send', return_value=None) 169 | bl.process(bl.MSG_HW, 100, 200, ['vw', 4, 1, 2]) 170 | assert bl._status == 'WRITE TEST[1, 2]' 171 | 172 | def test_process_read(self, bl, mocker): 173 | bl._events = {} 174 | 175 | @bl.handle_event('read V7') 176 | def read_handler(pin): 177 | bl._status = 'READ TEST{}'.format(pin) 178 | 179 | mocker.patch.object(bl, 'send', return_value=None) 180 | bl.process(bl.MSG_HW, 100, 200, ['vr', 7]) 181 | assert bl._status == 'READ TEST7' 182 | -------------------------------------------------------------------------------- /test/test_blynk_protocol.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import pytest 4 | from blynklib import Protocol, BlynkError 5 | 6 | 7 | class TestBlynkProtocol: 8 | @pytest.fixture 9 | def pb(self): 10 | protocol = Protocol() 11 | yield protocol 12 | 13 | def test_get_msg_id(self, pb): 14 | pb._msg_id = 4 15 | msg_id = pb._get_msg_id() 16 | assert msg_id == 5 17 | 18 | def test_get_msg_id_before_loop(self, pb): 19 | pb._msg_id = 0xFFFE 20 | msg_id = pb._get_msg_id() 21 | assert msg_id == 0xFFFF 22 | 23 | def test_get_msg_id_after_loop(self, pb): 24 | pb._msg_id = 0xFFFF 25 | msg_id = pb._get_msg_id() 26 | assert msg_id == 1 27 | 28 | def test_get_msg_id_defined(self, pb): 29 | pb._msg_id = 0xFFFF 30 | msg_id = pb._get_msg_id(msg_id=17) 31 | assert msg_id == 17 32 | 33 | def test_pack_msg(self, pb): 34 | msg_type = 20 35 | args = ['test', 1234, 745, 'abcde'] 36 | result = pb._pack_msg(msg_type, *args) 37 | assert result == b'\x14\x00\x01\x00\x13test\x001234\x00745\x00abcde' 38 | 39 | def test_pack_msg_no_args(self, pb): 40 | msg_type = 15 41 | args = [] 42 | result = pb._pack_msg(msg_type, *args) 43 | assert result == b'\x0f\x00\x01\x00\x00' 44 | 45 | def test_pack_msg_unicode(self, pb): 46 | if sys.version_info[0] == 2: 47 | pytest.skip('Python2 unicode compatibility issue') 48 | 49 | msg_type = 20 50 | args = ['ёж'] 51 | result = pb._pack_msg(msg_type, *args) 52 | assert result == b'\x14\x00\x01\x00\x04\xd1\x91\xd0\xb6' 53 | 54 | def test_parse_response_msg_hw(self, pb): 55 | data = b'\x14\x00\x02\x00\x13test\x001234\x00745\x00abcde' 56 | msg_buffer = 1024 57 | result = pb.parse_response(data, msg_buffer) 58 | assert result == (20, 2, 19, [u'test', u'1234', u'745', u'abcde']) 59 | 60 | def test_parse_response_msg_id_0(self, pb): 61 | data = b'\x14\x00\x00\x00\x13test\x001234\x00745\x00abcde' 62 | msg_buffer = 100 63 | with pytest.raises(BlynkError) as b_err: 64 | pb.parse_response(data, msg_buffer) 65 | assert 'invalid msg_id == 0' == str(b_err.value) 66 | 67 | def test_parse_response_more_data_than_buffer(self, pb): 68 | data = b'\x14\x00\x02\x00\x13test\x001234\x00745\x00abcde' 69 | msg_buffer = 10 70 | with pytest.raises(BlynkError) as b_err: 71 | pb.parse_response(data, msg_buffer) 72 | assert 'Command too long' in str(b_err.value) 73 | 74 | def test_parse_response_msg_ping(self, pb): 75 | data = b'\x06\x00\x04\x00\x13test\x001234\x00745\x00abcde' 76 | msg_buffer = 100 77 | result = pb.parse_response(data, msg_buffer) 78 | assert result == (6, 4, 19, []) 79 | 80 | def test_parse_response_corrupted_data(self, pb): 81 | data = b'\xd1\x91\xd0\xb6' 82 | msg_buffer = 100 83 | with pytest.raises(Exception) as exc: 84 | pb.parse_response(data, msg_buffer) 85 | assert 'Message parse error:' in str(exc.value) 86 | 87 | def test_parse_response_wrong_msg_type(self, pb): 88 | data = b'\x86\x00\x04\x00\x13test\x001234\x00745\x00abcde' 89 | msg_buffer = 100 90 | with pytest.raises(BlynkError) as b_err: 91 | pb.parse_response(data, msg_buffer) 92 | assert "Unknown message type: '134'" in str(b_err.value) 93 | 94 | def test_parse_response_msg_hw_unicode(self, pb): 95 | data = b'\x14\x00\x02\x00\x04\xd1\x91\xd0\xb6' 96 | msg_buffer = 1024 97 | result = pb.parse_response(data, msg_buffer) 98 | assert result == (20, 2, 4, [u'ёж']) 99 | 100 | def test_heartbeat_msg(self, pb): 101 | result = pb.heartbeat_msg(20, 2048) 102 | assert result == b'\x11\x00\x01\x00+ver\x000.2.6\x00buff-in\x002048\x00h-beat\x0020\x00dev\x00python' 103 | 104 | def test_login_msg(self, pb): 105 | result = pb.login_msg('1234') 106 | assert result == b'\x02\x00\x01\x00\x041234' 107 | 108 | def test_ping_msg(self, pb): 109 | result = pb.ping_msg() 110 | assert result == b'\x06\x00\x01\x00\x00' 111 | 112 | def test_response_msg(self, pb): 113 | result = pb.response_msg(202) 114 | assert result == b'\x00\x00\x01\x00\x03202' 115 | 116 | def test_virtual_write_msg(self, pb): 117 | result = pb.virtual_write_msg(127, 'abc', 123) 118 | assert result == b'\x14\x00\x01\x00\x0evw\x00127\x00abc\x00123' 119 | 120 | def test_virtual_sync_msg(self, pb): 121 | result = pb.virtual_sync_msg(1, 24) 122 | assert result == b'\x10\x00\x01\x00\x07vr\x001\x0024' 123 | 124 | def test_email_msg(self, pb): 125 | result = pb.email_msg('a@b.com', 'Test', 'MSG') 126 | assert result == b'\r\x00\x01\x00\x10a@b.com\x00Test\x00MSG' 127 | 128 | def test_tweet_msg(self, pb): 129 | result = pb.tweet_msg('tweet_msg_test') 130 | assert result == b'\x0c\x00\x01\x00\x0etweet_msg_test' 131 | 132 | def test_notify_msg(self, pb): 133 | result = pb.notify_msg('app_msg_test') 134 | assert result == b'\x0e\x00\x01\x00\x0capp_msg_test' 135 | 136 | def test_set_property_msg(self, pb): 137 | result = pb.set_property_msg(10, 'color', '#FF00EE') 138 | assert result == b'\x13\x00\x01\x00\x1010\x00color\x00#FF00EE' 139 | 140 | def test_internal_msg(self, pb): 141 | result = pb.internal_msg('rtc', 'sync') 142 | assert result == b'\x11\x00\x01\x00\x08rtc\x00sync' 143 | --------------------------------------------------------------------------------