difonlib 0.4.3__tar.gz → 0.4.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.
Files changed (23) hide show
  1. {difonlib-0.4.3 → difonlib-0.4.4}/PKG-INFO +1 -1
  2. {difonlib-0.4.3 → difonlib-0.4.4}/pyproject.toml +1 -1
  3. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/ng_lib.py +110 -14
  4. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/ng_lib_test3.py +46 -2
  5. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/tuya_devs.py +7 -1
  6. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib.egg-info/PKG-INFO +1 -1
  7. {difonlib-0.4.3 → difonlib-0.4.4}/README.md +0 -0
  8. {difonlib-0.4.3 → difonlib-0.4.4}/setup.cfg +0 -0
  9. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/__init__.py +0 -0
  10. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/bt_scanner.py +0 -0
  11. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/bt_utils.py +0 -0
  12. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/conn_kbd_devs.py +0 -0
  13. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/docker-android/get_tuya_preferences.py +0 -0
  14. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/input_devs.py +0 -0
  15. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/py.typed +0 -0
  16. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/remctrl.py +0 -0
  17. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib/utils.py +0 -0
  18. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib.egg-info/SOURCES.txt +0 -0
  19. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib.egg-info/dependency_links.txt +0 -0
  20. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib.egg-info/requires.txt +0 -0
  21. {difonlib-0.4.3 → difonlib-0.4.4}/src/difonlib.egg-info/top_level.txt +0 -0
  22. {difonlib-0.4.3 → difonlib-0.4.4}/tests/test_bt_utils.py +0 -0
  23. {difonlib-0.4.3 → difonlib-0.4.4}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: difonlib
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: python libraries
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "difonlib"
3
- version = "0.4.3"
3
+ version = "0.4.4"
4
4
  description = "python libraries"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -2,7 +2,7 @@ from nicegui import ui
2
2
  import inspect
3
3
  import asyncio
4
4
  from typing import Any, Callable, Awaitable, Literal
5
-
5
+ import threading
6
6
  from difonlib.utils import logdbg
7
7
 
8
8
  dbg = logdbg
