tinytoolslib 0.2.5__tar.gz → 0.3.2__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.
Files changed (24) hide show
  1. tinytoolslib-0.3.2/PKG-INFO +37 -0
  2. tinytoolslib-0.3.2/README.md +21 -0
  3. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/pyproject.toml +1 -1
  4. tinytoolslib-0.3.2/tinytoolslib/__init__.py +4 -0
  5. tinytoolslib-0.3.2/tinytoolslib/__version__.py +1 -0
  6. tinytoolslib-0.3.2/tinytoolslib/constants.py +40 -0
  7. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/discovery.py +62 -91
  8. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/flash.py +166 -65
  9. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/models.py +97 -78
  10. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/requests.py +53 -32
  11. tinytoolslib-0.3.2/tinytoolslib.egg-info/PKG-INFO +37 -0
  12. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib.egg-info/top_level.txt +2 -0
  13. tinytoolslib-0.2.5/PKG-INFO +0 -76
  14. tinytoolslib-0.2.5/README.md +0 -60
  15. tinytoolslib-0.2.5/tinytoolslib/__init__.py +0 -1
  16. tinytoolslib-0.2.5/tinytoolslib/__version__.py +0 -1
  17. tinytoolslib-0.2.5/tinytoolslib/constants.py +0 -14
  18. tinytoolslib-0.2.5/tinytoolslib.egg-info/PKG-INFO +0 -76
  19. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/setup.cfg +0 -0
  20. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/exceptions.py +0 -0
  21. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib/parsers.py +0 -0
  22. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib.egg-info/SOURCES.txt +0 -0
  23. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib.egg-info/dependency_links.txt +0 -0
  24. {tinytoolslib-0.2.5 → tinytoolslib-0.3.2}/tinytoolslib.egg-info/requires.txt +0 -0
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.1
2
+ Name: tinytoolslib
3
+ Version: 0.3.2
4
+ Summary: Set of tools for use with Tinycontrol devices like LK2.X, LK3.X, LK4.X or tcPDU.
5
+ Author-email: Bartek Barszczewski <tinycontrol.software@gmail.com>
6
+ Keywords: tinycontrol,lk,tcpdu
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: aiohttp<4,>=3.9.3
13
+ Requires-Dist: netifaces<1,>=0.11.0
14
+ Requires-Dist: requests<3,>=2.31.0
15
+ Requires-Dist: tftpy<1,>=0.8.2
16
+
17
+ # tinyToolsLib
18
+
19
+ Set of tools for use with tinycontrol devices like LK2.X, LK3.X, LK4.X or tcPDU.
20
+
21
+ ## Features
22
+
23
+ Easy to use functions for common actions with tinycontrol devices:
24
+
25
+ - Flashing firmware via TFTP (LK2.X, LK3.X).
26
+ - Flashing firmware via HTTP (LK4, tcPDU).
27
+ - Getting data from devices.
28
+ - Sending commands to devices (for common tasks like controlling OUTs, PWMs, etc.).
29
+ - Checking device version.
30
+
31
+ ## Usage
32
+
33
+ Usage is described in documentation.
34
+
35
+ ## Documentation
36
+
37
+ Documentation is available at <https://tinycontrol-software.gitlab.io/tinytoolslib/>
@@ -0,0 +1,21 @@
1
+ # tinyToolsLib
2
+
3
+ Set of tools for use with tinycontrol devices like LK2.X, LK3.X, LK4.X or tcPDU.
4
+
5
+ ## Features
6
+
7
+ Easy to use functions for common actions with tinycontrol devices:
8
+
9
+ - Flashing firmware via TFTP (LK2.X, LK3.X).
10
+ - Flashing firmware via HTTP (LK4, tcPDU).
11
+ - Getting data from devices.
12
+ - Sending commands to devices (for common tasks like controlling OUTs, PWMs, etc.).
13
+ - Checking device version.
14
+
15
+ ## Usage
16
+
17
+ Usage is described in documentation.
18
+
19
+ ## Documentation
20
+
21
+ Documentation is available at <https://tinycontrol-software.gitlab.io/tinytoolslib/>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tinytoolslib"
7
- version = "0.2.5"
7
+ version = "0.3.2"
8
8
  authors = [
9
9
  { name="Bartek Barszczewski", email="tinycontrol.software@gmail.com" },
10
10
  ]
