difonlib 0.2.3__tar.gz → 0.2.4__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.
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: difonlib
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: python libraries
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
+ Requires-Dist: bleak>=3.0.1
7
8
  Requires-Dist: evdev>=1.9.2
9
+ Requires-Dist: hid>=1.0.9
8
10
  Requires-Dist: nicegui>=3.2.0
9
11
  Requires-Dist: pexpect>=4.9.0
12
+ Requires-Dist: pydbus>=0.6.0
13
+ Requires-Dist: pygobject>=3.56.2
10
14
  Requires-Dist: pyyaml>=6.0.3
11
15
  Requires-Dist: tinytuya>=1.17.4
12
- Requires-Dist: types-pexpect>=4.9.0.20250916
13
- Requires-Dist: types-PyYAML>=6.0.12.20250915
14
- Requires-Dist: types-xmltodict>=1.0.1.20250920
15
16
  Requires-Dist: xmltodict>=1.0.2
16
17
 
17
18
  # Python libraries
@@ -1,18 +1,19 @@
1
1
  [project]
2
2
  name = "difonlib"
3
- version = "0.2.3"
3
+ version = "0.2.4"
4
4
  description = "python libraries"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
+ "bleak>=3.0.1",
8
9
  "evdev>=1.9.2",
10
+ "hid>=1.0.9",
9
11
  "nicegui>=3.2.0",
10
12
  "pexpect>=4.9.0",
13
+ "pydbus>=0.6.0",
14
+ "pygobject>=3.56.2",
11
15
  "pyyaml>=6.0.3",
12
16
  "tinytuya>=1.17.4",
13
- "types-pexpect>=4.9.0.20250916",
14
- "types-PyYAML>=6.0.12.20250915",
15
- "types-xmltodict>=1.0.1.20250920",
16
17
  "xmltodict>=1.0.2",
17
18
  ]
18
19
 
@@ -47,6 +48,9 @@ dev = [
47
48
  "black>=24.8.0",
48
49
  "mypy>=1.11.0",
49
50
  "twine>=5.1.1",
51
+ "types-pexpect>=4.9.0.20250916",
52
+ "types-PyYAML>=6.0.12.20250915",
53
+ "types-xmltodict>=1.0.1.20250920",
50
54
  ]
51
55
 
52
56
  [tool.ruff]
