something-x-dev 1.2.3.dev3__tar.gz → 1.3.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {something_x_dev-1.2.3.dev3/something_x_dev.egg-info → something_x_dev-1.3.0.dev5}/PKG-INFO +1 -1
  2. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/pages/device.py +30 -12
  3. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/protocol.py +27 -21
  4. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/window.py +30 -1
  5. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/pyproject.toml +1 -1
  6. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5/something_x_dev.egg-info}/PKG-INFO +1 -1
  7. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/LICENSE +0 -0
  8. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/README.md +0 -0
  9. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/__init__.py +0 -0
  10. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/application.py +0 -0
  11. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/bluetooth.py +0 -0
  12. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/data/__init__.py +0 -0
  13. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/data/com.something.x.omarchy.desktop +0 -0
  14. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/data/style.css +0 -0
  15. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/pages/__init__.py +0 -0
  16. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/pages/home.py +0 -0
  17. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/profiles.py +0 -0
  18. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/nothing_app/splash.py +0 -0
  19. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/setup.cfg +0 -0
  20. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/something_x_dev.egg-info/SOURCES.txt +0 -0
  21. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/something_x_dev.egg-info/dependency_links.txt +0 -0
  22. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/something_x_dev.egg-info/entry_points.txt +0 -0
  23. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/something_x_dev.egg-info/requires.txt +0 -0
  24. {something_x_dev-1.2.3.dev3 → something_x_dev-1.3.0.dev5}/something_x_dev.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: something-x-dev
3
- Version: 1.2.3.dev3
3
+ Version: 1.3.0.dev5
4
4
  Summary: Something X device manager for Omarchy / Linux
5
5
  Author: Raphael
6
6
  License: MIT