@@ -28,6 +28,103 @@ ui.add_css(
28
28
 
29
29
 
30
30
  class DialogBox:
31
+
32
+ async def dialog_processing(
33
+ self,
34
+ proc: Callable,
35
+ proc_cancel_event: bool = False,
36
+ label_txt: str = "Processing...",
37
+ label_txt_color: str = "green",
38
+ timeout: int | None = None,
39
+ ) -> tuple[Literal["Canceled", "Timeout"] | None, Any | None]:
40
+ """
41
+ See examples in ng_lib_test3.py
42
+ def proc_dummy2(cancel_event: threading.Event) -> None:
43
+ for i in range(10, 0, -1):
44
+ if cancel_event.is_set():
45
+ print(f"Exit")
46
+ return
47
+ print(f"Remaining: {i}")
48
+ time.sleep(1)
49
+ async def aproc_dummy(cnt: int = 20):
50
+ for i in range(cnt, 0, -1):
51
+ print(f"aRemaining: {i}")
52
+ await asyncio.sleep(1)
53
+ return 125
54
+
55
+ async def proc_dialog():
56
+ err, result = await dialog_box.dialog_processing(
57
+ functools.partial(aproc_dummy, cnt=4), timeout=5
58
+ )
59
+ dbg(f"ERROR: {err}; Result: {result}") # //Dima
60
+ ui.button(
61
+ "Test processing dialog",
62
+ on_click=proc_dialog,
63
+ )
64
+ """
65
+ task_proc: asyncio.Task | None = None
66
+ cancel_event = None
67
+ if proc_cancel_event:
68
+ cancel_event = threading.Event()
69
+
70
+ async def countdown(timeout: int, label: ui.label) -> None:
71
+ for counter in range(timeout, 0, -1):
72
+ label.text = f"{label_txt}{counter}"
73
+ await asyncio.sleep(1)
74
+
75
+ canceled = False
76
+ ret_value: Any | None = None
77
+ err: Literal["Canceled", "Timeout"] | None = None
78
+
79
+ async def on_cancel() -> None:
80
+ nonlocal canceled
81
+ canceled = True
82
+ if cancel_event:
83
+ cancel_event.set()
84
+ if task_proc:
85
+ task_proc.cancel()
86
+ dialog.close()
87
+
88
+ text_color = f"text-{label_txt_color}-500"
89
+ with (
90
+ ui.dialog().props("persistent") as dialog,
91
+ ui.card().classes("p-4 items-center justify-center"),
92
+ ):
93
+ with ui.row().classes("items-center gap-3"):
94
+ ui.spinner(size="md")
95
+ label = ui.label(f"{label_txt}").classes(f"{text_color} text-2xl")
96
+ ui.button("Cancel", on_click=on_cancel)
97
+ dialog.open()
98
+ task_count: asyncio.Task | None = None
99
+ if timeout:
100
+ task_count = asyncio.create_task(countdown(timeout=timeout, label=label))
101
+
102
+ if proc_cancel_event:
103
+ if inspect.iscoroutinefunction(getattr(proc, "func", proc)):
104
+ task_proc = asyncio.create_task(proc(cancel_event))
105
+ else:
106
+ task_proc = asyncio.create_task(asyncio.to_thread(proc, cancel_event))
107
+ else:
108
+ if inspect.iscoroutinefunction(getattr(proc, "func", proc)):
109
+ task_proc = asyncio.create_task(proc())
110
+ else:
111
+ task_proc = asyncio.create_task(asyncio.to_thread(proc))
112
+
113
+ try:
114
+ ret_value = await asyncio.wait_for(task_proc, timeout=timeout)
115
+ except (asyncio.TimeoutError, asyncio.CancelledError):
116
+ if cancel_event:
117
+ cancel_event.set()
118
+ err = "Canceled" if canceled else "Timeout"
119
+ ui.notify(err)
120
+
121
+ finally:
122
+ if task_count and not task_count.cancelled():
123
+ task_count.cancel()
124
+ dialog.close()
125
+
126
+ return (err, ret_value)
127
+
31
128
  async def dialog_ok(self, text: str = "Hello!)") -> None:
32
129
  with ui.dialog().props("persistent") as dialog, ui.card():
33
130
  ui.label(text=text)
@@ -155,33 +252,32 @@ class CardTable:
155
252
  """Add field 'name', needed for Quasar GUI"""
156
253
  return [{**col, "name": col.get("name", col["field"])} for col in columns]
157
254
 
158
- def set_row_display_filter(self, field_name: str, field_value: str | bool) -> None:
255
+ def set_row_display_filter(self, **field: str | bool) -> None:
159
256
  """
160
- card_table.set_row_display_filter("id","123456")
161
- Show rows with field id='123456':
162
- card_table.row_display_filter(state_on=True)
163
- Show all rows:
164
- card_table.row_display_filter()
257
+ card_table.set_row_display_filter(id="123456")
258
+ card_table.set_row_display_filter(_marked=True)
165
259
  """
260
+ assert len(field) == 1, "❌ Exactly one field expected"
261
+ field_name, field_value = next(iter(field.items()))
166
262
  if isinstance(field_value, bool):
167
- self.filter_value = field_value
263
+ self.filter_value = str(field_value).lower() # "true" / "false" для JS
168
264
  else:
169
265
  self.filter_value = f"\\'{field_value}\\'"
170
-
171
266
  self.table.props(rf"""
172
267
  :filter-method="(rows, terms) => !terms ? rows : rows.filter(r => r.{field_name} === terms)"
173
268
  """)
174
269
 
175
270
  def row_display_filter(self, state_on: bool = False) -> None:
176
271
  """
177
- card_table.set_row_display_filter(field_name="id", field_value="123456")
272
+ Show rows with field id='123456' :
273
+ card_table.set_row_display_filter(id="123456")
178
274
  card_table.row_display_filter(state_on=True)
179
- Show rows with field id='123456'
180
- card_table.set_row_display_filter("_marked", True)
275
+ Show rows with field _marked=True :
276
+ card_table.set_row_display_filter(_marked=True)
181
277
  card_table.row_display_filter(True)
182
- Show rows with field _marked=True
278
+ Show all rows:
279
+ card_table.row_display_filter()
183
280
  """
184
- dbg(f"self.filter_value: {self.filter_value}") # //Dima
185
281
  if state_on:
186
282
  self.table.props(f':filter="{self.filter_value}"')
187
283
  else:
@@ -1,12 +1,37 @@
1
+ import asyncio
1
2
  from nicegui import ui
2
3
  from ng_lib import CardTable, DialogBox
3
4
  import functools
5
+ import threading
6
+ import time
4
7
 
5
8
  from difonlib.bt_utils import logdbg
6
9
 
7
10
  dbg = logdbg
8
11
 
9
12
 
13
+ def proc_dummy2(cancel_event: threading.Event) -> None:
14
+ for i in range(10, 0, -1):
15
+ if cancel_event.is_set():
16
+ print("Exit")
17
+ return
18
+ print(f"Remaining: {i}")
19
+ time.sleep(1)
20
+
21
+
22
+ def proc_dummy(cnt: int = 20) -> None:
23
+ for i in range(cnt, 0, -1):
24
+ print(f"Remaining: {i}")
25
+ time.sleep(1)
26
+
27
+
28
+ async def aproc_dummy(cnt: int = 20) -> int:
29
+ for i in range(cnt, 0, -1):
30
+ print(f"aRemaining: {i}")
31
+ await asyncio.sleep(1)
32
+ return 125
33
+
34
+
10
35
  @ui.page("/", dark=True)
11
36
  def main() -> None:
12
37
 
@@ -33,8 +58,8 @@ def main() -> None:
33
58
  found = card_table.mark_row(id="129956")
34
59
  print(f" ==> found: {found}") # //Dima
35
60
 
36
- card_table.set_row_display_filter("_marked", True)
37
- # card_table.set_row_display_filter(field_name="id", field_value="123456")
61
+ card_table.set_row_display_filter(_marked=True)
62
+ # card_table.set_row_display_filter(id="123456")
38
63
 
39
64
  # self.table.props(':filter="\'online\'"')
40
65
  # self.table.update()
@@ -100,5 +125,24 @@ def main() -> None:
100
125
  on_click=dialog_input,
101
126
  )
