smartx-rfid 1.6.0__tar.gz → 1.7.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.
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/PKG-INFO +1 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/pyproject.toml +1 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/tag_list.py +24 -8
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/webhook/__init__.py +1 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/webhook/_main.py +66 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/webhook/xtrack.py +6 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/LICENSE +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/README.md +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/__init__.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/clients/rchlo.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/db/__init__.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/db/_main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/_main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/on_event.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/reader_config_example.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/reader_helpers.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/write_commands.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/_main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/ble_protocol.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/on_receive.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/rfid.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/serial_protocol.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/tcp_protocol.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/write_commands.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/__init__.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/generic/SERIAL/_main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/generic/TCP/_main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/generic/TCP/helpers.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/__init__.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/main.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/rfid_tag_parser/__init__.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/rfid_tag_parser/exceptions.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/schemas/events.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/schemas/tag.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/__init__.py +1 -1
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/event.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/logger_manager.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/path.py +0 -0
- {smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/utils/regex.py +0 -0
|
@@ -3,7 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
from threading import Lock
|
|
4
4
|
import logging
|
|
5
5
|
from pyepc import SGTIN
|
|
6
|
-
|
|
6
|
+
from smartx_rfid.schemas.tag import TagSchema
|
|
7
7
|
|
|
8
8
|
class TagList:
|
|
9
9
|
"""
|
|
@@ -13,7 +13,7 @@ class TagList:
|
|
|
13
13
|
Each tag is uniquely identified by either EPC or TID.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
def __init__(self, unique_identifier: Literal["epc", "tid"] = "epc"):
|
|
16
|
+
def __init__(self, unique_identifier: Literal["epc", "tid"] = "epc", prefix: str | list | None = None):
|
|
17
17
|
"""
|
|
18
18
|
Initialize the tag list.
|
|
19
19
|
|
|
@@ -27,6 +27,10 @@ class TagList:
|
|
|
27
27
|
self._tags: Dict[str, Dict[str, Any]] = {}
|
|
28
28
|
self._lock = Lock()
|
|
29
29
|
|
|
30
|
+
if isinstance(prefix, str):
|
|
31
|
+
prefix = [prefix]
|
|
32
|
+
self.prefix: list | None = prefix
|
|
33
|
+
|
|
30
34
|
def __len__(self) -> int:
|
|
31
35
|
"""
|
|
32
36
|
Return the number of stored tags.
|
|
@@ -55,20 +59,30 @@ class TagList:
|
|
|
55
59
|
(False, None) if an error occurs;
|
|
56
60
|
"""
|
|
57
61
|
try:
|
|
62
|
+
# Validate Tag
|
|
63
|
+
TagSchema.model_validate(tag)
|
|
64
|
+
|
|
58
65
|
identifier_value = tag.get(self.unique_identifier)
|
|
59
66
|
if not identifier_value:
|
|
60
67
|
raise ValueError(f"Tag missing '{self.unique_identifier}'")
|
|
61
68
|
|
|
69
|
+
# Check Prefix
|
|
70
|
+
if self.prefix is not None:
|
|
71
|
+
epc = tag.get("epc")
|
|
72
|
+
if epc is None or not any(epc.startswith(p) for p in self.prefix):
|
|
73
|
+
return False, None
|
|
74
|
+
|
|
75
|
+
# handle tag
|
|
62
76
|
with self._lock:
|
|
63
77
|
if identifier_value not in self._tags:
|
|
64
78
|
stored = self._new_tag(tag, device)
|
|
65
79
|
return True, stored
|
|
66
80
|
else:
|
|
67
|
-
stored = self._existing_tag(tag)
|
|
81
|
+
stored = self._existing_tag(tag, device)
|
|
68
82
|
return False, stored
|
|
69
83
|
|
|
70
84
|
except Exception as e:
|
|
71
|
-
logging.
|
|
85
|
+
logging.error(f"[ TAG ERROR ] {e}")
|
|
72
86
|
return False, None
|
|
73
87
|
|
|
74
88
|
def _new_tag(self, tag: Dict[str, Any], device: str) -> Dict[str, Any]:
|
|
@@ -90,18 +104,18 @@ class TagList:
|
|
|
90
104
|
gtin = None
|
|
91
105
|
|
|
92
106
|
stored_tag = {
|
|
93
|
-
**tag,
|
|
94
|
-
"device": device,
|
|
95
107
|
"timestamp": now,
|
|
96
|
-
"
|
|
108
|
+
"device": device,
|
|
109
|
+
**tag,
|
|
97
110
|
"gtin": gtin,
|
|
111
|
+
"count": 1,
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
self._tags[tag[self.unique_identifier]] = stored_tag
|
|
101
115
|
|
|
102
116
|
return stored_tag
|
|
103
117
|
|
|
104
|
-
def _existing_tag(self, tag: Dict[str, Any]) -> Dict[str, Any]:
|
|
118
|
+
def _existing_tag(self, tag: Dict[str, Any], device: str) -> Dict[str, Any]:
|
|
105
119
|
"""
|
|
106
120
|
Update an existing tag.
|
|
107
121
|
|
|
@@ -122,6 +136,8 @@ class TagList:
|
|
|
122
136
|
if old_rssi is None or abs(new_rssi) < abs(old_rssi):
|
|
123
137
|
current["rssi"] = new_rssi
|
|
124
138
|
current["ant"] = tag.get("ant")
|
|
139
|
+
current['device'] = device
|
|
140
|
+
|
|
125
141
|
|
|
126
142
|
return current
|
|
127
143
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from ._main import WebhookManager
|
|
2
|
-
from .xtrack import WebhookXtrack
|
|
2
|
+
from .xtrack import WebhookXtrack
|
|
@@ -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:
|
|
@@ -9,9 +9,14 @@ class WebhookXtrack:
|
|
|
9
9
|
|
|
10
10
|
async def post(self, tag: dict):
|
|
11
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")
|
|
12
17
|
payload = f"""<msg>
|
|
13
18
|
<command>ReportRead</command>
|
|
14
|
-
<data>EVENT=|DEVICENAME={
|
|
19
|
+
<data>EVENT=|DEVICENAME={device}|ANTENNANAME={ant}|TAGID={epc}|</data>
|
|
15
20
|
<cmpl>STATE=|DATA1=|DATA2=|DATA3=|DATA4=|DATA5=|</cmpl>
|
|
16
21
|
</msg>"""
|
|
17
22
|
async with httpx.AsyncClient() as client:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/reader_helpers.py
RENAMED
|
File without changes
|
{smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/R700_IOT/write_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/devices/RFID/X714/serial_protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/rfid_tag_parser/exceptions.py
RENAMED
|
File without changes
|
{smartx_rfid-1.6.0 → smartx_rfid-1.7.4}/src/smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|