smartx-rfid 1.5.4__py3-none-any.whl → 1.8.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.
- smartx_rfid/__init__.py +1 -1
- smartx_rfid/devices/RFID/R700_IOT/_main.py +0 -4
- smartx_rfid/devices/RFID/X714/rfid.py +4 -4
- smartx_rfid/utils/__init__.py +1 -1
- smartx_rfid/utils/tag_list.py +23 -7
- smartx_rfid/webhook/__init__.py +1 -0
- smartx_rfid/webhook/_main.py +66 -1
- smartx_rfid/webhook/xtrack.py +30 -0
- {smartx_rfid-1.5.4.dist-info → smartx_rfid-1.8.0.dist-info}/METADATA +1 -1
- {smartx_rfid-1.5.4.dist-info → smartx_rfid-1.8.0.dist-info}/RECORD +12 -11
- {smartx_rfid-1.5.4.dist-info → smartx_rfid-1.8.0.dist-info}/WHEEL +1 -1
- {smartx_rfid-1.5.4.dist-info → smartx_rfid-1.8.0.dist-info}/licenses/LICENSE +0 -0
smartx_rfid/__init__.py
CHANGED
|
@@ -145,10 +145,6 @@ class R700_IOT(OnEvent, ReaderHelpers, WriteCommands):
|
|
|
145
145
|
self.on_event(self.name, "connection", True)
|
|
146
146
|
await self.get_tag_list(session)
|
|
147
147
|
|
|
148
|
-
async def clear_tags(self):
|
|
149
|
-
"""Clear all stored tags from memory."""
|
|
150
|
-
self.tags = {}
|
|
151
|
-
|
|
152
148
|
async def write_gpo(
|
|
153
149
|
self,
|
|
154
150
|
pin: int = 1,
|
|
@@ -4,11 +4,11 @@ import asyncio
|
|
|
4
4
|
class RfidCommands:
|
|
5
5
|
"""RFID reader control commands for X714."""
|
|
6
6
|
|
|
7
|
-
def start_inventory(self):
|
|
7
|
+
async def start_inventory(self):
|
|
8
8
|
"""Start reading RFID tags."""
|
|
9
9
|
self.write("#READ:ON")
|
|
10
10
|
|
|
11
|
-
def stop_inventory(self):
|
|
11
|
+
async def stop_inventory(self):
|
|
12
12
|
"""Stop reading RFID tags."""
|
|
13
13
|
self.write("#READ:OFF")
|
|
14
14
|
|
|
@@ -70,9 +70,9 @@ class RfidCommands:
|
|
|
70
70
|
|
|
71
71
|
# Start Reading
|
|
72
72
|
if self.start_reading:
|
|
73
|
-
self.start_inventory()
|
|
73
|
+
asyncio.create_task(self.start_inventory())
|
|
74
74
|
else:
|
|
75
|
-
self.stop_inventory()
|
|
75
|
+
asyncio.create_task(self.stop_inventory())
|
|
76
76
|
|
|
77
77
|
async def auto_clear(self):
|
|
78
78
|
while True:
|
smartx_rfid/utils/__init__.py
CHANGED
smartx_rfid/utils/tag_list.py
CHANGED
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
from threading import Lock
|
|
4
4
|
import logging
|
|
5
5
|
from pyepc import SGTIN
|
|
6
|
+
from smartx_rfid.schemas.tag import TagSchema
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class TagList:
|
|
@@ -13,7 +14,7 @@ class TagList:
|
|
|
13
14
|
Each tag is uniquely identified by either EPC or TID.
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
|
-
def __init__(self, unique_identifier: Literal["epc", "tid"] = "epc"):
|
|
17
|
+
def __init__(self, unique_identifier: Literal["epc", "tid"] = "epc", prefix: str | list | None = None):
|
|
17
18
|
"""
|
|
18
19
|
Initialize the tag list.
|
|
19
20
|
|
|
@@ -27,6 +28,10 @@ class TagList:
|
|
|
27
28
|
self._tags: Dict[str, Dict[str, Any]] = {}
|
|
28
29
|
self._lock = Lock()
|
|
29
30
|
|
|
31
|
+
if isinstance(prefix, str):
|
|
32
|
+
prefix = [prefix]
|
|
33
|
+
self.prefix: list | None = prefix
|
|
34
|
+
|
|
30
35
|
def __len__(self) -> int:
|
|
31
36
|
"""
|
|
32
37
|
Return the number of stored tags.
|
|
@@ -55,20 +60,30 @@ class TagList:
|
|
|
55
60
|
(False, None) if an error occurs;
|
|
56
61
|
"""
|
|
57
62
|
try:
|
|
63
|
+
# Validate Tag
|
|
64
|
+
TagSchema.model_validate(tag)
|
|
65
|
+
|
|
58
66
|
identifier_value = tag.get(self.unique_identifier)
|
|
59
67
|
if not identifier_value:
|
|
60
68
|
raise ValueError(f"Tag missing '{self.unique_identifier}'")
|
|
61
69
|
|
|
70
|
+
# Check Prefix
|
|
71
|
+
if self.prefix is not None:
|
|
72
|
+
epc = tag.get("epc")
|
|
73
|
+
if epc is None or not any(epc.startswith(p) for p in self.prefix):
|
|
74
|
+
return False, None
|
|
75
|
+
|
|
76
|
+
# handle tag
|
|
62
77
|
with self._lock:
|
|
63
78
|
if identifier_value not in self._tags:
|
|
64
79
|
stored = self._new_tag(tag, device)
|
|
65
80
|
return True, stored
|
|
66
81
|
else:
|
|
67
|
-
stored = self._existing_tag(tag)
|
|
82
|
+
stored = self._existing_tag(tag, device)
|
|
68
83
|
return False, stored
|
|
69
84
|
|
|
70
85
|
except Exception as e:
|
|
71
|
-
logging.
|
|
86
|
+
logging.error(f"[ TAG ERROR ] {e}")
|
|
72
87
|
return False, None
|
|
73
88
|
|
|
74
89
|
def _new_tag(self, tag: Dict[str, Any], device: str) -> Dict[str, Any]:
|
|
@@ -90,18 +105,18 @@ class TagList:
|
|
|
90
105
|
gtin = None
|
|
91
106
|
|
|
92
107
|
stored_tag = {
|
|
93
|
-
**tag,
|
|
94
|
-
"device": device,
|
|
95
108
|
"timestamp": now,
|
|
96
|
-
"
|
|
109
|
+
"device": device,
|
|
110
|
+
**tag,
|
|
97
111
|
"gtin": gtin,
|
|
112
|
+
"count": 1,
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
self._tags[tag[self.unique_identifier]] = stored_tag
|
|
101
116
|
|
|
102
117
|
return stored_tag
|
|
103
118
|
|
|
104
|
-
def _existing_tag(self, tag: Dict[str, Any]) -> Dict[str, Any]:
|
|
119
|
+
def _existing_tag(self, tag: Dict[str, Any], device: str) -> Dict[str, Any]:
|
|
105
120
|
"""
|
|
106
121
|
Update an existing tag.
|
|
107
122
|
|
|
@@ -122,6 +137,7 @@ class TagList:
|
|
|
122
137
|
if old_rssi is None or abs(new_rssi) < abs(old_rssi):
|
|
123
138
|
current["rssi"] = new_rssi
|
|
124
139
|
current["ant"] = tag.get("ant")
|
|
140
|
+
current["device"] = device
|
|
125
141
|
|
|
126
142
|
return current
|
|
127
143
|
|
smartx_rfid/webhook/__init__.py
CHANGED
smartx_rfid/webhook/_main.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
from typing import Optional, Dict, Any
|
|
4
|
+
from datetime import datetime, date
|
|
5
|
+
from decimal import Decimal
|
|
4
6
|
|
|
5
7
|
import httpx
|
|
6
8
|
|
|
@@ -18,6 +20,66 @@ class WebhookManager:
|
|
|
18
20
|
|
|
19
21
|
self.default_headers = {"Content-Type": "application/json", "User-Agent": "SmartX-Connector/1.0"}
|
|
20
22
|
|
|
23
|
+
def _make_serializable(self, obj: Any) -> Any:
|
|
24
|
+
"""
|
|
25
|
+
Converts objects to JSON-serializable format.
|
|
26
|
+
|
|
27
|
+
Handles:
|
|
28
|
+
- datetime/date objects -> ISO format strings
|
|
29
|
+
- Decimal -> float
|
|
30
|
+
- Classes with __dict__ -> dict
|
|
31
|
+
- Sets -> lists
|
|
32
|
+
- Bytes -> string (decoded)
|
|
33
|
+
- Iterables -> lists
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
obj: Object to convert
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
JSON-serializable version of the object
|
|
40
|
+
"""
|
|
41
|
+
# Handle None
|
|
42
|
+
if obj is None:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
# Handle datetime and date
|
|
46
|
+
if isinstance(obj, (datetime, date)):
|
|
47
|
+
return obj.isoformat()
|
|
48
|
+
|
|
49
|
+
# Handle Decimal
|
|
50
|
+
if isinstance(obj, Decimal):
|
|
51
|
+
return float(obj)
|
|
52
|
+
|
|
53
|
+
# Handle bytes
|
|
54
|
+
if isinstance(obj, bytes):
|
|
55
|
+
try:
|
|
56
|
+
return obj.decode("utf-8")
|
|
57
|
+
except Exception:
|
|
58
|
+
return str(obj)
|
|
59
|
+
|
|
60
|
+
# Handle sets
|
|
61
|
+
if isinstance(obj, set):
|
|
62
|
+
return list(obj)
|
|
63
|
+
|
|
64
|
+
# Handle dictionaries (recursively)
|
|
65
|
+
if isinstance(obj, dict):
|
|
66
|
+
return {key: self._make_serializable(value) for key, value in obj.items()}
|
|
67
|
+
|
|
68
|
+
# Handle lists and tuples (recursively)
|
|
69
|
+
if isinstance(obj, (list, tuple)):
|
|
70
|
+
return [self._make_serializable(item) for item in obj]
|
|
71
|
+
|
|
72
|
+
# Handle objects with __dict__ (custom classes)
|
|
73
|
+
if hasattr(obj, "__dict__"):
|
|
74
|
+
return self._make_serializable(obj.__dict__)
|
|
75
|
+
|
|
76
|
+
# Handle primitive types (str, int, float, bool)
|
|
77
|
+
if isinstance(obj, (str, int, float, bool)):
|
|
78
|
+
return obj
|
|
79
|
+
|
|
80
|
+
# Last resort: convert to string
|
|
81
|
+
return str(obj)
|
|
82
|
+
|
|
21
83
|
async def post(
|
|
22
84
|
self, device: str, event_type: str, event_data: Any = None, headers: Optional[Dict[str, str]] = None
|
|
23
85
|
) -> bool:
|
|
@@ -38,7 +100,10 @@ class WebhookManager:
|
|
|
38
100
|
logging.warning("⚠️ WEBHOOK_URL not configured in settings")
|
|
39
101
|
return False
|
|
40
102
|
|
|
41
|
-
|
|
103
|
+
# Convert event_data to JSON-serializable format
|
|
104
|
+
serializable_event_data = self._make_serializable(event_data)
|
|
105
|
+
|
|
106
|
+
payload = {"device": device, "event_type": event_type, "event_data": serializable_event_data}
|
|
42
107
|
|
|
43
108
|
# Merge with custom headers if provided
|
|
44
109
|
if headers:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import httpx
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WebhookXtrack:
|
|
6
|
+
def __init__(self, url: str, timeout: int = 5):
|
|
7
|
+
self.url = url
|
|
8
|
+
self.timeout = timeout
|
|
9
|
+
|
|
10
|
+
async def post(self, tag: dict):
|
|
11
|
+
try:
|
|
12
|
+
device = tag.get("device", "unknown")
|
|
13
|
+
ant = tag.get("ant", "1")
|
|
14
|
+
epc = tag.get("epc", None)
|
|
15
|
+
if epc is None:
|
|
16
|
+
raise Exception("EPC is required")
|
|
17
|
+
payload = f"""<msg>
|
|
18
|
+
<command>ReportRead</command>
|
|
19
|
+
<data>EVENT=|DEVICENAME={device}|ANTENNANAME={ant}|TAGID={epc}|</data>
|
|
20
|
+
<cmpl>STATE=|DATA1=|DATA2=|DATA3=|DATA4=|DATA5=|</cmpl>
|
|
21
|
+
</msg>"""
|
|
22
|
+
async with httpx.AsyncClient() as client:
|
|
23
|
+
await client.post(
|
|
24
|
+
self.url,
|
|
25
|
+
content=payload,
|
|
26
|
+
headers={"Content-Type": "application/xml"},
|
|
27
|
+
timeout=self.timeout,
|
|
28
|
+
)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
logging.info(f"Error Xtrack: {e}")
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
smartx_rfid/__init__.py,sha256=
|
|
1
|
+
smartx_rfid/__init__.py,sha256=zNs6ASi3rUxNcqxcaNHx7DicO5Z1QHs3twVoxQZQCT4,274
|
|
2
2
|
smartx_rfid/clients/rchlo.py,sha256=z2xT9YnNEwkbHSeHsJD9BZvSJZ5QtnHZ9R7SpQ21Jxk,221
|
|
3
3
|
smartx_rfid/db/__init__.py,sha256=P8rsrVgc_5NDMH_p4FDs5OBLe3ohaQNR_sadVaeNvxA,35
|
|
4
4
|
smartx_rfid/db/_main.py,sha256=pKzJnkTb6VVSbKd8E34us4SAAKxyTGsE_diE_AxJShY,15450
|
|
5
|
-
smartx_rfid/devices/RFID/R700_IOT/_main.py,sha256=
|
|
5
|
+
smartx_rfid/devices/RFID/R700_IOT/_main.py,sha256=wBPW5j_8WB_f4AzVzEDIEjhiqCt8Jhhi9M8cVByefIE,7353
|
|
6
6
|
smartx_rfid/devices/RFID/R700_IOT/on_event.py,sha256=bG_33gY2ZHXquUbezXyMN0dyHO9SBwc36DPTc8iXc1U,799
|
|
7
7
|
smartx_rfid/devices/RFID/R700_IOT/reader_config_example.py,sha256=oi4vg3_zjYFDUeq1Y_d5InUx_Rq5M2CB5RDVkK3WCgU,2264
|
|
8
8
|
smartx_rfid/devices/RFID/R700_IOT/reader_helpers.py,sha256=8CvvYeKGplWqceVH5mEMN2_lA2Qgw7Wo1jpYuXLQlXo,5940
|
|
@@ -10,7 +10,7 @@ smartx_rfid/devices/RFID/R700_IOT/write_commands.py,sha256=GMROeq_daTScj4NZygF45
|
|
|
10
10
|
smartx_rfid/devices/RFID/X714/_main.py,sha256=LP1X_w6Sy9m5kHLvbaRUNZbzQJU37fS8OIEWvOfrk7M,6371
|
|
11
11
|
smartx_rfid/devices/RFID/X714/ble_protocol.py,sha256=M3Cs66WX6oa_EPdMTLEjD-IRxAjpfrKzGj4hn1vxlxQ,6951
|
|
12
12
|
smartx_rfid/devices/RFID/X714/on_receive.py,sha256=frrZIwSSDrsOjLO2GHdShVhoC-2VvHtr_AkJU91VDIo,2133
|
|
13
|
-
smartx_rfid/devices/RFID/X714/rfid.py,sha256
|
|
13
|
+
smartx_rfid/devices/RFID/X714/rfid.py,sha256=-YypTMhKuXJlzLYiMRvzTFL8bdvfJYTcpSMwp2TOEvI,2339
|
|
14
14
|
smartx_rfid/devices/RFID/X714/serial_protocol.py,sha256=eOGM5sK35SxXfS9KjB8JQzYhR1qs4H3vw3-n3oPOjQQ,3681
|
|
15
15
|
smartx_rfid/devices/RFID/X714/tcp_protocol.py,sha256=GthynyN6YMl6R16nrAnlrDK61QnuZAOhYhPqycBCwJA,4804
|
|
16
16
|
smartx_rfid/devices/RFID/X714/write_commands.py,sha256=tKLTATb4vDkghpw5rQuJwcnPNkViT4753fwToNPfKh0,1304
|
|
@@ -25,15 +25,16 @@ smartx_rfid/parser/rfid_tag_parser/exceptions.py,sha256=QsiXv0IHGYsLhYGgx2D-yE0j
|
|
|
25
25
|
smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py,sha256=uw-h7sYrH4Tfum_E9FyCRQKQMBlr5Igtjab99sJwxvA,23396
|
|
26
26
|
smartx_rfid/schemas/events.py,sha256=1iX0iSGirMqDbUAi2ir1CDhtVp8ROg-KZ6tY5nppWek,243
|
|
27
27
|
smartx_rfid/schemas/tag.py,sha256=haMvu_ALhkba8AIL-DrQN0egkRqeP3Wr8AyzyOnWH4Q,2242
|
|
28
|
-
smartx_rfid/utils/__init__.py,sha256=
|
|
28
|
+
smartx_rfid/utils/__init__.py,sha256=5--umtdkwvp7YuOFW34aHq1YymP72aFEX0XVg30BneY,101
|
|
29
29
|
smartx_rfid/utils/event.py,sha256=7QOdiSfiMscoAaTq4U3E3asYGSVnolr9OD3FSYGh6bg,469
|
|
30
30
|
smartx_rfid/utils/logger_manager.py,sha256=61HJ40wgX0hVwG4xW66IQ2jdJp8CFM_S0eoGzx8-FwU,5653
|
|
31
31
|
smartx_rfid/utils/path.py,sha256=eRxXKNzzDELna3USfGPNG1EPP2EkLlFxHmf21BMWAgA,3792
|
|
32
32
|
smartx_rfid/utils/regex.py,sha256=vxMR0PbZ6JxRHk2AAA9L3z98uZgrsVqslDFLcxC7P9Q,243
|
|
33
|
-
smartx_rfid/utils/tag_list.py,sha256=
|
|
34
|
-
smartx_rfid/webhook/__init__.py,sha256=
|
|
35
|
-
smartx_rfid/webhook/_main.py,sha256=
|
|
36
|
-
smartx_rfid
|
|
37
|
-
smartx_rfid-1.
|
|
38
|
-
smartx_rfid-1.
|
|
39
|
-
smartx_rfid-1.
|
|
33
|
+
smartx_rfid/utils/tag_list.py,sha256=Kf96VIhxAjAgHAMnQrtmnElcyY3b3DovAxydVuPKdWU,7123
|
|
34
|
+
smartx_rfid/webhook/__init__.py,sha256=cSJmsEI-iofZ1RQHHuPixyAQBts8cHepk7DY4nI-b7E,68
|
|
35
|
+
smartx_rfid/webhook/_main.py,sha256=gdOun2maK3iMfxCDYRLkkMp9NfmdRXbIrsXtmEv7arY,5118
|
|
36
|
+
smartx_rfid/webhook/xtrack.py,sha256=mU0akq4RxefloJcchhpmaG8SFdJ-Rert8G32X0oMikA,1068
|
|
37
|
+
smartx_rfid-1.8.0.dist-info/METADATA,sha256=3QXOy0qpKaQcEsZddG_fFXclXfxgvixEHPEMsSh9pWU,8705
|
|
38
|
+
smartx_rfid-1.8.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
39
|
+
smartx_rfid-1.8.0.dist-info/licenses/LICENSE,sha256=npGJO3HuHM3JdFNEXv1jRmP_wRYq9-ujkqJPmLxrtEw,1080
|
|
40
|
+
smartx_rfid-1.8.0.dist-info/RECORD,,
|
|
File without changes
|