@@ -0,0 +1,277 @@
1
+ """
2
+ Bluetooth HID Device Scanner
3
+ Requires: pip install bleak hid
4
+ On Linux may also need: sudo apt install libhidapi-hidraw0 libhidapi-libusb0
5
+ Run with: python bt_hid_scanner.py
6
+ https://claude.ai/share/54cf3394-15f2-4ca9-8730-b70bf2973646
7
+ """
8
+
9
+ import asyncio
10
+ import hid
11
+ import pydbus
12
+ from bleak import BleakScanner
13
+ from bleak.backends.device import BLEDevice
14
+ from bleak.backends.scanner import AdvertisementData
15
+ from ctypes import LittleEndianStructure, c_uint32
16
+
17
+ # HID Usage Page 0x01 = Generic Desktop
18
+ # HID Usages: 0x02=Mouse, 0x04=Joystick, 0x05=Gamepad, 0x06=Keyboard, 0x08=Multi-axis
19
+ HID_USAGE_NAMES = {
20
+ 0x01: "Pointer",
21
+ 0x02: "Mouse",
22
+ 0x04: "Joystick",
23
+ 0x05: "Gamepad",
24
+ 0x06: "Keyboard",
25
+ 0x07: "Keypad",
26
+ 0x08: "Multi-axis Controller",
27
+ 0x09: "Tablet PC Controls",
28
+ }
29
+
30
+
31
+ # Bluetooth HID Service UUID
32
+ HID_SERVICE_UUID = "00001812-0000-1000-8000-00805f9b34fb"
33
+
34
+ MAJOR_CLASSES = {
35
+ 0x00: "Miscellaneous",
36
+ 0x01: "Computer",
37
+ 0x02: "Phone",
38
+ 0x03: "LAN/Network Access Point",
39
+ 0x04: "Audio/Video",
40
+ 0x05: "HID Device",
41
+ 0x06: "Imaging",
42
+ 0x07: "Wearable",
43
+ 0x08: "Toy",
44
+ 0x09: "Health",
45
+ 0x1F: "Uncategorized",
46
+ }
47
+
48
+
49
+ # === Class of Device (CoD)===
50
+ class CoD(LittleEndianStructure):
51
+ _fields_ = [
52
+ ("FormatType", c_uint32, 2), # 2 бита
53
+ ("MinorDevClass", c_uint32, 6), # 6 бит
54
+ ("MajorDevClass", c_uint32, 5), # 5 бит
55
+ ("ServiceClass", c_uint32, 11), # 11 бит
56
+ ("Reserved", c_uint32, 8), # оставшиеся до 32
57
+ ]
58
+
59
+
60
+ def bt_parse_cod(cod_val: int) -> str:
61
+ """Разобрать Class of Device (CoD) в словарь с описанием"""
62
+ # cod32 = c_uint32(int(cod_val, 0))
63
+ cod32 = c_uint32(cod_val)
64
+ fields = CoD.from_buffer_copy(cod32)
65
+ major = fields.MajorDevClass
66
+ return MAJOR_CLASSES.get(major, f"0x{major:02X}")
67
+
68
+
69
+ async def adapter_restart(adapter_path: str = "/org/bluez/hci0", duration: float = 0.5) -> None:
70
+ bus = pydbus.SystemBus()
71
+ adapter = bus.get("org.bluez", adapter_path)
72
+ adapter.StartDiscovery()
73
+ await asyncio.sleep(duration)
74
+ try:
75
+ adapter.StopDiscovery()
76
+ except Exception:
77
+ pass
78
+ await asyncio.sleep(0.5)
79
+
80
+
81
+ # ─── USB HID Devices (connected right now) ────────────────────────────────────
82
+
83
+
84
+ def scan_usb_hid_devices() -> list[dict]:
85
+ """Return a list of currently connected USB HID devices."""
86
+ devices = []
87
+ seen = set()
88
+
89
+ for info in hid.enumerate():
90
+ key = (info["vendor_id"], info["product_id"], info["usage_page"], info["usage"])
91
+ if key in seen:
92
+ continue
93
+ seen.add(key)
94
+
95
+ devices.append(
96
+ {
97
+ "name": info.get("product_string") or "Unknown",
98
+ "manufacturer": info.get("manufacturer_string") or "Unknown",
99
+ "vid": f"0x{info['vendor_id']:04X}",
100
+ "pid": f"0x{info['product_id']:04X}",
101
+ "usage_page": f"0x{info['usage_page']:04X}",
102
+ "usage": HID_USAGE_NAMES.get(info["usage"], f"0x{info['usage']:04X}"),
103
+ "serial": info.get("serial_number") or "—",
104
+ "path": info.get("path", b"").decode(errors="replace"),
105
+ "interface": info.get("interface_number", -1),
106
+ "transport": "USB / HID",
107
+ }
108
+ )
109
+
110
+ return devices
111
+
112
+
113
+ # ─── Bluetooth LE HID Devices (nearby) ────────────────────────────────────────
114
+
115
+
116
+ class BluetoothScanner:
117
+ def __init__(self, duration: float = 5.0, dev_type: str = ""):
118
+ self.duration = duration
119
+ self.found: dict[str, dict] = {} # addr -> device info
120
+ self.dev_type = dev_type
121
+ self.available_types = [v for v in MAJOR_CLASSES.values()]
122
+
123
+ def _on_device(self, device: BLEDevice, adv: AdvertisementData) -> None:
124
+ # print(f"device: {device}")
125
+ # print(f"Class: {device.details['Class']}")
126
+ # print(f"adv: {adv}")
127
+ service_uuids = [u.lower() for u in (adv.service_uuids or [])]
128
+ ble_hid_dev = HID_SERVICE_UUID in service_uuids
129
+ dclass = device.details.get("props", {}).get("Class")
130
+ # print(f" = DETAILS: {device.details}")
131
+ dev_class = None
132
+ if ble_hid_dev:
133
+ dev_class = "HID Device"
134
+ elif dclass:
135
+ dev_class = bt_parse_cod(dclass)
136
+
137
+ addr = device.address
138
+ if addr in self.found:
139
+ # update RSSI
140
+ self.found[addr]["rssi"] = adv.rssi
141
+ return
142
+
143
+ self.found[addr] = {
144
+ "name": device.name or "Unknown",
145
+ "address": addr,
146
+ "dev_class": dev_class,
147
+ "rssi": adv.rssi,
148
+ "services": service_uuids,
149
+ "tx_power": adv.tx_power,
150
+ }
151
+ # print(
152
+ # f" [+] Found: {device.name or 'Unknown':30s} {addr} RSSI {adv.rssi} dBm"
153
+ # )
154
+ # print(f" service_uuids: {service_uuids}")
155
+
156
+ def _filter(self, devs: list[dict], dev_type: str) -> list[dict]:
157
+ _devs = []
158
+ for dev in devs:
159
+ if dev["dev_class"] == dev_type:
160
+ _devs.append(dev)
161
+ return _devs
162
+
163
+ async def run(self) -> list[dict]:
164
+ print(f" Scanning BLE for {self.duration}s ...")
165
+ async with BleakScanner(detection_callback=self._on_device):
166
+ await asyncio.sleep(self.duration)
167
+ if self.dev_type in self.available_types:
168
+ return self._filter(list(self.found.values()), self.dev_type)
169
+ elif self.dev_type != "":
170
+ print(" =!= Device Type ERROR")
171
+ print(f" Type '{self.dev_type}' is not available.")
172
+ print(f" Available types: {self.available_types}")
173
+ return list(self.found.values())
174
+
175
+
176
+ # def get_bt_hid_devs(devs:list[dict]) -> list[dict]:
177
+ # hid_devs = []
178
+ # for dev in devs:
179
+ # if HID_SERVICE_UUID in dev["services"] or dev['dev_class'] == "HID Device":
180
+ # hid_devs.append(dev)
181
+ # return hid_devs
182
+
183
+
184
+ # ─── Pretty printing ───────────────────────────────────────────────────────────
185
+
186
+
187
+ def print_separator(char: str = "─", width: int = 60) -> None:
188
+ print(char * width)
189
+
190
+
191
+ def print_usb_devices(devices: list[dict]) -> None:
192
+ print_separator("═")
193
+ print(f" USB HID DEVICES ({len(devices)} found)")
194
+ print_separator("═")
195
+
196
+ if not devices:
197
+ print(" No USB HID devices found.\n")
198
+ return
199
+
200
+ for i, d in enumerate(devices, 1):
201
+ print(f"\n [{i}] {d['name']}")
202
+ print(f" Manufacturer : {d['manufacturer']}")
203
+ print(f" VID / PID : {d['vid']} / {d['pid']}")
204
+ print(f" Usage : {d['usage']} (page {d['usage_page']})")
205
+ print(f" Serial : {d['serial']}")
206
+ print(f" Interface : {d['interface']}")
207
+ print(f" Path : {d['path']}")
208
+
209
+ print()
210
+
211
+
212
+ def print_bt_devices(devices: list[dict], type: str = "DEVICES") -> None:
213
+ print_separator("═")
214
+ print(f" BLUETOOTH AVAILABLE {type} ({len(devices)} found)")
215
+ print_separator("═")
216
+
217
+ if not devices:
218
+ print(" No devices found.\n")
219
+ return
220
+
221
+ for i, d in enumerate(devices, 1):
222
+ tx = f"{d['tx_power']} dBm" if d["tx_power"] is not None else "—"
223
+ print(f"\n [{i}] {d['name']}")
224
+ print(f" Address : {d['address']}")
225
+ print(f" Services : {d['services']}")
226
+ print(f" Device Class : {d['dev_class']}")
227
+ print(f" RSSI : {d['rssi']} dBm")
228
+ print(f" TX Power : {tx}")
229
+ # print(f" Transport : {d['transport']}")
230
+
231
+ print()
232
+
233
+
234
+ # ─── Main ──────────────────────────────────────────────────────────────────────
235
+
236
+
237
+ async def main() -> None:
238
+ print("\n╔══════════════════════════════════════════════════════════╗")
239
+ print("║ Bluetooth Device Scanner ║")
240
+ print("╚══════════════════════════════════════════════════════════╝\n")
241
+
242
+ # # 1. USB HID
243
+ # print("Scanning USB HID devices ...")
244
+ # usb_devices = scan_usb_hid_devices()
245
+ # print_usb_devices(usb_devices)
246
+
247
+ # # Сначала — inquiry, чтобы BlueZ узнал о Classic устройствах
248
+ # print("Triggering Classic BT inquiry via bluetoothctl...")
249
+ # await trigger_classic_inquiry(duration=2)
250
+
251
+ await adapter_restart()
252
+
253
+ # 2. BLE HID
254
+ try:
255
+ # bt_scanner = BluetoothScanner(duration=6.0, dev_type="Audio/Video")
256
+ dev_type = "HID Device"
257
+ # dev_type = ""
258
+ bt_scanner = BluetoothScanner(duration=6.0, dev_type=dev_type)
259
+ bt_devices = await bt_scanner.run()
260
+ print_bt_devices(bt_devices, type=dev_type)
261
+ except Exception as e:
262
+ print(f" Bluetooth device scan failed: {e}")
263
+ print(" Make sure Bluetooth is enabled and bleak is installed.\n")
264
+ bt_devices = []
265
+
266
+ # # 3. Summary
267
+ # print_separator("═")
268
+ # total = len(usb_devices) + len(ble_devices)
269
+ # print(
270
+ # f" SUMMARY: {len(usb_devices)} USB HID + {len(ble_devices)} BLE HID = {total} total devices"
271
+ # )
272
+ # print_separator("═")
273
+ # print(f"bt_devices: {bt_devices}")
274
+
275
+
276
+ if __name__ == "__main__":
277
+ asyncio.run(main())
@@ -1,17 +1,26 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+ import sys
4
+ import time
3
5
  import os
