smartx-rfid 1.1.1__py3-none-any.whl → 1.5.4__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/clients/rchlo.py +9 -0
- smartx_rfid/db/__init__.py +1 -0
- smartx_rfid/db/_main.py +432 -0
- smartx_rfid/devices/RFID/X714/on_receive.py +0 -3
- smartx_rfid/parser/__init__.py +1 -0
- smartx_rfid/parser/main.py +27 -0
- smartx_rfid/parser/rfid_tag_parser/__init__.py +15 -0
- smartx_rfid/parser/rfid_tag_parser/exceptions.py +15 -0
- smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py +674 -0
- smartx_rfid/schemas/events.py +7 -0
- smartx_rfid/schemas/tag.py +4 -4
- smartx_rfid/utils/__init__.py +1 -0
- smartx_rfid/utils/path.py +2 -2
- smartx_rfid/utils/regex.py +9 -0
- smartx_rfid/utils/tag_list.py +53 -18
- smartx_rfid/webhook/__init__.py +1 -0
- smartx_rfid/webhook/_main.py +88 -0
- smartx_rfid-1.5.4.dist-info/METADATA +344 -0
- {smartx_rfid-1.1.1.dist-info → smartx_rfid-1.5.4.dist-info}/RECORD +21 -9
- smartx_rfid-1.1.1.dist-info/METADATA +0 -83
- {smartx_rfid-1.1.1.dist-info → smartx_rfid-1.5.4.dist-info}/WHEEL +0 -0
- {smartx_rfid-1.1.1.dist-info → smartx_rfid-1.5.4.dist-info}/licenses/LICENSE +0 -0
smartx_rfid/schemas/tag.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import re
|
|
2
1
|
from typing import Optional
|
|
3
2
|
from pydantic import BaseModel, Field, field_validator
|
|
3
|
+
from smartx_rfid.utils import regex_hex
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class TagSchema(BaseModel):
|
|
@@ -15,7 +15,7 @@ class TagSchema(BaseModel):
|
|
|
15
15
|
return v
|
|
16
16
|
if len(v) != 24:
|
|
17
17
|
raise ValueError(f"{field} must have exactly 24 characters")
|
|
18
|
-
if not
|
|
18
|
+
if not regex_hex(v, 24):
|
|
19
19
|
raise ValueError(f"{field} must contain only hexadecimal characters (0-9, a-f)")
|
|
20
20
|
return v.lower()
|
|
21
21
|
|
|
@@ -42,7 +42,7 @@ class WriteTagValidator(BaseModel):
|
|
|
42
42
|
|
|
43
43
|
if len(v) != 24:
|
|
44
44
|
raise ValueError(f"{field} must have exactly 24 characters")
|
|
45
|
-
if not
|
|
45
|
+
if not regex_hex(v, 24):
|
|
46
46
|
raise ValueError(f"{field} must contain only hexadecimal characters (0-9, a-f)")
|
|
47
47
|
return v.lower()
|
|
48
48
|
|
|
@@ -50,6 +50,6 @@ class WriteTagValidator(BaseModel):
|
|
|
50
50
|
def validate_password_length_and_hex(cls, v, field):
|
|
51
51
|
if len(v) != 8:
|
|
52
52
|
raise ValueError(f"{field} must have exactly 8 characters")
|
|
53
|
-
if not
|
|
53
|
+
if not regex_hex(v, 8):
|
|
54
54
|
raise ValueError(f"{field} must contain only hexadecimal characters (0-9, a-f)")
|
|
55
55
|
return v.lower()
|
smartx_rfid/utils/__init__.py
CHANGED
smartx_rfid/utils/path.py
CHANGED
|
@@ -84,10 +84,10 @@ def include_all_routers(current_path: str, app) -> None:
|
|
|
84
84
|
logging.info(f"✅ Route loaded: {relative_path}")
|
|
85
85
|
|
|
86
86
|
else:
|
|
87
|
-
logging.warning(f"⚠️ File {current_path} does not define a 'router'")
|
|
87
|
+
logging.warning(f"⚠️ File {current_path}/{file_path.name} does not define a 'router'")
|
|
88
88
|
|
|
89
89
|
except Exception as e:
|
|
90
|
-
logging.error(f"❌ Error loading {current_path}: {e}", exc_info=True)
|
|
90
|
+
logging.error(f"❌ Error loading {current_path}/{file_path.name}: {e}", exc_info=True)
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
def load_file(file_path: str | Path) -> str:
|
smartx_rfid/utils/tag_list.py
CHANGED
|
@@ -27,6 +27,24 @@ class TagList:
|
|
|
27
27
|
self._tags: Dict[str, Dict[str, Any]] = {}
|
|
28
28
|
self._lock = Lock()
|
|
29
29
|
|
|
30
|
+
def __len__(self) -> int:
|
|
31
|
+
"""
|
|
32
|
+
Return the number of stored tags.
|
|
33
|
+
"""
|
|
34
|
+
return len(self._tags)
|
|
35
|
+
|
|
36
|
+
def __contains__(self, identifier: str) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Check if a tag identifier exists in the list.
|
|
39
|
+
"""
|
|
40
|
+
return identifier in self._tags
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
"""
|
|
44
|
+
Return a string representation of the stored tags.
|
|
45
|
+
"""
|
|
46
|
+
return repr(self.get_all())
|
|
47
|
+
|
|
30
48
|
def add(self, tag: Dict[str, Any], device: str = "Unknown") -> Tuple[bool, Optional[Dict[str, Any]]]:
|
|
31
49
|
"""
|
|
32
50
|
Add or update a tag.
|
|
@@ -117,18 +135,26 @@ class TagList:
|
|
|
117
135
|
with self._lock:
|
|
118
136
|
return list(self._tags.values())
|
|
119
137
|
|
|
120
|
-
def get_by_identifier(self,
|
|
138
|
+
def get_by_identifier(self, identifier_value: str, identifier_type: str = "epc") -> Optional[Dict[str, Any]]:
|
|
121
139
|
"""
|
|
122
|
-
Retrieve a tag by its
|
|
123
|
-
|
|
140
|
+
Retrieve a tag by its identifier.
|
|
124
141
|
Args:
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
identifier_value: The value of the identifier (EPC or TID).
|
|
143
|
+
identifier_type: The type of identifier ("epc" or "tid").
|
|
127
144
|
Returns:
|
|
128
145
|
The tag dictionary if found, otherwise None.
|
|
129
146
|
"""
|
|
130
|
-
|
|
131
|
-
|
|
147
|
+
if identifier_type not in ("epc", "tid"):
|
|
148
|
+
identifier_type = "epc"
|
|
149
|
+
|
|
150
|
+
if self.unique_identifier == identifier_type:
|
|
151
|
+
return self._tags.get(identifier_value)
|
|
152
|
+
|
|
153
|
+
for tag in self._tags.values():
|
|
154
|
+
if tag.get(identifier_type) == identifier_value:
|
|
155
|
+
return tag
|
|
156
|
+
|
|
157
|
+
return None
|
|
132
158
|
|
|
133
159
|
def clear(self) -> None:
|
|
134
160
|
"""
|
|
@@ -173,20 +199,29 @@ class TagList:
|
|
|
173
199
|
return tag.get("tid")
|
|
174
200
|
return None
|
|
175
201
|
|
|
176
|
-
def
|
|
177
|
-
"""
|
|
178
|
-
Return the number of stored tags.
|
|
202
|
+
def get_epcs(self) -> list[str]:
|
|
179
203
|
"""
|
|
180
|
-
|
|
204
|
+
Retrieve a list of all stored EPCs.
|
|
181
205
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Check if a tag identifier exists in the list.
|
|
206
|
+
Returns:
|
|
207
|
+
A list of EPC strings.
|
|
185
208
|
"""
|
|
186
|
-
|
|
209
|
+
with self._lock:
|
|
210
|
+
return [tag["epc"] for tag in self._tags.values() if "epc" in tag]
|
|
187
211
|
|
|
188
|
-
def
|
|
212
|
+
def get_gtin_counts(self) -> Dict[str, int]:
|
|
189
213
|
"""
|
|
190
|
-
|
|
214
|
+
Retrieve counts of tags grouped by GTIN.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
A dictionary mapping GTINs to their respective counts.
|
|
191
218
|
"""
|
|
192
|
-
|
|
219
|
+
gtin_counts: Dict[str, int] = {}
|
|
220
|
+
with self._lock:
|
|
221
|
+
for tag in self._tags.values():
|
|
222
|
+
gtin = tag.get("gtin")
|
|
223
|
+
if gtin is None:
|
|
224
|
+
gtin = "UNKNOWN"
|
|
225
|
+
gtin_counts[gtin] = gtin_counts.get(gtin, 0) + 1
|
|
226
|
+
|
|
227
|
+
return gtin_counts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._main import WebhookManager
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WebhookManager:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
timeout: float = 5.0,
|
|
13
|
+
max_retries: int = 2,
|
|
14
|
+
):
|
|
15
|
+
self.url = url
|
|
16
|
+
self.timeout = timeout
|
|
17
|
+
self.max_retries = max_retries
|
|
18
|
+
|
|
19
|
+
self.default_headers = {"Content-Type": "application/json", "User-Agent": "SmartX-Connector/1.0"}
|
|
20
|
+
|
|
21
|
+
async def post(
|
|
22
|
+
self, device: str, event_type: str, event_data: Any = None, headers: Optional[Dict[str, str]] = None
|
|
23
|
+
) -> bool:
|
|
24
|
+
"""
|
|
25
|
+
Sends data via POST to the configured webhook URL.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
device: Name of the device sending the webhook
|
|
29
|
+
event_type: Type of event being sent
|
|
30
|
+
event_data: Data to be sent (will be converted to JSON)
|
|
31
|
+
headers: Optional headers for the request
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
bool: True if sent successfully, False otherwise
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if not self.url:
|
|
38
|
+
logging.warning("⚠️ WEBHOOK_URL not configured in settings")
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
payload = {"device": device, "event_type": event_type, "event_data": event_data}
|
|
42
|
+
|
|
43
|
+
# Merge with custom headers if provided
|
|
44
|
+
if headers:
|
|
45
|
+
self.default_headers.update(headers)
|
|
46
|
+
|
|
47
|
+
retries = 0
|
|
48
|
+
last_error = None
|
|
49
|
+
|
|
50
|
+
while retries < self.max_retries:
|
|
51
|
+
try:
|
|
52
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
53
|
+
response = await client.post(self.url, json=payload, headers=self.default_headers)
|
|
54
|
+
|
|
55
|
+
# Consider success if status 2xx
|
|
56
|
+
if response.status_code < 300:
|
|
57
|
+
logging.info(f"✅ Webhook sent successfully to {self.url} - Status: {response.status_code}")
|
|
58
|
+
return True
|
|
59
|
+
else:
|
|
60
|
+
logging.warning(
|
|
61
|
+
f"⚠️ Webhook failed - Status: {response.status_code} - Response: {response.text[:200]}"
|
|
62
|
+
)
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
except httpx.TimeoutException:
|
|
66
|
+
last_error = f"Timeout after {self.timeout}s"
|
|
67
|
+
retries += 1
|
|
68
|
+
logging.warning(f"⏰ Webhook timeout (attempt {retries}/{self.max_retries})")
|
|
69
|
+
|
|
70
|
+
except httpx.ConnectError:
|
|
71
|
+
last_error = "Connection error"
|
|
72
|
+
retries += 1
|
|
73
|
+
logging.warning(f"🔌 Webhook connection error (attempt {retries}/{self.max_retries})")
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
last_error = str(e)
|
|
77
|
+
retries += 1
|
|
78
|
+
logging.error(f"❌ Webhook error (attempt {retries}/{self.max_retries}): {e}")
|
|
79
|
+
|
|
80
|
+
# Wait before retrying (exponential backoff)
|
|
81
|
+
if retries < self.max_retries:
|
|
82
|
+
wait_time = 2**retries # 2s, 4s, 8s...
|
|
83
|
+
logging.info(f"🔁 Retrying webhook in {wait_time}s...")
|
|
84
|
+
await asyncio.sleep(wait_time)
|
|
85
|
+
|
|
86
|
+
# If we reached here, all attempts failed
|
|
87
|
+
logging.error(f"❌ Webhook failed after {self.max_retries} attempts. Last error: {last_error}")
|
|
88
|
+
return False
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smartx-rfid
|
|
3
|
+
Version: 1.5.4
|
|
4
|
+
Summary: SmartX RFID library
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: python,library,RFID,smartx,packaging
|
|
8
|
+
Author: Gabriel Henrique Pascon
|
|
9
|
+
Author-email: gh.pascon@gmail.com
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Dist: bleak (>=1.1.1,<2.0.0)
|
|
21
|
+
Requires-Dist: httpx (==0.28.1)
|
|
22
|
+
Requires-Dist: psycopg2 (>=2.9.11,<3.0.0)
|
|
23
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
24
|
+
Requires-Dist: pyepc (==0.5.0)
|
|
25
|
+
Requires-Dist: pymysql (==1.1.1)
|
|
26
|
+
Requires-Dist: pyserial (==3.5)
|
|
27
|
+
Requires-Dist: pyserial-asyncio (==0.6)
|
|
28
|
+
Requires-Dist: sqlalchemy (==2.0.29)
|
|
29
|
+
Project-URL: Documentation, https://github.com/ghpascon/smartx_rfid#readme
|
|
30
|
+
Project-URL: Homepage, https://github.com/ghpascon/smartx_rfid
|
|
31
|
+
Project-URL: Repository, https://github.com/ghpascon/smartx_rfid
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# SmartX RFID
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
Python library for RFID device integration and data management.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install smartx-rfid
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from smartx_rfid.devices import X714
|
|
52
|
+
import asyncio
|
|
53
|
+
|
|
54
|
+
async def on_tag_read(name: str, tag_data: dict):
|
|
55
|
+
print(f"Tag: {tag_data['epc']} | RSSI: {tag_data['rssi']}dBm")
|
|
56
|
+
|
|
57
|
+
async def main():
|
|
58
|
+
reader = X714(name="RFID Reader", start_reading=True)
|
|
59
|
+
reader.on_event = lambda name, event_type, data: (
|
|
60
|
+
asyncio.create_task(on_tag_read(name, data))
|
|
61
|
+
if event_type == "tag" else None
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
await reader.connect()
|
|
65
|
+
|
|
66
|
+
while True:
|
|
67
|
+
await asyncio.sleep(1)
|
|
68
|
+
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
### Supported Devices
|
|
75
|
+
- **X714 RFID Reader** - Serial, TCP, Bluetooth LE connections
|
|
76
|
+
- **R700 IOT** - HTTP REST API integration
|
|
77
|
+
- **Generic Serial/TCP** - Custom protocol support
|
|
78
|
+
|
|
79
|
+
### Core Components
|
|
80
|
+
- **Device Management** - Async communication with auto-reconnection
|
|
81
|
+
- **Database Integration** - SQLAlchemy with multiple database support
|
|
82
|
+
- **Webhook System** - HTTP notifications with retry logic
|
|
83
|
+
- **Tag Management** - Thread-safe tag list with deduplication
|
|
84
|
+
|
|
85
|
+
## Device Examples
|
|
86
|
+
|
|
87
|
+
### X714 RFID Reader
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from smartx_rfid.devices import X714
|
|
91
|
+
|
|
92
|
+
# Serial connection (auto-detect)
|
|
93
|
+
reader = X714(name="X714-Serial")
|
|
94
|
+
|
|
95
|
+
# TCP connection
|
|
96
|
+
reader = X714(
|
|
97
|
+
name="X714-TCP",
|
|
98
|
+
connection_type="TCP",
|
|
99
|
+
ip="192.168.1.100"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Bluetooth LE
|
|
103
|
+
reader = X714(
|
|
104
|
+
name="X714-BLE",
|
|
105
|
+
connection_type="BLE"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def on_event(name: str, event_type: str, data: dict):
|
|
109
|
+
if event_type == "tag":
|
|
110
|
+
print(f"EPC: {data['epc']}, Antenna: {data['ant']}")
|
|
111
|
+
|
|
112
|
+
reader.on_event = on_event
|
|
113
|
+
await reader.connect()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### R700 IOT Reader
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from smartx_rfid.devices import R700_IOT, R700_IOT_config_example
|
|
120
|
+
|
|
121
|
+
reader = R700_IOT(
|
|
122
|
+
name="R700-Reader",
|
|
123
|
+
ip="192.168.1.200",
|
|
124
|
+
config=R700_IOT_config_example
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
reader.on_event = on_event
|
|
128
|
+
await reader.connect()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Database Integration
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from smartx_rfid.db import DatabaseManager
|
|
135
|
+
from sqlalchemy import Column, String, Float, Integer, DateTime
|
|
136
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
137
|
+
from datetime import datetime
|
|
138
|
+
|
|
139
|
+
class Base(DeclarativeBase):
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
class TagModel(Base):
|
|
143
|
+
__tablename__ = 'rfid_tags'
|
|
144
|
+
|
|
145
|
+
id = Column(Integer, primary_key=True)
|
|
146
|
+
epc = Column(String(50), unique=True, nullable=False)
|
|
147
|
+
tid = Column(String(50))
|
|
148
|
+
ant = Column(Integer)
|
|
149
|
+
rssi = Column(Float)
|
|
150
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
151
|
+
|
|
152
|
+
# Initialize database
|
|
153
|
+
db = DatabaseManager("sqlite:///rfid_tags.db")
|
|
154
|
+
db.register_models(TagModel)
|
|
155
|
+
db.create_tables()
|
|
156
|
+
|
|
157
|
+
# Use with sessions
|
|
158
|
+
with db.get_session() as session:
|
|
159
|
+
tag = TagModel(epc="E200001175000001", ant=1, rssi=-45.2)
|
|
160
|
+
session.add(tag)
|
|
161
|
+
|
|
162
|
+
# Raw SQL queries
|
|
163
|
+
results = db.execute_query_fetchall(
|
|
164
|
+
"SELECT * FROM rfid_tags WHERE rssi > :threshold",
|
|
165
|
+
params={"threshold": -50}
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Supported Databases
|
|
170
|
+
- PostgreSQL: `postgresql://user:pass@localhost/db`
|
|
171
|
+
- MySQL: `mysql+pymysql://user:pass@localhost/db`
|
|
172
|
+
- SQLite: `sqlite:///path/to/database.db`
|
|
173
|
+
|
|
174
|
+
## Webhook Integration
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from smartx_rfid.webhook import WebhookManager
|
|
178
|
+
|
|
179
|
+
webhook = WebhookManager("https://api.example.com/rfid-events")
|
|
180
|
+
|
|
181
|
+
# Send tag data
|
|
182
|
+
success = await webhook.post("device_01", "tag_read", {
|
|
183
|
+
"epc": "E200001175000001",
|
|
184
|
+
"rssi": -45.2,
|
|
185
|
+
"antenna": 1,
|
|
186
|
+
"timestamp": "2026-01-15T10:30:00Z"
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
if success:
|
|
190
|
+
print("Webhook sent successfully")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Tag Management
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from smartx_rfid.utils import TagList
|
|
197
|
+
|
|
198
|
+
# Create thread-safe tag list
|
|
199
|
+
tags = TagList(unique_identifier="epc")
|
|
200
|
+
|
|
201
|
+
def on_tag(device: str, tag_data: dict):
|
|
202
|
+
new_tag, tag = tags.add(tag_data, device=device)
|
|
203
|
+
|
|
204
|
+
if new_tag:
|
|
205
|
+
print(f"New tag: {tag['epc']}")
|
|
206
|
+
# Add custom data
|
|
207
|
+
tag['product_name'] = "Widget ABC"
|
|
208
|
+
else:
|
|
209
|
+
print(f"Existing tag: {tag['epc']}")
|
|
210
|
+
|
|
211
|
+
# Use with device events
|
|
212
|
+
reader.on_event = lambda name, event_type, data: (
|
|
213
|
+
on_tag(name, data) if event_type == "tag" else None
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Complete Integration Example
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import asyncio
|
|
221
|
+
from smartx_rfid.devices import X714
|
|
222
|
+
from smartx_rfid.db import DatabaseManager
|
|
223
|
+
from smartx_rfid.webhook import WebhookManager
|
|
224
|
+
from smartx_rfid.utils import TagList
|
|
225
|
+
|
|
226
|
+
async def rfid_system():
|
|
227
|
+
# Initialize components
|
|
228
|
+
reader = X714(name="Production-Scanner", start_reading=True)
|
|
229
|
+
db = DatabaseManager("postgresql://localhost/rfid_production")
|
|
230
|
+
webhook = WebhookManager("https://api.internal.com/rfid")
|
|
231
|
+
tags = TagList()
|
|
232
|
+
|
|
233
|
+
async def process_tag(name: str, tag_data: dict):
|
|
234
|
+
# Check if new tag
|
|
235
|
+
new_tag, tag = tags.add(tag_data, device=name)
|
|
236
|
+
|
|
237
|
+
if new_tag:
|
|
238
|
+
# Save to database
|
|
239
|
+
with db.get_session() as session:
|
|
240
|
+
session.add(TagModel(**tag_data))
|
|
241
|
+
|
|
242
|
+
# Send notification
|
|
243
|
+
await webhook.post(name, "new_tag", tag_data)
|
|
244
|
+
print(f"New tag processed: {tag_data['epc']}")
|
|
245
|
+
|
|
246
|
+
reader.on_event = lambda n, t, d: (
|
|
247
|
+
asyncio.create_task(process_tag(n, d)) if t == "tag" else None
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
await reader.connect()
|
|
251
|
+
|
|
252
|
+
asyncio.run(rfid_system())
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Configuration
|
|
256
|
+
|
|
257
|
+
### Device Configuration
|
|
258
|
+
```python
|
|
259
|
+
# High-performance settings
|
|
260
|
+
reader = X714(
|
|
261
|
+
name="FastScanner",
|
|
262
|
+
read_power=30, # Max power
|
|
263
|
+
session=2, # Session config
|
|
264
|
+
read_interval=100 # Fast scanning
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Database with connection pooling
|
|
268
|
+
db = DatabaseManager(
|
|
269
|
+
database_url="postgresql://user:pass@localhost/db",
|
|
270
|
+
pool_size=10,
|
|
271
|
+
max_overflow=20,
|
|
272
|
+
echo=True # Enable SQL logging
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Logging Setup
|
|
277
|
+
```python
|
|
278
|
+
import logging
|
|
279
|
+
|
|
280
|
+
logging.basicConfig(
|
|
281
|
+
level=logging.INFO,
|
|
282
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## API Reference
|
|
287
|
+
|
|
288
|
+
### Core Modules
|
|
289
|
+
- `smartx_rfid.devices` - Device communication classes
|
|
290
|
+
- `smartx_rfid.db` - Database management
|
|
291
|
+
- `smartx_rfid.webhook` - HTTP notification system
|
|
292
|
+
- `smartx_rfid.utils` - Utility classes and helpers
|
|
293
|
+
|
|
294
|
+
### Event System
|
|
295
|
+
All devices use a consistent event callback system:
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
def on_event(device_name: str, event_type: str, event_data: dict):
|
|
299
|
+
"""
|
|
300
|
+
Event types:
|
|
301
|
+
- "connected": Device connected successfully
|
|
302
|
+
- "disconnected": Device disconnected
|
|
303
|
+
- "tag": RFID tag detected
|
|
304
|
+
- "error": Error occurred
|
|
305
|
+
"""
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
device.on_event = on_event
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Examples
|
|
312
|
+
|
|
313
|
+
The `examples/` directory contains working examples for all supported devices and features:
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
examples/
|
|
317
|
+
├── devices/
|
|
318
|
+
│ ├── RFID/ # X714, R700_IOT examples
|
|
319
|
+
│ └── generic/ # Serial, TCP examples
|
|
320
|
+
├── db/ # Database integration examples
|
|
321
|
+
└── utils/ # Tag management examples
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Run examples:
|
|
325
|
+
```bash
|
|
326
|
+
python examples/devices/RFID/X714_SERIAL.py
|
|
327
|
+
python examples/db/showcase.py
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Requirements
|
|
331
|
+
|
|
332
|
+
- Python 3.11+
|
|
333
|
+
- Dependencies automatically installed with pip
|
|
334
|
+
|
|
335
|
+
## License
|
|
336
|
+
|
|
337
|
+
MIT License
|
|
338
|
+
|
|
339
|
+
## Support
|
|
340
|
+
|
|
341
|
+
- **Repository**: [https://github.com/ghpascon/smartx_rfid](https://github.com/ghpascon/smartx_rfid)
|
|
342
|
+
- **Issues**: [GitHub Issues](https://github.com/ghpascon/smartx_rfid/issues)
|
|
343
|
+
- **Email**: [gh.pascon@gmail.com](mailto:gh.pascon@gmail.com)
|
|
344
|
+
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
smartx_rfid/__init__.py,sha256=XCdKdP8o5LT3VjJtWOuFHJzztc0FXt3Ms89qsx0p6sM,282
|
|
2
|
+
smartx_rfid/clients/rchlo.py,sha256=z2xT9YnNEwkbHSeHsJD9BZvSJZ5QtnHZ9R7SpQ21Jxk,221
|
|
3
|
+
smartx_rfid/db/__init__.py,sha256=P8rsrVgc_5NDMH_p4FDs5OBLe3ohaQNR_sadVaeNvxA,35
|
|
4
|
+
smartx_rfid/db/_main.py,sha256=pKzJnkTb6VVSbKd8E34us4SAAKxyTGsE_diE_AxJShY,15450
|
|
2
5
|
smartx_rfid/devices/RFID/R700_IOT/_main.py,sha256=VL2yDBCXp8Avb3PXKZyBNwtBRhwlLQb5TSs3aTUeC6w,7458
|
|
3
6
|
smartx_rfid/devices/RFID/R700_IOT/on_event.py,sha256=bG_33gY2ZHXquUbezXyMN0dyHO9SBwc36DPTc8iXc1U,799
|
|
4
7
|
smartx_rfid/devices/RFID/R700_IOT/reader_config_example.py,sha256=oi4vg3_zjYFDUeq1Y_d5InUx_Rq5M2CB5RDVkK3WCgU,2264
|
|
@@ -6,7 +9,7 @@ smartx_rfid/devices/RFID/R700_IOT/reader_helpers.py,sha256=8CvvYeKGplWqceVH5mEMN
|
|
|
6
9
|
smartx_rfid/devices/RFID/R700_IOT/write_commands.py,sha256=GMROeq_daTScj4NZygF45UM9t0Pr3E6ugmrb7OlF73w,2284
|
|
7
10
|
smartx_rfid/devices/RFID/X714/_main.py,sha256=LP1X_w6Sy9m5kHLvbaRUNZbzQJU37fS8OIEWvOfrk7M,6371
|
|
8
11
|
smartx_rfid/devices/RFID/X714/ble_protocol.py,sha256=M3Cs66WX6oa_EPdMTLEjD-IRxAjpfrKzGj4hn1vxlxQ,6951
|
|
9
|
-
smartx_rfid/devices/RFID/X714/on_receive.py,sha256=
|
|
12
|
+
smartx_rfid/devices/RFID/X714/on_receive.py,sha256=frrZIwSSDrsOjLO2GHdShVhoC-2VvHtr_AkJU91VDIo,2133
|
|
10
13
|
smartx_rfid/devices/RFID/X714/rfid.py,sha256=XgHl2evlN3tGB6hhCUYzpEy_jw2dyplxUcDYsuPec2Q,2285
|
|
11
14
|
smartx_rfid/devices/RFID/X714/serial_protocol.py,sha256=eOGM5sK35SxXfS9KjB8JQzYhR1qs4H3vw3-n3oPOjQQ,3681
|
|
12
15
|
smartx_rfid/devices/RFID/X714/tcp_protocol.py,sha256=GthynyN6YMl6R16nrAnlrDK61QnuZAOhYhPqycBCwJA,4804
|
|
@@ -15,13 +18,22 @@ smartx_rfid/devices/__init__.py,sha256=EVhtb-IBLpits8dr-I1ZYYuWHw9ddqiu-98myg-iy
|
|
|
15
18
|
smartx_rfid/devices/generic/SERIAL/_main.py,sha256=7rEgviwxfd4uWpWjVXsV0qnOAET-MxJux9PcTKiE8G8,8562
|
|
16
19
|
smartx_rfid/devices/generic/TCP/_main.py,sha256=DY9c9m2ZchvUJ9n2TtPENL2GqCYS4hvdjkCAe6yGXxM,2770
|
|
17
20
|
smartx_rfid/devices/generic/TCP/helpers.py,sha256=GQ_yIvmSlx_yZci6pVvZfdo1wyKcS5gtZh-VDwt3pGs,1552
|
|
18
|
-
smartx_rfid/
|
|
19
|
-
smartx_rfid/
|
|
21
|
+
smartx_rfid/parser/__init__.py,sha256=KByVPGCuGVvil8XtF0hUeLyQfyqcuKvHJwv2au2I8Zg,38
|
|
22
|
+
smartx_rfid/parser/main.py,sha256=66RPYIWMy7m3ir6yTprlk1Br5iBCvNHyYNAEeVsuFR8,601
|
|
23
|
+
smartx_rfid/parser/rfid_tag_parser/__init__.py,sha256=_voIeFw0Y3_ooEqwOEhakJyP_PRxsKWKK0K2s1c4g_E,506
|
|
24
|
+
smartx_rfid/parser/rfid_tag_parser/exceptions.py,sha256=QsiXv0IHGYsLhYGgx2D-yE0jjTHgw9IFaH59J1QQc-g,282
|
|
25
|
+
smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py,sha256=uw-h7sYrH4Tfum_E9FyCRQKQMBlr5Igtjab99sJwxvA,23396
|
|
26
|
+
smartx_rfid/schemas/events.py,sha256=1iX0iSGirMqDbUAi2ir1CDhtVp8ROg-KZ6tY5nppWek,243
|
|
27
|
+
smartx_rfid/schemas/tag.py,sha256=haMvu_ALhkba8AIL-DrQN0egkRqeP3Wr8AyzyOnWH4Q,2242
|
|
28
|
+
smartx_rfid/utils/__init__.py,sha256=TllHEfkXJjmBwdwjPJqzbPn4hfCeqmTNOw6vHgzC-wU,101
|
|
20
29
|
smartx_rfid/utils/event.py,sha256=7QOdiSfiMscoAaTq4U3E3asYGSVnolr9OD3FSYGh6bg,469
|
|
21
30
|
smartx_rfid/utils/logger_manager.py,sha256=61HJ40wgX0hVwG4xW66IQ2jdJp8CFM_S0eoGzx8-FwU,5653
|
|
22
|
-
smartx_rfid/utils/path.py,sha256=
|
|
23
|
-
smartx_rfid/utils/
|
|
24
|
-
smartx_rfid
|
|
25
|
-
smartx_rfid
|
|
26
|
-
smartx_rfid
|
|
27
|
-
smartx_rfid-1.
|
|
31
|
+
smartx_rfid/utils/path.py,sha256=eRxXKNzzDELna3USfGPNG1EPP2EkLlFxHmf21BMWAgA,3792
|
|
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,,
|