Skip to content

Commit 27f5371

Browse files
authored
Add bluetooth support (#3604)
* Add bluetooth option
1 parent f0cb3ad commit 27f5371

11 files changed

Lines changed: 215 additions & 2 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import TYPE_CHECKING
2+
3+
from archinstall.lib.output import debug
4+
5+
if TYPE_CHECKING:
6+
from archinstall.lib.installer import Installer
7+
8+
9+
class Bluetooth:
10+
@property
11+
def packages(self) -> list[str]:
12+
return [
13+
'bluez',
14+
'bluez-utils',
15+
]
16+
17+
@property
18+
def services(self) -> list[str]:
19+
return [
20+
'bluetooth.service',
21+
]
22+
23+
def install(self, install_session: 'Installer') -> None:
24+
debug('Installing Bluetooth')
25+
install_session.add_additional_packages(self.packages)
26+
install_session.enable_service(self.services)

archinstall/default_profiles/desktops/cinnamon.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ def packages(self) -> list[str]:
1616
'system-config-printer',
1717
'gnome-keyring',
1818
'gnome-terminal',
19-
'blueman',
20-
'bluez-utils',
2119
'engrampa',
2220
'gnome-screenshot',
2321
'gvfs-smb',

archinstall/lib/applications/__init__.py

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import TYPE_CHECKING
2+
3+
from archinstall.applications.bluetooth import Bluetooth
4+
from archinstall.lib.models.application import ApplicationConfiguration
5+
6+
if TYPE_CHECKING:
7+
from archinstall.lib.installer import Installer
8+
9+
10+
class ApplicationHandler:
11+
def __init__(self) -> None:
12+
pass
13+
14+
def install_applications(
15+
self,
16+
install_session: 'Installer',
17+
app_config: ApplicationConfiguration,
18+
) -> None:
19+
if app_config.bluetooth_config:
20+
Bluetooth().install(install_session)
21+
22+
23+
application_handler = ApplicationHandler()
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from typing import override
2+
3+
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
4+
from archinstall.lib.models.application import ApplicationConfiguration, BluetoothConfiguration
5+
from archinstall.lib.translationhandler import tr
6+
from archinstall.tui.curses_menu import SelectMenu
7+
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
8+
from archinstall.tui.types import Alignment, Orientation
9+
10+
11+
class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
12+
def __init__(
13+
self,
14+
preset: ApplicationConfiguration | None = None,
15+
):
16+
if preset:
17+
self._app_config = preset
18+
else:
19+
self._app_config = ApplicationConfiguration()
20+
21+
menu_optioons = self._define_menu_options()
22+
self._item_group = MenuItemGroup(menu_optioons, checkmarks=True)
23+
24+
super().__init__(
25+
self._item_group,
26+
config=self._app_config,
27+
allow_reset=True,
28+
)
29+
30+
@override
31+
def run(self, additional_title: str | None = None) -> ApplicationConfiguration:
32+
super().run(additional_title=additional_title)
33+
return self._app_config
34+
35+
def _define_menu_options(self) -> list[MenuItem]:
36+
return [
37+
MenuItem(
38+
text=tr('Bluetooth'),
39+
action=select_bluetooth,
40+
value=self._app_config.bluetooth_config,
41+
preview_action=self._prev_bluetooth,
42+
key='bluetooth_config',
43+
),
44+
]
45+
46+
def _prev_bluetooth(self, item: MenuItem) -> str | None:
47+
if item.value is not None:
48+
bluetooth_config: BluetoothConfiguration = item.value
49+
50+
output = 'Bluetooth: '
51+
output += tr('Enabled') if bluetooth_config.enabled else tr('Disabled')
52+
return output
53+
return None
54+
55+
56+
def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
57+
group = MenuItemGroup.yes_no()
58+
group.focus_item = MenuItem.no()
59+
60+
if preset is not None:
61+
group.set_selected_by_value(preset.enabled)
62+
63+
header = tr('Would you like to configure Bluetooth?') + '\n'
64+
65+
result = SelectMenu[bool](
66+
group,
67+
header=header,
68+
alignment=Alignment.CENTER,
69+
columns=2,
70+
orientation=Orientation.HORIZONTAL,
71+
allow_skip=True,
72+
).run()
73+
74+
enabled = result.item() == MenuItem.yes()
75+
76+
return BluetoothConfiguration(enabled)

archinstall/lib/args.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from pydantic.dataclasses import dataclass as p_dataclass
1414

1515
from archinstall.lib.crypt import decrypt
16+
from archinstall.lib.models.application import ApplicationConfiguration
1617
from archinstall.lib.models.audio_configuration import AudioConfiguration
1718
from archinstall.lib.models.bootloader import Bootloader
1819
from archinstall.lib.models.device_model import DiskEncryption, DiskLayoutConfiguration
@@ -63,6 +64,7 @@ class ArchConfig:
6364
bootloader: Bootloader = field(default=Bootloader.get_default())
6465
uki: bool = False
6566
audio_config: AudioConfiguration | None = None
67+
application_config: ApplicationConfiguration | None = None
6668
hostname: str = 'archlinux'
6769
kernels: list[str] = field(default_factory=lambda: ['linux'])
6870
ntp: bool = True
@@ -105,6 +107,7 @@ def safe_json(self) -> dict[str, Any]:
105107
'custom_commands': self.custom_commands,
106108
'bootloader': self.bootloader.json(),
107109
'audio_config': self.audio_config.json() if self.audio_config else None,
110+
'app_config': self.application_config.json() if self.application_config else None,
108111
}
109112

