10000 Added blynktimer module · markom84/lib-python@d6f8b40 · GitHub
[go: up one dir, main page]

Skip to content

Commit d6f8b40

Browse files
committed
Added blynktimer module
1 parent 2b90be8 commit d6f8b40

File tree

3 files changed

+195
-12
lines changed

3 files changed

+195
-12
lines changed

README.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Full list of supported hardware can be found [here][blynk-hw].
3636
To exclude compatibility issue preferable versions are Python 2.7.9 (or greater) or Python 3.4 (or greater)
3737
If python not present you can download and install it from [here][python-org].
3838

39-
**NOTE:** To run python in "sandbox" you can try **virtualenv** module. Check [here][virtual-env] on how to do it.
39+
**NOTE:** To run python in "sandbox" you can try **virtualenv** module. Check [this document][virtual-env] how to do it.
4040

4141
- If you’re using preferable versions of python mentioned above, then **pip** comes installed with Python by default.
4242
Check pip availability:
@@ -101,12 +101,12 @@ This library supports Python2, Python3, and Micropython.
101101
- You will get Auth Token delivered to your email account.
102102
- Put this Auth Token within your python script to authenticate your device on [public][blynk-server-public] or [local][blynk-server]
103103

104-
```py
104+
```python
105105
BLYNK_AUTH = '<YourAuthToken>' #insert your Auth Token here
106106
```
107107

108108
#### Usage example
109-
```py
109+
```python
110110
import blynklib
111111

112112
BLYNK_AUTH = '<YourAuthToken>' #insert your Auth Token here
@@ -163,7 +163,7 @@ Examples can be found **[here][blynk-py-examples]** Check them all to get famili
163163
##### Raspberry Pi (any):
164164
Read [Raspberry Pi guide](https://github.com/blynkkk/lib-python/tree/master/examples/raspberry) first.
165165

166-
- (01_weather_station_pi3b.py)[https://github.com/blynkkk/lib-python/tree/master/examples/raspberry] Connect DHT22; BMP180 sensors and send data to Blynk app
166+
- [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
167167

168168
##### ESP32
169169
Read [ESP32 guide](https://github.com/blynkkk/lib-python/tree/master/examples/esp32) first.
@@ -197,15 +197,9 @@ Read [this document][esp8266-readme] to get more information.
197197

198198
**Social Media:**
199199

200-
[Facebook](https://www.fb.com/blynkapp)
200+
[Facebook](https://www.fb.com/blynkapp) [Twitter](https://twitter.com/blynk_app) [Youtube](https://www.youtube.com/blynk)
201201

202-
[Twitter](https://twitter.com/blynk_app)
203-
204-
[Youtube](https://www.youtube.com/blynk)
205-
206-
[Instagram](https://www.instagram.com/blynk.iot/)
207-
208-
[LinkedIn](https://www.linkedin.com/company/b-l-y-n-k/)
202+
[Instagram](https://www.instagram.com/blynk.iot/) [LinkedIn](https://www.linkedin.com/company/b-l-y-n-k/)
209203

210204

211205
## Blynk libraries for other platforms
@@ -250,3 +244,4 @@ This project is released under The MIT License (MIT)
250244
[micropython-pkg]: https://github.com/micropython/micropython/wiki/Getting-Started
251245
[virtual-env]: https://virtualenv.pypa.io/en/latest/installation/
252246
[esp8266-readme]: https://github.com/blynkkk/lib-python/blob/master/examples/esp8266/README.md
247+
[blynktimer-doc]: https://github.com/blynkkk/lib-python/blob/master/blynktimer.py

TIMERS.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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(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+
@blynk_timer.register(5)
30+
def your_periodical_function():
31+
print('Hello, Blynkers!')
32+
33+
while True:
34+
blynk_timer.run()
35+
36+
```
37+
38+
##### Advanced usage examples
39+
```python
40+
import time
41+
from blynktimer import Timer
42+
43+
# disable exception raise if all all timers were stopped
44+
blynk_timer = Timer(no_timers_err=False)
45+
46+
47+
# register two timers for single function with different function parameters
48+
@blynk_timer.register(2, 'p1', 'p2', c=1, run_once=True)
49+
@blynk_timer.register(3, 'fp1', 'fp2', run_once=False)
50+
def function1(a, b, c=2):
51+
time.sleep(c)
52+
print('Function params: {} {} {}'.format(a, b, c))
53+
54+
55+
# simple function registration for further stop
56+
@blynk_timer.register(4, run_once=False)
57+
def function2():
58+
print('Function2')
59+
60+
61+
# list available timers
62+
print(blynk_timer.get_timers())
63+
64+
# switch timer state to stopped by timer id
65+
# id = order_num + '_' + function_name
66+
blynk_timer.stop('2_function2')
67+
68+
while True:
69+
intervals = blynk_timer.run()
70+
# print real passed time for timer fired events
71+
# maybe needed for debug
72+
if any(intervals):
73+
print(intervals)
74+
```
75+
76+
To get more accuracy for timers intervals it is possible to decrease library WAIT_SEC parameter. Default value = 0.05 sec
77+
78+
### Blynk App timers
79+
Some Blynk app widgets have timer setting where yoy can define (1,2,5,10 etc) seconds intervals for reading
80+
virtual pin values.
81+
82+
Flow:
83+
- each N seconds Blynk app widget will do Virtual Pin reading operation.
84+
- Blynk Server for App read request will return current pin value
85+
- Additionally Blynk server will fire read virtual pin event and send it to hardware
86+
- If read pin event was registered on hardware certain handler will be executed
87+
88+
[micropython-timer]: https://docs.micropython.org/en/latest/library/machine.Timer.html
89+
[threading-timer]:https://docs.python.org/3/library/threading.html#threading.Timer
90+
[blynktimer]: https://github.com/blynkkk/lib-python/blob/master/blynktimer.py