4
6
  import re
5
7
  import pexpect
6
- import sys
7
- import time
8
8
  from typing import Optional
9
9
  from ctypes import LittleEndianStructure, c_uint32
10
+
11
+ from typing import Any
12
+
10
13
  from difonlib.utils import logdbg
11
14
  from difonlib.input_devs import get_connected_input_devices
12
15
 
16
+ import asyncio
17
+ import pydbus
18
+ from bleak import BleakScanner
19
+ from bleak.backends.device import BLEDevice
20
+ from bleak.backends.scanner import AdvertisementData
13
21
 
14
22
  dbg = logdbg
23
+ # dbg = print
15
24
 
16
25
  MAJOR_CLASSES = {
17
26
  0x00: "Miscellaneous",
@@ -39,15 +48,81 @@ class CoD(LittleEndianStructure):
39
48
  ]
40
49
 
41
50
 
42
- def bt_parse_cod(cod_val: str) -> str:
51
+ def bt_parse_cod(cod_val: Any) -> str:
43
52
  """Разобрать Class of Device (CoD) в словарь с описанием"""
44
- cod32 = c_uint32(int(cod_val, 0))
53
+ cod32 = c_uint32(int(cod_val))
45
54
  fields = CoD.from_buffer_copy(cod32)
46
55
  major = fields.MajorDevClass
