libosdp 2.4.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.
libosdp-2.4.0/PKG-INFO ADDED
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.1
2
+ Name: libosdp
3
+ Version: 2.4.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
+
14
+ # Python3 Bindings for LibOSDP
15
+
16
+ This is an initial effort at enabling python3 support for LibOSDP. This document
17
+ assumes that you are already familiar with [LibOSDP API][1] and device life
18
+ cycle management.
19
+
20
+ ## Build/Install
21
+
22
+ A python module `osdp` and classes `ControlPanel` and `PeripheralDevice` are
23
+ made available when the sources of this directory is built and installed.
24
+
25
+ After cloning this repo (with `--recurse-submodules`), you can do the following
26
+ (from the repo root) to build the python module.
27
+
28
+ ```bash
29
+ mkdir build && cd build
30
+ cmake ..
31
+ make python
32
+ make python_install
33
+ ```
34
+
35
+ If you want to undo this install, you can run `make python_uninstall`.
36
+
37
+ ## How to use this module?
38
+
39
+ ### Control Panel Mode
40
+
41
+ ```python
42
+ import osdp
43
+
44
+ ## Master key for secure channel
45
+ key = '01020304050607080910111213141516'
46
+
47
+ ## populate osdp_pd_info_t from python
48
+ pd_info = [
49
+ {
50
+ "address": 101,
51
+ "channel_type": "uart",
52
+ "channel_speed": 115200,
53
+ "channel_device": '/dev/ttyUSB0',
54
+ },
55
+ {
56
+ "address": 102,
57
+ "channel_type": "uart",
58
+ "channel_speed": 115200,
59
+ "channel_device": '/dev/ttyUSB1',
60
+ }
61
+ ]
62
+
63
+ ## Setup OSDP device in Control Panel mode
64
+ cp = osdp.ControlPanel(pd_info, master_key=key)
65
+
66
+ ## call this refresh method once every 50ms
67
+ cp.refresh()
68
+
69
+ ## create and output command
70
+ output_cmd = {
71
+ "command": osdp.CMD_OUTPUT,
72
+ "output_no": 0,
73
+ "control_code": 1,
74
+ "timer_count": 10
75
+ }
76
+
77
+ ## send a output command to PD-1
78
+ cp.send_command(1, output_cmd)
79
+ ```
80
+
81
+ see [samples/cp_app.py][2] for more details.
82
+
83
+ ### Peripheral Device mode:
84
+
85
+ ```python
86
+ ## Describe the PD
87
+ pd_info = {
88
+ "address": 101,
89
+ "channel_type": "uart",
90
+ "channel_speed": 115200,
91
+ "channel_device": '/dev/ttyUSB0',
92
+
93
+ "version": 1,
94
+ "model": 1,
95
+ "vendor_code": 0xCAFEBABE,
96
+ "serial_number": 0xDEADBEAF,
97
+ "firmware_version": 0x0000F00D
98
+ }
99
+
100
+ ## Indicate the PD's capabilities to LibOSDP.
101
+ pd_cap = [
102
+ {
103
+ # PD is capable of handling output commands
104
+ "function_code": osdp.CAP_OUTPUT_CONTROL,
105
+ "compliance_level": 1,
106
+ "num_items": 1
107
+ },
108
+ ]
109
+
110
+ ## Setup OSDP device in Peripheral Device mode
111
+ pd = osdp.PeripheralDevice(pd_info, capabilities=pd_cap)
112
+
113
+ ## call this refresh method periodically (at lease once every 50ms)
114
+ pd.refresh()
115
+
116
+ ## Define a callback handler. Must return 0 on success, -ve on failures.
117
+ def handle_command(address, command):
118
+ if address != pd_info['address']:
119
+ return { "return_code": -1 }
120
+ print("PD received command: ", command)
121
+ return { "return_code": 0 }
122
+
123
+ ## Set a handler for incoming commands from CP
124
+ pd.set_command_callback(handle_command)
125
+ ```
126
+
127
+ see [samples/pd_app.py][3] for more details.
128
+
129
+ [1]: https://libosdp.sidcha.dev/api/
130
+ [2]: https://github.com/goToMain/libosdp/blob/master/samples/python/cp_app.py
131
+ [3]: https://github.com/goToMain/libosdp/blob/master/samples/python/pd_app.py
@@ -0,0 +1,118 @@
1
+ # Python3 Bindings for LibOSDP
2
+
3
+ This is an initial effort at enabling python3 support for LibOSDP. This document
4
+ assumes that you are already familiar with [LibOSDP API][1] and device life
5
+ cycle management.
6
+
7
+ ## Build/Install
8
+
9
+ A python module `osdp` and classes `ControlPanel` and `PeripheralDevice` are
10
+ made available when the sources of this directory is built and installed.
11
+
12
+ After cloning this repo (with `--recurse-submodules`), you can do the following
13
+ (from the repo root) to build the python module.
14
+
15
+ ```bash
16
+ mkdir build && cd build
17
+ cmake ..
18
+ make python
19
+ make python_install
20
+ ```
21
+
22
+ If you want to undo this install, you can run `make python_uninstall`.
23
+
24
+ ## How to use this module?
25
+
26
+ ### Control Panel Mode
27
+
28
+ ```python
29
+ import osdp
30
+
31
+ ## Master key for secure channel
32
+ key = '01020304050607080910111213141516'
33
+
34
+ ## populate osdp_pd_info_t from python
35
+ pd_info = [
36
+ {
37
+ "address": 101,
38
+ "channel_type": "uart",
39
+ "channel_speed": 115200,
40
+ "channel_device": '/dev/ttyUSB0',
41
+ },
42
+ {
43
+ "address": 102,
44
+ "channel_type": "uart",
45
+ "channel_speed": 115200,
46
+ "channel_device": '/dev/ttyUSB1',
47
+ }
48
+ ]
49
+
50
+ ## Setup OSDP device in Control Panel mode
51
+ cp = osdp.ControlPanel(pd_info, master_key=key)
52
+
53
+ ## call this refresh method once every 50ms
54
+ cp.refresh()
55
+
56
+ ## create and output command
57
+ output_cmd = {
58
+ "command": osdp.CMD_OUTPUT,
59
+ "output_no": 0,
60
+ "control_code": 1,
61
+ "timer_count": 10
62
+ }
63
+
64
+ ## send a output command to PD-1
65
+ cp.send_command(1, output_cmd)
66
+ ```
67
+
68
+ see [samples/cp_app.py][2] for more details.
69
+
70
+ ### Peripheral Device mode:
71
+
72
+ ```python
73
+ ## Describe the PD
74
+ pd_info = {
75
+ "address": 101,
76
+ "channel_type": "uart",
77
+ "channel_speed": 115200,
78
+ "channel_device": '/dev/ttyUSB0',
79
+
80
+ "version": 1,
81
+ "model": 1,
82
+ "vendor_code": 0xCAFEBABE,
83
+ "serial_number": 0xDEADBEAF,
84
+ "firmware_version": 0x0000F00D
85
+ }
86
+
87
+ ## Indicate the PD's capabilities to LibOSDP.
88
+ pd_cap = [
89
+ {
90
+ # PD is capable of handling output commands
91
+ "function_code": osdp.CAP_OUTPUT_CONTROL,
92
+ "compliance_level": 1,
93
+ "num_items": 1
94
+ },
95
+ ]
96
+
97
+ ## Setup OSDP device in Peripheral Device mode
98
+ pd = osdp.PeripheralDevice(pd_info, capabilities=pd_cap)
99
+
100
+ ## call this refresh method periodically (at lease once every 50ms)
101
+ pd.refresh()
102
+
103
+ ## Define a callback handler. Must return 0 on success, -ve on failures.
104
+ def handle_command(address, command):
105
+ if address != pd_info['address']:
106
+ return { "return_code": -1 }
107
+ print("PD received command: ", command)
108
+ return { "return_code": 0 }
109
+
110
+ ## Set a handler for incoming commands from CP
111
+ pd.set_command_callback(handle_command)
112
+ ```
113
+
114
+ see [samples/pd_app.py][3] for more details.
115
+
116
+ [1]: https://libosdp.sidcha.dev/api/
117
+ [2]: https://github.com/goToMain/libosdp/blob/master/samples/python/cp_app.py
118
+ [3]: https://github.com/goToMain/libosdp/blob/master/samples/python/pd_app.py
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.1
2
+ Name: libosdp
3
+ Version: 2.4.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
+
14
+ # Python3 Bindings for LibOSDP
15
+
16
+ This is an initial effort at enabling python3 support for LibOSDP. This document
17
+ assumes that you are already familiar with [LibOSDP API][1] and device life
18
+ cycle management.
19
+
20
+ ## Build/Install
21
+
22
+ A python module `osdp` and classes `ControlPanel` and `PeripheralDevice` are
23
+ made available when the sources of this directory is built and installed.
24
+
25
+ After cloning this repo (with `--recurse-submodules`), you can do the following
26
+ (from the repo root) to build the python module.
27
+
28
+ ```bash
29
+ mkdir build && cd build
30
+ cmake ..
31
+ make python
32
+ make python_install
33
+ ```
34
+
35
+ If you want to undo this install, you can run `make python_uninstall`.
36
+
37
+ ## How to use this module?
38
+
39
+ ### Control Panel Mode
40
+
41
+ ```python
42
+ import osdp
43
+
44
+ ## Master key for secure channel
45
+ key = '01020304050607080910111213141516'
46
+
47
+ ## populate osdp_pd_info_t from python
48
+ pd_info = [
49
+ {
50
+ "address": 101,
51
+ "channel_type": "uart",
52
+ "channel_speed": 115200,
53
+ "channel_device": '/dev/ttyUSB0',
54
+ },
55
+ {
56
+ "address": 102,
57
+ "channel_type": "uart",
58
+ "channel_speed": 115200,
59
+ "channel_device": '/dev/ttyUSB1',
60
+ }
61
+ ]
62
+
63
+ ## Setup OSDP device in Control Panel mode
64
+ cp = osdp.ControlPanel(pd_info, master_key=key)
65
+
66
+ ## call this refresh method once every 50ms
67
+ cp.refresh()
68
+
69
+ ## create and output command
70
+ output_cmd = {
71
+ "command": osdp.CMD_OUTPUT,
72
+ "output_no": 0,
73
+ "control_code": 1,
74
+ "timer_count": 10
75
+ }
76
+
77
+ ## send a output command to PD-1
78
+ cp.send_command(1, output_cmd)
79
+ ```
80
+
81
+ see [samples/cp_app.py][2] for more details.
82
+
83
+ ### Peripheral Device mode:
84
+
85
+ ```python
86
+ ## Describe the PD
87
+ pd_info = {
88
+ "address": 101,
89
+ "channel_type": "uart",
90
+ "channel_speed": 115200,
91
+ "channel_device": '/dev/ttyUSB0',
92
+
93
+ "version": 1,
94
+ "model": 1,
95
+ "vendor_code": 0xCAFEBABE,
96
+ "serial_number": 0xDEADBEAF,
97
+ "firmware_version": 0x0000F00D
98
+ }
99
+
100
+ ## Indicate the PD's capabilities to LibOSDP.
101
+ pd_cap = [
102
+ {
103
+ # PD is capable of handling output commands
104
+ "function_code": osdp.CAP_OUTPUT_CONTROL,
105
+ "compliance_level": 1,
106
+ "num_items": 1
107
+ },
108
+ ]
109
+
110
+ ## Setup OSDP device in Peripheral Device mode
111
+ pd = osdp.PeripheralDevice(pd_info, capabilities=pd_cap)
112
+
113
+ ## call this refresh method periodically (at lease once every 50ms)
114
+ pd.refresh()
115
+
116
+ ## Define a callback handler. Must return 0 on success, -ve on failures.
117
+ def handle_command(address, command):
118
+ if address != pd_info['address']:
119
+ return { "return_code": -1 }
120
+ print("PD received command: ", command)
121
+ return { "return_code": 0 }
122
+
123
+ ## Set a handler for incoming commands from CP
124
+ pd.set_command_callback(handle_command)
125
+ ```
126
+
127
+ see [samples/pd_app.py][3] for more details.
128
+
129
+ [1]: https://libosdp.sidcha.dev/api/
130
+ [2]: https://github.com/goToMain/libosdp/blob/master/samples/python/cp_app.py
131
+ [3]: https://github.com/goToMain/libosdp/blob/master/samples/python/pd_app.py
@@ -0,0 +1,43 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/base.c
5
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/cp.c
6
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/data.c
7
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/module.c
8
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/pd.c
9
+ /Users/sidcha/work/osdp/libosdp/python/osdp_sys/utils.c
10
+ /Users/sidcha/work/osdp/libosdp/src/osdp_common.c
11
+ /Users/sidcha/work/osdp/libosdp/src/osdp_cp.c
12
+ /Users/sidcha/work/osdp/libosdp/src/osdp_file.c
13
+ /Users/sidcha/work/osdp/libosdp/src/osdp_pd.c
14
+ /Users/sidcha/work/osdp/libosdp/src/osdp_phy.c
15
+ /Users/sidcha/work/osdp/libosdp/src/osdp_sc.c
16
+ /Users/sidcha/work/osdp/libosdp/src/crypto/tinyaes.c
17
+ /Users/sidcha/work/osdp/libosdp/src/crypto/tinyaes_src.c
18
+ /Users/sidcha/work/osdp/libosdp/utils/src/bus_server.c
19
+ /Users/sidcha/work/osdp/libosdp/utils/src/channel.c
20
+ /Users/sidcha/work/osdp/libosdp/utils/src/disjoint_set.c
21
+ /Users/sidcha/work/osdp/libosdp/utils/src/event.c
22
+ /Users/sidcha/work/osdp/libosdp/utils/src/fdutils.c
23
+ /Users/sidcha/work/osdp/libosdp/utils/src/hashmap.c
24
+ /Users/sidcha/work/osdp/libosdp/utils/src/list.c
25
+ /Users/sidcha/work/osdp/libosdp/utils/src/logger.c
26
+ /Users/sidcha/work/osdp/libosdp/utils/src/memory.c
27
+ /Users/sidcha/work/osdp/libosdp/utils/src/queue.c
28
+ /Users/sidcha/work/osdp/libosdp/utils/src/serial.c
29
+ /Users/sidcha/work/osdp/libosdp/utils/src/slab.c
30
+ /Users/sidcha/work/osdp/libosdp/utils/src/sockutils.c
31
+ /Users/sidcha/work/osdp/libosdp/utils/src/strutils.c
32
+ /Users/sidcha/work/osdp/libosdp/utils/src/utils.c
33
+ /Users/sidcha/work/osdp/libosdp/utils/src/workqueue.c
34
+ libosdp.egg-info/PKG-INFO
35
+ libosdp.egg-info/SOURCES.txt
36
+ libosdp.egg-info/dependency_links.txt
37
+ libosdp.egg-info/top_level.txt
38
+ osdp/__init__.py
39
+ osdp/constants.py
40
+ osdp/control_panel.py
41
+ osdp/helpers.py
42
+ osdp/key_store.py
43
+ osdp/peripheral_device.py
@@ -0,0 +1,2 @@
1
+ osdp
2
+ osdp_sys
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ from .control_panel import ControlPanel
8
+ from .peripheral_device import PeripheralDevice
9
+ from .key_store import KeyStore
10
+ from .constants import (
11
+ LibFlag, Command, CommandLEDColor, CommandFileTxFlags,
12
+ Event, CardFormat, Capability, LogLevel
13
+ )
14
+ from .helpers import PDInfo, PDCapabilities
15
+
16
+ __author__ = 'Siddharth Chandrasekaran <sidcha.dev@gmail.com>'
17
+ __copyright__ = 'Copyright 2021-2023 Siddharth Chandrasekaran'
18
+ __license__ = 'Apache License, Version 2.0 (Apache-2.0)'
@@ -0,0 +1,71 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ import osdp_sys
8
+
9
+ class LibFlag:
10
+ EnforceSecure = osdp_sys.FLAG_ENFORCE_SECURE
11
+ InstallMode = osdp_sys.FLAG_INSTALL_MODE
12
+ IgnoreUnsolicited = osdp_sys.FLAG_IGN_UNSOLICITED
13
+
14
+ class LogLevel:
15
+ Emergency = osdp_sys.LOG_EMERG
16
+ Alert = osdp_sys.LOG_ALERT
17
+ Critical = osdp_sys.LOG_CRIT
18
+ Error = osdp_sys.LOG_ERROR
19
+ Warning = osdp_sys.LOG_WARNING
20
+ Notice = osdp_sys.LOG_NOTICE
21
+ Info = osdp_sys.LOG_INFO
22
+ Debug = osdp_sys.LOG_DEBUG
23
+
24
+ class Command:
25
+ Output = osdp_sys.CMD_OUTPUT
26
+ Buzzer = osdp_sys.CMD_BUZZER
27
+ LED = osdp_sys.CMD_LED
28
+ Comset = osdp_sys.CMD_COMSET
29
+ Text = osdp_sys.CMD_TEXT
30
+ Manufacturer = osdp_sys.CMD_MFG
31
+ Keyset = osdp_sys.CMD_KEYSET
32
+ FileTransfer = osdp_sys.CMD_FILE_TX
33
+
34
+ class CommandLEDColor:
35
+ Black = osdp_sys.LED_COLOR_NONE
36
+ Red = osdp_sys.LED_COLOR_RED
37
+ Green = osdp_sys.LED_COLOR_GREEN
38
+ Amber = osdp_sys.LED_COLOR_AMBER
39
+ Blue = osdp_sys.LED_COLOR_BLUE
40
+
41
+ class CommandFileTxFlags:
42
+ Cancel = osdp_sys.CMD_FILE_TX_FLAG_CANCEL
43
+
44
+ class Event:
45
+ CardRead = osdp_sys.EVENT_CARDREAD
46
+ KeyPress = osdp_sys.EVENT_KEYPRESS
47
+ ManufacturerReply = osdp_sys.EVENT_MFGREP
48
+ InputOutput = osdp_sys.EVENT_IO
49
+ Status = osdp_sys.EVENT_STATUS
50
+
51
+ class CardFormat:
52
+ Unspecified = osdp_sys.CARD_FMT_RAW_UNSPECIFIED
53
+ Wiegand = osdp_sys.CARD_FMT_RAW_WIEGAND
54
+ ASCII = osdp_sys.CARD_FMT_ASCII
55
+
56
+ class Capability:
57
+ Unused = osdp_sys.CAP_UNUSED
58
+ ContactStatusMonitoring = osdp_sys.CAP_CONTACT_STATUS_MONITORING
59
+ OutputControl = osdp_sys.CAP_OUTPUT_CONTROL
60
+ CardDataFormat = osdp_sys.CAP_CARD_DATA_FORMAT
61
+ LEDControl = osdp_sys.CAP_READER_LED_CONTROL
62
+ AudibleControl = osdp_sys.CAP_READER_AUDIBLE_OUTPUT
63
+ TextOutput = osdp_sys.CAP_READER_TEXT_OUTPUT
64
+ TimeKeeping = osdp_sys.CAP_TIME_KEEPING
65
+ CheckCharacter = osdp_sys.CAP_CHECK_CHARACTER_SUPPORT
66
+ CommunicationSecurity = osdp_sys.CAP_COMMUNICATION_SECURITY
67
+ ReceiveBufferSize = osdp_sys.CAP_RECEIVE_BUFFERSIZE
68
+ CombinedMessageSize = osdp_sys.CAP_LARGEST_COMBINED_MESSAGE_SIZE
69
+ SmartCard = osdp_sys.CAP_SMART_CARD_SUPPORT
70
+ Readers = osdp_sys.CAP_READERS
71
+ Biometrics = osdp_sys.CAP_BIOMETRICS
@@ -0,0 +1,193 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+ import sys
7
+ import os
8
+ import tempfile
9
+ import osdp_sys
10
+ import time
11
+ import queue
12
+ import threading
13
+
14
+ from .helpers import PDInfo
15
+ from .constants import LibFlag, LogLevel
16
+
17
+ class ControlPanel():
18
+ def __init__(self, pd_info_list, log_level: LogLevel=LogLevel.Info,
19
+ master_key: bytes=None):
20
+ self.pd_addr = []
21
+ info_list = []
22
+ self.num_pds = len(pd_info_list)
23
+ for pd_info in pd_info_list:
24
+ self.pd_addr.append(pd_info.address)
25
+ info_list.append(pd_info.get())
26
+ self.event_queue = [ queue.Queue() for i in self.pd_addr ]
27
+ if master_key:
28
+ self.ctx = osdp_sys.ControlPanel(info_list, master_key=master_key)
29
+ else:
30
+ self.ctx = osdp_sys.ControlPanel(info_list)
31
+ self.ctx.set_event_callback(self.event_handler)
32
+ self.ctx.set_loglevel(log_level)
33
+ self.event = None
34
+ self.lock = None
35
+ self.thread = None
36
+
37
+ @staticmethod
38
+ def refresh(event, lock, ctx):
39
+ while not event.is_set():
40
+ lock.acquire()
41
+ ctx.refresh()
42
+ lock.release()
43
+ time.sleep(0.020) #sleep for 20ms
44
+
45
+ def event_handler(self, pd, event):
46
+ self.event_queue[pd].put(event)
47
+
48
+ def get_event(self, address, timeout: int=5):
49
+ pd = self.pd_addr.index(address)
50
+ try:
51
+ event = self.event_queue[pd].get(timeout=timeout)
52
+ except queue.Empty:
53
+ return None
54
+ return event
55
+
56
+ def status(self):
57
+ self.lock.acquire()
58
+ bitmask = self.ctx.status()
59
+ self.lock.release()
60
+ return bitmask
61
+
62
+ def is_online(self, address):
63
+ pd = self.pd_addr.index(address)
64
+ return bool(self.status() & (1 << pd))
65
+
66
+ def get_num_online(self):
67
+ online = 0
68
+ bitmask = self.status()
69
+ for i in range(len(self.pd_addr)):
70
+ if bitmask & (1 << i):
71
+ online += 1
72
+ return online
73
+
74
+ def sc_status(self):
75
+ self.lock.acquire()
76
+ bitmask = self.ctx.sc_status()
77
+ self.lock.release()
78
+ return bitmask
79
+
80
+ def is_sc_active(self, address):
81
+ pd = self.pd_addr.index(address)
82
+ return bool(self.sc_status() & (1 << pd))
83
+
84
+ def get_num_sc_active(self):
85
+ sc_active = 0
86
+ bitmask = self.sc_status()
87
+ for i in range(len(self.pd_addr)):
88
+ if bitmask & (1 << i):
89
+ sc_active += 1
90
+ return sc_active
91
+
92
+ def send_command(self, address, cmd):
93
+ pd = self.pd_addr.index(address)
94
+ self.lock.acquire()
95
+ ret = self.ctx.send_command(pd, cmd)
96
+ self.lock.release()
97
+ return ret
98
+
99
+ def set_flag(self, address, flag: LibFlag):
100
+ pd = self.pd_addr.index(address)
101
+ self.lock.acquire()
102
+ ret = self.ctx.set_flag(pd, flag)
103
+ self.lock.release()
104
+ return ret
105
+
106
+ def clear_flag(self, address, flag: LibFlag):
107
+ pd = self.pd_addr.index(address)
108
+ self.lock.acquire()
109
+ ret = self.ctx.clear_flag(pd, flag)
110
+ self.lock.release()
111
+ return ret
112
+
113
+ def register_file_ops(self, address, fops):
114
+ pd = self.pd_addr.index(address)
115
+ self.lock.acquire()
116
+ ret = self.ctx.register_file_ops(pd, fops)
117
+ self.lock.release()
118
+ return ret
119
+
120
+ def get_file_tx_status(self, address):
121
+ pd = self.pd_addr.index(address)
122
+ self.lock.acquire()
123
+ ret = self.ctx.get_file_tx_status(pd)
124
+ self.lock.release()
125
+ return ret
126
+
127
+ def start(self):
128
+ if self.thread:
129
+ raise RuntimeError("Thread already running!")
130
+ self.event = threading.Event()
131
+ self.lock = threading.Lock()
132
+ args=(self.event, self.lock, self.ctx)
133
+ self.thread = threading.Thread(name='cp', target=self.refresh, args=args)
134
+ self.thread.start()
135
+
136
+ def stop(self):
137
+ if not self.thread:
138
+ raise RuntimeError("Thread not running!")
139
+ while self.thread.is_alive():
140
+ self.event.set()
141
+ self.thread.join(2)
142
+ if not self.thread.is_alive():
143
+ self.thread = None
144
+ break
145
+
146
+ def online_wait_all(self, timeout=10):
147
+ count = 0
148
+ res = False
149
+ while count < timeout * 2:
150
+ time.sleep(0.5)
151
+ if self.get_num_online() == len(self.pd_addr):
152
+ res = True
153
+ break
154
+ count += 1
155
+ return res
156
+
157
+ def online_wait(self, address, timeout=5):
158
+ count = 0
159
+ res = False
160
+ while count < timeout * 2:
161
+ time.sleep(0.5)
162
+ if self.is_online(address):
163
+ res = True
164
+ break
165
+ count += 1
166
+ return res
167
+
168
+ def sc_wait(self, address, timeout=5):
169
+ count = 0
170
+ res = False
171
+ while count < timeout * 2:
172
+ time.sleep(0.5)
173
+ if self.is_sc_active(address):
174
+ res = True
175
+ break
176
+ count += 1
177
+ return res
178
+
179
+ def sc_wait_all(self, timeout=5):
180
+ count = 0
181
+ res = False
182
+ all_pd_mask = (1 << self.num_pds) - 1
183
+ while count < timeout * 2:
184
+ time.sleep(0.5)
185
+ if self.sc_status() == all_pd_mask:
186
+ res = True
187
+ break
188
+ count += 1
189
+ return res
190
+
191
+ def teardown(self):
192
+ self.stop()
193
+ self.ctx = None
@@ -0,0 +1,78 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ import os
8
+ from .constants import Capability, LibFlag
9
+
10
+ class PDInfo:
11
+ def __init__(self, address: int, scbk: bytes=None, name: str='chn', flags=[],
12
+ channel_type: str='fifo'):
13
+ self.address = address
14
+ self.flags = flags
15
+ self.scbk = scbk
16
+ self.name = name
17
+ self.channel_device = '/tmp/pyosdp-' + name
18
+ self.channel_speed = 115200
19
+ self.channel_type = channel_type
20
+ self.version= 1
21
+ self.model= 1
22
+ self.vendor_code= 0xCAFEBABE
23
+ self.serial_number= 0xDEADBEAF
24
+ self.firmware_version= 0x0000F00D
25
+
26
+ if self.channel_type == 'unix_bus' and os.path.exists(self.channel_device):
27
+ os.remove(self.channel_device)
28
+
29
+ def get_flags(self) -> int:
30
+ ret = 0
31
+ for flag in self.flags:
32
+ ret |= flag
33
+ return ret
34
+
35
+ def get(self) -> dict:
36
+ return {
37
+ 'name': self.name,
38
+ 'address': self.address,
39
+ 'flags': self.get_flags(),
40
+ 'scbk': self.scbk,
41
+ 'channel_type': self.channel_type,
42
+ 'channel_speed': self.channel_speed,
43
+ 'channel_device': self.channel_device,
44
+
45
+ # Following are needed only for PD. For CP these are don't cares
46
+ 'version': self.version,
47
+ 'model': self.model,
48
+ 'vendor_code': self.vendor_code,
49
+ 'serial_number': self.serial_number,
50
+ 'firmware_version': self.firmware_version
51
+ }
52
+
53
+ class _PDCapEntity:
54
+ def __init__(self, function_code: Capability,
55
+ compliance_level: int=1, num_items: int=1):
56
+ self.function_code = function_code
57
+ self.compliance_level = compliance_level
58
+ self.num_items = num_items
59
+
60
+ def get(self) -> dict:
61
+ return {
62
+ 'function_code': self.function_code,
63
+ 'compliance_level': self.compliance_level,
64
+ 'num_items': self.num_items
65
+ }
66
+
67
+ class PDCapabilities:
68
+ def __init__(self, cap_list=[]):
69
+ self.capabilities = {}
70
+ for cap in cap_list:
71
+ (fc, cl, ni) = cap
72
+ self.capabilities[fc] = _PDCapEntity(fc, cl, ni)
73
+
74
+ def get(self):
75
+ ret = []
76
+ for fc in self.capabilities.keys():
77
+ ret.append(self.capabilities[fc].get())
78
+ return ret
@@ -0,0 +1,66 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ import os
8
+ import tempfile
9
+ import random
10
+
11
+ class KeyStore():
12
+ def __init__(self, dir=None):
13
+ self.temp_dir = None
14
+ self.keys = {}
15
+ if not dir:
16
+ self.temp_dir = tempfile.TemporaryDirectory()
17
+ self.key_dir = self.temp_dir.name
18
+ else:
19
+ self.key_dir = dir
20
+
21
+ def key_file(self, name):
22
+ return os.path.join(self.key_dir, 'key_' + name + '.bin')
23
+
24
+ @staticmethod
25
+ def gen_key(key_len=16):
26
+ key = []
27
+ for i in range(key_len):
28
+ key.append(random.randint(0, 255))
29
+ return bytes(key)
30
+
31
+ def store_key(self, name):
32
+ if name not in self.keys:
33
+ raise RuntimeError
34
+ key = self.keys[name]
35
+ with open(self.key_file(name), "w") as f:
36
+ f.write(key.hex())
37
+
38
+ def get_key(self, name):
39
+ if name not in self.keys:
40
+ raise RuntimeError
41
+ return self.keys[name]
42
+
43
+ def new_key(self, name, key_len=16, force=True):
44
+ if not force and name in self.keys:
45
+ raise RuntimeError
46
+ self.keys[name] = self.gen_key(key_len)
47
+ return self.keys[name]
48
+
49
+ def update_key(self, name, key):
50
+ if name not in self.keys:
51
+ raise RuntimeError
52
+ self.keys[name] = key
53
+
54
+ def load_key(self, name, key_len=16):
55
+ if not os.path.exists(self.key_file(name)):
56
+ raise RuntimeError
57
+ with open(self.key_file(name), "r") as f:
58
+ key = bytes.fromhex(f.read())
59
+ if not key or not isinstance(key, bytes) or len(key) != key_len:
60
+ raise RuntimeError
61
+ self.keys[name] = key
62
+ return key
63
+
64
+ def __del__(self):
65
+ if self.temp_dir:
66
+ self.temp_dir.cleanup()
@@ -0,0 +1,85 @@
1
+ #
2
+ # Copyright (c) 2021-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ import osdp_sys
8
+ import time
9
+ import queue
10
+ import threading
11
+
12
+ from .helpers import PDInfo, PDCapabilities
13
+ from .constants import LogLevel
14
+
15
+ class PeripheralDevice():
16
+ def __init__(self, pd_info: PDInfo, pd_cap: PDCapabilities,
17
+ log_level: LogLevel=LogLevel.Info):
18
+ self.command_queue = queue.Queue()
19
+ self.address = pd_info.address
20
+ self.ctx = osdp_sys.PeripheralDevice(pd_info.get(), capabilities=pd_cap.get())
21
+ self.ctx.set_loglevel(log_level)
22
+ self.ctx.set_command_callback(self.command_handler)
23
+ self.event = None
24
+ self.lock = None
25
+ self.thread = None
26
+
27
+ @staticmethod
28
+ def refresh(event, lock, ctx):
29
+ while not event.is_set():
30
+ lock.acquire()
31
+ ctx.refresh()
32
+ lock.release()
33
+ time.sleep(0.020) #sleep for 20ms
34
+
35
+ def command_handler(self, command):
36
+ self.command_queue.put(command)
37
+ return { "return_code": 0 }
38
+
39
+ def get_command(self, timeout: int=5):
40
+ try:
41
+ cmd = self.command_queue.get(timeout=timeout)
42
+ except queue.Empty:
43
+ return None
44
+ return cmd
45
+
46
+ def notify_event(self, event):
47
+ self.lock.acquire()
48
+ ret = self.ctx.notify_event(event)
49
+ self.lock.release()
50
+ return ret
51
+
52
+ def register_file_ops(self, fops):
53
+ self.lock.acquire()
54
+ ret = self.ctx.register_file_ops(0, fops)
55
+ self.lock.release()
56
+ return ret
57
+
58
+ def is_sc_active(self):
59
+ return self.ctx.is_sc_active()
60
+
61
+ def is_online(self):
62
+ return self.ctx.is_online()
63
+
64
+ def start(self):
65
+ if self.thread:
66
+ raise RuntimeError("Thread already running!")
67
+ self.event = threading.Event()
68
+ self.lock = threading.Lock()
69
+ args = (self.event, self.lock, self.ctx,)
70
+ self.thread = threading.Thread(name='pd', target=self.refresh, args=args)
71
+ self.thread.start()
72
+
73
+ def stop(self):
74
+ if not self.thread:
75
+ raise RuntimeError("Thread not running!")
76
+ while self.thread.is_alive():
77
+ self.event.set()
78
+ self.thread.join(2)
79
+ if not self.thread.is_alive():
80
+ self.thread = None
81
+ break
82
+
83
+ def teardown(self):
84
+ self.stop()
85
+ self.ctx = None
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 57.2.0"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
libosdp-2.4.0/setup.py ADDED
@@ -0,0 +1,203 @@
1
+ #
2
+ # Copyright (c) 2020-2023 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ #
6
+
7
+ import os
8
+ import re
9
+ from setuptools import setup, Extension
10
+ import shutil
11
+ import subprocess
12
+
13
+ current_dir = os.environ.get("PWD")
14
+ build_dir = os.path.abspath(os.path.join(current_dir, "build"))
15
+ root_dir = os.path.abspath(os.path.join(current_dir, ".."))
16
+
17
+ def read_version():
18
+ with open(os.path.join(root_dir, "CMakeLists.txt")) as f:
19
+ for line in f.readlines():
20
+ m = re.match(r"^project\((.+) VERSION (\d+\.\d+\.\d+)\)$", line)
21
+ if (m):
22
+ return m.groups()
23
+ raise RuntimeError("Failed to parse package name and version")
24
+
25
+ project_name, project_version = read_version()
26
+
27
+ def map_prefix(src_list, path, check_files=False):
28
+ paths = [ os.path.join(path, src) for src in src_list ]
29
+ for path in paths:
30
+ if check_files:
31
+ if not os.path.exists(path):
32
+ raise RuntimeError(f"Path '{path}' does not exist")
33
+ return paths
34
+
35
+ def exec_cmd(cmd):
36
+ r = subprocess.run(cmd, capture_output = True, text = True)
37
+ return r.stdout.strip()
38
+
39
+ def get_git_info():
40
+ d = {}
41
+ d["branch"] = exec_cmd(["git", "rev-parse", "--abbrev-ref", "HEAD"])
42
+ d["tag"] = exec_cmd([ "git", "describe", "--exact-match", "--tags" ])
43
+ d["diff"] = exec_cmd([ "git", "diff", "--quiet", "--exit-code" ])
44
+ d["rev"] = exec_cmd(["git", "log", "--pretty=format:%h", "-n", "1"])
45
+ d["root"] = exec_cmd(["git", "rev-parse", "--show-toplevel"])
46
+ return d
47
+
48
+ def configure_file(file, replacements):
49
+ with open(file) as f:
50
+ contents = f.read()
51
+ pat = re.compile(r"@(\w+)@")
52
+ for match in pat.findall(contents):
53
+ if match in replacements:
54
+ contents = contents.replace(f"@{match}@", replacements[match])
55
+ with open(file, 'w') as f:
56
+ f.write(contents)
57
+
58
+ def generate_osdp_build_headers():
59
+ os.makedirs(build_dir, exist_ok=True)
60
+ with open(os.path.join(build_dir, "osdp_export.h"), 'w') as f:
61
+ f.write("#define OSDP_EXPORT\n")
62
+
63
+ git = get_git_info()
64
+ dest = os.path.join(build_dir, "osdp_config.h")
65
+ shutil.copy(os.path.join(root_dir, "src/osdp_config.h.in"), dest)
66
+ configure_file(dest, {
67
+ "PROJECT_VERSION": project_version,
68
+ "PROJECT_NAME": project_name,
69
+ "GIT_BRANCH": git["branch"],
70
+ "GIT_REV": git["rev"],
71
+ "GIT_TAG": git["tag"],
72
+ "GIT_DIFF": git["diff"],
73
+ "REPO_ROOT": git["root"],
74
+ })
75
+
76
+ generate_osdp_build_headers()
77
+
78
+ utils_sources = map_prefix([
79
+ "utils/src/list.c",
80
+ "utils/src/queue.c",
81
+ "utils/src/slab.c",
82
+ "utils/src/utils.c",
83
+ "utils/src/logger.c",
84
+ "utils/src/disjoint_set.c",
85
+ "utils/src/serial.c",
86
+ "utils/src/channel.c",
87
+ "utils/src/hashmap.c",
88
+ "utils/src/strutils.c",
89
+ "utils/src/memory.c",
90
+ "utils/src/fdutils.c",
91
+ "utils/src/event.c",
92
+ "utils/src/workqueue.c",
93
+ "utils/src/sockutils.c",
94
+ "utils/src/bus_server.c",
95
+ ], root_dir)
96
+
97
+ lib_sources = map_prefix([
98
+ "src/osdp_common.c",
99
+ "src/osdp_phy.c",
100
+ "src/osdp_sc.c",
101
+ "src/osdp_file.c",
102
+ "src/osdp_pd.c",
103
+ "src/osdp_cp.c",
104
+ "src/crypto/tinyaes_src.c",
105
+ "src/crypto/tinyaes.c",
106
+ ], root_dir)
107
+
108
+ osdp_sys_sources = map_prefix([
109
+ "osdp_sys/module.c",
110
+ "osdp_sys/base.c",
111
+ "osdp_sys/cp.c",
112
+ "osdp_sys/pd.c",
113
+ "osdp_sys/data.c",
114
+ "osdp_sys/utils.c",
115
+ ], current_dir)
116
+
117
+ source_files = utils_sources + lib_sources + osdp_sys_sources
118
+
119
+ include_dirs = map_prefix([
120
+ "utils/include",
121
+ "include",
122
+ "src",
123
+ "python/build",
124
+ ], root_dir)
125
+
126
+ other_files = map_prefix([
127
+ "CMakeLists.txt",
128
+ "src/osdp_config.h.in"
129
+ ], root_dir)
130
+
131
+ include_files = map_prefix([
132
+ "utils/include/utils/list.h",
133
+ "utils/include/utils/queue.h",
134
+ "utils/include/utils/slab.h",
135
+ "utils/include/utils/utils.h",
136
+ "utils/include/utils/logger.h",
137
+ "utils/include/utils/disjoint_set.h",
138
+ "utils/include/utils/serial.h",
139
+ "utils/include/utils/channel.h",
140
+ "utils/include/utils/hashmap.h",
141
+ "utils/include/utils/strutils.h",
142
+ "utils/include/utils/memory.h",
143
+ "utils/include/utils/fdutils.h",
144
+ "utils/include/utils/event.h",
145
+ "utils/include/utils/workqueue.h",
146
+ "utils/include/utils/sockutils.h",
147
+ "utils/include/utils/bus_server.h",
148
+ "include/osdp.h",
149
+ "src/osdp_common.h",
150
+ "src/osdp_file.h",
151
+ "python/build/osdp_config.h",
152
+ "python/build/osdp_export.h",
153
+ ], root_dir, check_files=True)
154
+
155
+ libosdp_includes = [ "-I" + path for path in include_dirs ]
156
+
157
+ definitions = [
158
+ # "CONFIG_OSDP_PACKET_TRACE",
159
+ # "CONFIG_OSDP_DATA_TRACE",
160
+ # "CONFIG_OSDP_SKIP_MARK_BYTE",
161
+ ]
162
+
163
+ compile_args = (
164
+ [ "-I" + path for path in include_dirs ] +
165
+ [ "-D" + define for define in definitions ]
166
+ )
167
+
168
+ link_args = []
169
+
170
+ if os.path.exists("README.md"):
171
+ with open("README.md", "r") as f:
172
+ long_description = f.read()
173
+ else:
174
+ long_description = ""
175
+
176
+ setup(
177
+ name = project_name,
178
+ version = project_version,
179
+ author = "Siddharth Chandrasekaran",
180
+ author_email = "sidcha.dev@gmail.com",
181
+ description = "Library implementation of IEC 60839-11-5 OSDP (Open Supervised Device Protocol)",
182
+ url = "https://github.com/goToMain/libosdp",
183
+ ext_modules = [
184
+ Extension(
185
+ name = "osdp_sys",
186
+ sources = source_files,
187
+ extra_compile_args = compile_args,
188
+ extra_link_args = link_args,
189
+ define_macros = [],
190
+ language = "C",
191
+ )
192
+ ],
193
+ packages = [ "osdp" ],
194
+ classifiers = [
195
+ "Programming Language :: Python :: 3",
196
+ "License :: OSI Approved :: Apache Software License",
197
+ "Operating System :: OS Independent",
198
+ ],
199
+ long_description = long_description,
200
+ long_description_content_type = "text/markdown",
201
+ python_requires = ">=3.8",
202
+ package_data = { project_name : other_files }
203
+ )