micrOSDevToolKit 2.17.2__py3-none-any.whl → 2.19.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.
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +15 -15
- micrOS/source/Config.py +2 -2
- micrOS/source/Espnow.py +29 -12
- micrOS/source/InterConnect.py +107 -31
- micrOS/source/Server.py +2 -3
- micrOS/source/Shell.py +1 -1
- micrOS/source/Tasks.py +22 -20
- micrOS/source/micrOSloader.py +1 -1
- micrOS/source/modules/LM_espnow.py +18 -1
- micrOS/source/modules/LM_neoeffects.py +1 -1
- micrOS/source/modules/LM_neomatrix.py +1 -1
- micrOS/source/modules/LM_oled_ui.py +4 -3
- micrOS/source/modules/LM_oledui.py +4 -3
- micrOS/source/modules/LM_tcs3472.py +131 -17
- micrOS/source/modules/LM_telegram.py +18 -18
- {microsdevtoolkit-2.17.2.dist-info → microsdevtoolkit-2.19.0.dist-info}/METADATA +3 -3
- {microsdevtoolkit-2.17.2.dist-info → microsdevtoolkit-2.19.0.dist-info}/RECORD +39 -41
- toolkit/Gateway.py +1 -1
- toolkit/dashboard_apps/SystemTest.py +22 -18
- toolkit/micrOSdashboard.py +8 -13
- toolkit/socketClient.py +27 -7
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Espnow.mpy +0 -0
- toolkit/workspace/precompiled/InterConnect.mpy +0 -0
- toolkit/workspace/precompiled/Server.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Tasks.mpy +0 -0
- toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_espnow.py +18 -1
- toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_neomatrix.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oledui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_tcs3472.py +131 -17
- toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
- {microsdevtoolkit-2.17.2.data → microsdevtoolkit-2.19.0.data}/scripts/devToolKit.py +0 -0
- {microsdevtoolkit-2.17.2.dist-info → microsdevtoolkit-2.19.0.dist-info}/WHEEL +0 -0
- {microsdevtoolkit-2.17.2.dist-info → microsdevtoolkit-2.19.0.dist-info}/licenses/LICENSE +0 -0
- {microsdevtoolkit-2.17.2.dist-info → microsdevtoolkit-2.19.0.dist-info}/top_level.txt +0 -0
toolkit/micrOSdashboard.py
CHANGED
|
@@ -6,17 +6,12 @@ import os
|
|
|
6
6
|
import threading
|
|
7
7
|
import subprocess
|
|
8
8
|
import time
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from PyQt5.
|
|
13
|
-
from PyQt5.QtGui import QIcon
|
|
14
|
-
|
|
15
|
-
from PyQt5.QtWidgets import QApplication, QPlainTextEdit
|
|
16
|
-
from PyQt5.QtCore import QThread, pyqtSignal
|
|
17
|
-
from PyQt5.QtGui import QFont
|
|
18
|
-
from PyQt5 import QtGui
|
|
19
|
-
from PyQt5.QtGui import QPixmap
|
|
9
|
+
|
|
10
|
+
from PyQt5.QtWidgets import (QApplication, QPlainTextEdit, QPushButton, QProgressBar, QComboBox, QLabel, QCheckBox,
|
|
11
|
+
QLineEdit, QMessageBox, QWidget, QToolTip)
|
|
12
|
+
from PyQt5.QtCore import pyqtSlot, QThread, QSize, pyqtSignal
|
|
13
|
+
from PyQt5.QtGui import QIcon, QTextCursor, QFont, QPixmap
|
|
14
|
+
|
|
20
15
|
import concurrent.futures
|
|
21
16
|
|
|
22
17
|
MYPATH = os.path.dirname(__file__)
|
|
@@ -454,7 +449,7 @@ class HeaderInfo:
|
|
|
454
449
|
logo_path = os.path.join(MYPATH, '../media/logo_mini.png')
|
|
455
450
|
button = QPushButton('', self.parent_obj)
|
|
456
451
|
button.setIcon(QIcon(logo_path))
|
|
457
|
-
button.setIconSize(
|
|
452
|
+
button.setIconSize(QSize(50, 50))
|
|
458
453
|
button.setGeometry(20, 5, 50, 50)
|
|
459
454
|
button.setToolTip(f"Open micrOS repo documentation\n{self.url}")
|
|
460
455
|
button.setStyleSheet('border: 0px solid black;')
|
|
@@ -1219,7 +1214,7 @@ class micrOSGUI(QWidget):
|
|
|
1219
1214
|
|
|
1220
1215
|
def main():
|
|
1221
1216
|
app = QApplication(sys.argv)
|
|
1222
|
-
app.setWindowIcon(
|
|
1217
|
+
app.setWindowIcon(QIcon(os.path.join(MYPATH, '../media/logo.png')))
|
|
1223
1218
|
ex = micrOSGUI()
|
|
1224
1219
|
ex.draw()
|
|
1225
1220
|
sys.exit(app.exec_())
|
toolkit/socketClient.py
CHANGED
|
@@ -185,8 +185,8 @@ class ConnectionData:
|
|
|
185
185
|
return None, None, None, None
|
|
186
186
|
|
|
187
187
|
@staticmethod
|
|
188
|
-
def nodes_status():
|
|
189
|
-
spr_offset1 =
|
|
188
|
+
def nodes_status(feature_stat=True):
|
|
189
|
+
spr_offset1 = 25
|
|
190
190
|
spr_offset2 = 57
|
|
191
191
|
|
|
192
192
|
def _dev_status(ip, port, fuid, uid):
|
|
@@ -199,6 +199,10 @@ class ConnectionData:
|
|
|
199
199
|
Colors.WARN, Colors.NC)
|
|
200
200
|
version_data = '<n/a>'
|
|
201
201
|
elapsed_time = 'n/a'
|
|
202
|
+
webui_state = 'n/a'
|
|
203
|
+
espnow_state = 'n/a'
|
|
204
|
+
cron_state = 'n/a'
|
|
205
|
+
timirq_state = 'n/a'
|
|
202
206
|
online_ip = None
|
|
203
207
|
|
|
204
208
|
# is online
|
|
@@ -206,9 +210,17 @@ class ConnectionData:
|
|
|
206
210
|
# get version data
|
|
207
211
|
online_ip = ip
|
|
208
212
|
try:
|
|
213
|
+
connection = SocketDictClient(host=ip, port=port, silent_mode=True, tout=3)
|
|
214
|
+
# Get version and elapsed time data
|
|
209
215
|
start_comm = time.time()
|
|
210
|
-
version_data =
|
|
216
|
+
version_data = connection.non_interactive(['version'])
|
|
211
217
|
elapsed_time = "{:.3f}".format(time.time() - start_comm)
|
|
218
|
+
if feature_stat:
|
|
219
|
+
# Get active features info
|
|
220
|
+
webui_state = connection.non_interactive(['conf', 'webui'])
|
|
221
|
+
espnow_state = connection.non_interactive(['conf', 'espnow'])
|
|
222
|
+
cron_state = connection.non_interactive(['conf', 'cron'])
|
|
223
|
+
timirq_state = connection.non_interactive(['conf', 'timirq'])
|
|
212
224
|
except Exception as e:
|
|
213
225
|
print(f"Getting device version {fuid}:{uid} error: {e}")
|
|
214
226
|
|
|
@@ -216,14 +228,22 @@ class ConnectionData:
|
|
|
216
228
|
base_info = "{uid}{spr1}{fuid}".format(uid=uid, spr1=spacer1, fuid=fuid)
|
|
217
229
|
spacer1 = " " * (spr_offset2 - len(base_info))
|
|
218
230
|
spacer2 = "\t" if len(version_data) > 7 else "\t\t"
|
|
219
|
-
|
|
231
|
+
feature_info = ""
|
|
232
|
+
if feature_stat:
|
|
233
|
+
_on_str = f"{Colors.OKGREEN}{Colors.BOLD}ON {Colors.NC}"
|
|
234
|
+
_off_str = f"{Colors.BOLD}OFF{Colors.NC}"
|
|
235
|
+
_fspacer = " "*6
|
|
236
|
+
bool2str = lambda x: _on_str if x.strip() == "True" else _off_str if x != "n/a" else x
|
|
237
|
+
feature_info = f"\t\t{bool2str(webui_state)}{_fspacer}{bool2str(espnow_state)}{_fspacer}{bool2str(cron_state)}{_fspacer}{bool2str(timirq_state)}"
|
|
238
|
+
data_line_str = f"{base_info}{spacer1}{ip}\t{is_online}\t\t{version_data}{spacer2}{elapsed_time}{feature_info}"
|
|
220
239
|
return data_line_str, online_ip
|
|
221
240
|
return None
|
|
222
241
|
|
|
223
242
|
nodes_dict = ConnectionData.read_micrOS_device_cache()
|
|
224
|
-
spacer1 = " " * (spr_offset1 -
|
|
225
|
-
|
|
226
|
-
|
|
243
|
+
spacer1 = " " * (spr_offset1 - 8)
|
|
244
|
+
feature_header_str = "\t[WEBUI | ESPNOW | CRON | TIMIRQ]" if feature_stat else ""
|
|
245
|
+
print("{cols}[ UID ]{spr1}[ FUID ]\t\t[ IP ]\t\t[ STATUS ]\t[ VERSION ]\t[COMM SEC]{features}{cole}"
|
|
246
|
+
.format(spr1=spacer1, cols=Colors.OKBLUE + Colors.BOLD, features=feature_header_str, cole=Colors.NC))
|
|
227
247
|
|
|
228
248
|
# Start parallel status queries
|
|
229
249
|
query_list = []
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -17,6 +17,13 @@ def stats():
|
|
|
17
17
|
return ESPNOW.stats()
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def members():
|
|
21
|
+
"""
|
|
22
|
+
Get ESPNow devices
|
|
23
|
+
"""
|
|
24
|
+
return ESPNOW.members()
|
|
25
|
+
|
|
26
|
+
|
|
20
27
|
def handshake(peer:bytes|str):
|
|
21
28
|
"""
|
|
22
29
|
Handshake with ESPNow Peer
|
|
@@ -27,10 +34,20 @@ def handshake(peer:bytes|str):
|
|
|
27
34
|
return ESPNOW.handshake(peer)
|
|
28
35
|
|
|
29
36
|
|
|
37
|
+
def remove(peer:bytes):
|
|
38
|
+
"""
|
|
39
|
+
Remove peer by binary mac address
|
|
40
|
+
:param peer: binary mac address of espnow device
|
|
41
|
+
"""
|
|
42
|
+
return ESPNOW.remove_peer(peer)
|
|
43
|
+
|
|
44
|
+
|
|
30
45
|
def help():
|
|
31
46
|
"""
|
|
32
47
|
ESPNOW sender/receiver with LM execution
|
|
33
48
|
"""
|
|
34
49
|
return ('handshake peer=<mac-address>',
|
|
35
50
|
'send peer=<peer-name> cmd="hello"',
|
|
36
|
-
'
|
|
51
|
+
'remove peer=<binary-mac-address>',
|
|
52
|
+
'stats',
|
|
53
|
+
'members')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,26 +6,54 @@ Copyright (c) 2021 tti0
|
|
|
6
6
|
Licensed under the MIT License
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from struct import unpack
|
|
10
|
+
from time import sleep
|
|
11
|
+
from machine import I2C, Pin, PWM
|
|
10
12
|
from microIO import bind_pin, pinmap_search
|
|
11
|
-
import
|
|
13
|
+
from Types import resolve
|
|
12
14
|
|
|
15
|
+
from LM_neopixel import load as neo_load, color as neo_color, toggle as neo_toggle # local neopixel light indicator
|
|
16
|
+
from LM_cluster import run as cluster_run # DEMO: neomatrix cluster
|
|
17
|
+
|
|
18
|
+
CURRENT_ANIMATION_INDEX = 0 # DEMO: neomatrix cluster animation
|
|
13
19
|
|
|
14
20
|
class TCS3472:
|
|
15
21
|
INSTANCE = None
|
|
16
22
|
|
|
17
|
-
def __init__(self,
|
|
18
|
-
self._bus =
|
|
23
|
+
def __init__(self, address=0x29, led_pin=None):
|
|
24
|
+
self._bus = I2C(sda=Pin(bind_pin('i2c_sda')), scl=Pin(bind_pin('i2c_scl')))
|
|
19
25
|
self._i2c_address = address
|
|
20
26
|
self._bus.writeto(self._i2c_address, b'\x80\x03')
|
|
21
27
|
self._bus.writeto(self._i2c_address, b'\x81\x2b')
|
|
28
|
+
self.led = PWM(Pin(bind_pin('led', led_pin), Pin.OUT), freq=20480)
|
|
29
|
+
self.led_brightness = 20
|
|
22
30
|
TCS3472.INSTANCE = self
|
|
23
31
|
|
|
24
|
-
def scaled(self):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
def scaled(self, saturation=1.5):
|
|
33
|
+
"""
|
|
34
|
+
Normalize by strongest color, then adjust saturation.
|
|
35
|
+
saturation = 1.0 -> normal
|
|
36
|
+
saturation > 1.0 -> more vibrant
|
|
37
|
+
saturation < 1.0 -> more pastel
|
|
38
|
+
"""
|
|
39
|
+
_, r, g, b = self.raw()
|
|
40
|
+
m = max(r, g, b)
|
|
41
|
+
if m == 0:
|
|
42
|
+
return 0.0, 0.0, 0.0
|
|
43
|
+
|
|
44
|
+
# Normalize by strongest channel
|
|
45
|
+
r, g, b = r / m, g / m, b / m
|
|
46
|
+
|
|
47
|
+
# Grayscale = average of channels
|
|
48
|
+
gray = (r + g + b) / 3
|
|
49
|
+
|
|
50
|
+
# Interpolate between gray and color
|
|
51
|
+
r = gray + (r - gray) * saturation
|
|
52
|
+
g = gray + (g - gray) * saturation
|
|
53
|
+
b = gray + (b - gray) * saturation
|
|
54
|
+
|
|
55
|
+
# Clamp to 0..1
|
|
56
|
+
return max(0, min(1, r)), max(0, min(1, g)), max(0, min(1, b))
|
|
29
57
|
|
|
30
58
|
def rgb(self):
|
|
31
59
|
return tuple(int(x * 255) for x in self.scaled())
|
|
@@ -42,32 +70,118 @@ class TCS3472:
|
|
|
42
70
|
|
|
43
71
|
def raw(self):
|
|
44
72
|
self._bus.writeto(self._i2c_address, b'\xb4')
|
|
45
|
-
return
|
|
73
|
+
return unpack("<HHHH", self._bus.readfrom(self._i2c_address, 8))
|
|
46
74
|
|
|
47
75
|
|
|
48
76
|
############################ Exposed functions ############################
|
|
49
77
|
|
|
50
|
-
def load():
|
|
78
|
+
def load(led_pin=20):
|
|
51
79
|
"""
|
|
52
80
|
Load the TCS3472 Color sensor instance.
|
|
53
81
|
"""
|
|
54
82
|
if TCS3472.INSTANCE is None:
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
TCS3472(led_pin=led_pin)
|
|
84
|
+
neo_load(ledcnt=1)
|
|
85
|
+
led(False)
|
|
57
86
|
return TCS3472.INSTANCE
|
|
58
87
|
|
|
59
88
|
|
|
60
89
|
def pinmap():
|
|
61
|
-
|
|
90
|
+
"""
|
|
91
|
+
Show used pin mapping for this module.
|
|
92
|
+
"""
|
|
93
|
+
return pinmap_search(['i2c_scl', 'i2c_sda', 'led'])
|
|
62
94
|
|
|
63
95
|
|
|
64
96
|
def measure():
|
|
97
|
+
"""
|
|
98
|
+
MEASURE sensor
|
|
99
|
+
"""
|
|
100
|
+
sensor = load()
|
|
101
|
+
measurement = {"rgb": sensor.rgb(), "light": sensor.light(), "brightness": sensor.brightness()}
|
|
102
|
+
return measurement
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def led(state:bool=None, br:int=None):
|
|
106
|
+
"""
|
|
107
|
+
SENSOR LED toggle
|
|
108
|
+
:param state: None-automatic, True-ON, False-OFF
|
|
109
|
+
:param br: brightness 0-100
|
|
110
|
+
"""
|
|
111
|
+
def _set_duty(_br):
|
|
112
|
+
_br = sensor.led_brightness if _br is None else _br
|
|
113
|
+
sensor.led.duty(int(_br * 10))
|
|
114
|
+
if _br != 0:
|
|
115
|
+
sensor.led_brightness = _br
|
|
116
|
+
|
|
65
117
|
sensor = load()
|
|
66
|
-
|
|
118
|
+
if state is None:
|
|
119
|
+
# INVERT STATE
|
|
120
|
+
led_current_state = sensor.led.duty() > 0
|
|
121
|
+
if led_current_state:
|
|
122
|
+
_set_duty(br)
|
|
123
|
+
_set_duty(0)
|
|
124
|
+
neo_toggle(False)
|
|
125
|
+
else:
|
|
126
|
+
_set_duty(br)
|
|
127
|
+
neo_toggle(True)
|
|
128
|
+
else:
|
|
129
|
+
# SET STATE: ON/OFF
|
|
130
|
+
if state:
|
|
131
|
+
_set_duty(br)
|
|
132
|
+
neo_toggle(True)
|
|
133
|
+
else:
|
|
134
|
+
_set_duty(br)
|
|
135
|
+
_set_duty(0)
|
|
136
|
+
neo_toggle(False)
|
|
137
|
+
return f"LED on, {sensor.led_brightness}%" if sensor.led.duty()>0 else f"LED off"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def indicator(br=5):
|
|
141
|
+
"""
|
|
142
|
+
Color indicator Neopixel LED update
|
|
143
|
+
:param br: brightness 0-100
|
|
144
|
+
"""
|
|
145
|
+
r, g, b = measure()['rgb']
|
|
146
|
+
br = float(br / 100)
|
|
147
|
+
_r, _g, _b = int(r*br), int(g*br), int(b*br)
|
|
148
|
+
neo_color(_r, _g, _b, smooth=False)
|
|
149
|
+
return r, g, b
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def neomatrix_update():
|
|
153
|
+
"""
|
|
154
|
+
DEMO - Send color codes for all neomatrix devices over espnow cluster
|
|
155
|
+
"""
|
|
156
|
+
r, g, b = indicator()
|
|
157
|
+
command = f"neomatrix color_fill {r} {g} {b}"
|
|
158
|
+
cluster_run(command)
|
|
159
|
+
return {"cmd": command, "cluster": "task show con.espnow.*"}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def neomatrix_animation():
|
|
163
|
+
"""
|
|
164
|
+
DEMO - Set random animation on neomatrix espnow cluster
|
|
165
|
+
"""
|
|
166
|
+
global CURRENT_ANIMATION_INDEX
|
|
167
|
+
animations = ('spiral', 'snake', 'noise')
|
|
168
|
+
|
|
169
|
+
next_animation = CURRENT_ANIMATION_INDEX + 1
|
|
170
|
+
CURRENT_ANIMATION_INDEX = 0 if next_animation >= len(animations) else next_animation
|
|
171
|
+
command = f"neomatrix {animations[CURRENT_ANIMATION_INDEX]}"
|
|
172
|
+
cluster_run(command)
|
|
173
|
+
return {"cmd": command, "cluster": "task show con.espnow.*"}
|
|
67
174
|
|
|
68
175
|
|
|
69
|
-
def help(
|
|
176
|
+
def help(widgets=False):
|
|
70
177
|
"""
|
|
71
178
|
TCS3472 Color sensor
|
|
72
179
|
"""
|
|
73
|
-
return 'load',
|
|
180
|
+
return resolve(('load led_pin=20',
|
|
181
|
+
'TEXTBOX measure',
|
|
182
|
+
'BUTTON led state=<True,False>',
|
|
183
|
+
'SLIDER led state=True br=<0-100-5>',
|
|
184
|
+
'indicator br=<0-100>',
|
|
185
|
+
'BUTTON neomatrix_update',
|
|
186
|
+
'BUTTON neomatrix_animation',
|
|
187
|
+
'pinmap'), widgets=widgets)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|