173 lines
6.0 KiB
Python
Executable File
173 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import bluetooth
|
|
import os
|
|
import subprocess
|
|
import time
|
|
import datetime
|
|
import sys
|
|
import json
|
|
import configparser
|
|
import argparse
|
|
import shutil
|
|
from contextlib import nullcontext
|
|
|
|
CONFIG_DIR = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
|
|
CONFIG_FILE = os.path.join(CONFIG_DIR, "ble-lock-session/config.ini")
|
|
LOGFILE = os.getenv("BLE_LOCK_LOGFILE","-")
|
|
|
|
# Function to load the configuration from a JSON file
|
|
def load_config():
|
|
config = configparser.ConfigParser()
|
|
|
|
if not os.path.exists(CONFIG_FILE):
|
|
if not os.path.exists(os.path.dirname(CONFIG_FILE)):
|
|
os.makedirs(os.path.dirname(CONFIG_FILE))
|
|
|
|
desktop = os.getenv('XDG_CURRENT_DESKTOP', '').upper()
|
|
|
|
config['SETTINGS'] = {
|
|
'target_address': '',
|
|
'lock_cmd': get_default_lock_command(desktop),
|
|
'unlock_cmd': get_default_unlock_command(desktop),
|
|
'sleep_time': '5',
|
|
'discover_time': '25'
|
|
}
|
|
|
|
with open(CONFIG_FILE, 'w') as configfile:
|
|
config.write(configfile)
|
|
else:
|
|
config.read(CONFIG_FILE)
|
|
|
|
return config
|
|
|
|
# Function to save the configuration
|
|
def save_config(config):
|
|
with open(CONFIG_FILE, 'w') as configfile:
|
|
config.write(configfile)
|
|
|
|
def get_default_lock_command(desktop):
|
|
if 'GNOME' in desktop:
|
|
return "gnome-screensaver-command --lock"
|
|
elif 'SWAY' in desktop:
|
|
return "swaylock"
|
|
else:
|
|
return "loginctl lock-session"
|
|
|
|
def get_default_unlock_command(desktop):
|
|
if 'GNOME' in desktop:
|
|
return "gnome-screensaver-command -d"
|
|
elif 'SWAY' in desktop:
|
|
return "pkill -USR1 swaylock"
|
|
else:
|
|
return "loginctl unlock-session"
|
|
|
|
# Function to scan for Bluetooth devices
|
|
def scan_device(target_name, discover_time):
|
|
try:
|
|
for bdaddr in bluetooth.discover_devices(duration=discover_time):
|
|
if target_name == bluetooth.lookup_name(bdaddr):
|
|
return bdaddr
|
|
except bluetooth.BluetoothError as e:
|
|
print(f"Error scanning for device: {e}")
|
|
return None
|
|
|
|
# Main function to activate automatic lock/unlock
|
|
def start(target_address, lock_cmd, unlock_cmd, sleep_time, discover_time):
|
|
|
|
if not shutil.which(lock_cmd.split()[0]) or not shutil.which(unlock_cmd.split()[0]):
|
|
print("Error: lock_cmd or unlock_cmd is not a valid command.")
|
|
sys.exit(1)
|
|
|
|
state = 1
|
|
try:
|
|
open_log = lambda: nullcontext(sys.stdout) if LOGFILE == "-" else open(LOGFILE, 'w')
|
|
with open_log() as file:
|
|
while True:
|
|
try:
|
|
check = bluetooth.lookup_name(target_address, timeout=discover_time)
|
|
|
|
event = datetime.datetime.now().strftime("%d-%m-%y %H:%M:%S")
|
|
|
|
if check and state == 0:
|
|
subprocess.Popen(unlock_cmd, shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
output = f" [{event}] ➔ [UNLOCKED]"
|
|
file.write(output + '\n')
|
|
file.flush()
|
|
state = 1
|
|
elif not check and state == 1:
|
|
subprocess.Popen(lock_cmd, shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
output = f" [{event}] ➔ [LOCKED]"
|
|
file.write(output + '\n')
|
|
file.flush()
|
|
state = 0
|
|
|
|
time.sleep(sleep_time)
|
|
except bluetooth.BluetoothError as e:
|
|
print(f"Error checking device: {e}")
|
|
except KeyboardInterrupt:
|
|
print("Monitoring stopped by user.")
|
|
except Exception as e:
|
|
print(f"Unexpected error: {e}")
|
|
|
|
# Command-line arguments using argparse
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Automatic PC Lock/Unlock using Bluetooth proximity")
|
|
parser.add_argument("--scan", action="store_true", help="Search and save a Bluetooth device")
|
|
parser.add_argument("--start", action="store_true", help="Activate automatic lock/unlock")
|
|
parser.add_argument("--config", action="store_true", help="Modify the current configuration")
|
|
|
|
args = parser.parse_args()
|
|
|
|
config = load_config()
|
|
|
|
if args.scan:
|
|
target_name = input("Enter the name of the device to search : ")
|
|
bdaddr = scan_device(target_name, int(config["SETTINGS"]["discover_time"]))
|
|
if bdaddr:
|
|
print(f"Device found: {bdaddr}")
|
|
config["SETTINGS"]["target_address"] = bdaddr
|
|
save_config(config)
|
|
print("Address saved to configuration file.")
|
|
else:
|
|
print("Device not found.")
|
|
|
|
elif args.start:
|
|
if config["SETTINGS"]["target_address"] == "":
|
|
print("Error: No Bluetooth device configured. Use --scan to configure it.")
|
|
return
|
|
start(
|
|
config["SETTINGS"]["target_address"],
|
|
config["SETTINGS"]["lock_cmd"],
|
|
config["SETTINGS"]["unlock_cmd"],
|
|
int(config["SETTINGS"]["sleep_time"]),
|
|
int(config["SETTINGS"]["discover_time"])
|
|
)
|
|
|
|
elif args.config:
|
|
lock_cmd = input(f"Lock command (current: {config['SETTINGS']['lock_cmd']}) : ")
|
|
if lock_cmd:
|
|
config["SETTINGS"]["lock_cmd"] = lock_cmd
|
|
|
|
unlock_cmd = input(f"Unlock command (current: {config['SETTINGS']['unlock_cmd']}) : ")
|
|
if unlock_cmd:
|
|
config["SETTINGS"]["unlock_cmd"] = unlock_cmd
|
|
|
|
sleep_time = input(f"Time interval between checks in seconds (current: {config['SETTINGS']['sleep_time']}) : ")
|
|
if sleep_time:
|
|
config["SETTINGS"]["sleep_time"] = sleep_time
|
|
|
|
discover_time = input(f"Bluetooth device discovery time (current: {config['SETTINGS']['discover_time']}) : ")
|
|
if discover_time:
|
|
config["SETTINGS"]["discover_time"] = discover_time
|
|
|
|
save_config(config)
|
|
print("Configuration saved.")
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|