Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve bluetooth/wifi on/off settings #37

Closed
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
230 changes: 153 additions & 77 deletions modules/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
import logging
import os
import re
import shutil
import traceback
import math
Expand All @@ -29,6 +30,7 @@
exec_cmd,
exec_cmd_return_value,
is_running_as_service,
is_service_running,
)
from modules.utils.timer import Timer

Expand Down Expand Up @@ -806,25 +808,29 @@ async def delay_init(self):
self.network = Network(self)

# bluetooth
if self.G_IS_RASPI:
await self.gui.set_boot_status("initialize bluetooth modules...")
await self.gui.set_boot_status("initialize bluetooth modules...")

from modules.helper.bt_pan import (
BTPanDbus,
BTPanDbusNext,
HAS_DBUS_NEXT,
HAS_DBUS,
)
from modules.helper.bt_pan import (
BTPanDbus,
BTPanDbusNext,
HAS_DBUS_NEXT,
HAS_DBUS,
)

if HAS_DBUS_NEXT:
self.bt_pan = BTPanDbusNext()
elif HAS_DBUS:
self.bt_pan = BTPanDbus()
if HAS_DBUS_NEXT or HAS_DBUS:
is_available = await self.bt_pan.check_dbus()
if is_available:
self.G_BT_ADDRESSES = await self.bt_pan.find_bt_pan_devices()

if HAS_DBUS_NEXT:
self.bt_pan = BTPanDbusNext()
elif HAS_DBUS:
self.bt_pan = BTPanDbus()
if HAS_DBUS_NEXT or HAS_DBUS:
is_available = await self.bt_pan.check_dbus()
if is_available:
self.G_BT_ADDRESSES = await self.bt_pan.find_bt_pan_devices()
# make sure the bluebooth status is set accordingly to AutoEnable
self.onoff_bt_on_init()

# gadgetbridge
if self.G_IS_RASPI:
try:
from modules.helper.ble_gatt_server import GadgetbridgeService

Expand All @@ -843,25 +849,18 @@ async def delay_init(self):
if self.G_HEADLESS:
asyncio.create_task(self.keyboard_check())

# resume BT and thingsboard setting
if self.G_IS_RASPI:
self.G_BT_USE_ADDRESS = self.setting.get_config_pickle(
"G_BT_USE_ADDRESS", self.G_BT_USE_ADDRESS
)
self.G_THINGSBOARD_API["STATUS"] = self.setting.get_config_pickle(
"G_THINGSBOARD_API_STATUS", self.G_THINGSBOARD_API["STATUS"]
)
self.G_THINGSBOARD_API[
"AUTO_UPLOAD_VIA_BT"
] = self.setting.get_config_pickle(
"AUTO_UPLOAD_VIA_BT", self.G_THINGSBOARD_API["AUTO_UPLOAD_VIA_BT"]
)
# resume BT tethering
if (
self.G_BT_USE_ADDRESS
and not self.G_THINGSBOARD_API["AUTO_UPLOAD_VIA_BT"]
):
await self.bluetooth_tethering()
self.G_BT_USE_ADDRESS = self.setting.get_config_pickle(
"G_BT_USE_ADDRESS", self.G_BT_USE_ADDRESS
)
self.G_THINGSBOARD_API["STATUS"] = self.setting.get_config_pickle(
"G_THINGSBOARD_API_STATUS", self.G_THINGSBOARD_API["STATUS"]
)
self.G_THINGSBOARD_API["AUTO_UPLOAD_VIA_BT"] = self.setting.get_config_pickle(
"AUTO_UPLOAD_VIA_BT", self.G_THINGSBOARD_API["AUTO_UPLOAD_VIA_BT"]
)
# resume BT tethering
if self.G_BT_USE_ADDRESS and not self.G_THINGSBOARD_API["AUTO_UPLOAD_VIA_BT"]:
await self.bluetooth_tethering()

delta = t.stop()
self.boot_time += delta
Expand Down Expand Up @@ -1066,56 +1065,133 @@ def hardware_wifi_bt(self, status):
["sudo", "sed", "-i", f"$a{disable}", BOOT_FILE], False
)

def get_wifi_bt_status(self):
if not self.G_IS_RASPI:
return False, False
@staticmethod
def get_bluetooth_status():
# make sure bluetooth service is running (+ hciuart ??)
# hciuart_running = is_service_running("hciuart")
bluetooth_running = is_service_running("bluetooth")
status = False
if bluetooth_running:
# also make sure bluetooth is not powered off (through bluetoothctl)
raw_status = exec_cmd_return_value(["bluetoothctl", "show"], False)
try:
status = [
x.strip()
for x in raw_status.splitlines()
if x.strip().startswith("Powered:")
][0]
m = re.search(r"Powered: (?P<value>yes|no)", status)
status = True if m.group("value") == "yes" else False
except IndexError:
app_logger.warning(
"Could not get bluetooth power status, assume it's off then"
)
return bluetooth_running and status

