micrOSDevToolKit 2.8.6__py3-none-any.whl → 2.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of micrOSDevToolKit might be problematic. Click here for more details.
- env/driver_cp210x/.DS_Store +0 -0
- env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
- env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +0 -0
- micrOS/micropython/esp32-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/esp32s3-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/rpi-pico-w-20241129-v1.24.1.uf2 +0 -0
- micrOS/micropython/tinypico-20241129-v1.24.1.bin +0 -0
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +40 -40
- micrOS/source/Common.py +12 -17
- micrOS/source/Config.py +20 -1
- micrOS/source/Hooks.py +25 -15
- micrOS/source/LM_buzzer.py +16 -9
- micrOS/source/LM_cct.py +2 -3
- micrOS/source/LM_dimmer.py +1 -2
- micrOS/source/LM_distance.py +2 -4
- micrOS/source/LM_genIO.py +1 -1
- micrOS/source/LM_i2s_mic.py +4 -4
- micrOS/source/LM_keychain.py +2 -3
- micrOS/source/LM_light_sensor.py +1 -2
- micrOS/source/LM_neoeffects.py +22 -7
- micrOS/source/LM_neopixel.py +2 -3
- micrOS/source/LM_oledui.py +3 -4
- micrOS/source/LM_pet_feeder.py +3 -4
- micrOS/source/LM_ph_sensor.py +2 -2
- micrOS/source/LM_presence.py +2 -3
- micrOS/source/LM_rest.py +52 -9
- micrOS/source/LM_rgb.py +1 -2
- micrOS/source/LM_roboarm.py +1 -2
- micrOS/source/LM_sound_event.py +2 -3
- micrOS/source/LM_system.py +14 -3
- micrOS/source/LM_telegram.py +226 -15
- micrOS/source/Logger.py +6 -0
- micrOS/source/Notify.py +46 -218
- micrOS/source/Scheduler.py +7 -4
- micrOS/source/Shell.py +2 -2
- micrOS/source/Tasks.py +106 -98
- micrOS/source/Types.py +6 -2
- micrOS/source/Web.py +32 -8
- micrOS/source/dashboard.html +4 -4
- micrOS/source/micrOS.py +5 -10
- micrOS/source/micrOSloader.py +16 -18
- micrOS/source/uapi.js +5 -6
- micrOS/source/urequests.py +171 -85
- {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/METADATA +2 -2
- {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/RECORD +95 -97
- toolkit/DevEnvUSB.py +1 -6
- toolkit/MicrOSDevEnv.py +8 -5
- toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
- toolkit/simulator_lib/simulator.py +14 -0
- toolkit/simulator_lib/uasyncio.py +2 -2
- toolkit/simulator_lib/usocket.py +5 -1
- toolkit/workspace/precompiled/Common.mpy +0 -0
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Hooks.mpy +0 -0
- toolkit/workspace/precompiled/LM_buzzer.mpy +0 -0
- toolkit/workspace/precompiled/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/LM_distance.mpy +0 -0
- toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
- toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
- toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
- toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
- toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/LM_oledui.mpy +0 -0
- toolkit/workspace/precompiled/LM_pet_feeder.py +3 -4
- toolkit/workspace/precompiled/LM_ph_sensor.py +2 -2
- toolkit/workspace/precompiled/LM_presence.mpy +0 -0
- toolkit/workspace/precompiled/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
- toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
- toolkit/workspace/precompiled/LM_sound_event.mpy +0 -0
- toolkit/workspace/precompiled/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/Logger.mpy +0 -0
- toolkit/workspace/precompiled/Notify.mpy +0 -0
- toolkit/workspace/precompiled/Scheduler.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Tasks.mpy +0 -0
- toolkit/workspace/precompiled/Types.mpy +0 -0
- toolkit/workspace/precompiled/Web.mpy +0 -0
- toolkit/workspace/precompiled/dashboard.html +4 -4
- toolkit/workspace/precompiled/micrOS.mpy +0 -0
- toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
- toolkit/workspace/precompiled/node_config.json +1 -1
- toolkit/workspace/precompiled/uapi.js +5 -6
- toolkit/workspace/precompiled/urequests.mpy +0 -0
- env/driver_cp210x/CH34XSER_MAC/CH34X_DRV_INSTALL_INSTRUCTIONS.pdf +0 -0
- env/driver_cp210x/CH34XSER_MAC/CH34xVCPDriver.pkg +0 -0
- micrOS/micropython/esp32-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/esp32-GENERIC-20240602-v1.23.0.bin +0 -0
- micrOS/micropython/rpi-pico-w-20240602-v1.23.0.uf2 +0 -0
- micrOS/micropython/tinypico-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/tinypico_usbc-UM-20240105-v1.22.1.bin +0 -0
- /micrOS/micropython/{esp32c3-GENERIC-20240222-v1.22.2.bin → esp32c3-20240222-v1.22.2.bin} +0 -0
- /micrOS/micropython/{esp32s2-GENERIC-20240602-v1.23.0.bin → esp32s2-20240602-v1.23.0.bin} +0 -0
- /micrOS/micropython/{esp32s3-GENERIC-20240105-v1.22.1.bin → esp32s3-20240105-v1.22.1.bin} +0 -0
- {micrOSDevToolKit-2.8.6.data → micrOSDevToolKit-2.9.0.data}/scripts/devToolKit.py +0 -0
- {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/LICENSE +0 -0
- {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/WHEEL +0 -0
- {micrOSDevToolKit-2.8.6.dist-info → micrOSDevToolKit-2.9.0.dist-info}/top_level.txt +0 -0
micrOS/source/LM_neoeffects.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from LM_neopixel import load, segment, Data, status, pinmap as pm
|
|
2
2
|
from random import randint
|
|
3
|
+
from Types import resolve
|
|
4
|
+
from Common import manage_task
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
#################################
|
|
@@ -285,6 +287,13 @@ def fire(r=None, g=None, b=None, ledcnt=24):
|
|
|
285
287
|
effect.draw(cgen, shift=False)
|
|
286
288
|
return 'fire R{}:G{}:B{} N:{}'.format(r, g, b, effect.pix_cnt)
|
|
287
289
|
|
|
290
|
+
|
|
291
|
+
def stop_effects():
|
|
292
|
+
"""
|
|
293
|
+
Stop all running (neo)effects tasks
|
|
294
|
+
"""
|
|
295
|
+
return manage_task("neoeffects.*", "kill")
|
|
296
|
+
|
|
288
297
|
#######################
|
|
289
298
|
# LM helper functions #
|
|
290
299
|
#######################
|
|
@@ -307,10 +316,16 @@ def help(widgets=False):
|
|
|
307
316
|
(widgets=False) list of functions implemented by this application
|
|
308
317
|
(widgets=True) list of widget json for UI generation
|
|
309
318
|
"""
|
|
310
|
-
return 'meteor r=<0-255> g=<0-255> b=<0-255> shift=True ledcnt=24'
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
return resolve(('meteor r=<0-255> g=<0-255> b=<0-255> shift=True ledcnt=24',
|
|
320
|
+
'BUTTON meteor &&',
|
|
321
|
+
'cycle r g b shift=True ledcnt=24',
|
|
322
|
+
'BUTTON cycle &&50',
|
|
323
|
+
'rainbow step=1 br=<5-100> ledcnt=24 &&',
|
|
324
|
+
'BUTTON rainbow br=50 &&',
|
|
325
|
+
'fire r=None g=None b=None ledcnt=24',
|
|
326
|
+
'BUTTON fire &&200',
|
|
327
|
+
'BUTTON stop_effects',
|
|
328
|
+
'shader size=4 offset=0 shift=True ledcnt=24',
|
|
329
|
+
'random max_val=255',
|
|
330
|
+
'pinmap',
|
|
331
|
+
'COLOR color r=<0-255-10> g b'), widgets=widgets)
|
micrOS/source/LM_neopixel.py
CHANGED
|
@@ -3,7 +3,6 @@ from machine import Pin
|
|
|
3
3
|
from sys import platform
|
|
4
4
|
from utime import sleep_ms
|
|
5
5
|
from Common import transition_gen, micro_task
|
|
6
|
-
import uasyncio as asyncio
|
|
7
6
|
from microIO import resolve_pin, pinmap_search
|
|
8
7
|
from random import randint
|
|
9
8
|
from Types import resolve
|
|
@@ -269,7 +268,7 @@ def transition(r=None, g=None, b=None, sec=1.0, wake=False):
|
|
|
269
268
|
Data.DCACHE[1] = g_val if g_val > 5 else 5 # SAVE VALUE TO CACHE > 5 ! because toggle
|
|
270
269
|
Data.DCACHE[2] = b_val if b_val > 5 else 5 # SAVE VALUE TO CACHE > 5 ! because toggle
|
|
271
270
|
my_task.out = f"Dimming: R:{r_val} G:{g_val} B:{b_val}"
|
|
272
|
-
await
|
|
271
|
+
await my_task.feed(sleep_ms=ms_period)
|
|
273
272
|
if Data.DCACHE[3] == 1 or wake:
|
|
274
273
|
__state_machine(r=r_val, g=g_val, b=b_val)
|
|
275
274
|
my_task.out = f"Dimming DONE: R:{r_val} G:{g_val} B:{b_val}"
|
|
@@ -346,7 +345,7 @@ def help(widgets=False):
|
|
|
346
345
|
'BUTTON toggle state=<True,False> smooth=True',
|
|
347
346
|
'load ledcnt=24',
|
|
348
347
|
'SLIDER brightness percent=<0-100> smooth=True wake=True',
|
|
349
|
-
'
|
|
348
|
+
'segment r g b s=<0-n>',
|
|
350
349
|
'transition r g b sec=1.0 wake=False',
|
|
351
350
|
'BUTTON random smooth=True max_val=254',
|
|
352
351
|
'status',
|
micrOS/source/LM_oledui.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from utime import localtime, ticks_ms, ticks_diff, sleep_ms
|
|
2
|
-
import uasyncio as asyncio
|
|
3
2
|
from Common import syslog, micro_task, manage_task, exec_cmd
|
|
4
3
|
from Types import resolve
|
|
5
4
|
# Core modules
|
|
@@ -106,7 +105,7 @@ class Frame(BaseFrame):
|
|
|
106
105
|
my_task.out = 'paused' if self.paused else f'refresh: {period_ms} ms'
|
|
107
106
|
s = self.paused
|
|
108
107
|
if self.paused:
|
|
109
|
-
await
|
|
108
|
+
await my_task.feed(sleep_ms=period_ms) # extra wait in paused mode
|
|
110
109
|
else:
|
|
111
110
|
# Draw/Refresh frame
|
|
112
111
|
self.draw()
|
|
@@ -115,7 +114,7 @@ class Frame(BaseFrame):
|
|
|
115
114
|
if self._fast_refresh:
|
|
116
115
|
self._fast_refresh = False
|
|
117
116
|
break
|
|
118
|
-
await
|
|
117
|
+
await my_task.feed(sleep_ms=micro_sleep_ms)
|
|
119
118
|
|
|
120
119
|
def clb_refresh(self):
|
|
121
120
|
"""Fast reload app loop callbacks"""
|
|
@@ -486,7 +485,7 @@ class ScreenSaver(BaseFrame):
|
|
|
486
485
|
# Store data in task cache (task show mytask)
|
|
487
486
|
my_task.out = f'GameOfLife: {counter}'
|
|
488
487
|
# Async sleep - feed event loop
|
|
489
|
-
await
|
|
488
|
+
await my_task.feed(sleep_ms=period_ms)
|
|
490
489
|
my_task.out = f'GameOfLife stopped: {counter}'
|
|
491
490
|
|
|
492
491
|
def run(self, fps=10):
|
micrOS/source/LM_pet_feeder.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import uasyncio as asyncio
|
|
2
1
|
from LM_servo import sduty, deinit, pinmap as servo_pinmap
|
|
3
2
|
from LM_stepper import step, pinmap as stepper_pinmap
|
|
4
3
|
from Common import micro_task
|
|
@@ -16,13 +15,13 @@ async def __portion_task(portion, posmin, posmax):
|
|
|
16
15
|
# [1]Run pos fill up
|
|
17
16
|
for pos in range(posmin, posmax):
|
|
18
17
|
sduty(pos)
|
|
19
|
-
await
|
|
18
|
+
await my_task.feed(sleep_ms=15)
|
|
20
19
|
# [2]Wait between fill up / food out
|
|
21
|
-
await
|
|
20
|
+
await my_task.feed(sleep_ms=500)
|
|
22
21
|
# [3]Run pos food out
|
|
23
22
|
for pos in range(posmax, posmin, -1):
|
|
24
23
|
sduty(pos)
|
|
25
|
-
await
|
|
24
|
+
await my_task.feed(sleep_ms=20)
|
|
26
25
|
my_task.out = "{}/{} serving".format(p+1, portion)
|
|
27
26
|
deinit()
|
|
28
27
|
my_task.out += ": {} task done".format(Data.TASK_TAG)
|
micrOS/source/LM_ph_sensor.py
CHANGED
|
@@ -12,7 +12,7 @@ ADC.ATTN_11DB — the full range voltage: 3.3V
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def __measure(samples=10):
|
|
15
|
-
adc_obj = SmartADC.
|
|
15
|
+
adc_obj = SmartADC.get_instance(resolve_pin('ph'))
|
|
16
16
|
mbuf = 0
|
|
17
17
|
for k in range(0, samples):
|
|
18
18
|
mbuf += adc_obj.get()['volt']
|
|
@@ -22,7 +22,7 @@ def __measure(samples=10):
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def measure():
|
|
25
|
-
data = SmartADC.
|
|
25
|
+
data = SmartADC.get_instance(resolve_pin('ph')).get()
|
|
26
26
|
return "ADC data: {}\nAVG V: {}".format(data, __measure())
|
|
27
27
|
|
|
28
28
|
|
micrOS/source/LM_presence.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from microIO import resolve_pin, pinmap_search
|
|
2
2
|
from Common import SmartADC, micro_task, notify, syslog
|
|
3
|
-
import uasyncio as asyncio
|
|
4
3
|
from utime import ticks_ms
|
|
5
4
|
try:
|
|
6
5
|
import LM_intercon as InterCon
|
|
@@ -97,7 +96,7 @@ async def __task(ms_period, buff_size):
|
|
|
97
96
|
|
|
98
97
|
if Data.MIC_TYPE == Data.MIC_TYPES['ADC']:
|
|
99
98
|
# Create ADC object
|
|
100
|
-
Data.MIC_ADC = SmartADC.
|
|
99
|
+
Data.MIC_ADC = SmartADC.get_instance(resolve_pin('mic'))
|
|
101
100
|
elif Data.MIC_TYPE == Data.MIC_TYPES['I2S']:
|
|
102
101
|
if Data.I2S_MIC is None:
|
|
103
102
|
import LM_i2s_mic
|
|
@@ -115,7 +114,7 @@ async def __task(ms_period, buff_size):
|
|
|
115
114
|
my_task.out = f"{int(Data.OFF_EV_TIMER)-1} sec until off event"
|
|
116
115
|
Data.OFF_EV_TIMER -= round(ms_period / 1000, 3)
|
|
117
116
|
# Async sleep - feed event loop
|
|
118
|
-
await
|
|
117
|
+
await my_task.feed(sleep_ms=ms_period)
|
|
119
118
|
|
|
120
119
|
# RUN OFF CALLBACK (local + remote)
|
|
121
120
|
__exec_local_callbacks(Data.OFF_CALLBACKS)
|
micrOS/source/LM_rest.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import uasyncio as asyncio
|
|
1
2
|
from urequests import get as http_get
|
|
3
|
+
from urequests import aget as http_aget
|
|
4
|
+
from Common import micro_task
|
|
5
|
+
|
|
2
6
|
|
|
3
7
|
class Rest:
|
|
4
8
|
GATEWAY_HOST = None
|
|
@@ -13,23 +17,60 @@ def load(gateway_url=None):
|
|
|
13
17
|
if Rest.GATEWAY_HOST is None:
|
|
14
18
|
return 'Missing Gateway url'
|
|
15
19
|
return Rest.GATEWAY_HOST
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
if gateway_url.startswith("http"):
|
|
21
|
+
Rest.GATEWAY_HOST = gateway_url
|
|
22
|
+
else:
|
|
23
|
+
return f"URL have to starts with http/https"
|
|
24
|
+
return f'Gateway url: {gateway_url}'
|
|
18
25
|
|
|
19
26
|
|
|
20
|
-
def
|
|
21
|
-
"""
|
|
22
|
-
Execute rest call with given subdomain
|
|
23
|
-
:param subdomain: command parameters of url, like: /webhooks/template
|
|
24
|
-
"""
|
|
27
|
+
def _subdomain(subdomain):
|
|
25
28
|
if Rest.GATEWAY_HOST is None:
|
|
26
|
-
|
|
29
|
+
raise Exception('Missing Gateway url')
|
|
27
30
|
if not subdomain.startswith('/'):
|
|
28
31
|
subdomain = '/' + subdomain
|
|
29
32
|
domain = f'{Rest.GATEWAY_HOST}{subdomain}'
|
|
33
|
+
return domain
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def url(subdomain):
|
|
37
|
+
"""
|
|
38
|
+
Execute rest call with given subdomain / url
|
|
39
|
+
:param subdomain: url parameter, http(s) full url or gateway subdomain like: /webhooks/template
|
|
40
|
+
"""
|
|
41
|
+
if subdomain.startswith('http'):
|
|
42
|
+
domain = subdomain
|
|
43
|
+
else:
|
|
44
|
+
domain = _subdomain(subdomain)
|
|
30
45
|
status_code, response = http_get(domain, jsonify=True)
|
|
31
46
|
return {'status': status_code, 'response': response}
|
|
32
47
|
|
|
48
|
+
|
|
49
|
+
async def __task(subdomain, tag):
|
|
50
|
+
with micro_task(tag=tag) as my_task:
|
|
51
|
+
my_task.out = f"GET {subdomain}"
|
|
52
|
+
if subdomain.startswith('http'):
|
|
53
|
+
domain = subdomain
|
|
54
|
+
else:
|
|
55
|
+
domain = _subdomain(subdomain)
|
|
56
|
+
status_code, response = await http_aget(domain, jsonify=True)
|
|
57
|
+
my_task.out = f'status: {status_code}, response: {response}'
|
|
58
|
+
return {'status': status_code, 'response': response}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def aurl(subdomain):
|
|
62
|
+
"""
|
|
63
|
+
Execute async rest call with given subdomain / url
|
|
64
|
+
:param subdomain: url parameter, http(s) full url or gateway subdomain like: /webhooks/template
|
|
65
|
+
"""
|
|
66
|
+
# [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
|
|
67
|
+
tag = "rest." + subdomain.replace("http://", '').replace("https://", '')
|
|
68
|
+
if len(tag) > 50:
|
|
69
|
+
tag = tag[0:50]
|
|
70
|
+
state = micro_task(tag=tag, task=__task(subdomain, tag))
|
|
71
|
+
return f"Starting" if state else f"Already running"
|
|
72
|
+
|
|
73
|
+
|
|
33
74
|
def help(widgets=False):
|
|
34
75
|
"""
|
|
35
76
|
[i] micrOS LM naming convention - built-in help message
|
|
@@ -37,4 +78,6 @@ def help(widgets=False):
|
|
|
37
78
|
(widgets=False) list of functions implemented by this application
|
|
38
79
|
(widgets=True) list of widget json for UI generation
|
|
39
80
|
"""
|
|
40
|
-
return 'load gateway_url=<http://gateway.local:5000>',
|
|
81
|
+
return ('load gateway_url=<http://gateway.local:5000>',
|
|
82
|
+
'url subdomain=</webhooks/template>',
|
|
83
|
+
'aurl subdomain=</webhooks/template>')
|
micrOS/source/LM_rgb.py
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
from machine import Pin, PWM
|
|
5
5
|
from sys import platform
|
|
6
6
|
from Common import transition_gen, micro_task
|
|
7
|
-
import uasyncio as asyncio
|
|
8
7
|
from utime import sleep_ms
|
|
9
8
|
from microIO import resolve_pin, pinmap_search
|
|
10
9
|
from random import randint
|
|
@@ -240,7 +239,7 @@ def transition(r=None, g=None, b=None, sec=1.0, wake=False):
|
|
|
240
239
|
Data.RGB_CACHE[1] = _g if _g > 5 else 5 # SAVE VALUE TO CACHE > 5 ! because toggle
|
|
241
240
|
Data.RGB_CACHE[2] = _b if _b > 5 else 5 # SAVE VALUE TO CACHE > 5 ! because toggle
|
|
242
241
|
my_task.out = f"Dimming: R:{_r} G:{_g} B:{_b}"
|
|
243
|
-
await
|
|
242
|
+
await my_task.feed(sleep_ms=ms_period)
|
|
244
243
|
if Data.RGB_CACHE[3] == 1 or wake:
|
|
245
244
|
__state_machine(_r, _g, _b)
|
|
246
245
|
my_task.out = f"Dimming DONE: R:{_r} G:{_g} B:{_b}"
|
micrOS/source/LM_roboarm.py
CHANGED
|
@@ -3,7 +3,6 @@ from random import randint
|
|
|
3
3
|
import LM_servo as servo
|
|
4
4
|
from LM_switch import set_state, pinmap as switch_pinmap
|
|
5
5
|
from Common import transition, micro_task
|
|
6
|
-
import uasyncio as asyncio
|
|
7
6
|
from Types import resolve
|
|
8
7
|
|
|
9
8
|
|
|
@@ -179,7 +178,7 @@ async def _play(args, deinit, delay):
|
|
|
179
178
|
if task is not None:
|
|
180
179
|
task.out = "Roboarm X:{} Y:{}".format(x, y)
|
|
181
180
|
# Async wait between steps
|
|
182
|
-
await
|
|
181
|
+
await task.feed(sleep_ms=delay)
|
|
183
182
|
if deinit:
|
|
184
183
|
servo.deinit()
|
|
185
184
|
# OFF LASER
|
micrOS/source/LM_sound_event.py
CHANGED
|
@@ -37,7 +37,6 @@ Terminal ready
|
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
39
|
import LM_i2s_mic
|
|
40
|
-
import uasyncio as asyncio
|
|
41
40
|
import json
|
|
42
41
|
|
|
43
42
|
from microIO import pinmap_search
|
|
@@ -647,7 +646,7 @@ async def __control_task(capture_duration_ms,
|
|
|
647
646
|
:param pause_duration_ms: int - duration of pause frames to distinguish events
|
|
648
647
|
:param event_buffer_length: int - number of events to store at once
|
|
649
648
|
"""
|
|
650
|
-
with micro_task(tag=Data.CONTROL_TASK_TAG):
|
|
649
|
+
with micro_task(tag=Data.CONTROL_TASK_TAG) as my_task:
|
|
651
650
|
samples = []
|
|
652
651
|
frame_size = int((frame_size_ms/1000)*LM_i2s_mic.Data.SAMPLING_RATE)
|
|
653
652
|
pause_duration = int(pause_duration_ms/frame_size_ms)
|
|
@@ -674,7 +673,7 @@ async def __control_task(capture_duration_ms,
|
|
|
674
673
|
|
|
675
674
|
# Wait for new samples to be taken
|
|
676
675
|
ms_period = int(len(new_samples)/LM_i2s_mic.Data.SAMPLING_RATE)
|
|
677
|
-
await
|
|
676
|
+
await my_task.feed(sleep_ms=ms_period)
|
|
678
677
|
Data.EVENTS = Data.EVENTS[-event_buffer_length:]
|
|
679
678
|
except Exception as e:
|
|
680
679
|
console(f'[ERR] sound_event: {e}')
|
micrOS/source/LM_system.py
CHANGED
|
@@ -7,6 +7,7 @@ from Network import get_mac, ifconfig as network_config
|
|
|
7
7
|
from Time import ntp_time, set_time, uptime
|
|
8
8
|
from Tasks import Manager
|
|
9
9
|
from Types import resolve
|
|
10
|
+
from Notify import Notify
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
13
|
from gc import mem_free, mem_alloc, collect
|
|
@@ -177,15 +178,16 @@ def list_stations():
|
|
|
177
178
|
return [("NoAP", '')]
|
|
178
179
|
|
|
179
180
|
|
|
180
|
-
def pinmap(
|
|
181
|
+
def pinmap(keys='builtin irq1 irq2 irq3 irq4'):
|
|
181
182
|
"""
|
|
182
183
|
Get Logical pin by key runtime
|
|
183
|
-
:param
|
|
184
|
+
:param keys str: logical pin name or names to resolve
|
|
184
185
|
:return dict: key map
|
|
185
186
|
"""
|
|
186
187
|
from microIO import pinmap_search, pinmap_info
|
|
187
188
|
map = pinmap_info()
|
|
188
|
-
|
|
189
|
+
keys = keys.split()
|
|
190
|
+
map["search"] = pinmap_search(keys)
|
|
189
191
|
return map
|
|
190
192
|
|
|
191
193
|
@socket_stream
|
|
@@ -232,6 +234,14 @@ def urequests_host_cache():
|
|
|
232
234
|
return host_cache()
|
|
233
235
|
|
|
234
236
|
|
|
237
|
+
def notifications(enable=None):
|
|
238
|
+
"""
|
|
239
|
+
Global notifications control for micrOS
|
|
240
|
+
:param enable: True: Enable notifications / False: Disable notifications
|
|
241
|
+
return: state verdict
|
|
242
|
+
"""
|
|
243
|
+
return Notify.notifications(state=enable)
|
|
244
|
+
|
|
235
245
|
#######################
|
|
236
246
|
# LM helper functions #
|
|
237
247
|
#######################
|
|
@@ -246,5 +256,6 @@ def help(widgets=False):
|
|
|
246
256
|
return resolve(('info', 'TEXTBOX top', 'gclean', 'heartbeat', 'clock',
|
|
247
257
|
'setclock year month mday hour min sec',
|
|
248
258
|
'ntp', 'rssi', 'list_stations', 'pinmap key="dhtpin"/None', 'alarms clean=False',
|
|
259
|
+
'notifications enable=<None,True,False>',
|
|
249
260
|
'sun refresh=False', 'ifconfig', 'memory_usage',
|
|
250
261
|
'disk_usage', 'dat_dump', 'urequests_host_cache'), widgets=widgets)
|
micrOS/source/LM_telegram.py
CHANGED
|
@@ -1,7 +1,229 @@
|
|
|
1
|
+
from sys import modules
|
|
1
2
|
import uasyncio as asyncio
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
import urequests
|
|
4
|
+
from Notify import Notify
|
|
5
|
+
from Config import cfgget
|
|
6
|
+
from Common import micro_task, syslog, console_write
|
|
7
|
+
from LM_system import ifconfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Telegram(Notify):
|
|
11
|
+
# Telegram bot token and chat ID
|
|
12
|
+
# https://core.telegram.org/bots/api
|
|
13
|
+
_TOKEN = None # Telegram token
|
|
14
|
+
_CHAT_IDS = set() # Telegram bot chat IDs - multi group support - persistent caching
|
|
15
|
+
_API_PARAMS = "?offset=-1&limit=1&timeout=2" # Generic API params - optimization
|
|
16
|
+
_IN_MSG_ID = None
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def __id_cache(mode):
|
|
20
|
+
"""
|
|
21
|
+
pds - persistent data structure
|
|
22
|
+
modes:
|
|
23
|
+
r - recover, s - save
|
|
24
|
+
"""
|
|
25
|
+
if mode == 's':
|
|
26
|
+
# SAVE CACHE
|
|
27
|
+
console_write("[NTFY] Save chatIDs cache...")
|
|
28
|
+
with open('telegram.pds', 'w') as f:
|
|
29
|
+
f.write(','.join([str(k) for k in Telegram._CHAT_IDS]))
|
|
30
|
+
return
|
|
31
|
+
try:
|
|
32
|
+
# RESTORE CACHE
|
|
33
|
+
console_write("[NTFY] Restore chatIDs cache...")
|
|
34
|
+
with open('telegram.pds', 'r') as f:
|
|
35
|
+
# set() comprehension
|
|
36
|
+
Telegram._CHAT_IDS = {int(k) for k in f.read().strip().split(',')}
|
|
37
|
+
except:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def __bot_token():
|
|
42
|
+
"""Get bot token"""
|
|
43
|
+
if Telegram._TOKEN is None:
|
|
44
|
+
token = cfgget('telegram')
|
|
45
|
+
if token is None or token == 'n/a':
|
|
46
|
+
return None
|
|
47
|
+
Telegram._TOKEN = token
|
|
48
|
+
return Telegram._TOKEN
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def send_msg(text, reply_to=None, chat_id=None):
|
|
52
|
+
"""
|
|
53
|
+
Send a message to the Telegram chat by chat_id
|
|
54
|
+
:param text: text to send
|
|
55
|
+
:param reply_to: reply to specific message, if None, simple reply
|
|
56
|
+
:param chat_id: chat_id to reply on, if None, reply to all known
|
|
57
|
+
RETURN None when telegram bot token is missing
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def _send(chid):
|
|
61
|
+
"""Send message to chat_id (chid)"""
|
|
62
|
+
data = {"chat_id": chid, "text": f"{Telegram._DEVFID}⚙️\n{text}"}
|
|
63
|
+
if isinstance(reply_to, int):
|
|
64
|
+
data['reply_to_message_id'] = reply_to
|
|
65
|
+
Telegram._IN_MSG_ID = reply_to
|
|
66
|
+
_, _resp = urequests.post(url, headers={"Content-Type": "application/json"}, json=data, jsonify=True,
|
|
67
|
+
sock_size=128)
|
|
68
|
+
console_write(f"\tSend message:\n{data}\nresponse:\n{_resp}")
|
|
69
|
+
return _resp
|
|
70
|
+
|
|
71
|
+
def _get_chat_ids():
|
|
72
|
+
"""Return chat ID or None (in case of no token or cannot get ID)"""
|
|
73
|
+
if len(Telegram._CHAT_IDS) == 0:
|
|
74
|
+
Telegram.get_msg() # It will update the Telegram.CHAT_IDS
|
|
75
|
+
console_write(f"\tGet chatIDs: {Telegram._CHAT_IDS}")
|
|
76
|
+
return Telegram._CHAT_IDS
|
|
77
|
+
|
|
78
|
+
# --------------------- FUNCTION MAIN ------------------------ #
|
|
79
|
+
console_write("[NTFY] SEND MESSAGE")
|
|
80
|
+
# Check bot token
|
|
81
|
+
bot_token = Telegram.__bot_token()
|
|
82
|
+
if bot_token is None:
|
|
83
|
+
return None
|
|
84
|
+
url = f"https://api.telegram.org/bot{bot_token}/sendMessage{Telegram._API_PARAMS}"
|
|
85
|
+
|
|
86
|
+
verdict = ""
|
|
87
|
+
# Reply to ALL (notification) - chat_id was not provided
|
|
88
|
+
if chat_id is None:
|
|
89
|
+
console_write("\tREPLY ALL")
|
|
90
|
+
for _chat_id in _get_chat_ids():
|
|
91
|
+
resp_json = _send(chid=_chat_id)
|
|
92
|
+
verdict += f'Sent{_chat_id};' if resp_json['ok'] else str(resp_json)
|
|
93
|
+
else:
|
|
94
|
+
console_write(f"\tREPLY TO {chat_id}")
|
|
95
|
+
# Direct reply to chat_id
|
|
96
|
+
resp_json = _send(chid=chat_id)
|
|
97
|
+
verdict = f'Sent{chat_id}' if resp_json['ok'] else str(resp_json)
|
|
98
|
+
return verdict
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def get_msg():
|
|
102
|
+
"""
|
|
103
|
+
Get the last message from the Telegram chat.
|
|
104
|
+
RETURN None when telegram bot token is missing
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def _update_chat_ids():
|
|
108
|
+
"""
|
|
109
|
+
Update known chat_id-s and cache them
|
|
110
|
+
- return active chat_id frm resp_json
|
|
111
|
+
"""
|
|
112
|
+
console_write("[NTFY GET] update chatIDs")
|
|
113
|
+
_cid = None
|
|
114
|
+
if resp_json.get("ok", None) and len(resp_json["result"]) > 0:
|
|
115
|
+
_cid = resp_json["result"][-1]["message"]["chat"]["id"]
|
|
116
|
+
# LIMIT Telegram._CHAT_IDS NOTIFICATION CACHE TO 3 IDs
|
|
117
|
+
if len(Telegram._CHAT_IDS) < 4:
|
|
118
|
+
_ids = len(Telegram._CHAT_IDS)
|
|
119
|
+
Telegram._CHAT_IDS.add(_cid)
|
|
120
|
+
if len(Telegram._CHAT_IDS) - _ids > 0: # optimized save (slow storage access)
|
|
121
|
+
Telegram.__id_cache('s')
|
|
122
|
+
else:
|
|
123
|
+
Telegram.__id_cache('r')
|
|
124
|
+
if len(Telegram._CHAT_IDS) == 0:
|
|
125
|
+
error_message = resp_json.get("description", "Unknown error")
|
|
126
|
+
raise Exception(f"Error retrieving chat ID: {error_message}")
|
|
127
|
+
return _cid
|
|
128
|
+
|
|
129
|
+
# --------------------- FUNCTION MAIN ------------------------ #
|
|
130
|
+
console_write("[NTFY] GET MESSAGE")
|
|
131
|
+
bot_token = Telegram.__bot_token()
|
|
132
|
+
if bot_token is None:
|
|
133
|
+
return None
|
|
134
|
+
response = {'sender': None, 'text': None, 'm_id': -1, 'c_id': None}
|
|
135
|
+
url = f"https://api.telegram.org/bot{bot_token}/getUpdates{Telegram._API_PARAMS}"
|
|
136
|
+
console_write(f"\t1/2[GET] request: {url}")
|
|
137
|
+
_, resp_json = urequests.get(url, jsonify=True, sock_size=128)
|
|
138
|
+
if len(resp_json["result"]) > 0:
|
|
139
|
+
response['c_id'] = _update_chat_ids()
|
|
140
|
+
resp = resp_json["result"][-1]["message"]
|
|
141
|
+
response['sender'] = f"{resp['chat']['first_name']}{resp['chat']['last_name']}" if resp['chat'].get(
|
|
142
|
+
'username', None) is None else resp['chat']['username']
|
|
143
|
+
response['text'], response['m_id'] = resp['text'], resp['message_id']
|
|
144
|
+
console_write(f"\t2/2[GET] response: {response}")
|
|
145
|
+
return response
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def receive_eval():
|
|
149
|
+
"""
|
|
150
|
+
READ - VALIDATE - EXECUTE - REPLY LOOP
|
|
151
|
+
- can be used in async loop
|
|
152
|
+
RETURN None when telegram bot token is missing
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
console_write("[NTFY] REC&EVAL sequence")
|
|
156
|
+
|
|
157
|
+
# Return data structure template
|
|
158
|
+
verdict = None
|
|
159
|
+
|
|
160
|
+
def lm_execute(cmd_args):
|
|
161
|
+
nonlocal verdict, m_id
|
|
162
|
+
access, output = Telegram.lm_execute(cmd_args)
|
|
163
|
+
if access:
|
|
164
|
+
verdict = f'[UP] Exec: {" ".join(cmd_args[0])}'
|
|
165
|
+
Telegram.send_msg(output, reply_to=m_id)
|
|
166
|
+
else:
|
|
167
|
+
verdict = f'[UP] NoAccess: {cmd_args[0]}'
|
|
168
|
+
Telegram._IN_MSG_ID = m_id
|
|
169
|
+
|
|
170
|
+
# -------------------------- FUNCTION MAIN -------------------------- #
|
|
171
|
+
# Poll telegram chat
|
|
172
|
+
data = Telegram.get_msg()
|
|
173
|
+
if data is None:
|
|
174
|
+
return data
|
|
175
|
+
# Get msg, msg_id, chat_id as main input data source
|
|
176
|
+
msg_in, m_id, c_id = data['text'], data['m_id'], data['c_id']
|
|
177
|
+
if msg_in is not None and m_id != Telegram._IN_MSG_ID:
|
|
178
|
+
# replace single/double quotation to apostrophe (str syntax for repl interpretation)
|
|
179
|
+
msg_in = msg_in.replace('‘', "'").replace('’', "'").replace('“', '"').replace('”', '"')
|
|
180
|
+
if msg_in.startswith('/ping'):
|
|
181
|
+
# Parse loaded modules
|
|
182
|
+
_loaded_mods = [lm.replace('LM_', '') for lm in modules if lm.startswith('LM_')] + ['task']
|
|
183
|
+
Telegram.send_msg(', '.join(_loaded_mods), reply_to=m_id, chat_id=c_id)
|
|
184
|
+
elif msg_in.startswith('/cmd_select'):
|
|
185
|
+
cmd_lm = msg_in.strip().split()[1:]
|
|
186
|
+
# [Compare] cmd selected device param with DEVFID (device/prompt name)
|
|
187
|
+
if cmd_lm[0] in Telegram._DEVFID:
|
|
188
|
+
lm_execute(cmd_lm[1:])
|
|
189
|
+
else:
|
|
190
|
+
verdict = f'[UP] NoSelected: {cmd_lm[0]}'
|
|
191
|
+
elif msg_in.startswith('/cmd'):
|
|
192
|
+
cmd_lm = msg_in.strip().split()[1:]
|
|
193
|
+
lm_execute(cmd_lm)
|
|
194
|
+
elif msg_in.startswith('/notify'):
|
|
195
|
+
param = msg_in.strip().split()[1:]
|
|
196
|
+
if len(param) > 0:
|
|
197
|
+
verdict = Telegram.notifications(not param[0].strip().lower() in ("disable", "off", 'false'))
|
|
198
|
+
else:
|
|
199
|
+
verdict = Telegram.notifications()
|
|
200
|
+
Telegram.send_msg(verdict, reply_to=m_id)
|
|
201
|
+
else:
|
|
202
|
+
verdict = "[UP] NoExec"
|
|
203
|
+
console_write(f"\tREC&EVAL: {verdict}")
|
|
204
|
+
return verdict
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def set_commands():
|
|
208
|
+
"""
|
|
209
|
+
Set Custom Commands to the Telegram chat.
|
|
210
|
+
RETURN None when telegram bot token is missing
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
console_write("[NTFY] SET DEFAULT COMMANDS")
|
|
214
|
+
bot_token = Telegram.__bot_token()
|
|
215
|
+
if bot_token is None:
|
|
216
|
+
return None
|
|
217
|
+
url = f"https://api.telegram.org/bot{bot_token}/setMyCommands{Telegram._API_PARAMS}"
|
|
218
|
+
data = {"commands": [{"command": "ping", "description": "Ping All endpoints: list of active modules."},
|
|
219
|
+
{"command": "notify", "description": "Enable/Disable notifications: on or off"},
|
|
220
|
+
{"command": "cmd", "description": "Run active module function on all devices."},
|
|
221
|
+
{"command": "cmd_select",
|
|
222
|
+
"description": "Same as cmd, only first param must be device name."},
|
|
223
|
+
]}
|
|
224
|
+
_, resp_json = urequests.post(url, headers={"Content-Type": "application/json"}, json=data, jsonify=True,
|
|
225
|
+
sock_size=128)
|
|
226
|
+
return 'Custom commands was set' if resp_json['ok'] else str(resp_json)
|
|
5
227
|
|
|
6
228
|
#########################################
|
|
7
229
|
# micrOS Notifications #
|
|
@@ -15,6 +237,7 @@ def __init():
|
|
|
15
237
|
_sta_available = True if ifconfig()[0] == "STA" else False
|
|
16
238
|
if _sta_available:
|
|
17
239
|
TELEGRAM_OBJ = Telegram()
|
|
240
|
+
Notify.add_subscriber(TELEGRAM_OBJ)
|
|
18
241
|
else:
|
|
19
242
|
syslog("No STA: cannot init telegram")
|
|
20
243
|
|
|
@@ -103,17 +326,6 @@ def receiver_loop():
|
|
|
103
326
|
return "Starting" if state else "Already running"
|
|
104
327
|
|
|
105
328
|
|
|
106
|
-
def notifications(enable=None):
|
|
107
|
-
"""
|
|
108
|
-
Global notifications control for micrOS
|
|
109
|
-
:param enable: True: Enable notifications / False: Disable notifications
|
|
110
|
-
return: state verdict
|
|
111
|
-
"""
|
|
112
|
-
if enable is None:
|
|
113
|
-
enable = not Telegram.GLOBAL_NOTIFY
|
|
114
|
-
return Telegram.notifications(state=enable)
|
|
115
|
-
|
|
116
|
-
|
|
117
329
|
def help(widgets=False):
|
|
118
330
|
"""
|
|
119
331
|
[i] micrOS LM naming convention - built-in help message
|
|
@@ -125,5 +337,4 @@ def help(widgets=False):
|
|
|
125
337
|
'receive',
|
|
126
338
|
'receiver_loop',
|
|
127
339
|
'notify "message"',
|
|
128
|
-
'notifications enable=False',
|
|
129
340
|
'load', 'INFO: Send & Receive messages with Telegram bot')
|