@@ -0,0 +1,4 @@
1
+ from .__version__ import __version__
2
+ from .models import get_device, get_version
3
+ from .discovery import run_discovery
4
+ from .flash import run_flash, check_for_latest_firmware, get_latest_firmware
@@ -0,0 +1 @@
1
+ __version__ = '0.3.2'
@@ -0,0 +1,40 @@
1
+ """Constants related to tinycontrol devices."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ LK_UDP_PORT = 30403
7
+ """Port for UDP commands"""
8
+
9
+ LK_UDP_DISCOVERY_MSG = b"Discovery: Who is out there?"
10
+ """Message for Discovery query"""
11
+
12
+ LK_UDP_BOOTLOADER_MSG = b"\x12\xf4\x81"
13
+ """Message for starting bootloader/restart device"""
14
+
15
+
16
+ FW_URL_TEMPLATE = "https://tinycontrol.pl/firmware/{}/latest/"
17
+ """URL format for getting information about firmware from tinycontrol.pl"""
18
+
19
+
20
+ class FWUpdateMethod(str, Enum):
21
+ """Method used for updating FW.
22
+
23
+ HTTP - for LK4, tcPDU (generally ESP32 based ones)
24
+ TFTP - for LK3.5, LK3, LK2.5, LK2
25
+ """
26
+
27
+ TFTP = "TFTP"
28
+ HTTP = "HTTP"
29
+
30
+
31
+ class DeviceFamily(str, Enum):
32
+ """Device families.
33
+
34
+ PS and DCDC are pretty much the same as LK (UI differs)
35
+ """
36
+
37
+ LK = "LK"
38
+ PS = "PS" # Power socket
39
+ DCDC = "DCDC" # Converter DC/DC
40
+ TCPDU = "tcPDU"
@@ -1,99 +1,20 @@
1
+ """Discovery function for finding tinycontrol devices in the network.
2
+
3
+ Works with LK4, tcPPDU, LK3.5 SW 1.26+, LK2.5, LK2.0.
4
+ """
5
+
1
6
  import concurrent.futures
2
7
  import logging
3
8
  import socket
4
9
  import socketserver
5
- import threading
6
10
  import time
7
-
11
+ from typing import Any, Dict, List, Set, Union
8
12
  import netifaces
9
13
 
10
14
  from tinytoolslib.constants import LK_UDP_DISCOVERY_MSG, LK_UDP_PORT
11
15
  from tinytoolslib.models import detect_version, get_device_info
12
16
 
13
17
 
14
- def get_ips():
15
- """Return list of IPs to check."""
16
- try:
17
- gateways = netifaces.gateways()
18
- interfaces = []
19
- for key, value in gateways.items():
20
- if key != "default":
21
- for item in value:
22
- interfaces.append(item[1])
23
- addresses = set()
24
- for interface in interfaces:
25
- addresses_tmp = netifaces.ifaddresses(interface).get(2)
26
- if addresses_tmp:
27
- for addr in addresses_tmp:
28
- addresses.add(addr["addr"])
29
- except ValueError:
30
- addresses = socket.gethostbyname_ex(socket.gethostname())[2]
31
- return addresses
32
-
33
-
34
- def run_discovery_single(address, timelimit=3):
35
- """Run discovery for given address (tuple) in timelimit."""
36
- devices = []
37
- with DiscoveryServer(address, DiscoveryHandler) as server:
38
- server_thread = threading.Thread(target=server.serve_forever)
39
- server_thread.start()
40
- time.sleep(timelimit)
41
- server.shutdown()
42
- devices = server.devices.copy()
43
- del server_thread
44
- return devices
45
-
46
-
47
- def serve_forever(server):
48
- """Wrapper for serve_forever method of server."""
49
- server.serve_forever()
50
-
51
-
52
- def run_discovery_all(timelimit=3, port=LK_UDP_PORT, version=2, addresses=None):
53
- """Run discovery on all available addresses.
54
-
55
- `version` - 1 and 2 are parallel, where time execution of 2
56
- is closer to timelimit; 3 is sequential run;
57
- """
58
- if addresses is None:
59
- addresses = [ip for ip in get_ips() if not ip.startswith("169.254")]
60
- devices = []
61
- if version == 1:
62
- with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
63
- servers = [
64
- DiscoveryServer((address, port), DiscoveryHandler)
65
- for address in addresses
66
- ]
67
- executor.map(serve_forever, servers)
68
- time.sleep(timelimit)
69
- for server in servers:
70
- server.shutdown()
71
- devices.extend(server.devices)
72
- elif version == 2:
73
- with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
74
- servers = [
75
- DiscoveryServerAuto(
76
- (address, port), DiscoveryHandler, timelimit=timelimit
77
- )
78
- for address in addresses
79
- ]
80
- futures = {
81
- executor.submit(server.serve_forever): server for server in servers
82
- }
83
- for future in concurrent.futures.as_completed(futures):
84
- try:
85
- data = future.result()
86
- except Exception as exc:
87
- logging.warning("discovery error: %s", str(exc))
88
- else:
89
- devices.extend(data)
90
- else:
91
- for address in addresses:
92
- temp = run_discovery_single((address, port))
93
- devices.extend(temp)
94
- return devices
95
-
96
-
97
18
  class DiscoveryHandler(socketserver.BaseRequestHandler):
98
19
  """Handler for LK discovery server."""
99
20
 
@@ -147,21 +68,71 @@ class DiscoveryServer(socketserver.UDPServer):
147
68
 
148
69
 
149
70
  class DiscoveryServerAuto(DiscoveryServer):
150
- """DiscoveryServer that autmatically shuts down."""
71
+ """DiscoveryServer that automatically shuts down."""
151
72
 
152
73
  def __init__(self, *args, **kwargs):
153
- self.started_at = time.time()
154
- self.timelimit = kwargs.pop("timelimit", 3)
74
+ self.started_at: float = time.time()
75
+ """float: Timestamp of when discovery server started"""
76
+ self.time_limit: float = kwargs.pop("time_limit", 3)
77
+ """float: Limit of time for how long should discovery server run"""
155
78
  super().__init__(*args, **kwargs)
156
79
 
157
80
  def service_actions(self):
81
+ """Checks if should stop the server due to time_limit."""
158
82
  super().service_actions()
159
83
  now = time.time()
160
- if now - self.started_at >= self.timelimit:
161
- if not self._BaseServer__shutdown_request:
162
- self._BaseServer__shutdown_request = True
84
+ if now - self.started_at >= self.time_limit:
85
+ if not getattr(self, "_BaseServer__shutdown_request"):
86
+ setattr(self, "_BaseServer__shutdown_request", True)
163
87
 
164
88
  def serve_forever(self, *args, **kwargs):
165
89
  """Return devices list after auto shutdown."""
166
90
  super().serve_forever(*args, **kwargs)
167
91
  return self.devices
92
+
93
+
94
+ def get_ips() -> Set[str]:
95
+ """Return list of IPs to check."""
96
+ try:
97
+ gateways = netifaces.gateways()
98
+ interfaces = []
99
+ for key, value in gateways.items():
100
+ if key != "default":
101
+ for item in value:
102
+ interfaces.append(item[1])
103
+ addresses = set()
104
+ for interface in interfaces:
105
+ addresses_tmp = netifaces.ifaddresses(interface).get(2)
106
+ if addresses_tmp:
107
+ for addr in addresses_tmp:
108
+ addresses.add(addr["addr"])
109
+ except ValueError:
110
+ addresses = socket.gethostbyname_ex(socket.gethostname())[2]
111
+ return addresses
112
+
113
+
114
+ def run_discovery(
115
+ time_limit: int = 3,
116
+ port: int = LK_UDP_PORT,
117
+ addresses: Union[List[str], None] = None,
118
+ ) -> List[Dict[str, Any]]:
119
+ """Run discovery on all available addresses or given one."""
120
+ if addresses is None:
121
+ addresses = [ip for ip in get_ips() if not ip.startswith("169.254")]
122
+ devices = []
123
+ with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
124
+ servers = [
125
+ DiscoveryServerAuto(
126
+ (address, port), DiscoveryHandler, time_limit=time_limit
127
+ )
128
+ for address in addresses
129
+ ]
130
+ futures = {executor.submit(server.serve_forever): server for server in servers}
131
+ for future in concurrent.futures.as_completed(futures):
132
+ try:
133
+ data = future.result()
134
+ except Exception as exc:
135
+ logging.warning("discovery error: %s", str(exc))
136
+ else:
137
+ devices.extend(data)
138
+ return devices