status = {"wlan": False, "bluetooth": False}
@staticmethod
def get_wifi_status():
status = False
# that won't work if we have multiple Wi-Fi devices ?
try:
# json option requires raspbian buster
raw_status = exec_cmd_return_value(["sudo", "rfkill", "--json"], cmd_print=False)
raw_status = exec_cmd_return_value(
["sudo", "rfkill", "--json"], cmd_print=False
)
json_status = json.loads(raw_status)
# "": Raspberry Pi OS, "rfkilldevices":
self.parse_wifi_bt_json(json_status, status, ["", "rfkilldevices"])
except Exception as e:
app_logger.warning(f"Exception occurred trying to get wifi/bt status: {e}")
return status["wlan"], status["bluetooth"]

def parse_wifi_bt_json(self, json_status, status, keys):
get_status = False
for k in keys:
if k not in json_status:
continue
for device in json_status[k]:
if "type" not in device or device["type"] not in ["wlan", "bluetooth"]:
# "": Raspberry Pi OS, "rfkilldevices":
status = False
for k in ["", "rfkilldevices"]:
if k not in json_status:
continue
if device["soft"] == "unblocked" and device["hard"] == "unblocked":
status[device["type"]] = True
get_status = True
if get_status:
return
for device in json_status[k]:
if device.get("type") != "wlan":
continue
if device["soft"] == "unblocked" and device["hard"] == "unblocked":
status = True
break
except Exception as e:
app_logger.warning(f"Exception occurred trying to get wifi status: {e}")
return status

def onoff_wifi_bt(self, key=None):
# in the future, manage with pycomman
if not self.G_IS_RASPI:
return
def onoff_bt_on_init(self):
# read /etc/bluetooth/main.conf and make sure the current bluetooth status is set accordingly to AutoEnable
# we do this only if we run as a service
if is_running_as_service():
status = True # default to true (since bluez 5.65..)
with open("/etc/bluetooth/main.conf", "r") as f:
try:
data = [
x.strip() for x in f.read().splitlines() if "AutoEnable=" in x
][0]
# ignore if commented or missed. then it's default
if not data.startswith("#"):
m = re.search(r"^AutoEnable=(?P<value>true|false)", data)
status = True if m.group("value") == "true" else False
except IndexError as e:
pass
current_status = self.get_bluetooth_status()
if status != current_status:
app_logger.warning(
f"Incorrect status found: {current_status}. We wanted: {status}"
)
self.onoff_bt(status)

def onoff_bt(self, new_status):
# We will just use power off/on, on the adapter using bluetoothctl
# but to do this bluetooth service has to run, so check that first
# If the service is stopped/was disabled, this will re-enable and restart it.
# One other way would be to disable/enable the bluetooth service (but then it's fairly slow to start on demand
# OR having dtoverlay=disable-bt in /boot/config.txt (not sure how to re-enable the service then without reboot)
# The tradeoff is most likely a slower boot since bluetooth is always started

# unfortunately the setting AutoEnable does not seem to be respected on reboot
# (might depend on other packages installed)
# cf. https://discussion.fedoraproject.org/t/bluetooth-always-on-on-boot/65545/10
# so, we have the application check on start up the value of AutoEnable and switch it accordingly
auto_enable_status = f"s/AutoEnable={str(not new_status).lower()}/AutoEnable={str(new_status).lower()}/"

if not is_service_running("bluetooth"):
if new_status:
# make sure we have autoEnable on and reenable/start the service
exec_cmd(
[
"sudo",
"sed",
"-i",
auto_enable_status,
"/etc/bluetooth/main.conf",
],
False,
)
exec_cmd(["sudo", "systemctl", "enable", "bluetooth"], False)
exec_cmd(["sudo", "systemctl", "start", "bluetooth"], False)
else:
# Nothing to do, it's off already
pass
else:
# edit /etc/bluetooth/main.conf AutoEnable, so it keeps state after reboot
exec_cmd(
[
"sudo",
"sed",
"-i",
auto_enable_status,
"/etc/bluetooth/main.conf",
],
False,
)
exec_cmd(["bluetoothctl", "power", "on" if new_status else "off"], False)

def onoff_wifi(self, new_status):
return_code = exec_cmd(
["sudo", "rfkill", "unblock" if new_status else "block", "wifi"], False
)