102
127
 
128
+ async def proc_dialog() -> None:
129
+ err, result = await dialog_box.dialog_processing(
130
+ functools.partial(aproc_dummy, cnt=4), timeout=5
131
+ )
132
+ dbg(f"ERROR: {err}; Result: {result}") # //Dima
133
+ ui.notify(f"ERROR: {err}; Result: {result}")
134
+
135
+ ui.button(
136
+ "Test processing dialog",
137
+ on_click=proc_dialog,
138
+ # on_click=lambda: dialog_box.dialog_processing(proc_dummy, timeout=4),
139
+ # on_click=lambda: dialog_box.dialog_processing(
140
+ # functools.partial(aproc_dummy, cnt=5), timeout=6
141
+ # ),
142
+ # on_click=lambda: dialog_box.dialog_processing(
143
+ # proc_dummy2, proc_cancel_event=True, timeout=4
144
+ # ),
145
+ )
146
+
103
147
 
104
148
  ui.run(port=1111)
@@ -126,6 +126,11 @@ class TuyaDevs:
126
126
  # return con_devs
127
127
 
128
128
  def dev_is_connected(self, dev_id: str, timeout: int = 8, rescan: bool = False) -> bool:
129
+ """
130
+ if rescan:
131
+ self.scan(timeout=timeout)
132
+ Check if dev_id in last scan list (self.devs_online)
133
+ """
129
134
  if rescan:
130
135
  # update self.devs_online
131
136
  self.scan(timeout=timeout)
@@ -178,9 +183,10 @@ class TuyaDevs:
178
183
  return dev
179
184
 
180
185
  def outlet_dev(self, dev_id: str, local_key: str) -> Optional[OutletDevice]:
181
- """TurnOn/Off:
186
+ """TurnOn/Off & toggle:
182
187
  odev.turn_on()
183
188
  odev.turn_off()
189
+ odev.toggle()
184
190
  """
185
191
  try:
186
192
  dev = OutletDeviceM(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: difonlib
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: python libraries
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes
File without changes