micrOSDevToolKit 2.17.1__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.

Files changed (42) hide show
  1. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +15 -15
  2. micrOS/source/Config.py +2 -2
  3. micrOS/source/Espnow.py +29 -12
  4. micrOS/source/InterConnect.py +107 -31
  5. micrOS/source/Server.py +2 -3
  6. micrOS/source/Shell.py +1 -1
  7. micrOS/source/Tasks.py +22 -20
  8. micrOS/source/micrOSloader.py +1 -1
  9. micrOS/source/modules/LM_espnow.py +18 -1
  10. micrOS/source/modules/LM_neoeffects.py +1 -1
  11. micrOS/source/modules/LM_neomatrix.py +1 -1
  12. micrOS/source/modules/LM_oled_ui.py +4 -3
  13. micrOS/source/modules/LM_oledui.py +4 -3
  14. micrOS/source/modules/LM_tcs3472.py +131 -17
  15. micrOS/source/modules/LM_telegram.py +18 -18
  16. {microsdevtoolkit-2.17.1.dist-info → microsdevtoolkit-2.19.0.dist-info}/METADATA +3 -3
  17. {microsdevtoolkit-2.17.1.dist-info → microsdevtoolkit-2.19.0.dist-info}/RECORD +40 -42
  18. toolkit/DevEnvUSB.py +4 -1
  19. toolkit/Gateway.py +1 -1
  20. toolkit/dashboard_apps/SystemTest.py +22 -18
  21. toolkit/micrOSdashboard.py +8 -13
  22. toolkit/socketClient.py +27 -7
  23. toolkit/workspace/precompiled/Config.mpy +0 -0
  24. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  25. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  26. toolkit/workspace/precompiled/Server.mpy +0 -0
  27. toolkit/workspace/precompiled/Shell.mpy +0 -0
  28. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  29. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  30. toolkit/workspace/precompiled/modules/LM_espnow.py +18 -1
  31. toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
  32. toolkit/workspace/precompiled/modules/LM_neomatrix.mpy +0 -0
  33. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  34. toolkit/workspace/precompiled/modules/LM_oledui.mpy +0 -0
  35. toolkit/workspace/precompiled/modules/LM_tcs3472.py +131 -17
  36. toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
  37. micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
  38. micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
  39. {microsdevtoolkit-2.17.1.data → microsdevtoolkit-2.19.0.data}/scripts/devToolKit.py +0 -0
  40. {microsdevtoolkit-2.17.1.dist-info → microsdevtoolkit-2.19.0.dist-info}/WHEEL +0 -0
  41. {microsdevtoolkit-2.17.1.dist-info → microsdevtoolkit-2.19.0.dist-info}/licenses/LICENSE +0 -0
  42. {microsdevtoolkit-2.17.1.dist-info → microsdevtoolkit-2.19.0.dist-info}/top_level.txt +0 -0
@@ -6,17 +6,12 @@ import os
6
6
  import threading
7
7
  import subprocess
8
8
  import time
9
- from PyQt5.QtWidgets import QPushButton
10
- import PyQt5.QtCore as QtCore
11
- from PyQt5.QtCore import pyqtSlot
12
- from PyQt5.QtWidgets import *
13
- from PyQt5.QtGui import QIcon
14
- from PyQt5.QtGui import QTextCursor
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(QtCore.QSize(50, 50))
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(QtGui.QIcon(os.path.join(MYPATH, '../media/logo.png')))
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 = 30
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 = SocketDictClient(host=ip, port=port, silent_mode=True, tout=3).non_interactive(['version'])
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
- data_line_str = f"{base_info}{spacer1}{ip}\t{is_online}\t\t{version_data}{spacer2}{elapsed_time}"
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 - 14)
225
- print("{cols} [ UID ]{spr1}[ FUID ]\t\t[ IP ]\t\t[ STATUS ]\t[ VERSION ]\t[COMM SEC]{cole}"
226
- .format(spr1=spacer1, cols=Colors.OKBLUE + Colors.BOLD, cole=Colors.NC))
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
@@ -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
- 'stats')
51
+ 'remove peer=<binary-mac-address>',
52
+ 'stats',
53
+ 'members')
@@ -6,26 +6,54 @@ Copyright (c) 2021 tti0
6
6
  Licensed under the MIT License
7
7
  """
8
8
 
9
- from machine import I2C, Pin
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 struct
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, bus, address=0x29):
18
- self._bus = 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
- crgb = self.raw()
26
- if crgb[0] > 0:
27
- return tuple(float(x) / crgb[0] for x in crgb[1:])
28
- return 0, 0, 0
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 struct.unpack("<HHHH", self._bus.readfrom(self._i2c_address, 8))
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
- bus = I2C(sda=Pin(bind_pin('i2c_sda')), scl=Pin(bind_pin('i2c_scl')))
56
- TCS3472.INSTANCE = TCS3472(bus)
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
- return pinmap_search(['i2c_scl', 'i2c_sda'])
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
- return {"rgb": sensor.rgb(), "light": sensor.light(), "brightness": sensor.brightness()}
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(widgest=False):
176
+ def help(widgets=False):
70
177
  """
71
178
  TCS3472 Color sensor
72
179
  """
73
- return 'load', 'measure'
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)