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,13 @@
1
+ # VU Dials Community Applications
2
+
3
+ We will use this document to keep track of all the applications/scripts/services that you can use with VU dials.
4
+
5
+
6
+ # Windows Applications
7
+
8
+ 1. VU1 Demo App - [Download installer](https://vudials.com/download/demo_app) - [Source code](https://github.com/sasakaranovic/vu1-demo-app) - by Sasa Karanovic
9
+
10
+
11
+ # Linux Scripts
12
+
13
+ 1. Demo script for polling CPU usage - [Source code](https://github.com/SasaKaranovic/VU-Server/blob/master/Running_from_source_on_linux.md#example-command-line-usage) by Sasa Karanovic
@@ -0,0 +1,9 @@
1
+ server:
2
+ hostname: localhost
3
+ port: 5340
4
+ communication_timeout: 10
5
+ dial_update_period: 200
6
+ master_key: cTpAWYuRpA2zx75Yh961Cg
7
+
8
+ hardware:
9
+ port:
@@ -0,0 +1,256 @@
1
+ import os
2
+ import sqlite3
3
+ import random
4
+ from dials.base_logger import logger
5
+
6
+ class DialsDB:
7
+ connection = None
8
+ database_changes = 0
9
+
10
+ def __init__(self, database_file='vudials.db', init_if_missing=False):
11
+ # database_path = os.path.join(os.path.expanduser('~'), 'KaranovicResearch', 'vudials')
12
+ database_path = os.path.join(os.path.dirname(__file__))
13
+
14
+ if not os.path.exists(database_path):
15
+ os.makedirs(database_path)
16
+
17
+ self.database_file = os.path.join(database_path, database_file)
18
+ logger.info(f"VU1 Database file: {self.database_file}")
19
+
20
+ if not os.path.exists(self.database_file) and not init_if_missing:
21
+ raise SystemError("Database file does not exist!")
22
+
23
+ self.connection = sqlite3.connect(self.database_file)
24
+ self.connection.row_factory = sqlite3.Row
25
+
26
+ if init_if_missing:
27
+ self._init_database()
28
+
29
+ # -- Dial
30
+ def fetch_dial_info_or_create_default(self, dial_uid, dial_name='Not set'):
31
+
32
+ # check if dial exists
33
+ res = self._fetch_one_query(f"SELECT * FROM dials WHERE `dial_uid`='{dial_uid}' LIMIT 1")
34
+ if not res:
35
+ self._insert(f"INSERT INTO dials (`dial_uid`, `dial_name`) VALUES ('{dial_uid}', '{dial_name}')")
36
+ logger.debug(f"Added dial `{dial_uid}` to dial list with friendly name `{dial_name}`")
37
+ res = self._fetch_one_query(f"SELECT * FROM dials WHERE `dial_uid`='{dial_uid}' LIMIT 1")
38
+
39
+ return res
40
+
41
+ def dial_update_cell(self, dial_uid, cell, value):
42
+ logger.debug(f"Updating `{dial_uid}` to `{cell}`='{value}'")
43
+
44
+ logger.debug(f"Attempting to update `{dial_uid}` to `{cell}='{value}'")
45
+ self._insert(f"UPDATE dials SET `{cell}`='{value}' WHERE `dial_uid`='{dial_uid}'")
46
+
47
+ return self._more_than_one_changed()
48
+
49
+ def dial_update_cell_with_dict(self, dial_uid, values_dict):
50
+ if not isinstance(values_dict, dict):
51
+ logger.error(f"Expecting type(dictionary) but {type(values_dict)} given.")
52
+ return 0
53
+
54
+ logger.debug(f"Updating `{dial_uid}` to `{values_dict}'")
55
+
56
+ fields = ', '.join( f"`{key}`='{value}'" for key, value in values_dict.items())
57
+ query = f"UPDATE `dials` SET {fields} WHERE `dial_uid`='{dial_uid}'"
58
+ logger.debug(query)
59
+
60
+ logger.debug(f"Attempting to update `{dial_uid}` to `{values_dict}'")
61
+ self._insert(query)
62
+
63
+ return self._more_than_one_changed()
64
+
65
+ # -- API keys
66
+ def api_key_get_id(self, key):
67
+ res = self._fetch_one(table='api_keys', cell='key_id', where='key_uid', where_cmp=key, limit=1)
68
+ if not res:
69
+ return None
70
+ return res[0]
71
+
72
+ def api_key_list(self):
73
+ api_keys = {}
74
+ db_keys = self._fetch_all("SELECT * FROM api_keys")
75
+
76
+ if not db_keys:
77
+ return api_keys
78
+
79
+ for key in list(db_keys):
80
+ item = {}
81
+ item = {'key_name': key['key_name'], 'key_uid': key['key_uid'], 'priviledges': int(key['key_level'])}
82
+ item['dials'] = self.api_key_get_dial_access(key['key_id'])
83
+ api_keys[key['key_uid']] = item
84
+
85
+ return api_keys
86
+
87
+ def api_key_get_dial_access(self, key_id):
88
+ dials = []
89
+
90
+ key_access = self._fetch_all(f"SELECT `dial_uid` FROM `dial_access` WHERE `key_id`='{key_id}'")
91
+
92
+ if not key_access:
93
+ return dials
94
+
95
+ for item in key_access:
96
+ dials.append(item['dial_uid'])
97
+
98
+ return dials
99
+
100
+ def api_key_add_dial_access(self, key, dials):
101
+ key_id = self.api_key_get_id(key)
102
+ if not key_id:
103
+ return False
104
+
105
+ if not dials:
106
+ return False
107
+
108
+ # Wipe any existing entries that key has
109
+ self._query(f"DELETE FROM `dial_access` WHERE `key_id`={key_id}")
110
+
111
+ # Add dial access
112
+ for dial in dials:
113
+ self._insert(f"INSERT OR IGNORE INTO `dial_access` (dial_uid, key_id) VALUES ('{dial}', '{key_id}')")
114
+
115
+ return self._more_than_one_changed()
116
+
117
+
118
+ # Set master key to defined value (used to drive master key from .yaml file into sqlite database)
119
+ def api_update_master(self, new_key):
120
+ self._query(f"INSERT OR REPLACE INTO api_keys (key_id, key_name, key_uid, key_level) VALUES ('1', 'MASTER_KEY', '{new_key}', 99)")
121
+ return self._more_than_one_changed()
122
+
123
+ def api_key_generate(self, key_name='Not set', level=1):
124
+ generated_key = self.generate_api_key_str()
125
+ while self._fetch_one(table='api_keys', cell='key_id', where='key_uid', where_cmp=generated_key, limit=1):
126
+ generated_key = self.generate_api_key_str()
127
+
128
+ # self._insert(f"INSERT INTO api_keys (`key_uid`, `key_name`, `key_level`) VALUES ('{generated_key}', '{key_name}', '{level}')")
129
+ table_data = { 'key_uid': generated_key, 'key_name': key_name, 'key_level': level }
130
+ self._insert_dict('api_keys', table_data)
131
+ if self._more_than_one_changed():
132
+ return generated_key
133
+ raise SystemError("Failed to generate and store new API key to database!")
134
+
135
+ def generate_api_key_str(self):
136
+ s = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
137
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
138
+ return ''.join(random.sample(s, 16))
139
+
140
+ def api_key_update(self, key_uid, key_name=None, level=None):
141
+ # Find key in DB
142
+ key_id = self.api_key_get_id(key_uid)
143
+
144
+ query_update_level = ""
145
+ if level is not None:
146
+ query_update_level = f", `key_level`='{level}'"
147
+
148
+ # Rename key
149
+ if key_name is not None:
150
+ self._query(f"UPDATE `api_keys` SET `key_name`='{key_name}' {query_update_level} WHERE `key_id`='{key_id}'")
151
+ return self._more_than_one_changed()
152
+ return False
153
+
154
+ def api_key_delete(self, key_uid):
155
+ # Make sure we are not deleting master key!
156
+ res = self._fetch_one_query(f"SELECT `key_id` FROM `api_keys` WHERE `key_uid`='{key_uid}' AND `key_level` < '99' LIMIT 1")
157
+ key_id = res['key_id']
158
+ if not key_id:
159
+ return False
160
+
161
+ # Delete the KEY
162
+ query = f"DELETE FROM `api_keys` WHERE `key_id`='{key_id}'"
163
+ logger.debug(query)
164
+ self._query(query)
165
+ self._commit()
166
+ if self._more_than_one_changed():
167
+ # Delete dial access
168
+ self._query(f"DELETE FROM `dial_access` WHERE `key_id`='{key_id}'")
169
+ self._commit()
170
+
171
+ return self._more_than_one_changed()
172
+
173
+ return False
174
+
175
+
176
+ # -- Internal
177
+ def _insert_dict(self, table_name, dict_data):
178
+ cursor = self.connection.cursor()
179
+ attrib_names = ", ".join(dict_data.keys())
180
+ attrib_values = ", ".join("?" * len(dict_data.keys()))
181
+ sql = f"INSERT INTO {table_name} ({attrib_names}) VALUES ({attrib_values})"
182
+ cursor.execute(sql, list(dict_data.values()))
183
+ self._commit()
184
+
185
+ def _commit(self):
186
+ self.connection.commit()
187
+
188
+ def _insert(self, query):
189
+ self._query(query)
190
+ self.connection.commit()
191
+
192
+ def _query(self, query):
193
+ cursor = self.connection.cursor()
194
+ cursor.execute(query)
195
+
196
+ def _fetch_one(self, table, cell, where, where_cmp, limit=1):
197
+ cursor = self.connection.cursor()
198
+ query = f"SELECT {cell} FROM {table} WHERE {where} ='{where_cmp}' LIMIT {limit}"
199
+ logger.debug(query)
200
+ cursor.execute(query)
201
+ return cursor.fetchone()
202
+
203
+ def _fetch_one_query(self, query):
204
+ cursor = self.connection.cursor()
205
+ cursor.execute(query)
206
+ return cursor.fetchone()
207
+
208
+ def _fetch_all(self, query):
209
+ cursor = self.connection.cursor()
210
+ cursor.execute(query)
211
+ return cursor.fetchall()
212
+
213
+
214
+ def _more_than_one_changed(self):
215
+ if self.connection.total_changes > self.database_changes:
216
+ self.database_changes = self.connection.total_changes
217
+ return True
218
+ return False
219
+
220
+ def _init_database(self):
221
+ # Create DIALS table
222
+ self._query("""
223
+ CREATE TABLE IF NOT EXISTS dials (
224
+ "dial_id" INTEGER PRIMARY KEY AUTOINCREMENT,
225
+ "dial_uid" TEXT NOT NULL UNIQUE,
226
+ "dial_name" TEXT DEFAULT 'Not Set',
227
+ "dial_gen" TEXT DEFAULT 'VU1',
228
+ "dial_build_hash" TEXT DEFAULT '?',
229
+ "dial_fw_version" TEXT DEFAULT '?',
230
+ "dial_hw_version" TEXT DEFAULT '?',
231
+ "dial_protocol_version" TEXT DEFAULT 'V1',
232
+ "easing_dial_step" INTEGER DEFAULT 2,
233
+ "easing_dial_period" INTEGER DEFAULT 50,
234
+ "easing_backlight_step" INTEGER DEFAULT 5,
235
+ "easing_backlight_period" DEFAULT 100
236
+ )
237
+ """)
238
+
239
+ # Create API KEYS table
240
+ self._query("""
241
+ CREATE TABLE IF NOT EXISTS api_keys (
242
+ key_id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT ,
243
+ key_name TEXT,
244
+ key_uid TEXT NOT NULL UNIQUE,
245
+ key_level INTEGER)
246
+ """)
247
+
248
+ # Create DIAL ACCESS table
249
+ self._query("""
250
+ CREATE TABLE IF NOT EXISTS dial_access (
251
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
252
+ dial_uid TEXT NOT NULL,
253
+ key_id INTEGER NOT NULL)
254
+ """)
255
+
256
+ self.connection.commit()