110113
if self.locale_config:
@@ -184,6 +187,9 @@ def from_config(cls, args_config: dict[str, Any]) -> 'ArchConfig':
184187
if audio_config := args_config.get('audio_config', None):
185188
arch_config.audio_config = AudioConfiguration.parse_arg(audio_config)
186189

190+
if app_config := args_config.get('app_config', None):
191+
arch_config.application_config = ApplicationConfiguration.parse_arg(app_config)
192+
187193
if hostname := args_config.get('hostname', ''):
188194
arch_config.hostname = hostname
189195

archinstall/lib/global_menu.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from typing import override
44

55
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
6+
from archinstall.lib.models.application import ApplicationConfiguration
67
from archinstall.lib.models.device_model import DiskLayoutConfiguration, DiskLayoutType, EncryptionType, FilesystemType, PartitionModification
78
from archinstall.lib.packages import list_available_packages
89
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
910

11+
from .applications.application_menu import ApplicationMenu
1012
from .args import ArchConfig
1113
from .configuration import save_config
1214
from .hardware import SysInfo
@@ -132,6 +134,13 @@ def _get_menu_options(self) -> list[MenuItem]:
132134
preview_action=self._prev_audio,
133135
key='audio_config',
134136
),
137+
MenuItem(
138+
text=tr('Applications'),
139+
action=self._select_applications,
140+
value=[],
141+
preview_action=self._prev_applications,
142+
key='application_config',
143+
),
135144
MenuItem(
136145
text=tr('Kernels'),
137146
value=['linux'],
@@ -252,6 +261,10 @@ def _select_archinstall_language(self, preset: Language) -> Language:
252261

253262
return language
254263

264+
def _select_applications(self, preset: ApplicationConfiguration | None) -> ApplicationConfiguration | None:
265+
app_config = ApplicationMenu(preset).run()
266+
return app_config
267+
255268
def _update_lang_text(self) -> None:
256269
"""
257270
The options for the global menu are generated with a static text;
@@ -291,6 +304,19 @@ def _prev_additional_pkgs(self, item: MenuItem) -> str | None:
291304
return output
292305
return None
293306

307+
def _prev_applications(self, item: MenuItem) -> str | None:
308+
if item.value:
309+
app_config: ApplicationConfiguration = item.value
310+
output = ''
311+
312+
if app_config.bluetooth_config:
313+
output += f'{tr("Bluetooth")}: '
314+
output += tr('Enabled') if app_config.bluetooth_config.enabled else tr('Disabled') + '\n'
315+
316+
return output
317+
318+
return None
319+
294320
def _prev_tz(self, item: MenuItem) -> str | None:
295321
if item.value:
296322
return f'{tr("Timezone")}: {item.value}'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from dataclasses import dataclass
2+
from typing import NotRequired, TypedDict
3+
4+
5+
class BluetoothConfigSerialization(TypedDict):
6+
enabled: bool
7+
8+
9+
class ApplicationSerialization(TypedDict):
10+
bluetooth_config: NotRequired[BluetoothConfigSerialization]
11+
12+
13+
@dataclass
14+
class BluetoothConfiguration:
15+
enabled: bool
16+
17+
def json(self) -> BluetoothConfigSerialization:
18+
return {'enabled': self.enabled}
19+
20+
@staticmethod
21+
def parse_arg(arg: BluetoothConfigSerialization) -> 'BluetoothConfiguration':
22+
return BluetoothConfiguration(arg['enabled'])
23+
24+
25+
@dataclass
26+
class ApplicationConfiguration:
27+
bluetooth_config: BluetoothConfiguration | None = None
28+
29+
@staticmethod
30+
def parse_arg(args: ApplicationSerialization) -> 'ApplicationConfiguration':
31+
bluetooth_config: BluetoothConfiguration | None = None
32+
if 'bluetooth_config' in args:
33+
bluetooth_config = BluetoothConfiguration.parse_arg(args['bluetooth_config'])
34+
35+
return ApplicationConfiguration(
36+
bluetooth_config=bluetooth_config,
37+
)
38+
39+
def json(self) -> ApplicationSerialization:
40+
config: ApplicationSerialization = {}
41+
42+
if self.bluetooth_config:
43+
config['bluetooth_config'] = self.bluetooth_config.json()
44+
45+
return config

archinstall/scripts/guided.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pathlib import Path
33

44
from archinstall import SysInfo
5+
from archinstall.lib.applications.application_handler import application_handler
56
from archinstall.lib.args import arch_config_handler
67
from archinstall.lib.configuration import ConfigurationOutput
78
from archinstall.lib.disk.filesystem import FilesystemHandler
@@ -127,6 +128,9 @@ def perform_installation(mountpoint: Path) -> None:
127128
if profile_config := config.profile_config:
128129
profile_handler.install_profile_config(installation, profile_config)
129130

131+
if app_config := config.application_config:
132+
application_handler.install_applications(installation, app_config)
133+
130134
if timezone := config.timezone:
131135
installation.set_timezone(timezone)
132136

tests/data/test_config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
{
22
"archinstall-language": "English",
3+
"app_config": {
4+
"bluetooth_config": {
5+
"enabled": true
6+
}
7+
},
38
"audio_config": {
49
"audio": "pipewire"
510
},

0 commit comments

Comments
 (0)