@@ -265,11 +265,17 @@ def _settings_row(title: str, subtitle: str = "", right_widget: Gtk.Widget | Non
265
265
 
266
266
 
267
267
  class DevicePage(Gtk.Box):
268
- def __init__(self, bt_device: BluetoothDevice, bt_manager: BluetoothManager):
268
+ def __init__(
269
+ self,
270
+ bt_device: BluetoothDevice,
271
+ bt_manager: BluetoothManager,
272
+ nothing_dev: NothingDevice | None = None,
273
+ ):
269
274
  super().__init__(orientation=Gtk.Orientation.VERTICAL)
270
275
  self._bt_device = bt_device
271
276
  self._bt = bt_manager
272
277
  self._nothing_dev: NothingDevice | None = None
278
+ self._nd_handlers: list[int] = []
273
279
  self._anc_buttons: list[tuple[int, Gtk.Button]] = []
274
280
  self._eq_buttons: list[tuple[str, Gtk.Button]] = []
275
281
  self._updating_ui = False
@@ -281,7 +287,7 @@ class DevicePage(Gtk.Box):
281
287
  self._connect_retry_id: int | None = None
282
288
  self._build()
283
289
  if bt_device.is_nothing:
284
- self._connect_nothing()
290
+ self._connect_nothing(nothing_dev)
285
291
  if bt_device.connected:
286
292
  GLib.timeout_add(800, self._query_volume)
287
293
 
@@ -474,17 +480,31 @@ class DevicePage(Gtk.Box):
474
480
 
475
481
  def cleanup(self):
476
482
  if self._nothing_dev:
477
- self._nothing_dev.disconnect_rfcomm()
483
+ for h in self._nd_handlers:
484
+ try:
485
+ self._nothing_dev.disconnect(h)
486
+ except Exception:
487
+ pass
488
+ self._nd_handlers = []
478
489
  self._bt.disconnect(self._bt_conn_handler)
479
490
  self._bt.disconnect(self._bt_disc_handler)
480
491
 
481
- def _connect_nothing(self):
482
- self._nothing_dev = NothingDevice(self._bt_device.address)
483
- self._nothing_dev.connect("state-changed", self._on_state_changed)
484
- self._nothing_dev.connect("connected", self._on_rfcomm_connected)
485
- self._nothing_dev.connect("disconnected", self._on_rfcomm_disconnected)
486
- if self._bt_device.connected:
487
- self._nothing_dev.connect_rfcomm()
492
+ def _connect_nothing(self, existing: NothingDevice | None):
493
+ if existing is not None:
494
+ self._nothing_dev = existing
495
+ else:
496
+ self._nothing_dev = NothingDevice(self._bt_device.address)
497
+ if self._bt_device.connected:
498
+ self._nothing_dev.connect_rfcomm()
499
+
500
+ self._nd_handlers = [
501
+ self._nothing_dev.connect("state-changed", self._on_state_changed),
502
+ self._nothing_dev.connect("connected", self._on_rfcomm_connected),
503
+ self._nothing_dev.connect("disconnected", self._on_rfcomm_disconnected),
504
+ ]
505
+ if self._nothing_dev.rfcomm_connected:
506
+ self._on_state_changed(self._nothing_dev)
507
+ GLib.timeout_add(800, self._query_volume)
488
508
 
489
509
  def _on_state_changed(self, dev: NothingDevice):
490
510
  state = dev.state
@@ -568,8 +588,6 @@ class DevicePage(Gtk.Box):
568
588
 
569
589
  def _on_conn_btn_clicked(self, _btn):
570
590
  if self._bt_device.connected:
571
- if self._nothing_dev:
572
- self._nothing_dev.disconnect_rfcomm()
573
591
  self._bt.disconnect_device(self._bt_device.path)
574
592
  else:
575
593
  self._conn_btn.set_label("CONNECTING…")
@@ -147,7 +147,7 @@ class NothingDevice(GObject.Object):
147
147
  self._anc_pending_mode: int = ANCMode.OFF
148
148
  self._last_anc_level: int = _ANC_STRONG
149
149
  self._thread: threading.Thread | None = None
150
- self._low_bat_notified: set[str] = set()
150
+ self._low_bat_notified: dict[str, set[int]] = {}
151
151
 
152
152
  # ── Public API ────────────────────────────────────────────────────────────
153
153
 
@@ -602,29 +602,35 @@ class NothingDevice(GObject.Object):
602
602
  eq_val = EQ_PRESETS.get(p["eq"], 0)
603
603
  self._x55_send(_CMD_SET_EQ, bytes([eq_val]), label=f"restore EQ={p['eq']}")
604
604
 
605
+ _LOW_BAT_THRESHOLDS = (20, 15, 10, 5)
606
+
605
607
  def _check_low_battery(self, slot: str, pct: int, label: str):
606
608
  if pct < 0:
607
609
  return
608
- if pct <= 20 and slot not in self._low_bat_notified:
609
- self._low_bat_notified.add(slot)
610
- threading.Thread(
611
- target=subprocess.run,
612
- args=(
613
- [
614
- "notify-send",
615
- "-u",
616
- "critical",
617
- "-i",
618
- "battery-caution",
619
- "Something X",
620
- f"{label}: {pct}% battery remaining",
621
- ],
622
- ),
623
- kwargs={"capture_output": True},
624
- daemon=True,
625
- ).start()
626
- elif pct > 25:
627
- self._low_bat_notified.discard(slot)
610
+ if pct > 25:
611
+ self._low_bat_notified.pop(slot, None)
612
+ return
613
+ notified = self._low_bat_notified.setdefault(slot, set())
614
+ for threshold in self._LOW_BAT_THRESHOLDS:
615
+ if pct <= threshold and threshold not in notified:
616
+ notified.add(threshold)
617
+ threading.Thread(
618
+ target=subprocess.run,
619
+ args=(
620
+ [
621
+ "notify-send",
622
+ "-u",
623
+ "critical",
624
+ "-i",
625
+ "battery-caution",
626
+ "Something X",
627
+ f"{label}: {pct}% battery remaining",
628
+ ],
629
+ ),
630
+ kwargs={"capture_output": True},
631
+ daemon=True,
632
+ ).start()
633
+ break
628
634
 
629
635
  def _activation_fallback(self):
630
636
  if not self._activated and self._rfcomm_connected:
@@ -5,6 +5,7 @@ gi.require_version("Adw", "1")
5
5
  from gi.repository import Gtk, Adw
6
6
 
7
7
  from .bluetooth import BluetoothDevice, BluetoothManager
8
+ from .protocol import NothingDevice
8
9
  from .pages.home import HomePage
9
10
  from .pages.device import DevicePage
10
11
 
@@ -15,7 +16,34 @@ class SomethingXWindow(Adw.ApplicationWindow):
15
16
  self.set_default_size(420, 780)
16
17
  self.set_resizable(True)
17
18
  self._bt = bt_manager
19
+ self._nothing_devices: dict[str, NothingDevice] = {}
20
+ self._bt_conn_handler = bt_manager.connect("device-connected", self._on_bt_connected)
21
+ self._bt_disc_handler = bt_manager.connect("device-disconnected", self._on_bt_disconnected)
18
22
  self._build()
23
+ self._autoconnect_existing()
24
+
25
+ def _autoconnect_existing(self):
26
+ for dev in self._bt.get_nothing_devices():
27
+ if dev.connected:
28
+ self._start_nothing_device(dev.path, dev.address)
29
+
30
+ def _start_nothing_device(self, path: str, address: str):
31
+ if path in self._nothing_devices:
32
+ return
33
+ nd = NothingDevice(address)
34
+ self._nothing_devices[path] = nd
35
+ nd.connect_rfcomm()
36
+ print(f"[window] autoconnect RFCOMM → {address}")
37
+
38
+ def _on_bt_connected(self, _mgr, path: str):
39
+ dev = self._bt.devices.get(path)
40
+ if dev and dev.is_nothing:
41
+ self._start_nothing_device(path, dev.address)
42
+
43
+ def _on_bt_disconnected(self, _mgr, path: str):
44
+ nd = self._nothing_devices.pop(path, None)
45
+ if nd:
46
+ nd.disconnect_rfcomm()
19
47
 
20
48
  def _build(self):
21
49
  nav = Adw.NavigationView()
@@ -64,7 +92,8 @@ class SomethingXWindow(Adw.ApplicationWindow):
64
92
  header.add_css_class("nothing-header")
65
93
  toolbar_view.add_top_bar(header)
66
94
 
67
- device_page = DevicePage(bt_device=bt_device, bt_manager=self._bt)
95
+ nothing_dev = self._nothing_devices.get(bt_device.path)
96
+ device_page = DevicePage(bt_device=bt_device, bt_manager=self._bt, nothing_dev=nothing_dev)
68
97
  toolbar_view.set_content(device_page)
69
98
 
70
99
  nav_page.set_child(toolbar_view)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "something-x-dev"
7
- version = "1.2.3.dev3"
7
+ version = "1.3.0.dev5"
8
8
  description = "Something X device manager for Omarchy / Linux"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: something-x-dev
3
- Version: 1.2.3.dev3
3
+ Version: 1.3.0.dev5
4
4
  Summary: Something X device manager for Omarchy / Linux
5
5
  Author: Raphael
6
6
  License: MIT