benchlab-pytools 3.0.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.
Files changed (184) hide show
  1. benchlab/__init__.py +14 -0
  2. benchlab/__main__.py +21 -0
  3. benchlab/config/README.md +325 -0
  4. benchlab/config/__init__.py +9 -0
  5. benchlab/config/config_client.py +631 -0
  6. benchlab/config/config_manager.py +495 -0
  7. benchlab/config/config_tool.py +358 -0
  8. benchlab/config/diff.py +163 -0
  9. benchlab/config/examples/complete_config.json +56 -0
  10. benchlab/config/examples/rgb_lighting.json +24 -0
  11. benchlab/config/examples/simple_fan.json +32 -0
  12. benchlab/config/schema.py +138 -0
  13. benchlab/core/README.md +154 -0
  14. benchlab/core/__init__.py +78 -0
  15. benchlab/core/config.py +145 -0
  16. benchlab/core/datasource.py +1209 -0
  17. benchlab/core/datasource_manager.py +418 -0
  18. benchlab/core/device_registry.py +206 -0
  19. benchlab/core/discovery.py +92 -0
  20. benchlab/core/infrastructure.py +309 -0
  21. benchlab/core/process_manager.py +356 -0
  22. benchlab/core/retry.py +85 -0
  23. benchlab/core/shared_serial.py +235 -0
  24. benchlab/core/statistics.py +224 -0
  25. benchlab/csv_log/README.md +206 -0
  26. benchlab/csv_log/__init__.py +5 -0
  27. benchlab/csv_log/csv_logger_enhanced.py +416 -0
  28. benchlab/csv_log/message_batcher.py +361 -0
  29. benchlab/csv_log/requirements.txt +3 -0
  30. benchlab/csv_log/smart_retry.py +466 -0
  31. benchlab/graph/README.md +117 -0
  32. benchlab/graph/__init__.py +5 -0
  33. benchlab/graph/app.py +353 -0
  34. benchlab/graph/requirements.txt +2 -0
  35. benchlab/graph/runner.py +42 -0
  36. benchlab/graph/ui.py +288 -0
  37. benchlab/hwinfo/README.md +135 -0
  38. benchlab/hwinfo/__init__.py +5 -0
  39. benchlab/hwinfo/hwinfo_export.py +386 -0
  40. benchlab/hwinfo/requirements.txt +1 -0
  41. benchlab/launcher.py +387 -0
  42. benchlab/link/.env.EXAMPLE +10 -0
  43. benchlab/link/README.md +172 -0
  44. benchlab/link/__init__.py +1 -0
  45. benchlab/link/link_main.py +479 -0
  46. benchlab/main.py +462 -0
  47. benchlab/menu.py +729 -0
  48. benchlab/menu_classic.py +410 -0
  49. benchlab/mqtt/README.md +201 -0
  50. benchlab/mqtt/__init__.py +5 -0
  51. benchlab/mqtt/mqtt.config_template +2 -0
  52. benchlab/mqtt/mqtt_publisher.py +540 -0
  53. benchlab/mqtt/requirements.txt +3 -0
  54. benchlab/prefs.py +67 -0
  55. benchlab/restapi/.env.example +17 -0
  56. benchlab/restapi/__init__.py +5 -0
  57. benchlab/restapi/favicon.ico +0 -0
  58. benchlab/restapi/readme.md +202 -0
  59. benchlab/restapi/requirements.txt +5 -0
  60. benchlab/restapi/telemetry_api.py +808 -0
  61. benchlab/sources.py +515 -0
  62. benchlab/tools.py +147 -0
  63. benchlab/tui/README.md +266 -0
  64. benchlab/tui/__init__.py +5 -0
  65. benchlab/tui/config.py +477 -0
  66. benchlab/tui/requirements.txt +2 -0
  67. benchlab/tui/tui_core.py +1298 -0
  68. benchlab/tui/tui_main.py +418 -0
  69. benchlab/vu/README.md +64 -0
  70. benchlab/vu/VU-Server/NOTICE.md +13 -0
  71. benchlab/vu/VU-Server/README.md +80 -0
  72. benchlab/vu/VU-Server/Running_from_source_on_linux.md +97 -0
  73. benchlab/vu/VU-Server/__init__.py +0 -0
  74. benchlab/vu/VU-Server/assets/vu1_hello_world.png +0 -0
  75. benchlab/vu/VU-Server/community_applications.md +13 -0
  76. benchlab/vu/VU-Server/config.yaml +9 -0
  77. benchlab/vu/VU-Server/database.py +256 -0
  78. benchlab/vu/VU-Server/dial_driver.py +534 -0
  79. benchlab/vu/VU-Server/dials/Comms_Hub_Gauge.py +78 -0
  80. benchlab/vu/VU-Server/dials/Comms_Hub_Server.py +100 -0
  81. benchlab/vu/VU-Server/dials/__init__.py +0 -0
  82. benchlab/vu/VU-Server/dials/base_logger.py +100 -0
  83. benchlab/vu/VU-Server/installer/inc/icon.ico +0 -0
  84. benchlab/vu/VU-Server/installer/install.nsi +148 -0
  85. benchlab/vu/VU-Server/make_version.py +40 -0
  86. benchlab/vu/VU-Server/pylintrc +628 -0
  87. benchlab/vu/VU-Server/requirements.txt +9 -0
  88. benchlab/vu/VU-Server/serial_driver.py +244 -0
  89. benchlab/vu/VU-Server/server.py +670 -0
  90. benchlab/vu/VU-Server/server_config.py +255 -0
  91. benchlab/vu/VU-Server/server_dial_handler.py +334 -0
  92. benchlab/vu/VU-Server/vu_notifications.py +24 -0
  93. benchlab/vu/VU-Server/www/android-chrome-192x192.png +0 -0
  94. benchlab/vu/VU-Server/www/android-chrome-512x512.png +0 -0
  95. benchlab/vu/VU-Server/www/apple-touch-icon.png +0 -0
  96. benchlab/vu/VU-Server/www/assets/css/demo.css +291 -0
  97. benchlab/vu/VU-Server/www/assets/css/demo.min.css +9 -0
  98. benchlab/vu/VU-Server/www/assets/css/demo.rtl.css +276 -0
  99. benchlab/vu/VU-Server/www/assets/css/demo.rtl.min.css +9 -0
  100. benchlab/vu/VU-Server/www/assets/css/tabler-flags.css +1084 -0
  101. benchlab/vu/VU-Server/www/assets/css/tabler-flags.min.css +9 -0
  102. benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.css +1084 -0
  103. benchlab/vu/VU-Server/www/assets/css/tabler-flags.rtl.min.css +9 -0
  104. benchlab/vu/VU-Server/www/assets/css/tabler-payments.css +528 -0
  105. benchlab/vu/VU-Server/www/assets/css/tabler-payments.min.css +9 -0
  106. benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.css +528 -0
  107. benchlab/vu/VU-Server/www/assets/css/tabler-payments.rtl.min.css +9 -0
  108. benchlab/vu/VU-Server/www/assets/css/tabler-social.css +10 -0
  109. benchlab/vu/VU-Server/www/assets/css/tabler-social.min.css +8 -0
  110. benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.css +10 -0
  111. benchlab/vu/VU-Server/www/assets/css/tabler-social.rtl.min.css +8 -0
  112. benchlab/vu/VU-Server/www/assets/css/tabler-vendors.css +1504 -0
  113. benchlab/vu/VU-Server/www/assets/css/tabler-vendors.min.css +9 -0
  114. benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.css +1504 -0
  115. benchlab/vu/VU-Server/www/assets/css/tabler-vendors.rtl.min.css +9 -0
  116. benchlab/vu/VU-Server/www/assets/css/tabler.css +25813 -0
  117. benchlab/vu/VU-Server/www/assets/css/tabler.min.css +13 -0
  118. benchlab/vu/VU-Server/www/assets/css/tabler.rtl.css +25777 -0
  119. benchlab/vu/VU-Server/www/assets/css/tabler.rtl.min.css +13 -0
  120. benchlab/vu/VU-Server/www/assets/css/theme.min.css +261 -0
  121. benchlab/vu/VU-Server/www/assets/img/header.png +0 -0
  122. benchlab/vu/VU-Server/www/assets/img/logo.png +0 -0
  123. benchlab/vu/VU-Server/www/assets/js/dark-theme.js +35 -0
  124. benchlab/vu/VU-Server/www/assets/js/dark-theme.min.js +9 -0
  125. benchlab/vu/VU-Server/www/assets/js/demo.js +132 -0
  126. benchlab/vu/VU-Server/www/assets/js/demo.min.js +9 -0
  127. benchlab/vu/VU-Server/www/assets/js/jquery.min.js +2 -0
  128. benchlab/vu/VU-Server/www/assets/js/tabler.esm.js +7482 -0
  129. benchlab/vu/VU-Server/www/assets/js/tabler.esm.min.js +15 -0
  130. benchlab/vu/VU-Server/www/assets/js/tabler.js +7543 -0
  131. benchlab/vu/VU-Server/www/assets/js/tabler.min.js +15 -0
  132. benchlab/vu/VU-Server/www/assets/js/vu1_api.js +68 -0
  133. benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_key_settings.js +167 -0
  134. benchlab/vu/VU-Server/www/assets/js/vu1_gui_api_keys.js +138 -0
  135. benchlab/vu/VU-Server/www/assets/js/vu1_gui_dial.js +179 -0
  136. benchlab/vu/VU-Server/www/assets/js/vu1_gui_home.js +60 -0
  137. benchlab/vu/VU-Server/www/assets/js/vu1_gui_root.js +125 -0
  138. benchlab/vu/VU-Server/www/favicon-16x16.png +0 -0
  139. benchlab/vu/VU-Server/www/favicon-32x32.png +0 -0
  140. benchlab/vu/VU-Server/www/favicon.ico +0 -0
  141. benchlab/vu/VU-Server/www/favicon.png +0 -0
  142. benchlab/vu/VU-Server/www/index.html +47 -0
  143. benchlab/vu/VU-Server/www/views/api_keys.html +151 -0
  144. benchlab/vu/VU-Server/www/views/dial.html +286 -0
  145. benchlab/vu/VU-Server/www/views/footer.html +27 -0
  146. benchlab/vu/VU-Server/www/views/header.html +86 -0
  147. benchlab/vu/VU-Server/www/views/key_settings.html +132 -0
  148. benchlab/vu/VU-Server/www/views/start.html +124 -0
  149. benchlab/vu/__init__.py +5 -0
  150. benchlab/vu/assets/Barlow-Bold.ttf +0 -0
  151. benchlab/vu/assets/bl_dial_144x200.png +0 -0
  152. benchlab/vu/assets/bl_logo_144x200.png +0 -0
  153. benchlab/vu/devices.py +166 -0
  154. benchlab/vu/requirements.txt +24 -0
  155. benchlab/vu/sensors.py +18 -0
  156. benchlab/vu/vu_dial.config_template +90 -0
  157. benchlab/vu/vu_logo_gen.py +97 -0
  158. benchlab/vu/vu_server.config_template +5 -0
  159. benchlab/vu/vu_server_config.py +40 -0
  160. benchlab/vu/vu_server_manager.py +192 -0
  161. benchlab/vu/vu_tui.py +919 -0
  162. benchlab/vu/vu_updater.py +540 -0
  163. benchlab/wigidash/README.md +182 -0
  164. benchlab/wigidash/__init__.py +5 -0
  165. benchlab/wigidash/assets/Inter-Bold.ttf +0 -0
  166. benchlab/wigidash/assets/Inter.ttf +0 -0
  167. benchlab/wigidash/assets/benchlab.png +0 -0
  168. benchlab/wigidash/benchlab_fleet.py +150 -0
  169. benchlab/wigidash/benchlab_graph.py +517 -0
  170. benchlab/wigidash/benchlab_overview.py +426 -0
  171. benchlab/wigidash/benchlab_telemetry.py +105 -0
  172. benchlab/wigidash/benchlab_ui.py +190 -0
  173. benchlab/wigidash/benchlab_utils.py +182 -0
  174. benchlab/wigidash/requirements.txt +9 -0
  175. benchlab/wigidash/wigidash_device.py +541 -0
  176. benchlab/wigidash/wigidash_manager.py +342 -0
  177. benchlab/wigidash/wigidash_session.py +355 -0
  178. benchlab/wigidash/wigidash_usb.py +247 -0
  179. benchlab/wigidash/wigidash_widget.py +88 -0
  180. benchlab_pytools-3.0.0.dist-info/METADATA +239 -0
  181. benchlab_pytools-3.0.0.dist-info/RECORD +184 -0
  182. benchlab_pytools-3.0.0.dist-info/WHEEL +4 -0
  183. benchlab_pytools-3.0.0.dist-info/entry_points.txt +2 -0
  184. benchlab_pytools-3.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,132 @@
