Browse Source

Initial bluetooth_socket implementation

master
Dirk Alders 4 years ago
parent
commit
8084bf24c9
1 changed files with 207 additions and 0 deletions
  1. 207
    0
      __init__.py

+ 207
- 0
__init__.py View File

@@ -0,0 +1,207 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+#
4
+"""
5
+bluetooth_socket (Bluetooth Socket)
6
+===================================
7
+
8
+**Author:**
9
+
10
+* Dirk Alders <sudo-dirk@mount-mockery.de>
11
+
12
+**Description:**
13
+
14
+    This Module support bluetooth communication
15
+
16
+**Submodules:**
17
+
18
+* :mod:`mmod.module.sub1`
19
+* :class:`mmod.module.sub2`
20
+* :func:`mmod.module.sub2`
21
+
22
+**Unittest:**
23
+
24
+    See also the :download:`unittest <../../bluetooth_socket/_testresults_/unittest.pdf>` documentation.
25
+"""
26
+import stringtools
27
+
28
+import bluetooth
29
+import logging
30
+import time
31
+
32
+BT_TIMEOUT = 0.1
33
+
34
+
35
+def nearby_devices():
36
+    return bluetooth.discover_devices()
37
+
38
+
39
+def services(bt_id):
40
+    return bluetooth.find_service(address=bt_id)
41
+
42
+
43
+class bt_base(object):
44
+    def send(self, data, logger=None, log=True):
45
+        logit = logger or self.logger
46
+        if self.connected():
47
+            if log:
48
+                logit.debug('BT: TX -> %s', ' '.join(['%02x' % ord(byte) for byte in data]))
49
+            try:
50
+                self._client_sock.send(data)
51
+            except bluetooth.btcommon.BluetoothError, e:
52
+                if e.message == 'timed out':
53
+                    logit.debug('BT: On send - timeout')
54
+                    return False
55
+                else:
56
+                    logit.warning('BT: On send - %s', str(e))
57
+                    self._connection_established = False
58
+                    return False
59
+        else:
60
+            return False
61
+        return True
62
+
63
+    def receive(self, length=4096, logger=None, log=True):
64
+        logit = logger or self.logger
65
+        if self.connected():
66
+            try:
67
+                data = self._client_sock.recv(length)
68
+            except bluetooth.btcommon.BluetoothError, e:
69
+                if e.message == 'timed out':
70
+                    # logit.debug('BT: On receive - timeout')
71
+                    return None
72
+                else:
73
+                    logit.warning('BT: On receive - %s', str(e))
74
+                    self._connection_established = False
75
+                    return None
76
+            else:
77
+                if log:
78
+                    logit.debug('BT: RX <- %s', ' '.join(['%02x' % ord(byte) for byte in data]))
79
+                return data
80
+
81
+    def connected(self):
82
+        return self._connection_established
83
+
84
+
85
+class stp_bt_base(object):
86
+    def send(self, data, logger=None):
87
+        logit = logger or self.logger
88
+
89
+        logit.debug('BT: TX(ETP) -> %s', ' '.join(['%02x' % ord(byte) for byte in data]))
90
+        self._bt.send(self, stringtools.stp.build_frame(data), log=False)
91
+
92
+    def receive(self, timeout=5, logger=None):
93
+        logit = logger or self.logger
94
+        max_time = time.time() + timeout
95
+        while self.connected() and (timeout is None or time.time() < max_time):
96
+            data = self._bt.receive(self, 1, log=False)
97
+            if data is not None:
98
+                max_time += BT_TIMEOUT
99
+                msg = self._stp.process(data)
100
+                if msg is not None:
101
+                    logit.debug('BT: RX(ETP) <- %s', ' '.join(['%02x' % ord(byte) for byte in msg]))
102
+                    return msg
103
+
104
+
105
+class bt_server(bt_base):
106
+    def __init__(self, uuid, port=2, name='BT-RFCOM-Server', logger=None):
107
+        self._connection_established = False
108
+        self._uuid = uuid
109
+        self._name = name
110
+        self._server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
111
+        self._server_sock.bind(("Python", port))
112
+        self._server_sock.listen(1)
113
+
114
+        bluetooth.advertise_service(self._server_sock,
115
+                                    name,
116
+                                    service_classes=[self._uuid, bluetooth.SERIAL_PORT_CLASS],
117
+                                    profiles=[bluetooth.SERIAL_PORT_PROFILE]
118
+                                    )
119
+
120
+    def listen(self, logger=None):
121
+        logit = logger or self.logger
122
+        logit.info('BT: %s waiting for a connection on RFCOMM "%s", port=%d, uuid=%s', '??:??:??:??:??', self._name, self._server_sock.getsockname()[1], self._uuid)
123
+        # TODO: find method to get my bt_id and use it instead of '??:??:??:??:??'
124
+        self._client_sock, client_info = self._server_sock.accept()
125
+        self._client_sock.settimeout(BT_TIMEOUT)
126
+        logit.info("BT: Accepted connection from %s, port=%d", client_info[0], client_info[1])
127
+        self._connection_established = True
128
+
129
+    def listen_receive_loop(self, callback=None, logger=None):
130
+        while True:
131
+            self.listen()
132
+            while self.connected():
133
+                data = self.receive()
134
+                if callback is not None:
135
+                    callback(self, data, logger)
136
+
137
+    def close(self, logger=None):
138
+        if self._client_sock is not None:
139
+            self._client_sock.close()
140
+            self._client_sock = None
141
+        if self._server_sock is not None:
142
+            self._server_sock.close()
143
+            self._server_sock = None
144
+
145
+    def __del__(self):
146
+        self.close()
147
+
148
+
149
+class stp_bt_server(stp_bt_base, bt_server):
150
+    def __init__(self, *args, **kwargs):
151
+        bt_server.__init__(self, *args, **kwargs)
152
+        self._stp = stringtools.stp.stp()
153
+        self._bt = bt_server
154
+
155
+
156
+class bt_client(bt_base):
157
+    def __init__(self, bt_id, uuid, logger=None):
158
+        self.logger = logger or logging.getLogger('%s' % (self.__class__.__name__))
159
+        self._connection_established = False
160
+        self._bt_id = bt_id
161
+        self._uuid = uuid
162
+        self._client_sock = None
163
+        self._connected = False
164
+
165
+    def connect(self, logger=None):
166
+        logit = logger or self.logger
167
+        service_matches = bluetooth.find_service(uuid=self._uuid, address=self._bt_id)
168
+        # TODO: find_named_service and remove service_id from classes
169
+        if len(service_matches) == 0:
170
+            logit.debug("BT: couldn't find the RFCOM service %s", self._uuid)
171
+        else:
172
+            first_match = service_matches[0]
173
+            port = first_match["port"]
174
+            name = first_match["name"]
175
+            host = first_match["host"]
176
+
177
+            # Create the client socket
178
+            logit.debug("connecting to \"%s\" on %s", name, host)
179
+            self._client_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
180
+            try:
181
+                self._client_sock.connect((host, port))
182
+            except bluetooth.btcommon.BluetoothError, e:
183
+                if e.message == "(111, 'Connection refused')":
184
+                    logit.warning('BT: Connection refused')
185
+                else:
186
+                    raise e
187
+            else:
188
+                self._client_sock.settimeout(0.1)
189
+                logit.debug("connection established")
190
+                self._connection_established = True
191
+                return True
192
+        return False
193
+
194
+    def close(self, logger=None):
195
+        if self._client_sock is not None:
196
+            self._client_sock.close()
197
+            self._client_sock = None
198
+
199
+    def __del__(self):
200
+        self.close()
201
+
202
+
203
+class stp_bt_client(stp_bt_base, bt_client):
204
+    def __init__(self, *args, **kwargs):
205
+        bt_client.__init__(self, *args, **kwargs)
206
+        self._stp = stringtools.stp.stp()
207
+        self._bt = bt_client

Loading…
Cancel
Save