suprema-biostar-mcp 1.0.1__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.
- biostar_x_mcp_server/__init__.py +25 -0
- biostar_x_mcp_server/__main__.py +15 -0
- biostar_x_mcp_server/config.py +87 -0
- biostar_x_mcp_server/handlers/__init__.py +35 -0
- biostar_x_mcp_server/handlers/access_handler.py +2162 -0
- biostar_x_mcp_server/handlers/audit_handler.py +489 -0
- biostar_x_mcp_server/handlers/auth_handler.py +216 -0
- biostar_x_mcp_server/handlers/base_handler.py +228 -0
- biostar_x_mcp_server/handlers/card_handler.py +746 -0
- biostar_x_mcp_server/handlers/device_handler.py +4344 -0
- biostar_x_mcp_server/handlers/door_handler.py +3969 -0
- biostar_x_mcp_server/handlers/event_handler.py +1331 -0
- biostar_x_mcp_server/handlers/file_handler.py +212 -0
- biostar_x_mcp_server/handlers/help_web_handler.py +379 -0
- biostar_x_mcp_server/handlers/log_handler.py +1051 -0
- biostar_x_mcp_server/handlers/navigation_handler.py +109 -0
- biostar_x_mcp_server/handlers/occupancy_handler.py +541 -0
- biostar_x_mcp_server/handlers/user_handler.py +3568 -0
- biostar_x_mcp_server/schemas/__init__.py +21 -0
- biostar_x_mcp_server/schemas/access.py +158 -0
- biostar_x_mcp_server/schemas/audit.py +73 -0
- biostar_x_mcp_server/schemas/auth.py +24 -0
- biostar_x_mcp_server/schemas/cards.py +128 -0
- biostar_x_mcp_server/schemas/devices.py +496 -0
- biostar_x_mcp_server/schemas/doors.py +306 -0
- biostar_x_mcp_server/schemas/events.py +104 -0
- biostar_x_mcp_server/schemas/files.py +7 -0
- biostar_x_mcp_server/schemas/help.py +29 -0
- biostar_x_mcp_server/schemas/logs.py +33 -0
- biostar_x_mcp_server/schemas/occupancy.py +19 -0
- biostar_x_mcp_server/schemas/tool_response.py +29 -0
- biostar_x_mcp_server/schemas/users.py +166 -0
- biostar_x_mcp_server/server.py +335 -0
- biostar_x_mcp_server/session.py +221 -0
- biostar_x_mcp_server/tool_manager.py +172 -0
- biostar_x_mcp_server/tools/__init__.py +45 -0
- biostar_x_mcp_server/tools/access.py +510 -0
- biostar_x_mcp_server/tools/audit.py +227 -0
- biostar_x_mcp_server/tools/auth.py +59 -0
- biostar_x_mcp_server/tools/cards.py +269 -0
- biostar_x_mcp_server/tools/categories.py +197 -0
- biostar_x_mcp_server/tools/devices.py +1552 -0
- biostar_x_mcp_server/tools/doors.py +865 -0
- biostar_x_mcp_server/tools/events.py +305 -0
- biostar_x_mcp_server/tools/files.py +28 -0
- biostar_x_mcp_server/tools/help.py +80 -0
- biostar_x_mcp_server/tools/logs.py +123 -0
- biostar_x_mcp_server/tools/navigation.py +89 -0
- biostar_x_mcp_server/tools/occupancy.py +91 -0
- biostar_x_mcp_server/tools/users.py +1113 -0
- biostar_x_mcp_server/utils/__init__.py +31 -0
- biostar_x_mcp_server/utils/category_mapper.py +206 -0
- biostar_x_mcp_server/utils/decorators.py +101 -0
- biostar_x_mcp_server/utils/language_detector.py +51 -0
- biostar_x_mcp_server/utils/search.py +42 -0
- biostar_x_mcp_server/utils/timezone.py +122 -0
- suprema_biostar_mcp-1.0.1.dist-info/METADATA +163 -0
- suprema_biostar_mcp-1.0.1.dist-info/RECORD +61 -0
- suprema_biostar_mcp-1.0.1.dist-info/WHEEL +4 -0
- suprema_biostar_mcp-1.0.1.dist-info/entry_points.txt +2 -0
- suprema_biostar_mcp-1.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
from typing import Optional, Union, List
|
|
2
|
+
from pydantic import BaseModel, Field, model_validator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# ============================================================================
|
|
6
|
+
# Device Management Schemas
|
|
7
|
+
# ============================================================================
|
|
8
|
+
|
|
9
|
+
class ListDevicesInput(BaseModel):
|
|
10
|
+
"""Input schema for list-devices tool"""
|
|
11
|
+
group_id: Optional[int] = Field(None, description="Filter devices by device group ID")
|
|
12
|
+
device_type: Optional[str] = Field(None, description="Filter by device type (e.g., 'BioStation', 'BioEntry', 'FaceStation')")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GetDeviceInput(BaseModel):
|
|
16
|
+
"""Input schema for get-device tool"""
|
|
17
|
+
device_id: int = Field(..., description="ID of the device to retrieve")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AddDeviceInput(BaseModel):
|
|
21
|
+
"""Input schema for add-device tool"""
|
|
22
|
+
device_id: int = Field(..., description="Unique device ID")
|
|
23
|
+
name: str = Field(..., description="Name of the device")
|
|
24
|
+
ip_address: str = Field(..., description="IP address of the device")
|
|
25
|
+
device_group_id: int = Field(..., description="ID of the device group to add this device to")
|
|
26
|
+
device_type_id: Optional[int] = Field(None, description="Device type ID (e.g., 8 for BioStation 2, 9 for BioStation A2, 10 for FaceStation 2, 35 for BioStation 3)")
|
|
27
|
+
device_type_name: Optional[str] = Field(None, description="Device type name (e.g., 'BioStation 2', 'BioStation A2', 'FaceStation 2')")
|
|
28
|
+
port: Optional[int] = Field(default=51211, description="Port number (default: 51211)")
|
|
29
|
+
use_ssl: Optional[bool] = Field(default=False, description="Whether to use SSL connection")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UpdateDeviceInput(BaseModel):
|
|
33
|
+
"""Input schema for update-device tool"""
|
|
34
|
+
device_id: int = Field(..., description="ID of the device to update")
|
|
35
|
+
name: Optional[str] = Field(None, description="New name for the device")
|
|
36
|
+
description: Optional[str] = Field(None, description="Device description")
|
|
37
|
+
time_zone: Optional[str] = Field(None, description="Time zone for the device (e.g., 'America/New_York')")
|
|
38
|
+
volume: Optional[int] = Field(None, ge=0, le=100, description="Device volume level (0-100)")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RebootDeviceInput(BaseModel):
|
|
42
|
+
"""Input schema for reboot-device tool"""
|
|
43
|
+
device_ids: Optional[List[Union[int, str]]] = Field(None, description="One or more device IDs to restart (existence validated).")
|
|
44
|
+
device_id: Optional[int] = Field(None, description="Legacy single device id input.")
|
|
45
|
+
device_names: Optional[List[str]] = Field(None, description="Device names to restart (EXACT, case-sensitive; no fuzzy).")
|
|
46
|
+
device_search_text: Optional[str] = Field(None, description="Browse candidates by substring (case-insensitive). Returns candidates only; no action.")
|
|
47
|
+
confirm_multi: Optional[bool] = Field(default=False, description="Must be true to act on more than one device.")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class GetDeviceStatusInput(BaseModel):
|
|
51
|
+
"""Input schema for get-device-status tool"""
|
|
52
|
+
device_id: int = Field(..., description="ID of the device to check status")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ScanDevicesInput(BaseModel):
|
|
56
|
+
"""Input schema for scan-devices tool"""
|
|
57
|
+
network_range: Optional[str] = Field(None, description="IP range to scan (e.g., '192.168.1.0/24')")
|
|
58
|
+
port: Optional[int] = Field(default=51211, description="Port to scan (default: 51211)")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SyncDeviceTimeInput(BaseModel):
|
|
62
|
+
"""Input schema for sync-device-time tool"""
|
|
63
|
+
device_id: int = Field(..., description="ID of the device to sync time")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ClearDeviceLogInput(BaseModel):
|
|
67
|
+
"""Input schema for clear-device-log tool"""
|
|
68
|
+
device_id: int = Field(..., description="ID of the device")
|
|
69
|
+
log_type: Optional[str] = Field(default="all", description="Type of logs to clear")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class LockDeviceInput(BaseModel):
|
|
73
|
+
"""Input schema for lock-device tool"""
|
|
74
|
+
device_id: int = Field(..., description="ID of the device to lock")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class UnlockDeviceInput(BaseModel):
|
|
78
|
+
"""Input schema for unlock-device tool"""
|
|
79
|
+
device_id: int = Field(..., description="ID of the device to unlock")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TCPSearchInput(BaseModel):
|
|
83
|
+
"""Input schema for tcp-search tool"""
|
|
84
|
+
ip_address: str = Field(..., description="IP address of the device to connect to")
|
|
85
|
+
port: Optional[int] = Field(default=51211, description="Port number (default: 51211)")
|
|
86
|
+
timeout: Optional[int] = Field(default=10, description="Connection timeout in seconds (default: 10)")
|
|
87
|
+
retry_count: Optional[int] = Field(default=2, description="Number of retry attempts (default: 2)")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class UDPSearchInput(BaseModel):
|
|
91
|
+
"""Input schema for udp-search tool"""
|
|
92
|
+
timeout: Optional[int] = Field(default=5, description="Search timeout in seconds (default: 5)")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class DisconnectDeviceInput(BaseModel):
|
|
96
|
+
"""Input schema for disconnect-device tool"""
|
|
97
|
+
device_id: int = Field(..., description="ID of the device to disconnect")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RemoveDeviceInput(BaseModel):
|
|
101
|
+
"""Input schema for remove-device tool"""
|
|
102
|
+
device_id: int = Field(..., description="ID of the device to remove")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ============================================================================
|
|
106
|
+
# Device Group Schemas
|
|
107
|
+
# ============================================================================
|
|
108
|
+
|
|
109
|
+
class GetDeviceGroupsInput(BaseModel):
|
|
110
|
+
"""Input schema for get-device-groups tool"""
|
|
111
|
+
group_search_text: Optional[str] = Field(None, description="Case-insensitive substring filter on group name")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class GetDeviceGroupInput(BaseModel):
|
|
115
|
+
"""Input schema for get-device-group tool"""
|
|
116
|
+
group_id: Optional[int] = Field(None, description="Exact device group ID to fetch.")
|
|
117
|
+
group_name: Optional[str] = Field(None, description="EXACT (case-sensitive) device group name.")
|
|
118
|
+
group_search_text: Optional[str] = Field(None, description="Substring browse for group names. Returns candidates only.")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CreateDeviceGroupInput(BaseModel):
|
|
122
|
+
"""Input schema for create-device-group tool"""
|
|
123
|
+
name: str = Field(..., description="Name of the device group")
|
|
124
|
+
description: Optional[str] = Field(None, description="Description of the device group")
|
|
125
|
+
parent_id: Optional[int] = Field(default=1, description="Parent device group ID (default: 1 for root group)")
|
|
126
|
+
device_ids: Optional[List[int]] = Field(None, description="List of device IDs to include in the group")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class UpdateDeviceGroupInput(BaseModel):
|
|
130
|
+
"""Input schema for update-device-group tool"""
|
|
131
|
+
group_id: int = Field(..., description="ID of the device group to update")
|
|
132
|
+
name: Optional[str] = Field(None, description="New name for the device group")
|
|
133
|
+
description: Optional[str] = Field(None, description="New description for the device group")
|
|
134
|
+
device_ids: Optional[List[int]] = Field(None, description="New list of device IDs for the group")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class DeleteDeviceGroupInput(BaseModel):
|
|
138
|
+
"""Input schema for delete-device-group tool"""
|
|
139
|
+
group_id: int = Field(..., description="ID of the device group to delete")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class MoveDeviceToGroupInput(BaseModel):
|
|
143
|
+
"""Input schema for move-device-to-group tool"""
|
|
144
|
+
device_id: Optional[int] = Field(None, description="Device ID (preferred if known)")
|
|
145
|
+
device_name: Optional[str] = Field(None, description="Device name (substring match)")
|
|
146
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
147
|
+
target_group_id: Optional[int] = Field(None, description="Target device group ID")
|
|
148
|
+
target_group_name: Optional[str] = Field(None, description="Target device group name (substring match)")
|
|
149
|
+
group_search_text: Optional[str] = Field(None, description="Alternate group query (substring)")
|
|
150
|
+
|
|
151
|
+
@model_validator(mode="after")
|
|
152
|
+
def validate_identifiers(self):
|
|
153
|
+
"""Ensure device and group identifiers are provided"""
|
|
154
|
+
has_device = any([self.device_id, self.device_name, self.device_search_text])
|
|
155
|
+
has_group = any([self.target_group_id, self.target_group_name, self.group_search_text])
|
|
156
|
+
|
|
157
|
+
if not has_device:
|
|
158
|
+
raise ValueError("At least one device identifier (device_id, device_name, device_search_text) must be provided")
|
|
159
|
+
if not has_group:
|
|
160
|
+
raise ValueError("At least one group identifier (target_group_id, target_group_name, group_search_text) must be provided")
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class RemoveDeviceFromGroupInput(BaseModel):
|
|
165
|
+
"""Input schema for remove-device-from-group tool"""
|
|
166
|
+
device_id: Optional[int] = Field(None, description="Device ID (preferred if known)")
|
|
167
|
+
device_name: Optional[str] = Field(None, description="Device name (substring match)")
|
|
168
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
169
|
+
|
|
170
|
+
@model_validator(mode="after")
|
|
171
|
+
def validate_identifier(self):
|
|
172
|
+
"""Ensure at least one device identifier is provided"""
|
|
173
|
+
if not any([self.device_id, self.device_name, self.device_search_text]):
|
|
174
|
+
raise ValueError("At least one device identifier (device_id, device_name, device_search_text) must be provided")
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ============================================================================
|
|
179
|
+
# Device Configuration Schemas
|
|
180
|
+
# ============================================================================
|
|
181
|
+
|
|
182
|
+
class UpdateDeviceAuthModeInput(BaseModel):
|
|
183
|
+
"""Input schema for update-device-auth-mode tool"""
|
|
184
|
+
device_id: Optional[int] = Field(None, description="Target device id to update")
|
|
185
|
+
device_name: Optional[str] = Field(None, description="Target device name")
|
|
186
|
+
door_id: Optional[int] = Field(None, description="Door id; entry device will be used")
|
|
187
|
+
door_name: Optional[str] = Field(None, description="Door name; entry device will be used")
|
|
188
|
+
mode_codes: Optional[List[int]] = Field(None, description="Extended auth mode codes (11..49). Takes precedence over 'auth_specs'.")
|
|
189
|
+
auth_specs: Optional[List[str]] = Field(None, description="Specs like 'card+face+fingerprint' or 'face+pin' (ASCII-only).")
|
|
190
|
+
action: Optional[str] = Field(default="set", description="How to apply target modes.")
|
|
191
|
+
conflict_policy: Optional[str] = Field(default="overwrite", description="Policy when 'add' conflicts with business rules.")
|
|
192
|
+
schedule_id: Optional[int] = Field(default=1, description="Schedule id for each operation mode row.")
|
|
193
|
+
|
|
194
|
+
@model_validator(mode="after")
|
|
195
|
+
def validate_identifiers(self):
|
|
196
|
+
"""Ensure device identifier and auth mode are provided"""
|
|
197
|
+
if not any([self.device_id, self.device_name, self.door_id, self.door_name]):
|
|
198
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
199
|
+
if not any([self.mode_codes, self.auth_specs]):
|
|
200
|
+
raise ValueError("Either mode_codes or auth_specs must be provided")
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class UpdateDeviceFaceDistanceInput(BaseModel):
|
|
205
|
+
"""Input schema for update-device-face-distance tool"""
|
|
206
|
+
device_id: Optional[int] = Field(None, description="Target device id to update")
|
|
207
|
+
device_name: Optional[str] = Field(None, description="Target device name")
|
|
208
|
+
door_id: Optional[int] = Field(None, description="Door id; entry device will be used")
|
|
209
|
+
door_name: Optional[str] = Field(None, description="Door name; entry device will be used")
|
|
210
|
+
max_cm: Optional[Union[int, str]] = Field(None, description="One of 30,40,...,130 or 'over_130' (→255).")
|
|
211
|
+
|
|
212
|
+
@model_validator(mode="after")
|
|
213
|
+
def validate_identifiers(self):
|
|
214
|
+
"""Ensure device identifier and max_cm are provided"""
|
|
215
|
+
if not any([self.device_id, self.device_name, self.door_id, self.door_name]):
|
|
216
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
217
|
+
if self.max_cm is None:
|
|
218
|
+
raise ValueError("max_cm must be provided")
|
|
219
|
+
return self
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class UpdateDeviceDateTypeInput(BaseModel):
|
|
223
|
+
"""Input schema for update-device-date-type tool"""
|
|
224
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
225
|
+
device_name: Optional[str] = Field(None, description="Target device name (exact or substring depending on resolver)")
|
|
226
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
227
|
+
door_id: Optional[int] = Field(None, description="Door id; entry device will be used unless 'edge' says otherwise")
|
|
228
|
+
door_name: Optional[str] = Field(None, description="Door name (substring) → entry/exit device")
|
|
229
|
+
edge: Optional[str] = Field(None, description="When resolving by door, choose entry or exit device (default: entry)")
|
|
230
|
+
date_type: Optional[Union[int, str]] = Field(None, description="Date type code: 0=YYYY/MM/DD, 1=MM/DD/YYYY, 2=DD/MM/YYYY")
|
|
231
|
+
date_format: Optional[str] = Field(None, description="Alternative to 'date_type'. Will be mapped to 0|1|2.")
|
|
232
|
+
|
|
233
|
+
@model_validator(mode="after")
|
|
234
|
+
def validate_identifiers(self):
|
|
235
|
+
"""Ensure device identifier and date type are provided"""
|
|
236
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
237
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
238
|
+
if not any([self.date_type, self.date_format]):
|
|
239
|
+
raise ValueError("Either date_type or date_format must be provided")
|
|
240
|
+
return self
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class UpdateDeviceTimezoneInput(BaseModel):
|
|
244
|
+
"""Input schema for update-device-timezone tool"""
|
|
245
|
+
device_id: Optional[int] = Field(None, description="Target device ID (preferred)")
|
|
246
|
+
device_name: Optional[str] = Field(None, description="Exact device name (strict match)")
|
|
247
|
+
door_id: Optional[int] = Field(None, description="Resolve via door's entry device")
|
|
248
|
+
door_name: Optional[str] = Field(None, description="Resolve via door's entry device (exact)")
|
|
249
|
+
offset_seconds: Optional[int] = Field(None, description="Final UTC offset in seconds (e.g., 32400 for UTC+9, -28800 for UTC-8).")
|
|
250
|
+
utc_offset: Optional[str] = Field(None, description="UTC offset string like '+09:00', '-08:00', '+03:30', etc.")
|
|
251
|
+
tz_label: Optional[str] = Field(None, description="Dropdown label or alias (Korean), or short city/abbr like '서울','하와이','테헤란','PST'.")
|
|
252
|
+
dry_run: Optional[bool] = Field(default=False, description="If true, do not PUT; only resolve and return the computed offset.")
|
|
253
|
+
|
|
254
|
+
@model_validator(mode="after")
|
|
255
|
+
def validate_identifiers(self):
|
|
256
|
+
"""Ensure device identifier and timezone are provided"""
|
|
257
|
+
if not any([self.device_id, self.device_name, self.door_id, self.door_name]):
|
|
258
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
259
|
+
if not any([self.offset_seconds, self.utc_offset, self.tz_label]):
|
|
260
|
+
raise ValueError("At least one timezone specification (offset_seconds, utc_offset, tz_label) must be provided")
|
|
261
|
+
return self
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class FactoryResetDeviceInput(BaseModel):
|
|
265
|
+
"""Input schema for factory-reset-device tool"""
|
|
266
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
267
|
+
device_name: Optional[str] = Field(None, description="Target device name")
|
|
268
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
269
|
+
door_id: Optional[int] = Field(None, description="Resolve device by door id (entry by default)")
|
|
270
|
+
door_name: Optional[str] = Field(None, description="Resolve device by door name (substring)")
|
|
271
|
+
edge: Optional[str] = Field(None, description="When resolving by door, choose entry or exit device (default: entry)")
|
|
272
|
+
is_reset_without_network: Optional[bool] = Field(None, description="If true, perform reset without network; default false")
|
|
273
|
+
dry_run: Optional[bool] = Field(None, description="If true, only report what would happen without calling the reset API")
|
|
274
|
+
|
|
275
|
+
@model_validator(mode="after")
|
|
276
|
+
def validate_identifier(self):
|
|
277
|
+
"""Ensure at least one device identifier is provided"""
|
|
278
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
279
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
280
|
+
return self
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class UpdateDeviceAuthDisplayInput(BaseModel):
|
|
284
|
+
"""Input schema for update-device-auth-and-display tool"""
|
|
285
|
+
device_id: Optional[Union[int, str]] = Field(None, description="Target device id. If omitted, use device_name.")
|
|
286
|
+
device_name: Optional[str] = Field(None, description="Exact device name when device_id is not provided.")
|
|
287
|
+
auth_timeout: Optional[Union[int, str]] = Field(None, ge=3, le=20, description="Authentication wait time in seconds (3–20).")
|
|
288
|
+
enable_server_matching: Optional[bool] = None
|
|
289
|
+
enable_full_access: Optional[bool] = None
|
|
290
|
+
display_userid_option: Optional[Union[int, str]] = Field(None, description="User ID display: 0=all, 1=first-only, 2=hidden")
|
|
291
|
+
display_username_option: Optional[Union[int, str]] = Field(None, description="User name display: 0=all, 1=first-only, 2=hidden")
|
|
292
|
+
dry_run: Optional[bool] = None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class BulkAddDeviceItemInput(BaseModel):
|
|
296
|
+
"""Input schema for bulk-add-devices device item"""
|
|
297
|
+
device_id: int = Field(..., description="Unique device ID")
|
|
298
|
+
name: str = Field(..., description="Device name")
|
|
299
|
+
ip_address: str = Field(..., description="IP address")
|
|
300
|
+
device_group_id: int = Field(..., description="Target device group id")
|
|
301
|
+
device_type_id: Optional[int] = Field(None, description="Optional device type id")
|
|
302
|
+
device_type_name: Optional[str] = Field(None, description="Optional device type name")
|
|
303
|
+
port: Optional[int] = Field(default=51211, description="Port (default 51211)")
|
|
304
|
+
use_ssl: Optional[bool] = Field(None, description="Use SSL")
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class BulkAddDevicesInput(BaseModel):
|
|
308
|
+
"""Input schema for bulk-add-devices tool"""
|
|
309
|
+
devices: List[BulkAddDeviceItemInput] = Field(..., description="List of device objects; each item uses the same fields as 'add-device'.")
|
|
310
|
+
continue_on_error: Optional[bool] = Field(default=True, description="If false, stop at the first failure.")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class UpdateDeviceNetworkInput(BaseModel):
|
|
314
|
+
"""Input schema for update-device-network tool"""
|
|
315
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
316
|
+
device_name: Optional[str] = Field(None, description="Target device name")
|
|
317
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
318
|
+
door_id: Optional[int] = Field(None, description="Resolve via door id (entry by default)")
|
|
319
|
+
door_name: Optional[str] = Field(None, description="Resolve via door name (substring)")
|
|
320
|
+
edge: Optional[str] = Field(None, description="When resolving by door, choose entry or exit device (default: entry)")
|
|
321
|
+
dhcp_enabled: bool = Field(..., description="When true, static fields are ignored and disabled.")
|
|
322
|
+
ip_address: Optional[str] = Field(None, description="IPv4 address (required when dhcp_enabled=false)")
|
|
323
|
+
subnet_mask: Optional[str] = Field(None, description="IPv4 subnet mask (required when dhcp_enabled=false)")
|
|
324
|
+
gateway: Optional[str] = Field(None, description="IPv4 gateway (optional when dhcp_enabled=false)")
|
|
325
|
+
dns_server: Optional[str] = Field(None, description="IPv4 DNS server (optional when dhcp_enabled=false)")
|
|
326
|
+
device_port: Optional[int] = Field(default=51211, ge=1, le=65535, description="Device port (default 51211)")
|
|
327
|
+
|
|
328
|
+
@model_validator(mode="after")
|
|
329
|
+
def validate_identifiers(self):
|
|
330
|
+
"""Ensure device identifier and network settings are provided"""
|
|
331
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
332
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
333
|
+
if not self.dhcp_enabled and (not self.ip_address or not self.subnet_mask):
|
|
334
|
+
raise ValueError("ip_address and subnet_mask are required when dhcp_enabled=false")
|
|
335
|
+
return self
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class UpdateDeviceSerialCommInput(BaseModel):
|
|
339
|
+
"""Input schema for update-device-serial-comm tool"""
|
|
340
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
341
|
+
device_name: Optional[str] = Field(None, description="Target device name (exact match or resolver policy)")
|
|
342
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
343
|
+
door_id: Optional[int] = Field(None, description="Resolve via door id (entry by default)")
|
|
344
|
+
door_name: Optional[str] = Field(None, description="Resolve via door name (substring)")
|
|
345
|
+
edge: Optional[str] = Field(None, description="When resolving by door, choose entry or exit device (default: entry)")
|
|
346
|
+
rs485_mode: Union[int, str] = Field(..., description="RS-485 role: 1|master, 2|slave, 3|device_default (follow device default).")
|
|
347
|
+
baud_rate: int = Field(..., description="RS-485 baud rate.")
|
|
348
|
+
show_osdp_result: Optional[Union[int, str]] = Field(None, description="Only applicable when rs485_mode='slave'. 0|'controller' = show controller result, 1|'device' = show device result.")
|
|
349
|
+
dry_run: Optional[bool] = Field(default=False, description="If true, do not PUT; only resolve and return the computed payload.")
|
|
350
|
+
|
|
351
|
+
@model_validator(mode="after")
|
|
352
|
+
def validate_identifiers(self):
|
|
353
|
+
"""Ensure device identifier is provided"""
|
|
354
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
355
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
356
|
+
return self
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class UpdateDeviceServerCommInput(BaseModel):
|
|
360
|
+
"""Input schema for update-device-server-comm tool"""
|
|
361
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
362
|
+
device_name: Optional[str] = Field(None, description="Target device name (exact/contains per handler policy)")
|
|
363
|
+
device_search_text: Optional[str] = Field(None, description="Alternative substring search text")
|
|
364
|
+
door_id: Optional[int] = Field(None, description="Resolve via door id")
|
|
365
|
+
door_name: Optional[str] = Field(None, description="Resolve via door name")
|
|
366
|
+
edge: Optional[str] = Field(None, description="When resolving via door, choose entry or exit (default: entry)")
|
|
367
|
+
connect_from_device: Optional[bool] = Field(None, description="Checkbox 'Connect to server from device' (True=enable)")
|
|
368
|
+
server_address: Optional[str] = Field(None, description="Server address (IPv4 or hostname)")
|
|
369
|
+
server_port: Optional[int] = Field(default=51212, ge=1, le=65535, description="Server port (default 51212)")
|
|
370
|
+
dry_run: Optional[bool] = Field(default=False, description="If true, do not PUT; return payload preview only")
|
|
371
|
+
|
|
372
|
+
@model_validator(mode="after")
|
|
373
|
+
def validate_identifiers(self):
|
|
374
|
+
"""Ensure device identifier and server comm settings are provided"""
|
|
375
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
376
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
377
|
+
if not any([self.connect_from_device, self.server_address, self.server_port]):
|
|
378
|
+
raise ValueError("At least one server communication setting must be provided")
|
|
379
|
+
return self
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class UpdateDeviceFingerprintInput(BaseModel):
|
|
383
|
+
"""Input schema for update-device-fingerprint tool"""
|
|
384
|
+
device_id: Optional[Union[int, str]] = Field(None, description="Device id")
|
|
385
|
+
device_name: Optional[str] = Field(None, description="Device name (exact or substring depending on resolver)")
|
|
386
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
387
|
+
door_id: Optional[int] = Field(None, description="Resolve by door id (entry by default unless edge is specified)")
|
|
388
|
+
door_name: Optional[str] = Field(None, description="Resolve by door name (substring)")
|
|
389
|
+
edge: Optional[str] = Field(None, description="Edge selector when resolving by door")
|
|
390
|
+
security_level: Optional[Union[int, str]] = Field(None, description="Fingerprint 1:N security level (0=normal, 1=secure, 2=most secure) → fingerprint.security_level")
|
|
391
|
+
fast_mode: Optional[Union[int, str]] = Field(None, description="Fingerprint Fast Mode (0=auto, 1=normal, 2=fast, 3=fastest) → fingerprint.fast_mode")
|
|
392
|
+
sensitivity: Optional[int] = Field(None, ge=1, le=7, description="Sensor sensitivity (1..7) → fingerprint.sensitivity")
|
|
393
|
+
scan_wait_time: Optional[int] = Field(None, ge=1, le=20, description="Scan wait time in seconds (1..20) → fingerprint.scan_timeout")
|
|
394
|
+
matching_wait_time: Optional[int] = Field(None, ge=1, le=20, description="Matching wait time in seconds (1..20) → authentication.matching_timeout")
|
|
395
|
+
show_image: Optional[bool] = Field(None, description="Display fingerprint image → fingerprint.show_image")
|
|
396
|
+
sensor_mode: Optional[Union[int, str]] = Field(None, description="Sensor Mode (0=Auto On, 1=Always On) → fingerprint.sensor_mode")
|
|
397
|
+
advanced_enrollment: Optional[bool] = Field(None, description="Enrollment quality check → fingerprint.advanced_enrollment")
|
|
398
|
+
lfd_level: Optional[Union[int, str]] = Field(None, description="Fingerprint LFD level (0=Off, 1=Normal, 2=Strong, 3=Very Strong) → fingerprint.lfd_level")
|
|
399
|
+
duplicate_check: Optional[bool] = Field(None, description="Duplicate check → fingerprint.duplicate_check")
|
|
400
|
+
dry_run: Optional[bool] = Field(default=False, description="If true, do not PUT; return payload preview only")
|
|
401
|
+
|
|
402
|
+
@model_validator(mode="after")
|
|
403
|
+
def validate_identifier(self):
|
|
404
|
+
"""Ensure at least one device identifier is provided"""
|
|
405
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
406
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
407
|
+
return self
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class UpdateDeviceFaceInput(BaseModel):
|
|
411
|
+
"""Input schema for update-device-face tool"""
|
|
412
|
+
device_id: Optional[Union[int, str]] = Field(None, description="Device id")
|
|
413
|
+
device_name: Optional[str] = Field(None, description="Device name (resolved to id internally)")
|
|
414
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (resolved to id internally)")
|
|
415
|
+
door_id: Optional[int] = Field(None, description="Optional: resolve by door (fallback uses generic resolver)")
|
|
416
|
+
door_name: Optional[str] = Field(None, description="Optional: resolve by door (fallback uses generic resolver)")
|
|
417
|
+
edge: Optional[str] = Field(None, description="Edge selector for door resolution")
|
|
418
|
+
security_level: Optional[Union[int, str]] = Field(None, description="→ face.security_level")
|
|
419
|
+
motion_sensor: Optional[Union[int, str]] = Field(None, description="→ face.sensitivity")
|
|
420
|
+
sensitivity: Optional[Union[int, str]] = Field(None, description="[Alias of motion_sensor] → face.sensitivity")
|
|
421
|
+
lfd_level: Optional[Union[int, str]] = Field(None, description="→ face.lfd_level")
|
|
422
|
+
enroll_timeout: Optional[int] = Field(None, ge=10, le=20, description="→ face.scan_wait_time")
|
|
423
|
+
max_rotation: Optional[int] = Field(None, description="→ face.max_rotation")
|
|
424
|
+
detect_distance_min: Optional[int] = Field(None, description="→ face.detection_distance_min")
|
|
425
|
+
detect_distance_max: Optional[Union[int, str]] = Field(None, description="→ face.detection_distance_max")
|
|
426
|
+
face_width_min: Optional[int] = Field(None, ge=0, le=100, description="→ face.face_width_min")
|
|
427
|
+
face_width_max: Optional[int] = Field(None, ge=0, le=100, description="→ face.face_width_max")
|
|
428
|
+
operation_mode: Optional[Union[int, str]] = Field(None, description="→ face.operation_mode")
|
|
429
|
+
wide_search: Optional[bool] = Field(None, description="→ face.face_wide_search")
|
|
430
|
+
fast_enroll: Optional[bool] = Field(None, description="→ face.quick_enrollment")
|
|
431
|
+
save_image: Optional[bool] = Field(None, description="→ face.store_visual_face_image")
|
|
432
|
+
duplicate_check: Optional[bool] = Field(None, description="→ face.duplicate_check")
|
|
433
|
+
light_brightness: Optional[Union[int, str]] = Field(None, description="Mapped to face.proximity_level (0/1/3 only; 2 is unused)")
|
|
434
|
+
dry_run: Optional[bool] = Field(default=False, description="If true, do not PUT; return payload preview only")
|
|
435
|
+
|
|
436
|
+
@model_validator(mode="after")
|
|
437
|
+
def validate_identifier(self):
|
|
438
|
+
"""Ensure at least one device identifier is provided"""
|
|
439
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
440
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
441
|
+
return self
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class UpdateDeviceCardCSNInput(BaseModel):
|
|
445
|
+
"""Input schema for update-device-card-csn tool"""
|
|
446
|
+
device_id: Optional[int] = Field(None, description="Target device id")
|
|
447
|
+
device_name: Optional[str] = Field(None, description="Target device name (resolver-dependent)")
|
|
448
|
+
device_search_text: Optional[str] = Field(None, description="Alternate device query (substring)")
|
|
449
|
+
door_id: Optional[int] = Field(None, description="Resolve via door id (entry by default)")
|
|
450
|
+
door_name: Optional[str] = Field(None, description="Resolve via door name (substring)")
|
|
451
|
+
edge: Optional[str] = Field(None, description="Door edge selector when resolving by door")
|
|
452
|
+
csn_enabled: bool = Field(..., description="Map to Device.card.use_csn (required)")
|
|
453
|
+
em4100_enabled: Optional[bool] = Field(None, description="Map to Device.card.use_em")
|
|
454
|
+
mifare_felica_enabled: Optional[bool] = Field(None, description="Map to Device.card.use_mifare_felica")
|
|
455
|
+
byte_order: Optional[Union[str, int]] = Field(None, description="Map to Device.card.byte_order (LSB='1', MSB='0').")
|
|
456
|
+
format_type: Optional[str] = Field(None, description="Map to Device.card.use_wiegand_format")
|
|
457
|
+
wiegand_format: Optional[str] = Field(None, description="Human-friendly Wiegand CSN name. Will be translated to id using a static map.")
|
|
458
|
+
wiegand_format_id: Optional[Union[int, str]] = Field(None, description="Map to Device.wiegand.wiegand_csn_id.id (e.g., 7 for 'DESFire 56bit').")
|
|
459
|
+
|
|
460
|
+
@model_validator(mode="after")
|
|
461
|
+
def validate_identifier(self):
|
|
462
|
+
"""Ensure at least one device identifier is provided"""
|
|
463
|
+
if not any([self.device_id, self.device_name, self.device_search_text, self.door_id, self.door_name]):
|
|
464
|
+
raise ValueError("At least one device/door identifier must be provided")
|
|
465
|
+
return self
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
# ============================================================================
|
|
469
|
+
# Waiting Device Schemas
|
|
470
|
+
# ============================================================================
|
|
471
|
+
|
|
472
|
+
class ListWaitingDevicesInput(BaseModel):
|
|
473
|
+
"""Input schema for list-waiting-devices tool"""
|
|
474
|
+
ip_filter: Optional[str] = Field(None, description="Filter devices by IP address (substring match)")
|
|
475
|
+
device_type_id: Optional[int] = Field(None, description="Filter by device type ID (e.g., 35 for BioStation 3)")
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class AddWaitingDeviceInput(BaseModel):
|
|
479
|
+
"""Input schema for add-waiting-device tool"""
|
|
480
|
+
waiting_device_id: Union[int, str] = Field(..., description="Device ID from the waiting list (required)")
|
|
481
|
+
name: Optional[str] = Field(None, description="Name for the device. If not provided, auto-generates from device type and ID")
|
|
482
|
+
device_type_id: Optional[int] = Field(None, description="Device type ID (usually obtained from waiting device info)")
|
|
483
|
+
ip_address: Optional[str] = Field(None, description="IP address (from waiting device info)")
|
|
484
|
+
device_group_id: Optional[int] = Field(default=1, description="Device group ID to assign (default: 1 for root group)")
|
|
485
|
+
use_alphanumeric: Optional[bool] = Field(default=True, description="Enable alphanumeric user IDs")
|
|
486
|
+
follower_server_id: Optional[Union[int, str]] = Field(default=1, description="Follower server ID (default: 1)")
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
class BulkAddWaitingDevicesInput(BaseModel):
|
|
490
|
+
"""Input schema for bulk-add-waiting-devices tool"""
|
|
491
|
+
waiting_device_ids: List[Union[int, str]] = Field(..., description="Array of waiting device IDs to add")
|
|
492
|
+
device_group_id: Optional[int] = Field(default=1, description="Device group ID for all devices (default: 1)")
|
|
493
|
+
name_prefix: Optional[str] = Field(None, description="Optional prefix for auto-generated device names")
|
|
494
|
+
use_alphanumeric: Optional[bool] = Field(default=True, description="Enable alphanumeric user IDs for all devices")
|
|
495
|
+
continue_on_error: Optional[bool] = Field(default=True, description="Continue adding remaining devices if one fails")
|
|
496
|
+
|