libosdp 3.0.8__tar.gz → 3.2.0__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.
Files changed (59) hide show
  1. {libosdp-3.0.8/libosdp.egg-info → libosdp-3.2.0}/PKG-INFO +35 -3
  2. libosdp-3.0.8/PKG-INFO → libosdp-3.2.0/README.md +25 -14
  3. libosdp-3.0.8/README.md → libosdp-3.2.0/libosdp.egg-info/PKG-INFO +46 -1
  4. {libosdp-3.0.8 → libosdp-3.2.0}/libosdp.egg-info/SOURCES.txt +3 -2
  5. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/__init__.py +2 -2
  6. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/constants.py +8 -1
  7. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/control_panel.py +93 -47
  8. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/helpers.py +1 -1
  9. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/key_store.py +1 -1
  10. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/peripheral_device.py +49 -26
  11. {libosdp-3.0.8 → libosdp-3.2.0}/setup.py +60 -24
  12. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/include/osdp.h +249 -37
  13. libosdp-3.2.0/vendor/include/osdp_export.h +76 -0
  14. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/base.c +31 -18
  15. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/cp.c +308 -45
  16. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/data.c +33 -45
  17. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/module.c +10 -2
  18. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/module.h +21 -2
  19. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/pd.c +171 -19
  20. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/python/osdp_sys/utils.c +152 -7
  21. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/crypto/tinyaes.c +2 -2
  22. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_common.c +7 -23
  23. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_common.h +190 -74
  24. libosdp-3.2.0/vendor/src/osdp_config.h +102 -0
  25. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_cp.c +536 -247
  26. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_diag.c +1 -1
  27. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_diag.h +2 -2
  28. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_file.c +20 -21
  29. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_file.h +1 -1
  30. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_pd.c +329 -147
  31. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_phy.c +275 -104
  32. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/osdp_sc.c +4 -8
  33. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/assert.h +9 -1
  34. libosdp-3.2.0/vendor/utils/include/utils/crc16.h +25 -0
  35. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/disjoint_set.h +1 -1
  36. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/list.h +1 -1
  37. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/logger.h +1 -1
  38. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/pcap_gen.h +1 -1
  39. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/queue.h +1 -1
  40. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/slab.h +1 -1
  41. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/include/utils/utils.h +19 -5
  42. libosdp-3.2.0/vendor/utils/src/crc16.c +21 -0
  43. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/disjoint_set.c +1 -1
  44. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/list.c +1 -1
  45. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/logger.c +7 -2
  46. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/queue.c +1 -1
  47. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/utils.c +35 -1
  48. libosdp-3.0.8/vendor/src/osdp_config.h +0 -42
  49. libosdp-3.0.8/vendor/src/osdp_export.h +0 -1
  50. libosdp-3.0.8/vendor/utils/src/slab.c +0 -64
  51. {libosdp-3.0.8 → libosdp-3.2.0}/MANIFEST.in +0 -0
  52. {libosdp-3.0.8 → libosdp-3.2.0}/libosdp.egg-info/dependency_links.txt +0 -0
  53. {libosdp-3.0.8 → libosdp-3.2.0}/libosdp.egg-info/top_level.txt +0 -0
  54. {libosdp-3.0.8 → libosdp-3.2.0}/osdp/channel.py +0 -0
  55. {libosdp-3.0.8 → libosdp-3.2.0}/pyproject.toml +0 -0
  56. {libosdp-3.0.8 → libosdp-3.2.0}/setup.cfg +0 -0
  57. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/crypto/tinyaes_src.c +0 -0
  58. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/src/crypto/tinyaes_src.h +0 -0
  59. {libosdp-3.0.8 → libosdp-3.2.0}/vendor/utils/src/pcap_gen.c +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: libosdp
3
- Version: 3.0.8
3
+ Version: 3.2.0
4
4
  Summary: Library implementation of IEC 60839-11-5 OSDP (Open Supervised Device Protocol)