47
56
  return MAJOR_CLASSES.get(major, f"0x{major:02X}")
48
57
 
49
58
 
50
- def bt_scan() -> list:
59
+ # Bluetooth HID Service UUID
60
+ HID_SERVICE_UUID = "00001812-0000-1000-8000-00805f9b34fb"
61
+
62
+
63
+ class BluetoothScanner:
64
+ def __init__(self, duration: float = 5.0, dev_type: str = ""):
65
+ self.duration = duration
66
+ self.found: dict[str, dict] = {} # addr -> device info
67
+ self.dev_type = dev_type
68
+ self.available_types = [v for v in MAJOR_CLASSES.values()]
69
+
70
+ def _on_device(self, device: BLEDevice, adv: AdvertisementData) -> None:
71
+ # print(f"device: {device}")
72
+ # print(f"Class: {device.details['Class']}")
73
+ # print(f"adv: {adv}")
74
+ service_uuids = [u.lower() for u in (adv.service_uuids or [])]
75
+ ble_hid_dev = HID_SERVICE_UUID in service_uuids
76
+ dclass = device.details.get("props", {}).get("Class")
77
+ # print(f" = DETAILS: {device.details}")
78
+ dev_class = None
79
+ if ble_hid_dev:
80
+ dev_class = "HID Device"
81
+ elif dclass:
82
+ dev_class = bt_parse_cod(dclass)
83
+
84
+ addr = device.address
85
+ if addr in self.found:
86
+ # update RSSI
87
+ self.found[addr]["rssi"] = adv.rssi
88
+ return
89
+
90
+ self.found[addr] = {
91
+ "name": device.name or "Unknown",
92
+ "address": addr,
93
+ "dev_class": dev_class,
94
+ "rssi": adv.rssi,
95
+ "services": service_uuids,
96
+ "tx_power": adv.tx_power,
97
+ }
98
+ print(".", end="", flush=True)
99
+ # print(
100
+ # f" [+] Found: {device.name or 'Unknown':30s} {addr} RSSI {adv.rssi} dBm"
101
+ # )
102
+ # print(f" service_uuids: {service_uuids}")
103
+
104
+ def _filter(self, devs: list[dict], dev_type: str) -> list[dict]:
105
+ _devs = []
106
+ for dev in devs:
107
+ if dev["dev_class"] == dev_type:
108
+ _devs.append(dev)
109
+ return _devs
110
+
111
+ async def run(self) -> list[dict]:
112
+ print(f" Scanning for bluetooth devices {self.duration}s ...", end="")
113
+ async with BleakScanner(detection_callback=self._on_device):
114
+ await asyncio.sleep(self.duration)
115
+ print(" END\n")
116
+ if self.dev_type in self.available_types:
117
+ return self._filter(list(self.found.values()), self.dev_type)
118
+ elif self.dev_type != "":
119
+ print(" =!= Device Type ERROR")
120
+ print(f" Type '{self.dev_type}' is not available.")
121
+ print(f" Available types: {self.available_types}")
122
+ return list(self.found.values())
123
+
124
+
125
+ def bt_scan_hcitool_inq() -> list:
51
126
  # px = pexpect.spawn("hcitool scan", encoding="utf-8")