1
+ <!-- Page header -->
2
+ <div class="page-header d-print-none">
3
+ <div class="container-xl">
4
+ <div class="row g-2 align-items-center">
5
+ <div class="col">
6
+ <!-- Page pre-title -->
7
+ <div class="page-pretitle">
8
+ API Key Settings
9
+ </div>
10
+ <h2 class="page-title">
11
+ <span class="text-uppercase vu1-key-name"></span>
12
+ </h2>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </div>
17
+
18
+
19
+ <!-- Page body -->
20
+ <div class="page-body">
21
+ <div class="container-xl">
22
+ <div class="row row-deck row-cards">
23
+
24
+ <div class="col-12">
25
+ <div class="card">
26
+ <div class="card-header">
27
+ <h3 class="card-title">Update API key `<span class="vu1-key-name"></span>`</h3>
28
+ </div>
29
+
30
+ <div class="card-body" id="key-settings" >
31
+ <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
32
+ <div class="modal-content">
33
+ <div class="modal-body">
34
+
35
+ <div class="mb-3">
36
+ <label class="form-label">Key Name</label>
37
+ <input type="text" class="form-control vu1-key-name" id="input-key-name" name="modal-new-key-name" placeholder="Name the key to recognize it easier">
38
+ </div>
39
+
40
+ <div id="mb-3">
41
+
42
+ <div class="mb-3">
43
+ <div class="mb-3" id="modal-select-header">Select one or more dials that this API key will be able to control</div>
44
+ <div class="form-selectgroup" id="modal-dials-list">
45
+
46
+ </div>
47
+ </div>
48
+
49
+ <div class="mb-3">
50
+ <a href="#" class="btn btn-primary ms-auto" id="modal-select-all">
51
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-select-all" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
52
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v6a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path><path d="M12 20v.01"></path><path d="M16 20v.01"></path><path d="M8 20v.01"></path><path d="M4 20v.01"></path><path d="M4 16v.01"></path><path d="M4 12v.01"></path><path d="M4 8v.01"></path><path d="M4 4v.01"></path><path d="M8 4v.01"></path><path d="M12 4v.01"></path><path d="M16 4v.01"></path><path d="M20 4v.01"></path><path d="M20 8v.01"></path><path d="M20 12v.01"></path><path d="M20 16v.01"></path><path d="M20 20v.01"></path>
53
+ </svg>
54
+ Select all dials
55
+ </a>
56
+
57
+ <a href="#" class="btn btn-primary ms-auto" id="modal-deselect-all">
58
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-deselect" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
59
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
60
+ <path d="M12 8h3a1 1 0 0 1 1 1v3"></path>
61
+ <path d="M16 16h-7a1 1 0 0 1 -1 -1v-7"></path>
62
+ <path d="M12 20v.01"></path>
63
+ <path d="M16 20v.01"></path>
64
+ <path d="M8 20v.01"></path>
65
+ <path d="M4 20v.01"></path>
66
+ <path d="M4 16v.01"></path>
67
+ <path d="M4 12v.01"></path>
68
+ <path d="M4 8v.01"></path>
69
+ <path d="M8 4v.01"></path>
70
+ <path d="M12 4v.01"></path>
71
+ <path d="M16 4v.01"></path>
72
+ <path d="M20 4v.01"></path>
73
+ <path d="M20 8v.01"></path>
74
+ <path d="M20 12v.01"></path>
75
+ <path d="M20 16v.01"></path>
76
+ <path d="M3 3l18 18"></path>
77
+ </svg>
78
+ Select none
79
+ </a>
80
+ </div>
81
+
82
+ </div>
83
+
84
+ </div>
85
+ <div class="mb-4"> </div>
86
+ <div class="mb-3">
87
+
88
+ <div class="btn-list justify-content-end">
89
+ <a href="#" class="btn btn-danger" id="btn-delete-key">
90
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-trash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
91
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
92
+ <path d="M4 7l16 0"></path>
93
+ <path d="M10 11l0 6"></path>
94
+ <path d="M14 11l0 6"></path>
95
+ <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path>
96
+ <path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path>
97
+ </svg>
98
+ Delete
99
+ </a>
100
+
101
+ <a href="index.html?page=api_keys" class="btn ms-auto" id="submit-cancel-key">
102
+ <!-- Download SVG icon from http://tabler-icons.io/i/plus -->
103
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
104
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
105
+ <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
106
+ <path d="M10 10l4 4m0 -4l-4 4"></path>
107
+ </svg>
108
+ Cancel
109
+ </a>
110
+
111
+ <a href="#" class="btn btn-success justify-content-end" id="submit-update-key">
112
+ <!-- Download SVG icon from http://tabler-icons.io/i/plus -->
113
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-check-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
114
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
115
+ <path d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z" stroke-width="0" fill="currentColor"></path>
116
+ </svg>
117
+ Update key
118
+ </a>
119
+ </div>
120
+
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div> <!-- end Add new profile modal -->
125
+
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <script src="assets/js/vu1_gui_api_key_settings.js" defer></script>
@@ -0,0 +1,124 @@
1
+
2
+ <!-- Page header -->
3
+ <div class="page-header d-print-none">
4
+ <div class="container-xl">
5
+ <div class="row g-2 align-items-center">
6
+ <div class="col">
7
+ <!-- Page pre-title -->
8
+ <div class="page-pretitle">
9
+ Overview
10
+ </div>
11
+ <h2 class="page-title">
12
+ VU1 Dials
13
+ </h2>
14
+ </div>
15
+ <!-- Page title actions -->
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+
21
+ <!-- Page body -->
22
+ <div class="page-body">
23
+ <div class="container-xl">
24
+ <div class="row row-deck row-cards">
25
+ <div class="col-12">
26
+ <div class="row row-cards">
27
+ <div class="col-sm-6 col-lg-3">
28
+ <div class="card card-sm">
29
+ <div class="card-body">
30
+ <div class="row align-items-center">
31
+ <div class="col-auto">
32
+ <span class="bg-primary text-white avatar"><!-- Download SVG icon from http://tabler-icons.io/i/currency-dollar -->
33
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-needle" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
34
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
35
+ <path d="M3 21c-.667 -.667 3.262 -6.236 11.785 -16.709a3.5 3.5 0 1 1 5.078 4.791c-10.575 8.612 -16.196 12.585 -16.863 11.918z"></path>
36
+ <path d="M17.5 6.5l-1 1"></path>
37
+ </svg>
38
+ </span>
39
+ </div>
40
+ <div class="col">
41
+ <div class="font-weight-medium">
42
+ VU1 Dials
43
+ </div>
44
+ <div class="text-secondary" id="card-dial-count">
45
+ ?? Online
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <div class="col-sm-6 col-lg-3">
53
+ <div class="card card-sm">
54
+ <div class="card-body">
55
+ <div class="row align-items-center">
56
+ <div class="col-auto">
57
+ <span class="bg-green text-white avatar"><!-- Download SVG icon from http://tabler-icons.io/i/shopping-cart -->
58
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-key" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
59
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
60
+ <path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"></path>
61
+ <path d="M15 9h.01"></path>
62
+ </svg>
63
+ </span>
64
+ </div>
65
+ <div class="col">
66
+ <div class="font-weight-medium">
67
+ API keys
68
+ </div>
69
+ <div class="text-secondary" id="card-api-count">
70
+ ?? API keys
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="col-auto ms-auto d-print-none">
79
+ <div class="btn-list">
80
+ <a href="#" id="btn-search-for-dials" class="btn btn-primary d-none d-sm-inline-block">
81
+ <!-- Download SVG icon from http://tabler-icons.io/i/plus -->
82
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
83
+ Search for new dials
84
+ </a>
85
+ </div>
86
+ </div>
87
+
88
+
89
+ </div>
90
+ </div>
91
+
92
+
93
+ <div class="col-12">
94
+ <div class="card">
95
+ <div class="card-header">
96
+ <h3 class="card-title">VU1 Dials</h3>
97
+ </div>
98
+ <div class="table-responsive">
99
+ <table class="table card-table table-vcenter text-nowrap datatable" id="table_dials">
100
+ <thead>
101
+ <tr>
102
+ <th class="w-1">Unique ID
103
+ </th>
104
+ <th>Name</th>
105
+ <th></th>
106
+ </tr>
107
+ </thead>
108
+ <tbody></tbody>
109
+ </table>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <script src="assets/js/vu1_gui_home.js" defer></script>
118
+
119
+
120
+ <!-- Tabler Core -->
121
+ <script src="assets/js/tabler.js" defer></script>
122
+ <script src="assets/js/demo.min.js" defer></script>
123
+ <!-- <script src="assets/js/jquery.min.js" defer></script> -->
124
+ <script src="assets/js/vu1_api.js" defer></script>
@@ -0,0 +1,5 @@
1
+ """
2
+ BENCHLAB Telemetry Package
3
+ """
4
+
5
+ __version__ = "0.8.2"
Binary file
Binary file
Binary file
benchlab/vu/devices.py ADDED
@@ -0,0 +1,166 @@
1
+ # benchlab/vu/devices.py
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import time
7
+
8
+ import requests
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ DUMMY_UID = "0000000000000000"
13
+ DUMMY_DIAL = (DUMMY_UID, "No Dial")
14
+
15
+ CONFIG_PATH = os.path.join(os.path.dirname(__file__), "vu_server.config")
16
+ TEMPLATE_PATH = os.path.join(
17
+ os.path.dirname(__file__),
18
+ "vu_server.config_template")
19
+
20
+ _DEFAULT_VU_CONFIG = {
21
+ "vu_server_url": "http://localhost:5340",
22
+ "api_key": "",
23
+ "logo_file": ""}
24
+
25
+
26
+ def load_vu_server_config(
27
+ config_path=CONFIG_PATH,
28
+ template_path=TEMPLATE_PATH):
29
+ """Load vu_server.config, creating it from the template (or a hardcoded
30
+ default) if missing. Never raises — any failure at any step falls back
31
+ to _DEFAULT_VU_CONFIG so a malformed config or template can't crash
32
+ import of this module.
33
+ """
34
+ try:
35
+ with open(config_path, "r") as f:
36
+ return json.load(f)
37
+ except FileNotFoundError:
38
+ try:
39
+ if os.path.exists(template_path):
40
+ with open(template_path, "r") as f:
41
+ cfg = json.load(f)
42
+ logger.info("Created VU server config from template")
43
+ else:
44
+ cfg = dict(_DEFAULT_VU_CONFIG)
45
+ logger.info("Created default VU server config")
46
+ with open(config_path, "w") as f:
47
+ json.dump(cfg, f, indent=2)
48
+ return cfg
49
+ except Exception as e:
50
+ logger.error(f"Failed to load VU server config template: {e}")
51
+ return dict(_DEFAULT_VU_CONFIG)
52
+ except Exception as e:
53
+ logger.error(f"Failed to load VU server config: {e}")
54
+ return dict(_DEFAULT_VU_CONFIG)
55
+
56
+
57
+ # --- Load VU server config ---
58
+ VU_CONFIG = load_vu_server_config()
59
+
60
+ VU_SERVER_URL = VU_CONFIG.get("vu_server_url", "http://localhost:5340")
61
+ API_KEY = VU_CONFIG.get("api_key", "")
62
+
63
+
64
+ def get_benchlab_devices(datasource=None) -> list:
65
+ """Return list of {port, uid, name} dicts via datasource (no serial
66
+ access)."""
67
+ if datasource is None:
68
+ logger.warning("get_benchlab_devices called without a datasource")
69
+ return []
70
+ try:
71
+ raw = datasource.list_devices()
72
+ if isinstance(raw, dict):
73
+ return [{"port": info.get("port", "?"), "uid": uid,
74
+ "name": f"Benchlab {info.get('port', uid)}"}
75
+ for uid, info in raw.items()]
76
+ return [{"port": d.get("port", "?"), "uid": d.get("uid", "?"),
77
+ "name": f"Benchlab {d.get('port', d.get('uid', '?'))}"}
78
+ for d in raw]
79
+ except Exception as e:
80
+ logger.warning(f"list_devices failed: {e}")
81
+ return []
82
+
83
+
84
+ def get_vu_dials(vu_server_url=VU_SERVER_URL, api_key=API_KEY):
85
+ """Fetch VU dials from the server. Returns [(uid, dial_name), ...]."""
86
+ try:
87
+ response = requests.get(
88
+ f"{vu_server_url}/api/v0/dial/list",
89
+ params={"key": api_key},
90
+ timeout=2.0,
91
+ )
92
+ response.raise_for_status()
93
+ data = response.json().get("data", [])
94
+ if not data:
95
+ return [DUMMY_DIAL]
96
+ return [(d.get("uid", DUMMY_UID), d.get("dial_name", "No Dial"))
97
+ for d in data]
98
+ except requests.RequestException as e:
99
+ logger.error(f"VU server request failed: {e}")
100
+ return [DUMMY_DIAL]
101
+
102
+
103
+ def provision_vu_dials(vu_server_url=VU_SERVER_URL, api_key=API_KEY):
104
+ """Ask the VU hub to scan and provision new dials."""
105
+ try:
106
+ response = requests.get(
107
+ f"{vu_server_url}/api/v0/dial/provision",
108
+ params={"admin_key": api_key},
109
+ timeout=5.0,
110
+ )
111
+ response.raise_for_status()
112
+ data = response.json()
113
+ if data.get("status") == "ok":
114
+ logger.info("VU dial provisioning completed successfully.")
115
+ return True
116
+ logger.error(f"Provisioning failed: {data.get('message', 'Unknown')}")
117
+ return False
118
+ except requests.RequestException as e:
119
+ logger.error(f"Provisioning request failed: {e}")
120
+ return False
121
+
122
+
123
+ def provision_missing_vu_dials(datasource, vu_server_url=VU_SERVER_URL,
124
+ api_key=API_KEY, dry_run=False, max_wait=1.0):
125
+ """Provision dials that are physically connected but not yet on the hub."""
126
+ physical_devices = get_benchlab_devices(datasource)
127
+ vu_dials = get_vu_dials(vu_server_url, api_key)
128
+ vu_uids = {uid for uid, _ in vu_dials}
129
+ unprovisioned = [d for d in physical_devices if d["uid"] not in vu_uids]
130
+
131
+ if not unprovisioned:
132
+ logger.info("All physical dials are already provisioned.")
133
+ return []
134
+
135
+ logger.info(f"Found {len(unprovisioned)} unprovisioned dials: "
136
+ f"{[d['uid'] for d in unprovisioned]}")
137
+
138
+ if dry_run:
139
+ return [d["uid"] for d in unprovisioned]
140
+
141
+ if not provision_vu_dials(vu_server_url, api_key):
142
+ logger.error("Provisioning failed, new dials may not appear.")
143
+ return []
144
+
145
+ start = time.time()
146
+ newly_provisioned = []
147
+ while time.time() - start < max_wait:
148
+ updated_uids = {uid for uid, _ in get_vu_dials(vu_server_url, api_key)}
149
+ newly_provisioned = [
150
+ d["uid"]
151
+ for d in unprovisioned if d["uid"] in updated_uids]
152
+ if newly_provisioned:
153
+ break
154
+ time.sleep(0.1)
155
+
156
+ logger.info(f"Successfully provisioned: {newly_provisioned}")
157
+ return newly_provisioned
158
+
159
+
160
+ def vu_server_check(vu_server_url=VU_SERVER_URL, api_key=API_KEY, timeout=0.5):
161
+ try:
162
+ r = requests.get(f"{vu_server_url}/api/v0/dial/list",
163
+ params={"key": api_key}, timeout=timeout)
164
+ return r.status_code in (200, 403)
165
+ except requests.RequestException:
166
+ return False
@@ -0,0 +1,24 @@
1
+ # VU-Config
2
+ requests>=2.31.0
3
+ PyYAML>=6.0
4
+ blessed>=1.20.0
5
+ Pillow>=10.0.0
6
+ pyserial>=3.5
7
+
8
+ # VU-Server (runtime deps only -- pyinstaller and argparse dropped: pyinstaller
9
+ # is only needed to build a standalone VU-Server binary, not to run it, and
10
+ # argparse is a legacy PyPI shim for a module that's been in the stdlib since
11
+ # Python 3.2. Both were pulled in verbatim from the upstream VU-Server
12
+ # requirements.txt. Since `pip install -r` installs the whole file as one
13
+ # atomic command, a failure building pyinstaller's native extensions on a
14
+ # given Linux distro -- a known source of install friction for that package --
15
+ # would silently block installing tornado/pyserial/etc. that VU-Server
16
+ # actually needs, breaking -vu/-vuconfig with no error pointing at the real
17
+ # cause.
18
+ tornado
19
+ numpy
20
+ pillow
21
+ requests
22
+ pyyaml
23
+ ruamel.yaml
24
+ pyserial
benchlab/vu/sensors.py ADDED
@@ -0,0 +1,18 @@
1
+ # benchlab/vu/sensors.py
2
+
3
+
4
+ def get_available_sensors(snapshot: dict = None) -> list:
5
+ """Return sensor keys from a live datasource snapshot.
6
+
7
+ Falls back to an empty list if no snapshot is available yet.
8
+ """
9
+ if not snapshot:
10
+ return []
11
+ return [k for k in snapshot if k.lower() != "timestamp"]
12
+
13
+
14
+ def get_sensor_value(snapshot: dict, sensor_name: str):
15
+ """Return the value of sensor_name from a telemetry snapshot dict."""
16
+ if not snapshot or not sensor_name:
17
+ return None
18
+ return snapshot.get(sensor_name)
@@ -0,0 +1,90 @@
1
+ [
2
+ {
3
+ "dial_uid": "35002F000650564139323920",
4
+ "dial_name": "Dial 1",
5
+ "benchlab_port": "COM16",
6
+ "benchlab_uid": "2C003D001457435735363620",
7
+ "sensor": "Ambient_Temp",
8
+ "min": 20.0,
9
+ "max": 40.0,
10
+ "backlight": [
11
+ 100,
12
+ 100,
13
+ 100
14
+ ],
15
+ "easing_dial": [
16
+ 50,
17
+ 5
18
+ ],
19
+ "easing_backlight": [
20
+ 50,
21
+ 5
22
+ ]
23
+ },
24
+ {
25
+ "dial_uid": "890024000650564139323920",
26
+ "dial_name": "Dial 2",
27
+ "benchlab_port": "COM17",
28
+ "benchlab_uid": "unknown",
29
+ "sensor": "Ambient_Temp",
30
+ "min": 20.0,
31
+ "max": 40.0,
32
+ "backlight": [
33
+ 100,
34
+ 100,
35
+ 100
36
+ ],
37
+ "easing_dial": [
38
+ 50,
39
+ 5
40
+ ],
41
+ "easing_backlight": [
42
+ 50,
43
+ 5
44
+ ]
45
+ },
46
+ {
47
+ "dial_uid": "4A0038000650564139323920",
48
+ "dial_name": "Dial 3",
49
+ "benchlab_port": "COM17",
50
+ "benchlab_uid": "26001F001357435735363620",
51
+ "sensor": "Humidity",
52
+ "min": 25.0,
53
+ "max": 40.0,
54
+ "backlight": [
55
+ 100,
56
+ 100,
57
+ 100
58
+ ],
59
+ "easing_dial": [
60
+ 50,
61
+ 5
62
+ ],
63
+ "easing_backlight": [
64
+ 50,
65
+ 5
66
+ ]
67
+ },
68
+ {
69
+ "dial_uid": "4A0027000650564139323920",
70
+ "dial_name": "Dial 4",
71
+ "benchlab_port": "COM17",
72
+ "benchlab_uid": "26001F001357435735363620",
73
+ "sensor": "Humidity",
74
+ "min": 25.0,
75
+ "max": 35.0,
76
+ "backlight": [
77
+ 100,
78
+ 100,
79
+ 100
80
+ ],
81
+ "easing_dial": [
82
+ 50,
83
+ 5
84
+ ],
85
+ "easing_backlight": [
86
+ 50,
87
+ 5
88
+ ]
89
+ }
90
+ ]
@@ -0,0 +1,97 @@
1
+ # vu_logo_gen.py
2
+
3
+ from pathlib import Path
4
+ from PIL import Image, ImageDraw, ImageFont
5
+ import hashlib
6
+
7
+
8
+ def generate_sensor_logo(
9
+ template_path,
10
+ sensor_name,
11
+ min_val,
12
+ max_val,
13
+ benchlab_port=None):
14
+ """
15
+ Generate a VU sensor logo from a template, caching based on parameters.
16
+
17
+ Args:
18
+ template_path (Path or str): Path to the base image template.
19
+ sensor_name (str): Name of the sensor.
20
+ min_val (float): Minimum value.
21
+ max_val (float): Maximum value.
22
+ benchlab_port (str, optional): Benchlab port to display.
23
+
24
+ Returns:
25
+ Path: Path to the saved generated logo.
26
+ """
27
+ template_path = Path(template_path)
28
+ if not template_path.exists():
29
+ raise FileNotFoundError(f"Template not found: {template_path}")
30
+
31
+ # --- Create a unique filename based on parameters ---
32
+ params_str = f"{sensor_name}_{min_val}_{max_val}_{benchlab_port}"
33
+ # Use a hash to avoid extremely long filenames
34
+ hash_digest = hashlib.md5(params_str.encode("utf-8")).hexdigest()
35
+ safe_name = sensor_name.replace(" ", "_").replace("/", "-")
36
+
37
+ output_dir = Path(__file__).parent / "generated"
38
+ output_dir.mkdir(exist_ok=True)
39
+ output_path = output_dir / f"{safe_name}_{hash_digest}_logo.png"
40
+
41
+ # If file already exists, return cached version
42
+ if output_path.exists():
43
+ return output_path
44
+
45
+ # --- Load template and draw ---
46
+ img = Image.open(template_path).convert("L")
47
+ draw = ImageDraw.Draw(img)
48
+ width, height = img.size
49
+ header_height = 25
50
+
51
+ # Fonts
52
+ font_path = Path(__file__).parent / "assets/Barlow-Bold.ttf"
53
+ font_sensor = ImageFont.truetype(str(font_path), 20)
54
+ font_minmax = ImageFont.truetype(str(font_path), 12)
55
+
56
+ # Sensor name (top-center)
57
+ bbox = draw.textbbox((0, 0), sensor_name, font=font_sensor)
58
+ x = (width - (bbox[2] - bbox[0])) // 2
59
+ y = header_height + 10
60
+ draw.text((x, y), sensor_name, fill=0, font=font_sensor)
61
+
62
+ # Benchlab port (bottom-center)
63
+ if benchlab_port:
64
+ text_port = str(benchlab_port)
65
+ bbox_port = draw.textbbox((0, 0), text_port, font=font_minmax)
66
+ x_port = (width - (bbox_port[2] - bbox_port[0])) // 2
67
+ y_port = height - (bbox_port[3] - bbox_port[1]) - 20
68
+ draw.text((x_port, y_port), text_port, fill=0, font=font_minmax)
69
+
70
+ # Min (bottom-left)
71
+ text_min = f"Min: {min_val}"
72
+ bbox_min = draw.textbbox((0, 0), text_min, font=font_minmax)
73
+ y_min = height - (bbox_min[3] - bbox_min[1]) - 6
74
+ draw.text((2, y_min), text_min, fill=0, font=font_minmax)
75
+
76
+ # Max (bottom-right)
77
+ text_max = f"Max: {max_val}"
78
+ bbox_max = draw.textbbox((0, 0), text_max, font=font_minmax)
79
+ x_max = width - (bbox_max[2] - bbox_max[0]) - 2
80
+ y_max = height - (bbox_max[3] - bbox_max[1]) - 6
81
+ draw.text((x_max, y_max), text_max, fill=0, font=font_minmax)
82
+
83
+ # Save as 1-bit PNG
84
+ img.convert("1", dither=Image.NONE).save(output_path, format="PNG")
85
+ return output_path
86
+
87
+
88
+ # --- Quick test ---
89
+ if __name__ == "__main__":
90
+ template = Path(__file__).parent / "assets/bl_dial_144x200.png"
91
+ output = generate_sensor_logo(
92
+ template,
93
+ "Temp Sensor",
94
+ 0,
95
+ 100,
96
+ benchlab_port="35001")
97
+ print(f"Generated logo at: {output}")
@@ -0,0 +1,5 @@
1
+ {
2
+ "vu_server_url": "http://localhost:5340",
3
+ "api_key": "cTpAWYuRpA2zx75Yh961Cg",
4
+ "logo_file": "assets/bl_logo_144x200.png"
5
+ }