blynktimer.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) 2019 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+
try:
7+
import utime as time
8+
import uselect as select
9+
except ImportError:
10+
import time
11+
import select
12+
13+
WAIT_SEC = 0.05
14+
MAX_TIMERS = 16
15+
16+
17+
class TimerError(Exception):
18+
pass
19+
20+
21+
class Timer(object):
22+
timers = {}
23+
24+
def __init__(self, no_timers_err=True):
25+
self.no_timers_err = no_timers_err
26+
27+
def _get_func_name(self, obj):
28+
if getattr(obj, '__name__', None) is None:
29+
return self._get_func_name(obj.func)
30+
return obj.__name__
31+
32+
def register(blynk, interval, *args, run_once=False, **kwargs):
33+
class Deco(object):
34+
def __init__(self, func):
35+
self.func = func
36+
if len(Timer.timers.keys()) >= MAX_TIMERS:
37+
raise TimerError('Max allowed timers num={}'.format(MAX_TIMERS))
38+
_timer = _Timer(interval, func, run_once, *args, **kwargs)
39+
Timer.timers['{}_{}'.format(len(Timer.timers.keys()), blynk._get_func_name(func))] = _timer
40+
41+
def __call__(self, *f_args, **f_kwargs):
42+
return self.func(*f_args, **f_kwargs)
43+
44+
return Deco
45+
46+
@staticmethod
47+
def stop(t_id):
48+
timer = Timer.timers.get(t_id, None)
49+
if timer is None:
50+
raise TimerError('Timer id={} not found'.format(t_id))
51+
Timer.timers[t_id].stopped = True
52+
53+
@staticmethod
54+
def is_stopped(t_id):
55+
timer = Timer.timers.get(t_id, None)
56+
if timer is None:
57+
raise TimerError('Timer id={} not found'.format(t_id))
10000 58+
return timer.stopped
59+
60+
def get_timers(self):
61+
states = {True: 'Stopped', False: 'Running'}
62+
return {k: states[v.stopped] for k, v in self.timers.items()}
63+
64+
def run(self):
65+
# select call used cause time.sleep loads CPU up to 100% with small polling time
66+
select.select([], [], [], WAIT_SEC)
67+
timers_intervals = [curr_timer.run() for curr_timer in Timer.timers.values() if not curr_timer.stopped]
68+
if not timers_intervals and self.no_timers_err:
69+
raise TimerError('Running timers not found')
70+
return timers_intervals
71+
72+
73+
class _Timer(object):
74+
def __init__(self, interval, deco, run_once, *args, **kwargs):
75+
self.interval = interval
76+
self.deco = deco
77+
self.args = args
78+
self.run_once = run_once
79+
self.kwargs = kwargs
80+
self.fire_time = None
81+
self.fire_time_prev = None
82+
self.stopped = False
83+
84+
def run(self):
85+
timer_real_interval = 0
86+
if self.fire_time is None:
87+
self.fire_time = time.time() + self.interval
88+
if self.fire_time_prev is None:
89+
self.fire_time_prev = time.time()
90+
curr_time = time.time()
91+
if curr_time >= self.fire_time:
92+
self.deco(*self.args, **self.kwargs)
93+
if self.run_once:
94+
self.stopped = True
95+
timer_real_interval = curr_time - self.fire_time_prev
96+
self.fire_time_prev = self.fire_time
97+
self.fire_time = curr_time + self.interval
98+
return timer_real_interval

0 commit comments

Comments
 (0)
0