5
5
  Home-page: https://github.com/goToMain/libosdp
6
6
  Author: Siddharth Chandrasekaran
@@ -10,6 +10,14 @@ Classifier: License :: OSI Approved :: Apache Software License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: home-page
19
+ Dynamic: requires-python
20
+ Dynamic: summary
13
21
 
14
22
  # LibOSDP for Python
15
23
 
@@ -77,6 +85,18 @@ while True:
77
85
 
78
86
  see [examples/cp_app.py][2] for more details.
79
87
 
88
+ Optional command completion callback:
89
+
90
+ ```python
91
+ from osdp import CompletionStatus
92
+
93
+ def on_command_complete(address, command, status):
94
+ if status == CompletionStatus.Ok:
95
+ print("command completed")
96
+
97
+ cp.set_command_completion_handler(on_command_complete)
98
+ ```
99
+
80
100
  ### Peripheral Device mode:
81
101
 
82
102
  ```python
@@ -108,6 +128,18 @@ while True:
108
128
 
109
129
  see [examples/pd_app.py][3] for more details.
110
130
 
111
- [1]: https://libosdp.sidcha.dev/api/
131
+ Optional event completion callback:
132
+
133
+ ```python
134
+ from osdp import CompletionStatus
135
+
136
+ def on_event_complete(event, status):
137
+ if status == CompletionStatus.Flushed:
138
+ print("event removed by flush")
139
+
140
+ pd.set_event_completion_handler(on_event_complete)
141
+ ```
142
+
143
+ [1]: https://doc.osdp.dev/api/
112
144
  [2]: https://github.com/goToMain/libosdp/blob/master/examples/python/cp_app.py
113
145
  [3]: https://github.com/goToMain/libosdp/blob/master/examples/python/pd_app.py
@@ -1,16 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: libosdp
3
- Version: 3.0.8
4
- Summary: Library implementation of IEC 60839-11-5 OSDP (Open Supervised Device Protocol)
5
- Home-page: https://github.com/goToMain/libosdp
6
- Author: Siddharth Chandrasekaran
7
- Author-email: sidcha.dev@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: Apache Software License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
-
14
1
  # LibOSDP for Python
15
2
 
16
3
  This package exposes the C/C++ library for OSDP devices to python to enable rapid
@@ -77,6 +64,18 @@ while True:
77
64
 
78
65
  see [examples/cp_app.py][2] for more details.
79
66
 
67
+ Optional command completion callback:
68
+
69
+ ```python
70
+ from osdp import CompletionStatus
71
+
72
+ def on_command_complete(address, command, status):
73
+ if status == CompletionStatus.Ok:
74
+ print("command completed")
75
+
76
+ cp.set_command_completion_handler(on_command_complete)
77
+ ```
78
+
80
79
  ### Peripheral Device mode:
81
80
 
82
81
  ```python
@@ -108,6 +107,18 @@ while True:
108
107
 
109
108
  see [examples/pd_app.py][3] for more details.
110
109
 
111
- [1]: https://libosdp.sidcha.dev/api/
110
+ Optional event completion callback:
111
+
112
+ ```python
113
+ from osdp import CompletionStatus
114
+
115
+ def on_event_complete(event, status):
116
+ if status == CompletionStatus.Flushed:
117
+ print("event removed by flush")
118
+
119
+ pd.set_event_completion_handler(on_event_complete)
120
+ ```
121
+
122
+ [1]: https://doc.osdp.dev/api/
112
123
  [2]: https://github.com/goToMain/libosdp/blob/master/examples/python/cp_app.py
113
124
  [3]: https://github.com/goToMain/libosdp/blob/master/examples/python/pd_app.py
@@ -1,3 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: libosdp
3
+ Version: 3.2.0
4
+ Summary: Library implementation of IEC 60839-11-5 OSDP (Open Supervised Device Protocol)
5
+ Home-page: https://github.com/goToMain/libosdp
6
+ Author: Siddharth Chandrasekaran
7
+ Author-email: sidcha.dev@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: home-page
19
+ Dynamic: requires-python
20
+ Dynamic: summary
21
+
1
22
  # LibOSDP for Python