52
127
  devs = []
53
128
  # inquery remote devices
@@ -68,16 +143,7 @@ def bt_scan() -> list:
68
143
  return devs
69
144
 
70
145
 
71
- def bt_scan_hid_devs() -> list:
72
- devs = bt_scan()
73
- hid_devs = []
74
- for dev in devs:
75
- if dev["class"] == "HID Device":
76
- hid_devs.append(dev)
77
- return hid_devs
78
-
79
-
80
- def bt_scan2() -> list:
146
+ def bt_scan_hcitool_scan() -> list:
81
147
  # px = pexpect.spawn("hcitool scan", encoding="utf-8")
82
148
  devs = []
83
149
  _devs = os.popen("hcitool scan").readlines()
@@ -95,6 +161,40 @@ def bt_scan2() -> list:
95
161
  return devs
96
162
 
97
163
 
164
+ def bt_scan_hid_devs() -> list:
165
+ devs = bt_scan_hcitool_inq()
166
+ hid_devs = []
167
+ for dev in devs:
168
+ if dev["class"] == "HID Device":
169
+ hid_devs.append(dev)
170
+ return hid_devs
171
+
172
+
173
+ async def bt_scan_devs( # universally version for classic and ble devices
174
+ dev_type: str = "HID Device",
175
+ inquiry_warmup: float = 4.0, # время для Classic Inquiry
176
+ scan_duration: float = 6.0, # время bleak сканирования
177
+ adapter_path: str = "/org/bluez/hci0",
178
+ ) -> list[dict]:
179
+ bus = pydbus.SystemBus()
180
+ adapter = bus.get("org.bluez", adapter_path)
181
+ adapter.SetDiscoveryFilter({})
182
+ adapter.StartDiscovery()
183
+ await asyncio.sleep(inquiry_warmup)
184
+ try:
185
+ bt_scanner = BluetoothScanner(duration=scan_duration, dev_type=dev_type)
186
+ bt_devices = await bt_scanner.run()
187
+ except Exception as e:
188
+ print(f" Bluetooth scan failed: {e}")
189
+ bt_devices = []
190
+ finally:
191
+ try:
192
+ adapter.StopDiscovery()
193
+ except Exception as e:
194
+ print(f" StopDiscovery() error: {e}")
195
+ return bt_devices
196
+
197
+
98
198
  def btctl_add(mac_address: str, timeout: int = 10) -> Optional[dict]:
99
199
  """
100
200
  Подключает HID или любое Bluetooth устройство по MAC через bluetoothctl.
@@ -319,18 +419,23 @@ def bt_hid_conn_devs() -> list:
319
419
 
320
420
  if __name__ == "__main__":
321
421
 
322
- dev_mac = "41:42:68:D8:DA:39"
422
+ # dev_mac = "41:42:68:D8:DA:39"
423
+
424
+ # from difonlib.utils import print_dicts_list
323
425
 
324
- from difonlib.utils import print_dicts_list
426
+ # print(" ======== ALL connected devices ==========")
427
+ # all_connected_devs = get_connected_input_devices()
428
+ # print_dicts_list(all_connected_devs)
325
429
 
326
- print(" ======== ALL connected devices ==========")
327
- all_connected_devs = get_connected_input_devices()
328
- print_dicts_list(all_connected_devs)
430
+ # print(" ======== HID connected devices ==========")
431
+ # conn_devs = bt_hid_conn_devs()
432
+ # print_dicts_list(conn_devs)
329
433
 
330
- print(" ======== HID connected devices ==========")
331
- conn_devs = bt_hid_conn_devs()
332
- print_dicts_list(conn_devs)
434
+ # input("ssssssssssssssssssss")
333
435
 
436
+ # devs = asyncio.run(bt_scan_devs(dev_type=""))
437
+ devs = asyncio.run(bt_scan_devs())
438
+ print(f"devs: {devs}")
334
439
  # available_bt_devs = bt_scan()
335
440
  # print_lists(available_bt_devs) # //Dima
336
441
 
@@ -2,6 +2,7 @@ from pathlib import Path
2
2
  from evdev import InputDevice, categorize, ecodes, list_devices
3
3
  from evdev.events import KeyEvent
4
4
  from typing import Dict, Any, List, Optional
5
+
5
6
  from difonlib.utils import logdbg
6
7
  from dataclasses import dataclass
7
8
  import re
@@ -39,11 +39,23 @@ MSG_COLOR = f"\x1b[{RESET};38;5;{45}m"
39
39
  # DEBUG 10
40
40
  # NOTSET 0
41
41
 
42
- logging.basicConfig(
43
- format=f"{MSG_COLOR}[%(filename)s:%(lineno)d]: %(message)s{COLOR_OFF}",
44
- level=logging.DEBUG,
42
+
43
+ # Глобальные логгеры библиотек — заглушить
44
+ logging.getLogger().setLevel(logging.WARNING) # root logger
45
+
46
+ # Свой именованный логгер
47
+ logger = logging.getLogger("DifonLibLogger")
48
+ logger.setLevel(logging.DEBUG)
49
+
50
+ # Handler только на свой логгер
51
+ handler = logging.StreamHandler()
52
+ handler.setFormatter(
53
+ logging.Formatter(f"{MSG_COLOR}[%(filename)s:%(lineno)d]: %(message)s{COLOR_OFF}")
45
54
  )
46
- logdbg = logging.debug
55
+ logger.addHandler(handler)
56
+ logger.propagate = False # не пускать в root logger
57
+
58
+ logdbg = logger.debug
47
59
 
48
60
 
49
61
  class UtilsError(Exception):
@@ -182,14 +194,14 @@ class YamlConfig:
182
194
  def load(self) -> None:
183
195
  if not self.config_path.exists():
184
196
  self.save()
185
- with self.config_path.open() as f:
186
- self.config = yaml.safe_load(f) or {}
197
+ with self.config_path.open() as fcfg:
198
+ self.config = yaml.safe_load(fcfg) or {}
187
199
 
188
200
  def save(self) -> None:
189
- with self.config_path.open("w") as f:
201
+ with self.config_path.open("w") as fcfg:
190
202
  yaml.safe_dump(
191
203
  self.config,
192
- f,
204
+ fcfg,
193
205
  sort_keys=False,
194
206
  allow_unicode=True,
195
207
  )
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: difonlib
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: python libraries
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
7
+ Requires-Dist: bleak>=3.0.1
7
8
  Requires-Dist: evdev>=1.9.2
9
+ Requires-Dist: hid>=1.0.9
8
10
  Requires-Dist: nicegui>=3.2.0
9
11
  Requires-Dist: pexpect>=4.9.0
12
+ Requires-Dist: pydbus>=0.6.0
13
+ Requires-Dist: pygobject>=3.56.2
10
14
  Requires-Dist: pyyaml>=6.0.3
11
15
  Requires-Dist: tinytuya>=1.17.4
12
- Requires-Dist: types-pexpect>=4.9.0.20250916
13
- Requires-Dist: types-PyYAML>=6.0.12.20250915
14
- Requires-Dist: types-xmltodict>=1.0.1.20250920
15
16
  Requires-Dist: xmltodict>=1.0.2
16
17
 
17
18
  # Python libraries
@@ -1,6 +1,7 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  src/difonlib/__init__.py
4
+ src/difonlib/bt_scanner.py
4
5
  src/difonlib/bt_utils.py
5
6
  src/difonlib/input_devs.py
6
7
  src/difonlib/ng_lib.py
@@ -0,0 +1,10 @@
1
+ bleak>=3.0.1
2
+ evdev>=1.9.2
3
+ hid>=1.0.9
4
+ nicegui>=3.2.0
5
+ pexpect>=4.9.0
6
+ pydbus>=0.6.0
7
+ pygobject>=3.56.2
8
+ pyyaml>=6.0.3
9
+ tinytuya>=1.17.4
10
+ xmltodict>=1.0.2
@@ -1,9 +0,0 @@
1
- evdev>=1.9.2
2
- nicegui>=3.2.0
3
- pexpect>=4.9.0
4
- pyyaml>=6.0.3
5
- tinytuya>=1.17.4
6
- types-pexpect>=4.9.0.20250916
7
- types-PyYAML>=6.0.12.20250915
8
- types-xmltodict>=1.0.1.20250920
9
- xmltodict>=1.0.2
File without changes
File without changes
File without changes
File without changes