actionstreamer 0.4.4__tar.gz
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.
- actionstreamer-0.4.4/PKG-INFO +20 -0
- actionstreamer-0.4.4/README.md +3 -0
- actionstreamer-0.4.4/actionstreamer/CommonFunctions/CommonFunctions.py +254 -0
- actionstreamer-0.4.4/actionstreamer/CommonFunctions/Wifi/Wifi.py +93 -0
- actionstreamer-0.4.4/actionstreamer/CommonFunctions/Wifi/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/CommonFunctions/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/Config/Config.py +127 -0
- actionstreamer-0.4.4/actionstreamer/Config/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/Device/Device.py +143 -0
- actionstreamer-0.4.4/actionstreamer/Device/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/Model/Model.py +127 -0
- actionstreamer-0.4.4/actionstreamer/Model/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/API/API.py +55 -0
- actionstreamer-0.4.4/actionstreamer/WebService/API/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Event/Event.py +187 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Event/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/File/File.py +90 -0
- actionstreamer-0.4.4/actionstreamer/WebService/File/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Health/Health.py +73 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Health/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/LogMessage/LogMessage.py +37 -0
- actionstreamer-0.4.4/actionstreamer/WebService/LogMessage/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Patch/Patch.py +14 -0
- actionstreamer-0.4.4/actionstreamer/WebService/Patch/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/VideoClip/VideoClip.py +82 -0
- actionstreamer-0.4.4/actionstreamer/WebService/VideoClip/__init__.py +4 -0
- actionstreamer-0.4.4/actionstreamer/WebService/__init__.py +10 -0
- actionstreamer-0.4.4/actionstreamer/__init__.py +7 -0
- actionstreamer-0.4.4/pyproject.toml +17 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: actionstreamer
|
|
3
|
+
Version: 0.4.4
|
|
4
|
+
Summary: A library for the ActionStreamer API.
|
|
5
|
+
Author: ActionStreamer
|
|
6
|
+
Author-email: joe@actionstreamer.com
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Requires-Dist: pytz (>=2024.1,<2025.0)
|
|
13
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
14
|
+
Requires-Dist: wmi (>=1.5.1,<2.0.0) ; sys_platform == "win32"
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# ActionStreamer
|
|
18
|
+
|
|
19
|
+
A library for the ActionStreamer API.
|
|
20
|
+
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import hashlib
|
|
3
|
+
import hmac
|
|
4
|
+
import uuid
|
|
5
|
+
import time
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
import requests
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
import pytz
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
from ActionStreamer.Config import WebServiceConfig
|
|
15
|
+
|
|
16
|
+
class StandardResult:
|
|
17
|
+
|
|
18
|
+
def __init__(self, code: int, description: str):
|
|
19
|
+
self.Code = code
|
|
20
|
+
self.Description = description
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class switch(object):
|
|
24
|
+
|
|
25
|
+
def __init__(self, value):
|
|
26
|
+
self.value = value
|
|
27
|
+
self.fall = False
|
|
28
|
+
|
|
29
|
+
def __iter__(self):
|
|
30
|
+
"""Return the match method once, then stop"""
|
|
31
|
+
yield self.match
|
|
32
|
+
raise StopIteration
|
|
33
|
+
|
|
34
|
+
def match(self, *args) -> bool:
|
|
35
|
+
"""Indicate whether or not to enter a case suite"""
|
|
36
|
+
if self.fall or not args:
|
|
37
|
+
return True
|
|
38
|
+
elif self.value in args:
|
|
39
|
+
self.fall = True
|
|
40
|
+
return True
|
|
41
|
+
else:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_exception_info() -> tuple[str, int] | tuple[None, None]:
|
|
46
|
+
exception_type, exception_object, exception_traceback = sys.exc_info()
|
|
47
|
+
if exception_traceback is not None:
|
|
48
|
+
filename = exception_traceback.tb_frame.f_code.co_filename
|
|
49
|
+
line_number = exception_traceback.tb_lineno
|
|
50
|
+
return filename, line_number
|
|
51
|
+
return None, None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_line_number() -> int | None:
|
|
55
|
+
exception_type, exception_object, exception_traceback = sys.exc_info()
|
|
56
|
+
if exception_traceback is not None:
|
|
57
|
+
return exception_traceback.tb_lineno
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def log_to_console(message: str, agent_name='') -> None:
|
|
62
|
+
# Get the current UTC time
|
|
63
|
+
utc_now = datetime.now(pytz.utc)
|
|
64
|
+
|
|
65
|
+
# Format the UTC time
|
|
66
|
+
utc_time_formatted = utc_now.strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
67
|
+
|
|
68
|
+
# Prepend the formatted UTC time to the string
|
|
69
|
+
if (agent_name):
|
|
70
|
+
result_string = f"[{utc_time_formatted}]: [{agent_name}]: {message}"
|
|
71
|
+
else:
|
|
72
|
+
result_string = f"[{utc_time_formatted}]: {message}"
|
|
73
|
+
|
|
74
|
+
# Print the result to standard output
|
|
75
|
+
print(result_string)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def send_signed_request(ws_config: WebServiceConfig, method: str, url: str, path: str, headers: dict = None, parameters: str = None, body: str = None) -> tuple[int, str]:
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
if headers is None:
|
|
82
|
+
headers = {"Content-Type": "application/json"}
|
|
83
|
+
elif isinstance(headers, str):
|
|
84
|
+
headers = dict(header.strip().split(':', 1) for header in headers.split('\n'))
|
|
85
|
+
|
|
86
|
+
nonce = str(uuid.uuid4())
|
|
87
|
+
timestamp = str(int(time.time()))
|
|
88
|
+
|
|
89
|
+
headers['X-Nonce'] = nonce
|
|
90
|
+
headers['X-Timestamp'] = timestamp
|
|
91
|
+
headers['Authorization'] = 'HMAC-SHA256 ' + ws_config.access_key
|
|
92
|
+
headers['X-AccessKey'] = ws_config.access_key
|
|
93
|
+
|
|
94
|
+
# Generate HMAC signature
|
|
95
|
+
signature, string_to_sign = get_hmac_signature(ws_config.secret_key, method, path, headers, parameters, body)
|
|
96
|
+
|
|
97
|
+
# Include signature in headers
|
|
98
|
+
headers['X-Signature'] = signature
|
|
99
|
+
|
|
100
|
+
verify = not ws_config.ignore_ssl
|
|
101
|
+
|
|
102
|
+
if method.upper() == 'POST':
|
|
103
|
+
response = requests.post(url, headers=headers, data=body, verify=verify, timeout=ws_config.timeout)
|
|
104
|
+
elif method.upper() == 'GET':
|
|
105
|
+
response = requests.get(url, headers=headers, params=parameters, verify=verify, timeout=ws_config.timeout)
|
|
106
|
+
if method.upper() == 'PUT':
|
|
107
|
+
response = requests.put(url, headers=headers, data=body, verify=verify, timeout=ws_config.timeout)
|
|
108
|
+
elif method.upper() == 'PATCH':
|
|
109
|
+
response = requests.patch(url, headers=headers, data=body, params=parameters, verify=verify, timeout=ws_config.timeout)
|
|
110
|
+
elif method.upper() == 'DELETE':
|
|
111
|
+
response = requests.delete(url, headers=headers, params=parameters, verify=verify, timeout=ws_config.timeout)
|
|
112
|
+
|
|
113
|
+
status_code = response.status_code
|
|
114
|
+
response_string = response.content.decode('utf-8')
|
|
115
|
+
|
|
116
|
+
except Exception as ex:
|
|
117
|
+
filename, line_number = get_exception_info()
|
|
118
|
+
if filename is not None and line_number is not None:
|
|
119
|
+
print(f"Exception occurred at line {line_number} in {filename}")
|
|
120
|
+
print(ex)
|
|
121
|
+
status_code = -1
|
|
122
|
+
response_string = "Error in send_signed_request"
|
|
123
|
+
|
|
124
|
+
return status_code, response_string
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_hmac_signature(secret_key: str, method: str, path: str, headers, parameters: dict, body: str = None)-> tuple[str, str] | None:
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
if 'Content-Type' in headers:
|
|
131
|
+
del headers['Content-Type']
|
|
132
|
+
|
|
133
|
+
headerString = dictionary_to_string(headers)
|
|
134
|
+
parameterString = dictionary_to_string(parameters)
|
|
135
|
+
|
|
136
|
+
# Path should be in the format /v1/event
|
|
137
|
+
if not path.startswith('/'):
|
|
138
|
+
path = '/' + path
|
|
139
|
+
|
|
140
|
+
if path.endswith('/') and len(path) > 1:
|
|
141
|
+
path = path[:-1]
|
|
142
|
+
|
|
143
|
+
string_to_sign = '\n'.join([method, path, headerString, parameterString, body if body else ''])
|
|
144
|
+
|
|
145
|
+
string_to_sign = string_to_sign.strip()
|
|
146
|
+
#log_to_console("stringToSign: " + string_to_sign)
|
|
147
|
+
|
|
148
|
+
# Generate the HMAC SHA256 signature
|
|
149
|
+
hmac_signature = hmac.new(secret_key.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha256)
|
|
150
|
+
|
|
151
|
+
# Convert the HMAC signature to hexadecimal
|
|
152
|
+
return hmac_signature.hexdigest(), string_to_sign
|
|
153
|
+
|
|
154
|
+
except Exception as ex:
|
|
155
|
+
filename, line_number = get_exception_info()
|
|
156
|
+
if filename is not None and line_number is not None:
|
|
157
|
+
print(f"Exception occurred at line {line_number} in {filename}")
|
|
158
|
+
print(ex)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def dictionary_to_string(dictionary: dict) -> str:
|
|
162
|
+
|
|
163
|
+
result = ''
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
sorted_keys = sorted(dictionary.keys())
|
|
167
|
+
|
|
168
|
+
for key in sorted_keys:
|
|
169
|
+
result += f"{key}: {dictionary[key]}\n"
|
|
170
|
+
|
|
171
|
+
except Exception as ex:
|
|
172
|
+
filename, line_number = get_exception_info()
|
|
173
|
+
#if filename is not None and line_number is not None:
|
|
174
|
+
#print(f"Exception occurred at line {line_number} in {filename}")
|
|
175
|
+
#print(ex)
|
|
176
|
+
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def upload_file_to_s3(file_path: str, signed_url: str) -> int:
|
|
181
|
+
|
|
182
|
+
retry = True
|
|
183
|
+
result = 0
|
|
184
|
+
|
|
185
|
+
while retry:
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
with open(file_path, 'rb') as file:
|
|
189
|
+
response = requests.put(signed_url, data=file)
|
|
190
|
+
|
|
191
|
+
if response.status_code == 200:
|
|
192
|
+
retry = False
|
|
193
|
+
result = 0
|
|
194
|
+
else:
|
|
195
|
+
print(f"Error uploading file to S3. Status code: {response.status_code}")
|
|
196
|
+
result = -1
|
|
197
|
+
|
|
198
|
+
except Exception as ex:
|
|
199
|
+
print("Exception occurred while uploading file to S3:", str(ex))
|
|
200
|
+
result = -2
|
|
201
|
+
|
|
202
|
+
return result
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_sha256_hash_for_file(file_path: str) -> str:
|
|
206
|
+
# Initialize the hash object (SHA-256 is used in this example)
|
|
207
|
+
hash_object = hashlib.sha256()
|
|
208
|
+
|
|
209
|
+
# Open the file in binary mode to read its contents
|
|
210
|
+
with open(file_path, "rb") as file:
|
|
211
|
+
# Read the file in chunks to avoid loading the entire file into memory
|
|
212
|
+
for chunk in iter(lambda: file.read(4096), b""):
|
|
213
|
+
hash_object.update(chunk)
|
|
214
|
+
|
|
215
|
+
# Get the hexadecimal representation of the hash
|
|
216
|
+
file_hash = hash_object.hexdigest()
|
|
217
|
+
|
|
218
|
+
return file_hash
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def create_folders(path: str) -> None:
|
|
222
|
+
# Split the path into individual folders
|
|
223
|
+
folders = path.split(os.sep)
|
|
224
|
+
|
|
225
|
+
# Initialize the base folder to the root of the file system
|
|
226
|
+
base_folder = ""
|
|
227
|
+
|
|
228
|
+
# Loop through each folder in the path
|
|
229
|
+
for folder in folders:
|
|
230
|
+
# Append the current folder to the base folder
|
|
231
|
+
base_folder = os.path.join(base_folder, folder)
|
|
232
|
+
|
|
233
|
+
# Check if the current folder exists
|
|
234
|
+
if not os.path.exists(base_folder):
|
|
235
|
+
# If not, create the folder
|
|
236
|
+
os.makedirs(base_folder)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_cpu_frequency() -> float:
|
|
240
|
+
with open("/proc/cpuinfo") as f:
|
|
241
|
+
cpuinfo = f.read()
|
|
242
|
+
# Find the first occurrence of "cpu MHz"
|
|
243
|
+
match = re.search(r"cpu MHz\s+:\s+(\d+\.\d+)", cpuinfo)
|
|
244
|
+
if match:
|
|
245
|
+
return float(match.group(1))
|
|
246
|
+
else:
|
|
247
|
+
raise RuntimeError("Unable to find CPU frequency in /proc/cpuinfo")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def get_clock_cycles_per_millisecond() -> float:
|
|
251
|
+
frequency_mhz = get_cpu_frequency()
|
|
252
|
+
frequency_hz = frequency_mhz * 1_000_000
|
|
253
|
+
cycles_per_millisecond = frequency_hz / 1_000
|
|
254
|
+
return cycles_per_millisecond
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def add_wifi_connection(ssid: str, password: str, connection_name: str, priority=1) -> None:
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'add', 'type', 'wifi', 'ifname', 'wlan0', 'con-name', connection_name, 'ssid', ssid, 'connection.autoconnect-priority', str(priority)], check=True)
|
|
9
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'modify', connection_name, 'wifi-sec.key-mgmt', 'wpa-psk'], check=True)
|
|
10
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'modify', connection_name, 'wifi-sec.psk', password], check=True)
|
|
11
|
+
|
|
12
|
+
# Activate the connection
|
|
13
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'up', connection_name], check=True)
|
|
14
|
+
#print(f"Added and activated new connection: {connection_name}")
|
|
15
|
+
|
|
16
|
+
except subprocess.CalledProcessError as ex:
|
|
17
|
+
print(f"Failed to add connection: {connection_name}. Error: {ex}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def remove_wifi_connection(ssid: str) -> None:
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'delete', ssid], check=True)
|
|
24
|
+
print(f"Removed existing connection: {ssid}")
|
|
25
|
+
except subprocess.CalledProcessError as ex:
|
|
26
|
+
print(f"Failed to remove connection: {ssid}. It might not exist.")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def set_wifi_priority(ssid: str, priority: int) -> None:
|
|
30
|
+
|
|
31
|
+
# Example usage:
|
|
32
|
+
# set_wifi_priority("YourSSID", 100)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Get the UUID of the connection
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
['nmcli', '-g', 'uuid', 'connection', 'show', ssid],
|
|
38
|
+
capture_output=True, text=True, check=True
|
|
39
|
+
)
|
|
40
|
+
uuid = result.stdout.strip()
|
|
41
|
+
|
|
42
|
+
if not uuid:
|
|
43
|
+
raise ValueError(f"Connection {ssid} not found.")
|
|
44
|
+
|
|
45
|
+
# Set the autoconnect priority
|
|
46
|
+
subprocess.run(
|
|
47
|
+
['nmcli', 'connection', 'modify', uuid, 'connection.autoconnect-priority', str(priority)],
|
|
48
|
+
check=True
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Restart Network Manager to apply the changes
|
|
52
|
+
subprocess.run(['sudo', 'systemctl', 'restart', 'NetworkManager'], check=True)
|
|
53
|
+
|
|
54
|
+
print(f"Priority for {ssid} set to {priority}.")
|
|
55
|
+
|
|
56
|
+
except subprocess.CalledProcessError as ex:
|
|
57
|
+
print(f"An error occurred while running nmcli: {ex}")
|
|
58
|
+
|
|
59
|
+
except ValueError as ex:
|
|
60
|
+
print(ex)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def back_up_connections(backup_file_path: str) -> None:
|
|
64
|
+
# Backup all connection data to a JSON file
|
|
65
|
+
try:
|
|
66
|
+
result = subprocess.run(['sudo', 'nmcli', '--json', 'connection', 'export'], capture_output=True, text=True, check=True)
|
|
67
|
+
connections_data = json.loads(result.stdout)
|
|
68
|
+
|
|
69
|
+
with open(backup_file_path, 'w') as f:
|
|
70
|
+
json.dump(connections_data, f, indent=4)
|
|
71
|
+
|
|
72
|
+
print(f"Connections backed up to {backup_file_path}.")
|
|
73
|
+
|
|
74
|
+
except subprocess.CalledProcessError as ex:
|
|
75
|
+
print(f"Failed to backup connections. Error: {ex}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def restore_connections(backup_file_path: str) -> None:
|
|
79
|
+
# Restore all connections from a JSON backup file
|
|
80
|
+
try:
|
|
81
|
+
with open(backup_file_path, 'r') as f:
|
|
82
|
+
connections_data = json.load(f)
|
|
83
|
+
|
|
84
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'delete', 'id', 'all'], check=True)
|
|
85
|
+
|
|
86
|
+
for connection in connections_data:
|
|
87
|
+
subprocess.run(['sudo', 'nmcli', 'connection', 'import', 'json', json.dumps(connection)], check=True)
|
|
88
|
+
|
|
89
|
+
print(f"Connections restored from {backup_file_path}.")
|
|
90
|
+
|
|
91
|
+
except (subprocess.CalledProcessError, json.JSONDecodeError) as ex:
|
|
92
|
+
print(f"Failed to restore connections. Error: {ex}")
|
|
93
|
+
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
from .CommonFunctions import StandardResult, switch, get_exception_info, get_line_number, log_to_console, send_signed_request, get_hmac_signature, dictionary_to_string, upload_file_to_s3, get_sha256_hash_for_file, create_folders
|
|
2
|
+
from . import Wifi
|
|
3
|
+
|
|
4
|
+
__all__ = ['StandardResult', 'switch', 'get_exception_info', 'get_line_number', 'log_to_console', 'send_signed_request', 'get_hmac_signature', 'dictionary_to_string', 'upload_file_to_s3', 'get_sha256_hash_for_file', 'Wifi', 'create_folders']
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
class WebServiceConfig:
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Configuration for connecting to the web service.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
access_key (str): The access key for authentication.
|
|
11
|
+
secret_key (str): The secret key for authentication.
|
|
12
|
+
base_url (str): The base URL of the web service.
|
|
13
|
+
timeout (int): The timeout for requests in seconds.
|
|
14
|
+
ignore_ssl (bool): Whether to ignore SSL verification.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, access_key: str, secret_key: str, base_url: str, timeout: int = 30, ignore_ssl: bool = False):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the WebServiceConfig.
|
|
19
|
+
|
|
20
|
+
:param access_key: The access key for authentication.
|
|
21
|
+
:param secret_key: The secret key for authentication.
|
|
22
|
+
:param base_url: The base URL of the web service.
|
|
23
|
+
:param timeout: The timeout for requests in seconds (default is 30).
|
|
24
|
+
:param ignore_ssl: Whether to ignore SSL verification (default is False).
|
|
25
|
+
"""
|
|
26
|
+
self.access_key = access_key
|
|
27
|
+
self.secret_key = secret_key
|
|
28
|
+
self.base_url = base_url
|
|
29
|
+
self.timeout = timeout
|
|
30
|
+
self.ignore_ssl = ignore_ssl
|
|
31
|
+
|
|
32
|
+
class LogConfig:
|
|
33
|
+
|
|
34
|
+
def __init__(self, ws_config: WebServiceConfig, device_name: str, agent_type: str, agent_version: str, agent_index: int, process_id: int):
|
|
35
|
+
self.ws_config = ws_config
|
|
36
|
+
self.device_name = device_name
|
|
37
|
+
self.agent_type = agent_type
|
|
38
|
+
self.agent_version = agent_version
|
|
39
|
+
self.agent_index = agent_index
|
|
40
|
+
self.process_id = process_id
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_windows() -> bool:
|
|
44
|
+
return platform.system() == 'Windows'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_config_folder_path(app_name: str, base_folder_path: str = '') -> str:
|
|
48
|
+
|
|
49
|
+
if is_windows():
|
|
50
|
+
username = os.getlogin()
|
|
51
|
+
config_dir = os.path.join('C:\\Users', username, 'AppData', 'Roaming', app_name, "config")
|
|
52
|
+
# If a virtual environment is used, the path will be in something like:
|
|
53
|
+
# C:\Users\Username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\Roaming
|
|
54
|
+
|
|
55
|
+
else:
|
|
56
|
+
if base_folder_path:
|
|
57
|
+
config_dir = os.path.join(base_folder_path, ".config", app_name)
|
|
58
|
+
else:
|
|
59
|
+
config_dir = os.path.expanduser(os.path.join("~", ".config", app_name))
|
|
60
|
+
|
|
61
|
+
# Create the directory if it doesn't exist
|
|
62
|
+
if not os.path.exists(config_dir):
|
|
63
|
+
os.makedirs(config_dir)
|
|
64
|
+
|
|
65
|
+
return config_dir
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_appdata_folder_path(app_name: str, base_folder_path: str = '') -> str:
|
|
69
|
+
|
|
70
|
+
if is_windows():
|
|
71
|
+
username = os.getlogin()
|
|
72
|
+
appdata_folder_path = os.path.join('C:\\Users', username, 'AppData', 'Roaming', app_name)
|
|
73
|
+
# If a virtual environment is used, the path will be in something like:
|
|
74
|
+
# C:\Users\Username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\Roaming
|
|
75
|
+
else:
|
|
76
|
+
if base_folder_path:
|
|
77
|
+
appdata_folder_path = os.path.join(base_folder_path, ".appdata", app_name)
|
|
78
|
+
else:
|
|
79
|
+
appdata_folder_path = os.path.expanduser(os.path.join("~", ".appdata", app_name))
|
|
80
|
+
|
|
81
|
+
# Create the directory if it doesn't exist
|
|
82
|
+
if not os.path.exists(appdata_folder_path):
|
|
83
|
+
os.makedirs(appdata_folder_path)
|
|
84
|
+
|
|
85
|
+
return appdata_folder_path
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_config_value(config_folder_path: str, name: str, default_value: str = '') -> str | None:
|
|
89
|
+
|
|
90
|
+
if not os.path.exists(config_folder_path):
|
|
91
|
+
os.makedirs(config_folder_path)
|
|
92
|
+
|
|
93
|
+
file_path = os.path.join(config_folder_path, name + '.txt')
|
|
94
|
+
|
|
95
|
+
if not os.path.exists(file_path):
|
|
96
|
+
with open(file_path, 'w') as file:
|
|
97
|
+
file.write(default_value)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
with open(file_path, 'r') as file:
|
|
101
|
+
contents = file.read()
|
|
102
|
+
return contents
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
print(f"File '{name}' not found in the specified folder '{config_folder_path}'")
|
|
105
|
+
return None
|
|
106
|
+
except Exception as ex:
|
|
107
|
+
print(f"Error occurred while reading '{name}': {ex}")
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def set_config_value(config_folder_path: str, name: str, value: str) -> None:
|
|
112
|
+
|
|
113
|
+
if not os.path.exists(config_folder_path):
|
|
114
|
+
os.makedirs(config_folder_path)
|
|
115
|
+
|
|
116
|
+
# Create the directory if it doesn't exist
|
|
117
|
+
if not os.path.exists(config_folder_path):
|
|
118
|
+
os.makedirs(config_folder_path)
|
|
119
|
+
|
|
120
|
+
file_path = os.path.join(config_folder_path, name + '.txt')
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
with open(file_path, 'w') as file:
|
|
124
|
+
file.write(value)
|
|
125
|
+
print(f"Successfully set the value in '{name}'")
|
|
126
|
+
except Exception as ex:
|
|
127
|
+
print(f"Error occurred while setting value in '{name}': {ex}")
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
from .Config import WebServiceConfig, LogConfig, is_windows, get_config_folder_path, get_appdata_folder_path, get_config_value, set_config_value
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
__all__ = ['WebServiceConfig', 'LogConfig', 'is_windows', 'get_config_folder_path', 'get_appdata_folder_path', 'get_config_value', 'set_config_value']
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import getopt
|
|
2
|
+
import socket
|
|
3
|
+
import sys
|
|
4
|
+
import platform
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
def process_cpuinfo() -> str:
|
|
8
|
+
|
|
9
|
+
device_name = "0000000000000000"
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
optlist, args = getopt.getopt(sys.argv[1:], 'm:')
|
|
13
|
+
|
|
14
|
+
except getopt.GetoptError as err:
|
|
15
|
+
# Print help information and exit:
|
|
16
|
+
print(str(err)) # This will print something like "option -a not recognized"
|
|
17
|
+
return device_name
|
|
18
|
+
|
|
19
|
+
for option, argument in optlist:
|
|
20
|
+
if option == "-m":
|
|
21
|
+
device_name = argument
|
|
22
|
+
|
|
23
|
+
return device_name
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_serial_number() -> str:
|
|
27
|
+
|
|
28
|
+
serial_number = ""
|
|
29
|
+
manufacturer = ""
|
|
30
|
+
manufacturer, return_code = get_manufacturer()
|
|
31
|
+
|
|
32
|
+
if platform.system() == "Windows":
|
|
33
|
+
serial_number = get_cpu_serial_number_windows()
|
|
34
|
+
|
|
35
|
+
elif platform.system() == "Linux":
|
|
36
|
+
serial_number = get_cpu_serial_number_linux()
|
|
37
|
+
|
|
38
|
+
else:
|
|
39
|
+
serial_number = ""
|
|
40
|
+
|
|
41
|
+
return manufacturer + "_" + serial_number
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_ip_address() -> str:
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
# Get the local hostname
|
|
48
|
+
hostname = socket.gethostname()
|
|
49
|
+
# Get the IP address associated with the hostname
|
|
50
|
+
ip_address = socket.gethostbyname(hostname)
|
|
51
|
+
except socket.error:
|
|
52
|
+
# If an error occurs, return a default IP address
|
|
53
|
+
ip_address = "0.0.0.0"
|
|
54
|
+
|
|
55
|
+
return ip_address
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_cpu_serial_number_windows() -> str:
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
import wmi
|
|
62
|
+
wmi_object = wmi.WMI()
|
|
63
|
+
for processor in wmi_object.Win32_Processor():
|
|
64
|
+
return processor.ProcessorId.strip()
|
|
65
|
+
|
|
66
|
+
except Exception as ex:
|
|
67
|
+
return str(ex)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_cpu_serial_number_linux() -> str:
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
with open('/proc/cpuinfo', 'r') as f:
|
|
74
|
+
for line in f:
|
|
75
|
+
if line.strip().startswith("Serial"):
|
|
76
|
+
return line.strip().split(":")[1].strip()
|
|
77
|
+
|
|
78
|
+
except Exception as ex:
|
|
79
|
+
return str(ex)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_manufacturer() -> tuple[str, int]:
|
|
83
|
+
|
|
84
|
+
if platform.system() == "Windows":
|
|
85
|
+
return get_manufacturer_windows()
|
|
86
|
+
else:
|
|
87
|
+
return get_manufacturer_linux()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_manufacturer_windows() -> tuple[str, int]:
|
|
91
|
+
try:
|
|
92
|
+
import wmi
|
|
93
|
+
w = wmi.WMI()
|
|
94
|
+
for processor in w.Win32_Processor():
|
|
95
|
+
return parse_manufacturer(processor.Manufacturer.strip()), 0
|
|
96
|
+
except Exception as e:
|
|
97
|
+
print(f"Exception in get_manufacturer_windows: {e}")
|
|
98
|
+
return "", 1
|
|
99
|
+
|
|
100
|
+
return "", 1
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_manufacturer_linux() -> tuple[str, int]:
|
|
104
|
+
try:
|
|
105
|
+
with open("/proc/cpuinfo", "r") as f:
|
|
106
|
+
for line in f:
|
|
107
|
+
if line.startswith("Model"):
|
|
108
|
+
model = line.split(":")[1].strip()
|
|
109
|
+
if "Raspberry Pi" in model:
|
|
110
|
+
return parse_raspberry_pi_model(model), 0
|
|
111
|
+
# Fall back to using dmidecode if not a Raspberry Pi
|
|
112
|
+
result = subprocess.run(['dmidecode', '-t', 'processor'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
113
|
+
for line in result.stdout.splitlines():
|
|
114
|
+
if line.strip().startswith("Manufacturer"):
|
|
115
|
+
return parse_manufacturer(line.split(":")[1].strip()), 0
|
|
116
|
+
except Exception as e:
|
|
117
|
+
print(f"Exception in get_manufacturer_linux: {e}")
|
|
118
|
+
return "", 1
|
|
119
|
+
|
|
120
|
+
return "", 1
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def parse_raspberry_pi_model(model: str) -> str:
|
|
124
|
+
|
|
125
|
+
if "Raspberry Pi" in model:
|
|
126
|
+
parts = model.split()
|
|
127
|
+
if len(parts) >= 3 and parts[2].isdigit():
|
|
128
|
+
return f"RPi{parts[2]}"
|
|
129
|
+
return model
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def parse_manufacturer(manufacturer: str) -> str:
|
|
133
|
+
|
|
134
|
+
if "Intel" in manufacturer:
|
|
135
|
+
return "Intel"
|
|
136
|
+
elif "AMD" in manufacturer:
|
|
137
|
+
return "AMD"
|
|
138
|
+
elif "Broadcom" in manufacturer:
|
|
139
|
+
return "Broadcom"
|
|
140
|
+
elif "ARM" in manufacturer:
|
|
141
|
+
return "ARM"
|
|
142
|
+
else:
|
|
143
|
+
return manufacturer
|