2
23
 
3
24
  This package exposes the C/C++ library for OSDP devices to python to enable rapid
@@ -64,6 +85,18 @@ while True:
64
85
 
65
86
  see [examples/cp_app.py][2] for more details.
66
87
 
88
+ Optional command completion callback:
89
+
90
+ ```python
91
+ from osdp import CompletionStatus
92
+
93
+ def on_command_complete(address, command, status):
94
+ if status == CompletionStatus.Ok:
95
+ print("command completed")
96
+
97
+ cp.set_command_completion_handler(on_command_complete)
98
+ ```
99
+
67
100
  ### Peripheral Device mode:
68
101
 
69
102
  ```python
@@ -95,6 +128,18 @@ while True:
95
128
 
96
129
  see [examples/pd_app.py][3] for more details.
97
130
 
98
- [1]: https://libosdp.sidcha.dev/api/
131
+ Optional event completion callback:
132
+
133
+ ```python
134
+ from osdp import CompletionStatus
135
+
136
+ def on_event_complete(event, status):
137
+ if status == CompletionStatus.Flushed:
138
+ print("event removed by flush")
139
+
140
+ pd.set_event_completion_handler(on_event_complete)
141
+ ```
142
+
143
+ [1]: https://doc.osdp.dev/api/
99
144
  [2]: https://github.com/goToMain/libosdp/blob/master/examples/python/cp_app.py
100
145
  [3]: https://github.com/goToMain/libosdp/blob/master/examples/python/pd_app.py
@@ -14,6 +14,7 @@ osdp/helpers.py
14
14
  osdp/key_store.py
15
15
  osdp/peripheral_device.py
16
16
  vendor/include/osdp.h
17
+ vendor/include/osdp_export.h
17
18
  vendor/python/osdp_sys/base.c
18
19
  vendor/python/osdp_sys/cp.c
19
20
  vendor/python/osdp_sys/data.c
@@ -27,7 +28,6 @@ vendor/src/osdp_config.h
27
28
  vendor/src/osdp_cp.c
28
29
  vendor/src/osdp_diag.c
29
30
  vendor/src/osdp_diag.h
30
- vendor/src/osdp_export.h
31
31
  vendor/src/osdp_file.c
32
32
  vendor/src/osdp_file.h
33
33
  vendor/src/osdp_pd.c
@@ -37,6 +37,7 @@ vendor/src/crypto/tinyaes.c
37
37
  vendor/src/crypto/tinyaes_src.c
38
38
  vendor/src/crypto/tinyaes_src.h
39
39
  vendor/utils/include/utils/assert.h
40
+ vendor/utils/include/utils/crc16.h
40
41
  vendor/utils/include/utils/disjoint_set.h
41
42
  vendor/utils/include/utils/list.h
42
43
  vendor/utils/include/utils/logger.h
@@ -44,10 +45,10 @@ vendor/utils/include/utils/pcap_gen.h
44
45
  vendor/utils/include/utils/queue.h
45
46
  vendor/utils/include/utils/slab.h
46
47
  vendor/utils/include/utils/utils.h
48
+ vendor/utils/src/crc16.c
47
49
  vendor/utils/src/disjoint_set.c
48
50
  vendor/utils/src/list.c
49
51
  vendor/utils/src/logger.c
50
52
  vendor/utils/src/pcap_gen.c
51
53
  vendor/utils/src/queue.c
52
- vendor/utils/src/slab.c
53
54
  vendor/utils/src/utils.c
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -9,7 +9,7 @@ from .peripheral_device import PeripheralDevice
9
9
  from .key_store import KeyStore
10
10
  from .constants import (
11
11
  LibFlag, Command, CommandLEDColor, CommandFileTxFlags, Event, EventNotification,
12
- CardFormat, Capability, LogLevel, StatusReportType
12
+ CardFormat, Capability, LogLevel, StatusReportType, CompletionStatus
13
13
  )
14
14
  from .helpers import PdId, PDInfo, PDCapabilities
15
15
  from .channel import Channel
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -35,6 +35,7 @@ class Command:
35
35
  Buzzer = osdp_sys.CMD_BUZZER
36
36
  LED = osdp_sys.CMD_LED
37
37
  Comset = osdp_sys.CMD_COMSET
38
+ ComsetDone = osdp_sys.CMD_COMSET_DONE
38
39
  Text = osdp_sys.CMD_TEXT
39
40
  Manufacturer = osdp_sys.CMD_MFG
40
41
  Keyset = osdp_sys.CMD_KEYSET
@@ -59,6 +60,12 @@ class EventNotification:
59
60
  SecureChannelStatus = osdp_sys.EVENT_NOTIFICATION_SC_STATUS
60
61
  PeripheralDeviceStatus = osdp_sys.EVENT_NOTIFICATION_PD_STATUS
61
62
 
63
+ class CompletionStatus:
64
+ Ok = getattr(osdp_sys, "COMPLETION_OK", 0)
65
+ Failed = getattr(osdp_sys, "COMPLETION_FAILED", 1)
66
+ Flushed = getattr(osdp_sys, "COMPLETION_FLUSHED", 2)
67
+ Aborted = getattr(osdp_sys, "COMPLETION_ABORTED", 3)
68
+
62
69
  class Event:
63
70
  CardRead = osdp_sys.EVENT_CARDREAD
64
71
  KeyPress = osdp_sys.EVENT_KEYPRESS
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -17,7 +17,8 @@ class ControlPanel():
17
17
  self,
18
18
  pd_info_list: list[PDInfo],
19
19
  log_level: LogLevel=LogLevel.Info,
20
- event_handler: Callable[[int, dict], int]=None
20
+ event_handler: Callable[[int, dict], int]=None,
21
+ command_completion_handler: Callable[[int, dict, int], None]=None
21
22
  ) -> None:
22
23
  self.pd_addr = []
23
24
  info_list = []
@@ -26,9 +27,18 @@ class ControlPanel():
26
27
  self.pd_addr.append(pd_info.address)
27
28
  info_list.append(pd_info.get())
28
29
  self.event_queue = [ queue.Queue() for i in self.pd_addr ]
30
+ self.user_event_handler = None
31
+ self.user_command_completion_handler = None
29
32
  osdp_sys.set_loglevel(log_level)
30
33
  self.ctx = osdp_sys.ControlPanel(info_list)
34
+ # Always use our internal handler to ensure queue functionality
35
+ self.ctx.set_event_callback(self._internal_event_handler)
36
+ if hasattr(self.ctx, "set_command_completion_callback"):
37
+ self.ctx.set_command_completion_callback(
38
+ self._internal_command_completion_handler
39
+ )
31
40
  self.set_event_handler(event_handler)
41
+ self.set_command_completion_handler(command_completion_handler)
32
42
  self.event = None
33
43
  self.lock = None
34
44
  self.thread = None
@@ -36,21 +46,42 @@ class ControlPanel():
36
46
  @staticmethod
37
47
  def refresh(event, lock, ctx):
38
48
  while not event.is_set():
39
- lock.acquire()
40
- ctx.refresh()
41
- lock.release()
49
+ with lock:
50
+ ctx.refresh()
42
51
  time.sleep(0.020) #sleep for 20ms
43
52
 
44
53
  def set_event_handler(self, handler: Callable[[int, dict], int]):
45
- if handler:
46
- self.ctx.set_event_callback(handler)
47
- else:
48
- self.ctx.set_event_callback(self.event_handler)
54
+ """Set user event handler while maintaining queue functionality"""
55
+ self.user_event_handler = handler
56
+
57
+ def set_command_completion_handler(
58
+ self, handler: Callable[[int, dict, int], None]):
59
+ self.user_command_completion_handler = handler
49
60
 
50
- def event_handler(self, pd, event) -> int:
61
+ def _internal_event_handler(self, pd, event) -> int:
62
+ """Internal handler that manages both queue and user callback"""
63
+ # Always put event in queue for get_event() compatibility
51
64
  self.event_queue[pd].put(event)
65
+
66
+ # If user has set a custom handler, call it too
67
+ if self.user_event_handler:
68
+ try:
69
+ return self.user_event_handler(pd, event)
70
+ except Exception as e:
71
+ print(f"Error in user event handler: {e}")
72
+ return -1
73
+
52
74
  return 0
53
75
 
76
+ def _internal_command_completion_handler(self, pd, command, status) -> None:
77
+ if self.user_command_completion_handler:
78
+ try:
79
+ self.user_command_completion_handler(
80
+ self.pd_addr[pd], command, status
81
+ )
82
+ except Exception as e:
83
+ print(f"Error in user command completion handler: {e}")
84
+
54
85
  def get_event(self, address, timeout: int=5):
55
86
  pd = self.pd_addr.index(address)
56
87
  block = timeout >= 0
@@ -61,10 +92,8 @@ class ControlPanel():
61
92
  return event
62
93
 
63
94
  def status(self):
64
- self.lock.acquire()
65
- bitmask = self.ctx.status()
66
- self.lock.release()
67
- return bitmask
95
+ with self.lock:
96
+ return self.ctx.status()
68
97
 
69
98
  def is_online(self, address):
70
99
  pd = self.pd_addr.index(address)
@@ -72,9 +101,8 @@ class ControlPanel():
72
101
 
73
102
  def get_pd_id(self, address: int) -> PdId:
74
103
  pd = self.pd_addr.index(address)
75
- self.lock.acquire()
76
- pd_id_dict = self.ctx.get_pd_id(pd)
77
- self.lock.release()
104
+ with self.lock:
105
+ pd_id_dict = self.ctx.get_pd_id(pd)
78
106
  if pd_id_dict:
79
107
  # version: int, model: int, vendor_code: int, serial_number: int, firmware_version: int
80
108
  pd_id = PdId(
@@ -88,9 +116,8 @@ class ControlPanel():
88
116
 
89
117
  def check_capability(self, address: int, cap: Capability) -> Tuple[int, int]:
90
118
  pd = self.pd_addr.index(address)
91
- self.lock.acquire()
92
- compliance_level, num_items = self.ctx.check_capability(pd, cap)
93
- self.lock.release()
119
+ with self.lock:
120
+ compliance_level, num_items = self.ctx.check_capability(pd, cap)
94
121
  return (compliance_level, num_items)
95
122
 
96
123
  def get_num_online(self):
@@ -102,10 +129,8 @@ class ControlPanel():
102
129
  return online
103
130
 
104
131
  def sc_status(self):
105
- self.lock.acquire()
106
- bitmask = self.ctx.sc_status()
107
- self.lock.release()
108
- return bitmask
132
+ with self.lock:
133
+ return self.ctx.sc_status()
109
134
 
110
135
  def is_sc_active(self, address):
111
136
  pd = self.pd_addr.index(address)
@@ -119,40 +144,55 @@ class ControlPanel():
119
144
  sc_active += 1
120
145
  return sc_active
121
146
 
122
- def send_command(self, address, cmd):
147
+ def submit_command(self, address, cmd):
123
148
  pd = self.pd_addr.index(address)
124
- self.lock.acquire()
125
- ret = self.ctx.send_command(pd, cmd)
126
- self.lock.release()
127
- return ret
149
+ with self.lock:
150
+ return self.ctx.submit_command(pd, cmd)
151
+
152
+ def flush_commands(self, address):
153
+ pd = self.pd_addr.index(address)
154
+ with self.lock:
155
+ return self.ctx.flush_commands(pd)
156
+
157
+ def send_command(self, address, cmd):
158
+ from warnings import warn
159
+ warn("This method has been renamed to submit_command", DeprecationWarning, 2)
160
+ return self.submit_command(address, cmd)
128
161
 
129
162
  def set_flag(self, address, flag: LibFlag):
130
163
  pd = self.pd_addr.index(address)
131
- self.lock.acquire()
132
- ret = self.ctx.set_flag(pd, flag)
133
- self.lock.release()
134
- return ret
164
+ with self.lock:
165
+ return self.ctx.set_flag(pd, flag)
135
166
 
136
167
  def clear_flag(self, address, flag: LibFlag):
137
168
  pd = self.pd_addr.index(address)
138
- self.lock.acquire()
139
- ret = self.ctx.clear_flag(pd, flag)
140
- self.lock.release()
141
- return ret
169
+ with self.lock:
170
+ return self.ctx.clear_flag(pd, flag)
171
+
172
+ def disable_pd(self, address: int) -> bool:
173
+ pd = self.pd_addr.index(address)
174
+ with self.lock:
175
+ return self.ctx.disable_pd(pd)
176
+
177
+ def enable_pd(self, address: int) -> bool:
178
+ pd = self.pd_addr.index(address)
179
+ with self.lock:
180
+ return self.ctx.enable_pd(pd)
181
+
182
+ def is_pd_enabled(self, address: int) -> bool:
183
+ pd = self.pd_addr.index(address)
184
+ with self.lock:
185
+ return self.ctx.is_pd_enabled(pd)
142
186
 
143
187
  def register_file_ops(self, address, fops):
144
188
  pd = self.pd_addr.index(address)
145
- self.lock.acquire()
146
- ret = self.ctx.register_file_ops(pd, fops)
147
- self.lock.release()
148
- return ret
189
+ with self.lock:
190
+ return self.ctx.register_file_ops(pd, fops)
149
191
 
150
192
  def get_file_tx_status(self, address):
151
193
  pd = self.pd_addr.index(address)
152
- self.lock.acquire()
153
- ret = self.ctx.get_file_tx_status(pd)
154
- self.lock.release()
155
- return ret
194
+ with self.lock:
195
+ return self.ctx.get_file_tx_status(pd)
156
196
 
157
197
  def start(self):
158
198
  if self.thread:
@@ -197,8 +237,14 @@ class ControlPanel():
197
237
 
198
238
  def offline_wait(self, address, timeout=8):
199
239
  count = 0
200
- time.sleep(timeout)
201
- return self.is_online(address) == False
240
+ res = False
241
+ while count < timeout * 2:
242
+ time.sleep(0.5)
243
+ if not self.is_online(address):
244
+ res = True
245
+ break
246
+ count += 1
247
+ return res
202
248
 
203
249
  def sc_wait(self, address, timeout=5):
204
250
  count = 0
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2021-2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
2
+ # Copyright (c) 2021-2026 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
3
  #
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
  #
@@ -16,16 +16,22 @@ from .constants import LogLevel
16
16
  class PeripheralDevice():
17
17
  def __init__(self, pd_info: PDInfo, pd_cap: PDCapabilities,
18
18
  log_level: LogLevel=LogLevel.Info,
19
- command_handler: Callable[[dict], Tuple[int, dict]]=None):
19
+ command_handler: Callable[[dict], Tuple[int, dict]]=None,
20
+ event_completion_handler: Callable[[dict, int], None]=None):
20
21
  self.command_queue = queue.Queue()
21
22
  self.address = pd_info.address
23
+ self.user_command_handler = None
24
+ self.user_event_completion_handler = None
22
25
  osdp_sys.set_loglevel(log_level)
23
26
  self.ctx = osdp_sys.PeripheralDevice(pd_info.get(), capabilities=pd_cap.get())
24
- if command_handler:
25
- self.ctx.set_command_callback(command_handler)
26
- else:
27
- self.ctx.set_command_callback(self.command_handler)
27
+ # Always use our internal handler to ensure queue functionality
28
+ self.ctx.set_command_callback(self._internal_command_handler)
29
+ if hasattr(self.ctx, "set_event_completion_callback"):
30
+ self.ctx.set_event_completion_callback(
31
+ self._internal_event_completion_handler
32
+ )
28
33
  self.set_command_handler(command_handler)
34
+ self.set_event_completion_handler(event_completion_handler)
29
35
  self.event = None
30
36
  self.lock = None
31
37
  self.thread = None
@@ -33,20 +39,38 @@ class PeripheralDevice():
33
39
  @staticmethod
34
40
  def refresh(event, lock, ctx):
35
41
  while not event.is_set():
36
- lock.acquire()
37
- ctx.refresh()
38
- lock.release()
42
+ with lock:
43
+ ctx.refresh()
39
44
  time.sleep(0.020) #sleep for 20ms
40
45
 
41
- def command_handler(self, command) -> Tuple[int, dict]:
46
+ def _internal_command_handler(self, command) -> Tuple[int, dict]:
47
+ """Internal handler that manages both queue and user callback"""
48
+ # Always put command in queue for get_command() compatibility
42
49
  self.command_queue.put(command)
50
+
51
+ # If user has set a custom handler, call it too
52
+ if self.user_command_handler:
53
+ try:
54
+ return self.user_command_handler(command)
55
+ except Exception as e:
56
+ print(f"Error in user command handler: {e}")
57
+ return -1, None
58
+
43
59
  return 0, None
44
60
 
45
61
  def set_command_handler(self, handler: Callable[[dict], Tuple[int, dict]]):
46
- if handler:
47
- self.ctx.set_command_callback(handler)
48
- else:
49
- self.ctx.set_command_callback(self.command_handler)
62
+ """Set user command handler while maintaining queue functionality"""
63
+ self.user_command_handler = handler
64
+
65
+ def set_event_completion_handler(self, handler: Callable[[dict, int], None]):
66
+ self.user_event_completion_handler = handler
67
+
68
+ def _internal_event_completion_handler(self, event, status) -> None:
69
+ if self.user_event_completion_handler:
70
+ try:
71
+ self.user_event_completion_handler(event, status)
72
+ except Exception as e:
73
+ print(f"Error in user event completion handler: {e}")
50
74
 
51
75
  def get_command(self, timeout: int=5):
52
76
  block = timeout >= 0
@@ -56,17 +80,18 @@ class PeripheralDevice():
56
80
  return None
57
81
  return cmd
58
82
 
83
+ def submit_event(self, event):
84
+ with self.lock:
85
+ return self.ctx.submit_event(event)
86
+
59
87
  def notify_event(self, event):
60
- self.lock.acquire()
61
- ret = self.ctx.notify_event(event)
62
- self.lock.release()
63
- return ret
88
+ from warnings import warn
89
+ warn("This method has been renamed to submit_event", DeprecationWarning, 2)
90
+ return self.submit_event(event)
64
91
 
65
92
  def register_file_ops(self, fops):
66
- self.lock.acquire()
67
- ret = self.ctx.register_file_ops(0, fops)
68
- self.lock.release()
69
- return ret
93
+ with self.lock:
94
+ return self.ctx.register_file_ops(0, fops)
70
95
 
71
96
  def is_sc_active(self):
72
97
  return self.ctx.is_sc_active()
@@ -95,10 +120,8 @@ class PeripheralDevice():
95
120
  self.thread.start()
96
121
 
97
122
  def get_file_tx_status(self):
98
- self.lock.acquire()
99
- ret = self.ctx.get_file_tx_status(0)
100
- self.lock.release()
101
- return ret
123
+ with self.lock:
124
+ return self.ctx.get_file_tx_status(0)
102
125
 
103
126
  def stop(self):
104
127
  if not self.thread: