gshock-api 2.0.32__tar.gz → 2.0.35__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 (55) hide show
  1. {gshock_api-2.0.32 → gshock_api-2.0.35}/PKG-INFO +11 -10
  2. {gshock_api-2.0.32 → gshock_api-2.0.35}/README.rst +7 -7
  3. {gshock_api-2.0.32 → gshock_api-2.0.35}/pyproject.toml +3 -2
  4. {gshock_api-2.0.32 → gshock_api-2.0.35}/setup.cfg +5 -4
  5. {gshock_api-2.0.32 → gshock_api-2.0.35}/setup.py +1 -1
  6. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/examples/api_tests.py +7 -8
  7. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/cancelable_result.py +1 -1
  8. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/connection.py +5 -5
  9. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/event.py +2 -1
  10. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/gshock_api.py +23 -54
  11. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/alarms_io.py +3 -2
  12. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/events_io.py +2 -2
  13. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/time_io.py +1 -1
  14. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/world_cities_io.py +2 -2
  15. gshock_api-2.0.35/src/gshock_api/logger.py +46 -0
  16. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/scanner.py +3 -4
  17. gshock_api-2.0.35/src/gshock_api/watch_info.py +248 -0
  18. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/PKG-INFO +11 -10
  19. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/requires.txt +1 -0
  20. gshock_api-2.0.32/src/gshock_api/logger.py +0 -45
  21. gshock_api-2.0.32/src/gshock_api/watch_info.py +0 -344
  22. {gshock_api-2.0.32 → gshock_api-2.0.35}/LICENSE.txt +0 -0
  23. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/examples/app_notifications_tests.py +0 -0
  24. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/examples/args.py +0 -0
  25. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/examples/gshock_server.py +0 -0
  26. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/examples/health_test.py +0 -0
  27. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/__init__.py +0 -0
  28. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/alarms.py +0 -0
  29. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/always_connected_watch_filter.py +0 -0
  30. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/app_notification.py +0 -0
  31. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/casio_constants.py +0 -0
  32. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/exceptions.py +0 -0
  33. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/__init__.py +0 -0
  34. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/app_info_io.py +0 -0
  35. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/app_notification_io.py +0 -0
  36. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/button_pressed_io.py +0 -0
  37. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/connection_protocol.py +0 -0
  38. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/dst_for_world_cities_io.py +0 -0
  39. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/dst_watch_state_io.py +0 -0
  40. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/error_io.py +0 -0
  41. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/settings_io.py +0 -0
  42. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/time_adjustement_io.py +0 -0
  43. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/timer_io.py +0 -0
  44. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/unknown_io.py +0 -0
  45. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/watch_condition_io.py +0 -0
  46. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/iolib/watch_name_io.py +0 -0
  47. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/message_dispatcher.py +0 -0
  48. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/settings.py +0 -0
  49. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api/utils.py +0 -0
  50. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/SOURCES.txt +0 -0
  51. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/dependency_links.txt +0 -0
  52. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/entry_points.txt +0 -0
  53. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/not-zip-safe +0 -0
  54. {gshock_api-2.0.32 → gshock_api-2.0.35}/src/gshock_api.egg-info/top_level.txt +0 -0
  55. {gshock_api-2.0.32 → gshock_api-2.0.35}/tests/test_code.py +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gshock_api
3
- Version: 2.0.32
4
- Summary: Pythgon API for GShock Watches using BLE
5
- Home-page: https://github.com/pyscaffold/pyscaffold/
3
+ Version: 2.0.35
4
+ Summary: Python API for GShock Watches using BLE
5
+ Home-page: https://github.com/izivkov/gshock_api/
6
6
  Author: Ivo Zivkov
7
7
  Author-email: Ivo Zivkov <izivkov@gmail.com>
8
8
  License-Expression: MIT
@@ -17,6 +17,7 @@ Requires-Dist: pydantic>=2.0.0
17
17
  Requires-Dist: types-requests
18
18
  Requires-Dist: python-dotenv
19
19
  Requires-Dist: typing-extensions>=4.12
20
+ Requires-Dist: twine>=6.2.0
20
21
  Provides-Extra: dev
21
22
  Requires-Dist: basedpyright>=1.31.7; extra == "dev"
22
23
  Requires-Dist: ruff; extra == "dev"
@@ -109,14 +110,14 @@ It is recommended that you create a virtual environment to run the tests:
109
110
 
110
111
  .. code-block:: sh
111
112
 
112
- python3 src/examples/api_tests.py [--multi-watch]
113
+ uv run src/examples/api_tests.py
113
114
 
114
- The optional **`--multi-watch`** parameter forces the library to scan for watches every time it tries to connect to a watch. If not provided,
115
- the library will try to connect to the last connected watch only. If you have multiple watches, you should use this parameter.
116
-
117
- The optional **`--multi-watch`** parameter forces the library to scan for watches every time it tries to connect to a watch. If not provided,
118
- the library will try to connect to the last connected watch only. If you have multiple watches, you should use this parameter.
115
+ or activate `venv`` first and then run using python:
119
116
 
117
+ .. code-block:: sh
118
+
119
+ source .venv/bin/activate
120
+ python src/examples/api_tests.py
120
121
 
121
122
  Installing the library for your project:
122
123
  ========================================
@@ -127,7 +128,7 @@ Installing the library for your project:
127
128
 
128
129
  See `this project <https://github.com/izivkov/GShockTimeServer>`_ using this library to run a time server for G-Shock watches.
129
130
 
130
- See also `this blog <https://digitalsober.wordpress.com/2025/05/05/g-shock-watch-integration-with-sxmo/>`_ for using the library in the `SXMO <https://sxmo.org/>`_ mobile environment.
131
+ See also `this blog <https://digitalsober.wordpress.com/2024/05/05/g-shock-watch-integration-with-sxmo/>`_ for using the library in the `SXMO <https://sxmo.org/>`_ mobile environment.
131
132
 
132
133
  Troubleshooting:
133
134
  ================
@@ -81,14 +81,14 @@ It is recommended that you create a virtual environment to run the tests:
81
81
 
82
82
  .. code-block:: sh
83
83
 
84
- python3 src/examples/api_tests.py [--multi-watch]
84
+ uv run src/examples/api_tests.py
85
85
 
86
- The optional **`--multi-watch`** parameter forces the library to scan for watches every time it tries to connect to a watch. If not provided,
87
- the library will try to connect to the last connected watch only. If you have multiple watches, you should use this parameter.
88
-
89
- The optional **`--multi-watch`** parameter forces the library to scan for watches every time it tries to connect to a watch. If not provided,
90
- the library will try to connect to the last connected watch only. If you have multiple watches, you should use this parameter.
86
+ or activate `venv`` first and then run using python:
91
87
 
88
+ .. code-block:: sh
89
+
90
+ source .venv/bin/activate
91
+ python src/examples/api_tests.py
92
92
 
93
93
  Installing the library for your project:
94
94
  ========================================
@@ -99,7 +99,7 @@ Installing the library for your project:
99
99
 
100
100
  See `this project <https://github.com/izivkov/GShockTimeServer>`_ using this library to run a time server for G-Shock watches.
101
101
 
102
- See also `this blog <https://digitalsober.wordpress.com/2025/05/05/g-shock-watch-integration-with-sxmo/>`_ for using the library in the `SXMO <https://sxmo.org/>`_ mobile environment.
102
+ See also `this blog <https://digitalsober.wordpress.com/2024/05/05/g-shock-watch-integration-with-sxmo/>`_ for using the library in the `SXMO <https://sxmo.org/>`_ mobile environment.
103
103
 
104
104
  Troubleshooting:
105
105
  ================
@@ -11,8 +11,8 @@ build-backend = "setuptools.build_meta"
11
11
 
12
12
  [project]
13
13
  name = "gshock_api" # Change to your project name
14
- version = "2.0.32"
15
- description = "Pythgon API for GShock Watches using BLE"
14
+ version = "2.0.35"
15
+ description = "Python API for GShock Watches using BLE"
16
16
  authors = [{ name = "Ivo Zivkov", email = "izivkov@gmail.com" }]
17
17
 
18
18
  license = "MIT"
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "types-requests",
26
26
  "python-dotenv",
27
27
  "typing-extensions>=4.12",
28
+ "twine>=6.2.0",
28
29
  ]
29
30
  classifiers = [
30
31
  "Development Status :: 4 - Beta",
@@ -6,10 +6,11 @@ author_email = izivkov@gmail.com
6
6
  license = MIT
7
7
  license_files = LICENSE.txt
8
8
  long_description = file: README.rst
9
- long_description_content_type = text/x-rst; charset=UTF-8
10
- url = https://github.com/pyscaffold/pyscaffold/
9
+ long_description_content_type = text/x-rst
10
+ url = https://github.com/izivkov/gshock_api/
11
11
  project_urls =
12
- Documentation = https://pyscaffold.org/
12
+ Documentation = https://github.com/izivkov/gshock_api/
13
+ Source = https://github.com/izivkov/gshock_api/
13
14
  platforms = any
14
15
  classifiers =
15
16
  Development Status :: 4 - Beta
@@ -63,7 +64,7 @@ exclude =
63
64
 
64
65
  [pyscaffold]
65
66
  version = 4.4
66
- package = gshocktimeserver
67
+ package = gshock_api
67
68
 
68
69
  [egg_info]
69
70
  tag_build =
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="gshock_api",
5
- version="2.0.31",
5
+ version="2.0.35",
6
6
  package_dir={"": "src"},
7
7
  packages=find_packages(where="src"),
8
8
  install_requires=[
@@ -2,9 +2,9 @@ import asyncio
2
2
  from collections.abc import Sequence
3
3
  from datetime import datetime
4
4
  import json
5
+ from pprint import pformat
5
6
  import sys
6
7
  import time
7
- from typing import Optional
8
8
 
9
9
  import pytz
10
10
 
@@ -58,7 +58,7 @@ async def run_api_tests(argv: Sequence[str]) -> None: # noqa: PLR0915
58
58
  await api.set_time(time.time() + 10 * 60)
59
59
 
60
60
  alarms = await api.get_alarms()
61
- logger.info(f"alarms: {alarms}")
61
+ logger.info(f"alarms: {pformat(alarms)}")
62
62
 
63
63
  alarms[3]["enabled"] = True
64
64
  alarms[3]["hour"] = 7
@@ -79,7 +79,7 @@ async def run_api_tests(argv: Sequence[str]) -> None: # noqa: PLR0915
79
79
  logger.info(f"condition: {condition}")
80
80
 
81
81
  settings_local = await api.get_basic_settings()
82
- logger.info(f"settings: {settings_local}")
82
+ logger.info(f"settings: {pformat(settings_local)}")
83
83
 
84
84
  settings_local["button_tone"] = True
85
85
  settings_local["language"] = "Russian"
@@ -111,11 +111,11 @@ async def run_api_tests(argv: Sequence[str]) -> None: # noqa: PLR0915
111
111
  + """}}"""
112
112
  )
113
113
  Event().create_event(json.loads(event_json_str))
114
- logger.info(f"Created event: {event_json_str}")
114
+ logger.info(f"Created event: {pformat(json.loads(event_json_str))}")
115
115
 
116
116
  reminders = await api.get_reminders()
117
117
  for reminder in reminders:
118
- logger.info(f"reminder: {reminder}")
118
+ logger.info (f"reminder: {pformat(reminder)}")
119
119
 
120
120
  reminders[3]["title"] = "Test Event"
121
121
 
@@ -204,11 +204,10 @@ async def app_notifications(api: GshockAPI) -> None:
204
204
  await api.send_app_notification(email_notification2)
205
205
 
206
206
 
207
- def convert_time_string_to_epoch(time_string: str) -> Optional[float]:
207
+ def convert_time_string_to_epoch(time_string: str) -> float | None:
208
208
  try:
209
209
  time_object = datetime.strptime(time_string, "%H:%M:%S")
210
- timestamp = time_object.timestamp()
211
- return timestamp
210
+ return time_object.timestamp()
212
211
  except ValueError:
213
212
  logger.info("Invalid time format. Please use the format HH:MM:SS.")
214
213
  return None
@@ -37,4 +37,4 @@ class CancelableResult(Generic[T]): # noqa: UP046
37
37
 
38
38
  def set_result(self, value: T) -> None:
39
39
  if not self._future.done():
40
- self._future.set_result(value)
40
+ self._future.set_result(value)
@@ -100,8 +100,7 @@ class Connection:
100
100
  def is_service_supported(self, handle: int) -> bool:
101
101
  """Checks if a characteristic UUID mapped to a handle is present in the discovered characteristics."""
102
102
  uuid: str | None = self.handles_map.get(handle)
103
-
104
- return uuid not in self.characteristics_map
103
+ return uuid is not None and uuid in self.characteristics_map
105
104
 
106
105
  async def write(self, handle: int, data: bytes) -> None:
107
106
  """Writes data to a characteristic identified by its handle."""
@@ -118,13 +117,14 @@ class Connection:
118
117
  )
119
118
  return
120
119
 
121
- responseType: bool = handle == 0x0E # noqa: N806, PLR2004
120
+ # 0x0E is CASIO_ALL_FEATURES_CHARACTERISTIC_UUID (requires response)
121
+ response_type: bool = handle == 0x0E
122
122
 
123
123
  cmd_data: bytes = to_casio_cmd(data)
124
124
 
125
125
  if self.client:
126
126
  await self.client.write_gatt_char(
127
- uuid, cmd_data, response=responseType
127
+ uuid, cmd_data, response=response_type
128
128
  )
129
129
 
130
130
  except Exception as e:
@@ -155,6 +155,6 @@ class Connection:
155
155
  return handles_map
156
156
 
157
157
  # Replaced Any with TypeVar T
158
- async def sendMessage(self, message: T) -> None: # noqa: N802
158
+ async def send_message(self, message: T) -> None:
159
159
  """Sends a message to the watch using the message dispatcher."""
160
160
  await message_dispatcher.MessageDispatcher.send_to_watch(message)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations # Enables forward references like 'Event'
2
2
 
3
3
  from dataclasses import dataclass
4
- from datetime import date, datetime, tzinfo
4
+ from datetime import datetime
5
5
  import json
6
6
  import types
7
7
  from typing import TYPE_CHECKING, Final, TypeVar
@@ -10,6 +10,7 @@ if TYPE_CHECKING:
10
10
  from collections.abc import ( # Using Mapping for dict types where object values are complex
11
11
  Mapping,
12
12
  )
13
+ from datetime import date, tzinfo
13
14
 
14
15
  # Define a Type Variable T for generic message objects
15
16
  T = TypeVar("T")
@@ -54,23 +54,23 @@ class GshockAPI:
54
54
  result: WatchButton = await message_dispatcher.ButtonPressedIO.request(self.connection)
55
55
  return result
56
56
 
57
- async def get_world_cities(self, cityNumber: int) -> str: # noqa: N803
57
+ async def get_world_cities(self, city_number: int) -> str:
58
58
  """Get the name for a particular World City set on the watch."""
59
- return await self._get_world_cities(cityNumber)
59
+ return await self._get_world_cities(city_number)
60
60
 
61
- async def _get_world_cities(self, key: int) -> str:
61
+ async def _get_world_cities(self, city_number: int) -> str:
62
62
  # Assuming WorldCitiesIO.request returns a string
63
- result: str = await message_dispatcher.WorldCitiesIO.request(self.connection, key)
63
+ result: str = await message_dispatcher.WorldCitiesIO.request(self.connection, city_number)
64
64
  return result
65
65
 
66
- async def get_dst_for_world_cities(self, cityNumber: int) -> str: # noqa: N803
66
+ async def get_dst_for_world_cities(self, city_number: int) -> str:
67
67
  """Get the **Daylight Saving Time** for a particular World City set on the watch."""
68
- return await self._get_dst_for_world_cities(cityNumber)
68
+ return await self._get_dst_for_world_cities(city_number)
69
69
 
70
- async def _get_dst_for_world_cities(self, key: int) -> str:
70
+ async def _get_dst_for_world_cities(self, city_number: int) -> str:
71
71
  # Assuming DstForWorldCitiesIO.request returns a string
72
72
  result: str = await message_dispatcher.DstForWorldCitiesIO.request(
73
- self.connection, key
73
+ self.connection, city_number
74
74
  )
75
75
  return result
76
76
 
@@ -124,38 +124,16 @@ class GshockAPI:
124
124
  await self.read_and_write(function, state)
125
125
 
126
126
  async def read_write_dst_for_world_cities(self) -> None:
127
- # Use dict instead of generic Map/Any
128
- array_of_get_dst_for_world_cities: list[dict[str, RequestFunction | int]] = [ # noqa: F821 # type: ignore
129
- {"function": self.get_dst_for_world_cities, "city_number": 0},
130
- {"function": self.get_dst_for_world_cities, "city_number": 1},
131
- {"function": self.get_dst_for_world_cities, "city_number": 2},
132
- {"function": self.get_dst_for_world_cities, "city_number": 3},
133
- {"function": self.get_dst_for_world_cities, "city_number": 4},
134
- {"function": self.get_dst_for_world_cities, "city_number": 5},
135
- ]
127
+ fn = self.get_dst_for_world_cities
136
128
 
137
- # Type inference for item and its keys
138
- for item in array_of_get_dst_for_world_cities[: watch_info.worldCitiesCount]:
139
- function: RequestFunction = item["function"] # type: ignore[assignment] # noqa: F821
140
- city_number: int = item["city_number"] # type: ignore[assignment]
141
- await self.read_and_write(function, city_number)
129
+ for city_number in range(watch_info.worldCitiesCount):
130
+ await self.read_and_write(fn, city_number)
142
131
 
143
132
  async def read_write_world_cities(self) -> None:
144
- # Use dict instead of generic Map/Any
145
- array_of_world_cities: list[dict[str, RequestFunction | int]] = [ # noqa: F821 # type: ignore
146
- {"function": self.get_world_cities, "city_number": 0},
147
- {"function": self.get_world_cities, "city_number": 1},
148
- {"function": self.get_world_cities, "city_number": 2},
149
- {"function": self.get_world_cities, "city_number": 3},
150
- {"function": self.get_world_cities, "city_number": 4},
151
- {"function": self.get_world_cities, "city_number": 5},
152
- ]
133
+ fn = self.get_world_cities
153
134
 
154
- # Type inference for item and its keys
155
- for item in array_of_world_cities[: watch_info.worldCitiesCount]:
156
- function: RequestFunction = item["function"] # noqa: F821 # type: ignore
157
- city_number: int = item["city_number"] # type: ignore[assignment]
158
- await self.read_and_write(function, city_number)
135
+ for city_number in range(watch_info.worldCitiesCount):
136
+ await self.read_and_write(fn, city_number)
159
137
 
160
138
  # current_time is unknown type, using object | None
161
139
  async def set_time(
@@ -191,8 +169,8 @@ class GshockAPI:
191
169
  # Assuming T objects are JSON serializable
192
170
  alarms_str: str = json.dumps(alarms)
193
171
  set_action_cmd: str = f'{{"action":"SET_ALARMS", "value":{alarms_str} }}'
194
- # connection.sendMessage expects T, which must be convertible to a watch message
195
- await self.connection.sendMessage(set_action_cmd)
172
+ # connection.send_message expects T, which must be convertible to a watch message
173
+ await self.connection.send_message(set_action_cmd)
196
174
 
197
175
  async def get_timer(self) -> int:
198
176
  """Get Timer value in seconds."""
@@ -203,10 +181,10 @@ class GshockAPI:
203
181
  result: int = await message_dispatcher.TimerIO.request(self.connection)
204
182
  return result
205
183
 
206
- async def set_timer(self, timerValue: int) -> None: # noqa: N803
184
+ async def set_timer(self, timer_value: int) -> None:
207
185
  """Set Timer value in seconds."""
208
- message: str = f'{{"action": "SET_TIMER", "value": {timerValue} }}'
209
- await self.connection.sendMessage(message)
186
+ message: str = f'{{"action": "SET_TIMER", "value": {timer_value} }}'
187
+ await self.connection.send_message(message)
210
188
 
211
189
  # Watch condition request returns an unknown object (object)
212
190
  async def get_watch_condition(self) -> object:
@@ -224,7 +202,7 @@ class GshockAPI:
224
202
  ) -> None:
225
203
  """Sets auto-tame adjustment for the watch"""
226
204
  message: str = f"""{{"action": "SET_TIME_ADJUSTMENT", "timeAdjustment": "{time_adjustement}", "minutesAfterHour": "{minutes_after_hour}" }}"""
227
- await self.connection.sendMessage(message)
205
+ await self.connection.send_message(message)
228
206
 
229
207
  # SettingsIO returns a list of unknown Setting objects (list[T])
230
208
  async def get_basic_settings(self) -> list[T]:
@@ -237,20 +215,11 @@ class GshockAPI:
237
215
  """Set settings to the watch."""
238
216
  setting_json: str = json.dumps(settings)
239
217
  message: str = f'{{"action": "SET_SETTINGS", "value": {setting_json} }}'
240
- await self.connection.sendMessage(message)
218
+ await self.connection.send_message(message)
241
219
 
242
220
  # get_reminders returns a list of unknown Event objects (list[T])
243
221
  async def get_reminders(self) -> list[T]:
244
- """Gets the current reminders (events) from the watch."""
245
- reminders: list[T] = [] # type: ignore[assignment]
246
-
247
- reminders.append(await self.get_event_from_watch(1)) # type: ignore[arg-type]
248
- reminders.append(await self.get_event_from_watch(2)) # type: ignore[arg-type]
249
- reminders.append(await self.get_event_from_watch(3)) # type: ignore[arg-type]
250
- reminders.append(await self.get_event_from_watch(4)) # type: ignore[arg-type]
251
- reminders.append(await self.get_event_from_watch(5)) # type: ignore[arg-type]
252
-
253
- return reminders
222
+ return [await self.get_event_from_watch(i) for i in range(1, 6)]
254
223
 
255
224
  # get_event_from_watch returns an unknown Event object (T)
256
225
  async def get_event_from_watch(self, event_number: int) -> T:
@@ -284,7 +253,7 @@ class GshockAPI:
284
253
  events_as_json: list[Mapping[str, object]] = to_json(events)
285
254
  enabled: list[Mapping[str, object]] = get_enabled_events(events_as_json)
286
255
 
287
- await self.connection.sendMessage(
256
+ await self.connection.send_message(
288
257
  f"""{{"action": "SET_REMINDERS", "value": {json.dumps(enabled)}}}"""
289
258
  )
290
259
 
@@ -45,12 +45,13 @@ class AlarmsIO:
45
45
  AlarmsIO.connection = connection
46
46
  alarms_inst_typed.clear()
47
47
  await AlarmsIO._get_alarms(connection)
48
- assert AlarmsIO.result is not None # noqa: S101
48
+ if AlarmsIO.result is None:
49
+ raise RuntimeError("AlarmsIO.result must not be None after _get_alarms")
49
50
  return AlarmsIO.result
50
51
 
51
52
  @staticmethod
52
53
  async def _get_alarms(connection: ConnectionProtocol) -> CancelableResult[list[dict[str, object]]]:
53
- await connection.sendMessage('{ "action": "GET_ALARMS"}')
54
+ await connection.send_message('{ "action": "GET_ALARMS"}')
54
55
  AlarmsIO.result = CancelableResult[list[dict[str, object]]]()
55
56
  return await AlarmsIO.result.get_result()
56
57
 
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import List, TypedDict # noqa: UP035
2
+ from typing import TypedDict
3
3
 
4
4
  from gshock_api.cancelable_result import CancelableResult
5
5
  from gshock_api.casio_constants import CasioConstants
@@ -45,7 +45,7 @@ class ReminderTimeDict(TypedDict):
45
45
  repeat_period: str
46
46
  start_date: DateDict
47
47
  end_date: DateDict
48
- days_of_week: List[str]
48
+ days_of_week: list[str]
49
49
 
50
50
 
51
51
  class EventsIO:
@@ -27,7 +27,7 @@ class TimeIO:
27
27
  "offset": offset, # must always be an integer
28
28
  },
29
29
  }
30
- await connection.sendMessage(json.dumps(message))
30
+ await connection.send_message(json.dumps(message))
31
31
 
32
32
  @staticmethod
33
33
  async def send_to_watch_set(message: str) -> None:
@@ -10,9 +10,9 @@ class WorldCitiesIO:
10
10
  connection: ConnectionProtocol | None = None
11
11
 
12
12
  @staticmethod
13
- async def request(connection: ConnectionProtocol, cityNumber: int) -> CancelableResult[bytes]:
13
+ async def request(connection: ConnectionProtocol, city_number: int) -> CancelableResult[bytes]:
14
14
  WorldCitiesIO.connection = connection
15
- key = f"1f0{cityNumber}"
15
+ key = f"1f0{city_number}"
16
16
  await connection.request(key)
17
17
 
18
18
  WorldCitiesIO.result = CancelableResult[bytes]()
@@ -0,0 +1,46 @@
1
+ import logging
2
+ from typing import Final
3
+
4
+ LogLevel: Final[int] = int
5
+
6
+ _logger: logging.Logger = logging.getLogger(__name__)
7
+
8
+
9
+ class Logger:
10
+ """
11
+ A simple wrapper around the standard Python logging module for consistent configuration.
12
+ """
13
+ DEFAULT_LOG_LEVEL: Final[LogLevel] = logging.INFO
14
+
15
+ def __init__(self, log_level: LogLevel = DEFAULT_LOG_LEVEL) -> None:
16
+ self.log_level = log_level
17
+
18
+ logging.basicConfig(
19
+ level=self.log_level,
20
+ handlers=[logging.StreamHandler()],
21
+ format="%(asctime)-15s %(name)-15s %(levelname)s: %(message)s",
22
+ )
23
+
24
+ def _join(self, *args: object) -> str:
25
+ """Join args like print() does."""
26
+ return " ".join(str(a) for a in args)
27
+
28
+ # Logging methods -----------
29
+
30
+ def error(self, *args: object) -> None:
31
+ _logger.error(self._join(*args))
32
+
33
+ def info(self, *args: object) -> None:
34
+ _logger.info(self._join(*args))
35
+
36
+ def debug(self, *args: object) -> None:
37
+ _logger.debug(self._join(*args))
38
+
39
+ def warn(self, *args: object) -> None:
40
+ _logger.warning(self._join(*args))
41
+
42
+ def warning(self, *args: object) -> None:
43
+ _logger.warning(self._join(*args))
44
+
45
+
46
+ logger: Logger = Logger()
@@ -24,10 +24,10 @@ MAX_SCAN_RETRIES: Final[int] = 60
24
24
  # --- Type Aliases ---
25
25
 
26
26
  # WatchFilter is a function that takes a BLEDevice name (str) and returns a boolean.
27
- WatchFilter: Final[Callable[[str], bool]] = Callable[[str], bool]
27
+ type WatchFilter = Callable[[str], bool] | None
28
28
 
29
29
  # Type for the filter used in find_device_by_filter (takes device and ad data, returns bool)
30
- BleakDeviceFilter: Final[Callable[[BLEDevice, AdvertisementData], bool]] = Callable[[BLEDevice, AdvertisementData], bool]
30
+ type BleakDeviceFilter = Callable[[BLEDevice, AdvertisementData], bool]
31
31
 
32
32
 
33
33
  class Scanner:
@@ -38,8 +38,7 @@ class Scanner:
38
38
  async def scan(
39
39
  self,
40
40
  device_address: str | None = None,
41
- # Replaced the untyped watch_filter=None with a properly typed Callable or None
42
- watch_filter: WatchFilter | None = None, # type: ignore
41
+ watch_filter: WatchFilter = None,
43
42
  max_retries: int = MAX_SCAN_RETRIES
44
43
  ) -> BLEDevice | None:
45
44