Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/debian-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Debian Python dependencies and daemon
on:
push:
branches: [debian13-python-venv]
pull_request:
branches: [beta]
workflow_dispatch:
permissions:
contents: read
jobs:
dependencies-and-daemon:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
debian: [bookworm, trixie]
container: debian:${{ matrix.debian }}
steps:
- name: Install checkout and daemon prerequisites
run: apt-get update && apt-get install -y git ca-certificates sudo
- uses: actions/checkout@v7
- name: Install actual BLEA dependencies
run: bash resources/install_apt.sh /tmp/blea-progress
- name: Verify installed Python dependencies
run: resources/python_venv/bin/python3 -c 'import bluepy.btle, bluetooth, requests, serial, pyudev, PIL, numpy, cryptography; from Crypto.Cipher import AES'
- name: Test three daemon socket and SIGTERM cycles without a Bluetooth adapter
run: python3 tests/daemonLifecycle.py
16 changes: 14 additions & 2 deletions core/class/blea.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ public static function launchremote($_remoteId) {
$user = $remoteObject->getConfiguration('remoteUser');
$device = $remoteObject->getConfiguration('remoteDevice');
$script_path = '/home/' . $user . '/blead/resources/blead';
$cmd = '/usr/bin/python3 ' . $script_path . '/blead.py';
$python = '/home/' . $user . '/blead/resources/python_venv/bin/python3';
$cmd = 'python=' . escapeshellarg($python) . '; [ -x "$python" ] || python=/usr/bin/python3; "$python" ' . $script_path . '/blead.py';
$cmd .= ' --loglevel ' . log::convertLogLevel(log::getLogLevel('blea'));
$cmd .= ' --device ' . $device;
$cmd .= ' --socketport ' . config::byKey('socketport', 'blea');
Expand Down Expand Up @@ -444,6 +445,13 @@ public static function dependancy_info() {
$return['log'] = 'blea_update';
$return['progress_file'] = jeedom::getTmpFolder('blea') . '/dependance';
$return['state'] = 'ok';
$python = __DIR__ . '/../../resources/python_venv/bin/python3';
if (is_executable($python)) {
$modules = 'import bluepy.btle, bluetooth, requests, serial, pyudev, PIL, numpy, cryptography; from Crypto.Cipher import AES';
exec(escapeshellarg($python) . ' -c ' . escapeshellarg($modules) . ' 2>/dev/null', $output, $status);
$return['state'] = ($status === 0) ? 'ok' : 'nok';
return $return;
}
if (exec(system::getCmdSudo() . system::get('cmd_check') . '-E "python3\-serial|python3\-requests|python3\-pyudev|rfkill" | wc -l') < 4) {
$return['state'] = 'nok';
}
Expand Down Expand Up @@ -472,7 +480,11 @@ public static function deamon_start() {
}
$port = jeedom::getBluetoothMapping(config::byKey('port', 'blea'));
$blea_path = realpath(dirname(__FILE__) . '/../../resources/blead');
$cmd = 'sudo /usr/bin/python3 ' . $blea_path . '/blead.py';
$python = __DIR__ . '/../../resources/python_venv/bin/python3';
if (!is_executable($python)) {
$python = '/usr/bin/python3';
}
$cmd = system::getCmdSudo() . escapeshellarg($python) . ' ' . $blea_path . '/blead.py';
$cmd .= ' --loglevel ' . log::convertLogLevel(log::getLogLevel('blea'));
$cmd .= ' --device ' . $port;
$cmd .= ' --socketport ' . config::byKey('socketport', 'blea');
Expand Down
6 changes: 6 additions & 0 deletions resources/install_apt.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#!/bin/bash
# Debian 12+ protects the system Python environment (PEP 668).
. /etc/os-release
if [ "${VERSION_ID%%.*}" -ge 12 ]; then
exec bash "$(dirname -- "$0")/install_apt_venv.sh" "$@"
fi
PROGRESS_FILE=/tmp/dependancy_blea_in_progress
if [ ! -z $1 ]; then
PROGRESS_FILE=$1
Expand Down
20 changes: 20 additions & 0 deletions resources/install_apt_venv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
set -Eeuo pipefail
BASE_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
PROGRESS_FILE=${1:-/tmp/dependancy_blea_in_progress}
VENV_DIR="$BASE_DIR/python_venv"
trap 'echo "BLEA dependency installation failed at line $LINENO" >&2' ERR
printf '0\n' >"$PROGRESS_FILE"
apt-get update
apt-get install -y python3 python3-dev python3-venv build-essential libglib2.0-dev \
bluez rfkill python3-bluez python3-requests python3-serial python3-pyudev \
python3-pil python3-numpy python3-cryptography
printf '40\n' >"$PROGRESS_FILE"
python3 -m venv --system-site-packages "$VENV_DIR"
"$VENV_DIR/bin/python3" -m pip install 'bluepy==1.3.0' pycryptodome
printf '85\n' >"$PROGRESS_FILE"
"$VENV_DIR/bin/python3" -c 'import bluepy.btle, bluetooth, requests, serial, pyudev, PIL, numpy, cryptography; from Crypto.Cipher import AES'
chown -R "$(stat -c '%u:%g' "$BASE_DIR/..")" "$VENV_DIR"
printf '100\n' >"$PROGRESS_FILE"
rm -f -- "$PROGRESS_FILE"
echo 'BLEA dependencies installed in an isolated Python environment'
29 changes: 29 additions & 0 deletions tests/daemonLifecycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from http.server import HTTPServer,BaseHTTPRequestHandler
import threading,subprocess,time,socket,os,tempfile
class H(BaseHTTPRequestHandler):
def do_GET(self):self.send_response(200);self.end_headers();self.wfile.write(b'OK')
def do_POST(self):self.rfile.read(int(self.headers.get('Content-Length','0')));self.do_GET()
def log_message(self,*a):pass
server=HTTPServer(('127.0.0.1',18994),H);threading.Thread(target=server.serve_forever,daemon=True).start()
base=os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'resources')
for cycle in range(3):
pid='/tmp/suite4-blea-'+str(cycle)+'.pid'
with open('/tmp/suite4-blea-'+str(cycle)+'.log','w') as log:
p=subprocess.Popen([base+'/python_venv/bin/python3',base+'/blead/blead.py','--device','hci0','--socketport','18996','--sockethost','127.0.0.1','--callback','http://127.0.0.1:18994/','--apikey','synthetic','--pid',pid,'--daemonname','suite4','--loglevel','debug'],stdout=log,stderr=log)
try:
for i in range(100):
if p.poll() is not None:raise RuntimeError('BLEA stopped before opening socket; see cycle log')
try:
with socket.create_connection(('127.0.0.1',18996),timeout=.1):break
except OSError:time.sleep(.1)
else:raise RuntimeError('No BLEA socket')
print('BLEA_SOCKET_START_PASS',cycle,flush=True)
finally:
p.terminate()
try:p.wait(timeout=10)
except subprocess.TimeoutExpired:p.kill();p.wait();raise
assert p.returncode==0,p.returncode
assert not os.path.exists(pid),pid
print('BLEA_SIGTERM_CLEAN_STOP_PASS',cycle,flush=True)
server.shutdown();server.server_close()
print('NO_BLUETOOTH_RADIO_VALIDATION')