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 CHANGED
@@ -3,7 +3,7 @@
3
3
  from importlib.metadata import version
4
4
 
5
5
  try:
6
- __version__ = version("publish-lib-example")
6
+ __version__ = version("smartx_rfid")
7
7
  except Exception:
8
8
  # Fallback for development environments
9
9
  __version__ = "0.0.0-dev"
@@ -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:
@@ -1,3 +1,3 @@
1
+ from .regex import regex_hex
1
2
  from .tag_list import TagList
2
3
  from .logger_manager import LoggerManager
3
- from .regex import regex_hex
@@ -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.warning(f"[ TAG ERROR ] {e}")
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
- "count": 1,
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
 
@@ -1 +1,2 @@
1
1
  from ._main import WebhookManager
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
- payload = {"device": device, "event_type": event_type, "event_data": event_data}
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smartx-rfid
3
- Version: 1.5.4
3
+ Version: 1.8.0
4
4
  Summary: SmartX RFID library
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,8 +1,8 @@
1
- smartx_rfid/__init__.py,sha256=XCdKdP8o5LT3VjJtWOuFHJzztc0FXt3Ms89qsx0p6sM,282
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=VL2yDBCXp8Avb3PXKZyBNwtBRhwlLQb5TSs3aTUeC6w,7458
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=XgHl2evlN3tGB6hhCUYzpEy_jw2dyplxUcDYsuPec2Q,2285
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=TllHEfkXJjmBwdwjPJqzbPn4hfCeqmTNOw6vHgzC-wU,101
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=_AP0ITue9JHTaDBGC2VYjXfr6qcJZQdEHm0Yp2sC_-8,6550
34
- smartx_rfid/webhook/__init__.py,sha256=IO_xPVNOfY8nsE60yajjbXqyqVR66BIFuQoNMuKY_j4,34
35
- smartx_rfid/webhook/_main.py,sha256=FyP5-jFnT9_27_3Sa3ltpeQ4FkC9vlajzzrObXQmmzY,3213
36
- smartx_rfid-1.5.4.dist-info/METADATA,sha256=QH-nnV_HQyWUhDq1uHeLbJM0fBZe0cnzatbNykHOJZM,8705
37
- smartx_rfid-1.5.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
38
- smartx_rfid-1.5.4.dist-info/licenses/LICENSE,sha256=npGJO3HuHM3JdFNEXv1jRmP_wRYq9-ujkqJPmLxrtEw,1080
39
- smartx_rfid-1.5.4.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any