mirror of
https://github.com/kawasaki/pyscrlink.git
synced 2025-09-06 17:50:20 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e34ec61f3b | ||
|
e552bd21bd | ||
|
dc46869760 | ||
|
461377f7ea | ||
|
923c036cec | ||
|
8a69c2c917 | ||
|
1e7f81906e | ||
|
73523120b9 |
23
README.md
23
README.md
@@ -73,7 +73,7 @@ Installation
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
Ubuntu
|
Ubuntu
|
||||||
$ sudo apt install bluez libbluetooth-dev libnss3-tools libcap2-bin
|
$ sudo apt install bluez libbluetooth-dev libnss3-tools libcap2-bin libglib2.0-dev
|
||||||
Arch
|
Arch
|
||||||
$ sudo pacman -S bluez bluez-utils nss libcap
|
$ sudo pacman -S bluez bluez-utils nss libcap
|
||||||
```
|
```
|
||||||
@@ -186,3 +186,24 @@ In Case You Fail to Connect
|
|||||||
|
|
||||||
3. If scratch_link.py says "failed to connect to BT device: [Errno 13] Permission denied",
|
3. If scratch_link.py says "failed to connect to BT device: [Errno 13] Permission denied",
|
||||||
make sure to pair the bluetooth device to your PC before connecting to Scratch.
|
make sure to pair the bluetooth device to your PC before connecting to Scratch.
|
||||||
|
|
||||||
|
Issus Reporting
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Please file issues to [GitHub issue tracker](https://github.com/kawasaki/pyscrlink/issues).
|
||||||
|
|
||||||
|
Releases
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 0.2.1
|
||||||
|
|
||||||
|
* Added libglib to required package list in README.md
|
||||||
|
* Improved setcap and getcap tool finding
|
||||||
|
|
||||||
|
Release 0.2.0
|
||||||
|
|
||||||
|
* Latency issue fix for BLE devices' write characteristics
|
||||||
|
|
||||||
|
Release 0.1.0
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
@@ -22,10 +22,26 @@ logger.propagate = False
|
|||||||
# Check dependent tools
|
# Check dependent tools
|
||||||
DEPENDENT_TOOLS = {
|
DEPENDENT_TOOLS = {
|
||||||
"setcap": "libcap2-bin (Ubuntu) or libcap (Arch)",
|
"setcap": "libcap2-bin (Ubuntu) or libcap (Arch)",
|
||||||
|
"getcap": "libcap2-bin (Ubuntu) or libcap (Arch)",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tools = {}
|
||||||
|
|
||||||
for cmd in DEPENDENT_TOOLS:
|
for cmd in DEPENDENT_TOOLS:
|
||||||
if not shutil.which(cmd):
|
# find the tools in PATH
|
||||||
|
path = shutil.which(cmd)
|
||||||
|
if path:
|
||||||
|
tools[cmd] = path
|
||||||
|
logger.debug(f"{cmd} found: {path}")
|
||||||
|
continue
|
||||||
|
# find the tools out of PATH but in major directories
|
||||||
|
for d in ["/usr/bin", "/bin", "/usr/sbin", "/sbin"]:
|
||||||
|
path = d + '/' + cmd
|
||||||
|
if os.path.isfile(path) and os.access(path, os.X_OK):
|
||||||
|
tools[cmd] = path
|
||||||
|
logger.debug(f"{cmd} found: {path}")
|
||||||
|
break
|
||||||
|
if not cmd in tools:
|
||||||
print(f"'{cmd}' not found. Install package {DEPENDENT_TOOLS[cmd]}.")
|
print(f"'{cmd}' not found. Install package {DEPENDENT_TOOLS[cmd]}.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -42,7 +58,7 @@ def helper_path():
|
|||||||
|
|
||||||
def is_set():
|
def is_set():
|
||||||
path = helper_path()
|
path = helper_path()
|
||||||
p = subprocess.run(["getcap", path], stdout=subprocess.PIPE)
|
p = subprocess.run([tools["getcap"], path], stdout=subprocess.PIPE)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
logger.error(f"Failed to get capability of {path}")
|
logger.error(f"Failed to get capability of {path}")
|
||||||
return False
|
return False
|
||||||
@@ -53,8 +69,8 @@ def setcap():
|
|||||||
path = helper_path()
|
path = helper_path()
|
||||||
if is_set():
|
if is_set():
|
||||||
return True
|
return True
|
||||||
p = subprocess.run(["sudo", "setcap", "cap_net_raw,cap_net_admin+eip",
|
p = subprocess.run(["sudo", tools["setcap"],
|
||||||
path])
|
"cap_net_raw,cap_net_admin+eip", path])
|
||||||
if p.returncode !=0:
|
if p.returncode !=0:
|
||||||
logger.error(f"Failed to set capability to {path}")
|
logger.error(f"Failed to set capability to {path}")
|
||||||
return False
|
return False
|
||||||
|
@@ -423,6 +423,7 @@ class BLESession(Session):
|
|||||||
self.deviceName = None
|
self.deviceName = None
|
||||||
self.perip = None
|
self.perip = None
|
||||||
self.delegate = None
|
self.delegate = None
|
||||||
|
self.characteristics_cache = []
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.status = self.DONE
|
self.status = self.DONE
|
||||||
@@ -490,6 +491,25 @@ class BLESession(Session):
|
|||||||
charas = self.perip.getCharacteristics(uuid=chara_id)
|
charas = self.perip.getCharacteristics(uuid=chara_id)
|
||||||
return charas[0]
|
return charas[0]
|
||||||
|
|
||||||
|
def _cache_characteristics(self):
|
||||||
|
if not self.perip:
|
||||||
|
return
|
||||||
|
with self.lock:
|
||||||
|
self.characteristics_cache = self.perip.getCharacteristics()
|
||||||
|
if not self.characteristics_cache:
|
||||||
|
logger.debug("Characteristics are not cached")
|
||||||
|
|
||||||
|
def _get_characteristic_cached(self, chara_id):
|
||||||
|
if not self.perip:
|
||||||
|
return None
|
||||||
|
if not self.characteristics_cache:
|
||||||
|
self._cache_characteristics()
|
||||||
|
if self.characteristics_cache:
|
||||||
|
for characteristic in self.characteristics_cache:
|
||||||
|
if characteristic.uuid == chara_id:
|
||||||
|
return characteristic
|
||||||
|
return _get_characteristic(chara_id)
|
||||||
|
|
||||||
def handle_request(self, method, params):
|
def handle_request(self, method, params):
|
||||||
"""Handle requests from Scratch"""
|
"""Handle requests from Scratch"""
|
||||||
if self.delegate:
|
if self.delegate:
|
||||||
@@ -508,7 +528,8 @@ class BLESession(Session):
|
|||||||
if self.status == self.INITIAL and method == 'discover':
|
if self.status == self.INITIAL and method == 'discover':
|
||||||
if not bluepy_helper_cap.is_set():
|
if not bluepy_helper_cap.is_set():
|
||||||
logger.error("Capability is not set to bluepy helper.")
|
logger.error("Capability is not set to bluepy helper.")
|
||||||
logger.error("Run bluepy_setcap.py with root privilege.")
|
logger.error("Run bluepy_helper_cap(.py).")
|
||||||
|
logger.error("e.g. $ bluepy_helper_cap")
|
||||||
logger.error("e.g. $ sudo bluepy_helper_cap.py")
|
logger.error("e.g. $ sudo bluepy_helper_cap.py")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
found_ifaces = 0
|
found_ifaces = 0
|
||||||
@@ -558,6 +579,7 @@ class BLESession(Session):
|
|||||||
self.status = self.CONNECTED
|
self.status = self.CONNECTED
|
||||||
self.delegate = self.BLEDelegate(self)
|
self.delegate = self.BLEDelegate(self)
|
||||||
self.perip.withDelegate(self.delegate)
|
self.perip.withDelegate(self.delegate)
|
||||||
|
self._cache_characteristics()
|
||||||
else:
|
else:
|
||||||
err_msg = f"BLE connect failed: {self.deviceName}"
|
err_msg = f"BLE connect failed: {self.deviceName}"
|
||||||
res["error"] = { "message": err_msg }
|
res["error"] = { "message": err_msg }
|
||||||
@@ -595,7 +617,7 @@ class BLESession(Session):
|
|||||||
logger.debug("handle write request")
|
logger.debug("handle write request")
|
||||||
service_id = params['serviceId']
|
service_id = params['serviceId']
|
||||||
chara_id = params['characteristicId']
|
chara_id = params['characteristicId']
|
||||||
c = self._get_characteristic(chara_id)
|
c = self._get_characteristic_cached(chara_id)
|
||||||
if not c or c.uuid != UUID(chara_id):
|
if not c or c.uuid != UUID(chara_id):
|
||||||
logger.error(f"Failed to get characteristic {chara_id}")
|
logger.error(f"Failed to get characteristic {chara_id}")
|
||||||
self.status = self.DONE
|
self.status = self.DONE
|
||||||
|
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="pyscrlink",
|
name="pyscrlink",
|
||||||
version="0.1.0",
|
version="0.2.1",
|
||||||
author="Shin'ichiro Kawasaki",
|
author="Shin'ichiro Kawasaki",
|
||||||
author_email='kawasaki@juno.dti.ne.jp',
|
author_email='kawasaki@juno.dti.ne.jp',
|
||||||
description='Scratch-link for Linux with Python',
|
description='Scratch-link for Linux with Python',
|
||||||
|
Reference in New Issue
Block a user