onoff_cmd = {
"Wifi": {
True: ["sudo", "rfkill", "block", "wifi"],
False: ["sudo"", rfkill", "unblock", "wifi"],
},
"Bluetooth": {
True: ["sudo", "rfkill", "block", "bluetooth"],
False: ["sudo", "rfkill", "unblock", "bluetooth"],
},
}
status = {}
status["Wifi"], status["Bluetooth"] = self.get_wifi_bt_status()
exec_cmd(onoff_cmd[key][status[key]])
if return_code:
# TODO custom exception
raise OSError("Rfkill command failed")

async def bluetooth_tethering(self, disconnect=False):
if not self.G_IS_RASPI or not self.G_BT_USE_ADDRESS or not self.bt_pan:
if not self.G_BT_USE_ADDRESS or not self.bt_pan:
return

if not disconnect:
Expand Down
4 changes: 2 additions & 2 deletions modules/logger_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -897,8 +897,8 @@ def update_track(self, timestamp):
shutil.copy(self.config.G_LOG_DB, db_file)

query = (
'SELECT distance,position_lat,position_long FROM BIKECOMPUTER_LOG '
+ 'WHERE position_lat is not null AND position_long is not null '
"SELECT distance,position_lat,position_long FROM BIKECOMPUTER_LOG "
+ "WHERE position_lat is not null AND position_long is not null "
+ 'and typeof(position_lat) = "real" and typeof(position_long) = "real"'
)
if timestamp is not None:
Expand Down
37 changes: 19 additions & 18 deletions modules/pyqt/menu/pyqt_system_menu_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,10 @@ def debug(self):

class NetworkMenuWidget(MenuWidget):
def setup_menu(self):
wifi_bt_button_func_wifi = None
wifi_bt_button_func_bt = None

if self.config.G_IS_RASPI:
wifi_bt_button_func_wifi = lambda: self.onoff_wifi_bt(True, "Wifi")
wifi_bt_button_func_bt = lambda: self.onoff_wifi_bt(True, "Bluetooth")

button_conf = (
# Name(page_name), button_attribute, connected functions, layout
("Wifi", "toggle", wifi_bt_button_func_wifi),
("Bluetooth", "toggle", wifi_bt_button_func_bt),
("Wifi", "toggle", self.onoff_wifi),
("Bluetooth", "toggle", self.onoff_bt),
("BT Tethering", "submenu", self.bt_tething),
("IP Address", "dialog", self.show_ip_address),
("Gadgetbridge", "toggle", lambda: self.onoff_ble_uart_service(True)),
Expand All @@ -74,18 +67,26 @@ def setup_menu(self):

def preprocess(self):
# initialize toggle button status
if self.config.G_IS_RASPI:
self.onoff_wifi_bt(change=False, key="Wifi")
self.onoff_wifi_bt(change=False, key="Bluetooth")
self.buttons["Bluetooth"].change_toggle(self.config.get_bluetooth_status())
self.buttons["Wifi"].change_toggle(self.config.get_wifi_status())
self.onoff_ble_uart_service(change=False)
self.onoff_gadgetbridge_gps(change=False)

def onoff_wifi_bt(self, change=True, key=None):
if change:
self.config.onoff_wifi_bt(key)
status = {}
status["Wifi"], status["Bluetooth"] = self.config.get_wifi_bt_status()
self.buttons[key].change_toggle(status[key])
def onoff_bt(self):
try:
new_status = not self.buttons["Bluetooth"].status
self.config.onoff_bt(new_status)
self.buttons["Bluetooth"].change_toggle(new_status)
except Exception as e:
app_logger.warning(f"Could not change bluetooth status: {e}")

def onoff_wifi(self):
try:
new_status = not self.buttons["Wifi"].status
self.config.onoff_wifi(new_status)
self.buttons["Wifi"].change_toggle(new_status)
except Exception as e:
app_logger.warning(f"Could not change wifi status: {e}")

def bt_tething(self):
self.change_page("BT Tethering", preprocess=True)
Expand Down
1 change: 1 addition & 0 deletions reqs/full.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-r min.in

dbus-next==0.2.3
garminconnect==0.1.55
polyline==2.0.0
stravacookies==1.3
Expand Down
1 change: 1 addition & 0 deletions reqs/full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ certifi==2023.7.22
charset-normalizer==3.2.0
cloudscraper==1.2.71
crdp @ git+https://github.com/hishizuka/crdp.git
dbus-next==0.2.3
frozenlist==1.4.0
garminconnect==0.1.55
html5lib==1.1
Expand Down