gshock-api 2.0.5__tar.gz → 2.0.7__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.
- {gshock_api-2.0.5/src/gshock_api.egg-info → gshock_api-2.0.7}/PKG-INFO +1 -1
- {gshock_api-2.0.5 → gshock_api-2.0.7}/pyproject.toml +3 -2
- {gshock_api-2.0.5 → gshock_api-2.0.7}/setup.py +1 -1
- gshock_api-2.0.7/src/gshock_api/cancelable_result.py +23 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/connection.py +13 -11
- gshock_api-2.0.7/src/gshock_api/exceptions.py +9 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/gshock_api.py +1 -3
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/alarms_io.py +2 -5
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/app_info_io.py +2 -6
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/button_pressed_io.py +2 -8
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/dst_for_world_cities_io.py +2 -5
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/dst_watch_state_io.py +3 -5
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/events_io.py +2 -7
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/settings_io.py +2 -7
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/time_adjustement_io.py +3 -8
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/time_io.py +9 -5
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/timer_io.py +3 -8
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/watch_condition_io.py +2 -9
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/watch_name_io.py +2 -3
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/world_cities_io.py +2 -5
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/message_dispatcher.py +0 -1
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/scanner.py +0 -2
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/watch_info.py +0 -2
- {gshock_api-2.0.5 → gshock_api-2.0.7/src/gshock_api.egg-info}/PKG-INFO +1 -1
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api.egg-info/SOURCES.txt +1 -0
- gshock_api-2.0.5/src/gshock_api/cancelable_result.py +0 -38
- {gshock_api-2.0.5 → gshock_api-2.0.7}/LICENSE.txt +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/README.rst +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/setup.cfg +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/__init__.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/alarms.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/app_notification.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/casio_constants.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/configurator.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/event.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/__init__.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/app_notification_io.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/error_io.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/iolib/unknown_io.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/logger.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/mailsener.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/settings.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api/utils.py +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api.egg-info/dependency_links.txt +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api.egg-info/not-zip-safe +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api.egg-info/requires.txt +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/src/gshock_api.egg-info/top_level.txt +0 -0
- {gshock_api-2.0.5 → gshock_api-2.0.7}/tests/test_code.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools", "wheel"]
|
|
2
|
+
# requires = ["setuptools", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
|
+
requires = ["setuptools>=61.0"]
|
|
4
5
|
|
|
5
6
|
[project]
|
|
6
7
|
name = "gshock_api" # Change to your project name
|
|
7
|
-
version = "2.0.
|
|
8
|
+
version = "2.0.7"
|
|
8
9
|
description = "Pythgon API for GShock Watches using BLE"
|
|
9
10
|
readme = "README.rst"
|
|
10
11
|
authors = [{ name = "Ivo Zivkov", email = "izivkov@gmail.com" }]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any
|
|
3
|
+
from gshock_api.exceptions import GShockConnectionError
|
|
4
|
+
|
|
5
|
+
class CancelableResult:
|
|
6
|
+
def __init__(self, timeout: float = 10.0):
|
|
7
|
+
self._timeout = timeout
|
|
8
|
+
self._future: asyncio.Future[Any] = asyncio.Future()
|
|
9
|
+
|
|
10
|
+
async def get_result(self) -> Any:
|
|
11
|
+
try:
|
|
12
|
+
result = await asyncio.wait_for(self._future, timeout=self._timeout)
|
|
13
|
+
return result
|
|
14
|
+
except asyncio.TimeoutError as e:
|
|
15
|
+
if not self._future.done():
|
|
16
|
+
self._future.set_result('') # or raise an exception instead
|
|
17
|
+
raise GShockConnectionError(f"Timeout occured waiting for response from the watch: {e}") from e
|
|
18
|
+
|
|
19
|
+
def set_result(self, result: Any) -> None:
|
|
20
|
+
if not self._future.done():
|
|
21
|
+
self._future.set_result(result)
|
|
22
|
+
|
|
23
|
+
cancelable_result = CancelableResult()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import asyncio
|
|
1
|
+
import asyncio, time
|
|
2
|
+
from tracemalloc import start
|
|
2
3
|
from bleak import BleakClient
|
|
3
4
|
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
4
5
|
from gshock_api.casio_constants import CasioConstants
|
|
@@ -8,6 +9,7 @@ from gshock_api.logger import logger
|
|
|
8
9
|
from gshock_api.watch_info import watch_info
|
|
9
10
|
from bleak.backends.device import BLEDevice
|
|
10
11
|
from gshock_api.scanner import scanner
|
|
12
|
+
from gshock_api.exceptions import GShockConnectionError
|
|
11
13
|
|
|
12
14
|
class Connection:
|
|
13
15
|
def __init__(self, address = None):
|
|
@@ -25,7 +27,7 @@ class Connection:
|
|
|
25
27
|
"""
|
|
26
28
|
Prints all services and characteristics of the connected BLE device.
|
|
27
29
|
"""
|
|
28
|
-
services =
|
|
30
|
+
services = self.client.services
|
|
29
31
|
for service in services:
|
|
30
32
|
for char in service.characteristics:
|
|
31
33
|
self.characteristics_map[char.uuid] = char.uuid # Store in map
|
|
@@ -35,7 +37,6 @@ class Connection:
|
|
|
35
37
|
# Scan for device if address not provided
|
|
36
38
|
device = await scanner.scan(device_address=self.address if self.address else None)
|
|
37
39
|
self.address = device.address # Always update with actual device found
|
|
38
|
-
logger.info(f"Found device: {device.name} ({self.address})")
|
|
39
40
|
|
|
40
41
|
self.client = BleakClient(self.address)
|
|
41
42
|
await self.client.connect()
|
|
@@ -50,7 +51,7 @@ class Connection:
|
|
|
50
51
|
return True
|
|
51
52
|
|
|
52
53
|
except Exception as e:
|
|
53
|
-
logger.
|
|
54
|
+
logger.info(f"[GShock Connect] Connection failed: {e}")
|
|
54
55
|
return False
|
|
55
56
|
|
|
56
57
|
async def disconnect(self):
|
|
@@ -59,30 +60,31 @@ class Connection:
|
|
|
59
60
|
def is_service_supported(self, handle):
|
|
60
61
|
uuid = self.handles_map.get(handle)
|
|
61
62
|
supported = (uuid not in self.characteristics_map)
|
|
62
|
-
print(f"write, service with handle {handle} is supported: {supported}")
|
|
63
63
|
return supported
|
|
64
64
|
|
|
65
65
|
async def write(self, handle, data):
|
|
66
66
|
try:
|
|
67
67
|
uuid = self.handles_map.get(handle)
|
|
68
|
+
|
|
68
69
|
if (uuid not in self.characteristics_map):
|
|
69
|
-
logger.
|
|
70
|
+
logger.info(
|
|
70
71
|
"write failed: handle {} not in characteristics map".format(handle)
|
|
71
72
|
)
|
|
72
73
|
if (handle == 13):
|
|
73
|
-
logger.
|
|
74
|
-
"Your watch does not
|
|
74
|
+
logger.info(
|
|
75
|
+
"Your watch does not support notifications..."
|
|
75
76
|
)
|
|
76
77
|
return
|
|
77
|
-
|
|
78
|
+
|
|
78
79
|
await self.client.write_gatt_char(
|
|
79
80
|
uuid, to_casio_cmd(data)
|
|
80
81
|
)
|
|
82
|
+
|
|
81
83
|
except Exception as e:
|
|
82
|
-
|
|
84
|
+
print("Exception type:", type(e))
|
|
85
|
+
raise GShockConnectionError(f"Unable to write to G-Shock device: {e}") from e
|
|
83
86
|
|
|
84
87
|
async def request(self, request):
|
|
85
|
-
logger.info("write: {}".format(request))
|
|
86
88
|
await self.write(0xC, request)
|
|
87
89
|
|
|
88
90
|
def init_handles_map(self):
|
|
@@ -205,8 +205,6 @@ class GshockAPI:
|
|
|
205
205
|
if current_time == None:
|
|
206
206
|
current_time = time.time()
|
|
207
207
|
|
|
208
|
-
self.logger.info(f"=======> passed: ${current_time}, ${time.time()}")
|
|
209
|
-
|
|
210
208
|
await self.initialize_for_setting_time()
|
|
211
209
|
await self._set_time(current_time)
|
|
212
210
|
current_time = None
|
|
@@ -231,7 +229,7 @@ class GshockAPI:
|
|
|
231
229
|
|
|
232
230
|
async def _get_alarms(self):
|
|
233
231
|
result = await message_dispatcher.AlarmsIO.request(self.connection)
|
|
234
|
-
return
|
|
232
|
+
return result
|
|
235
233
|
|
|
236
234
|
async def set_alarms(self, alarms):
|
|
237
235
|
"""Sets alarms to the watch. Up to 5 alarms are supported on the watch.
|
|
@@ -9,14 +9,12 @@ from gshock_api.casio_constants import CasioConstants
|
|
|
9
9
|
|
|
10
10
|
CHARACTERISTICS = CasioConstants.CHARACTERISTICS
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
class AlarmsIO:
|
|
14
13
|
result: CancelableResult = None
|
|
15
14
|
connection = None
|
|
16
15
|
|
|
17
16
|
@staticmethod
|
|
18
17
|
async def request(connection):
|
|
19
|
-
await connection.request("GET_ALARMS")
|
|
20
18
|
AlarmsIO.connection = connection
|
|
21
19
|
|
|
22
20
|
alarms_inst.clear()
|
|
@@ -27,9 +25,8 @@ class AlarmsIO:
|
|
|
27
25
|
async def _get_alarms(connection):
|
|
28
26
|
await connection.sendMessage("""{ "action": "GET_ALARMS"}""")
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
AlarmsIO.result
|
|
32
|
-
return AlarmsIO.result
|
|
28
|
+
AlarmsIO.result = CancelableResult()
|
|
29
|
+
return await AlarmsIO.result.get_result()
|
|
33
30
|
|
|
34
31
|
@staticmethod
|
|
35
32
|
async def send_to_watch(message=""):
|
|
@@ -15,13 +15,11 @@ class AppInfoIO:
|
|
|
15
15
|
|
|
16
16
|
@staticmethod
|
|
17
17
|
async def request(connection):
|
|
18
|
-
logger.info(f"AppInfoIO request")
|
|
19
18
|
AppInfoIO.connection = connection
|
|
20
19
|
await connection.request("22")
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
AppInfoIO.result
|
|
24
|
-
return AppInfoIO.result
|
|
21
|
+
AppInfoIO.result = CancelableResult()
|
|
22
|
+
return AppInfoIO.result.get_result()
|
|
25
23
|
|
|
26
24
|
@staticmethod
|
|
27
25
|
async def send_to_watch(connection):
|
|
@@ -29,8 +27,6 @@ class AppInfoIO:
|
|
|
29
27
|
|
|
30
28
|
@staticmethod
|
|
31
29
|
def on_received(data):
|
|
32
|
-
logger.info(f"AppInfoIO onReceived")
|
|
33
|
-
|
|
34
30
|
def set_app_info(data: str):
|
|
35
31
|
# App info:
|
|
36
32
|
# This is needed to re-enable button D (Lower-right) after the watch has been reset or BLE has been cleared.
|
|
@@ -26,9 +26,8 @@ class ButtonPressedIO:
|
|
|
26
26
|
ButtonPressedIO.connection = connection
|
|
27
27
|
await connection.request("10")
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
ButtonPressedIO.result
|
|
31
|
-
return ButtonPressedIO.result
|
|
29
|
+
ButtonPressedIO.result = CancelableResult()
|
|
30
|
+
return ButtonPressedIO.result.get_result()
|
|
32
31
|
|
|
33
32
|
@staticmethod
|
|
34
33
|
async def send_to_watch(connection):
|
|
@@ -36,14 +35,10 @@ class ButtonPressedIO:
|
|
|
36
35
|
|
|
37
36
|
@staticmethod
|
|
38
37
|
async def send_to_watch_set(data):
|
|
39
|
-
logger.info(f"TimerIO sendToWatchSet: {data}")
|
|
40
|
-
|
|
41
38
|
await ButtonPressedIO.connection.write(0x000E, data)
|
|
42
39
|
|
|
43
40
|
@staticmethod
|
|
44
41
|
def on_received(data):
|
|
45
|
-
logger.info(f"ButtonPressedIO onReceived")
|
|
46
|
-
|
|
47
42
|
def button_pressed_callback(data):
|
|
48
43
|
"""
|
|
49
44
|
RIGHT BUTTON: 0x10 17 62 07 38 85 CD 7F ->04<- 03 0F FF FF FF FF 24 00 00 00
|
|
@@ -57,7 +52,6 @@ class ButtonPressedIO:
|
|
|
57
52
|
if len(data) >= 19:
|
|
58
53
|
ble_int_arr = to_int_array(to_hex_string(data))
|
|
59
54
|
button_indicator = ble_int_arr[8]
|
|
60
|
-
logger.info(f"Buttom code pressed: ${button_indicator}")
|
|
61
55
|
ret = (
|
|
62
56
|
WatchButton.LOWER_LEFT
|
|
63
57
|
if (button_indicator == 0 or button_indicator == 1)
|
|
@@ -14,14 +14,12 @@ class DstForWorldCitiesIO:
|
|
|
14
14
|
|
|
15
15
|
@staticmethod
|
|
16
16
|
async def request(connection, city_number: int):
|
|
17
|
-
logger.info(f"DstForWorldCitiesIO request")
|
|
18
17
|
DstForWorldCitiesIO.connection = connection
|
|
19
18
|
key = "1e0{}".format(city_number)
|
|
20
19
|
await connection.request(key)
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
DstForWorldCitiesIO.result
|
|
24
|
-
return DstForWorldCitiesIO.result
|
|
21
|
+
DstForWorldCitiesIO.result = CancelableResult()
|
|
22
|
+
return DstForWorldCitiesIO.result.get_result()
|
|
25
23
|
|
|
26
24
|
@staticmethod
|
|
27
25
|
async def send_to_watch(connection):
|
|
@@ -29,5 +27,4 @@ class DstForWorldCitiesIO:
|
|
|
29
27
|
|
|
30
28
|
@staticmethod
|
|
31
29
|
def on_received(data):
|
|
32
|
-
logger.info(f"DstForWorldCitiesIO onReceived")
|
|
33
30
|
DstForWorldCitiesIO.result.set_result(data)
|
|
@@ -20,14 +20,13 @@ class DstWatchStateIO:
|
|
|
20
20
|
|
|
21
21
|
@staticmethod
|
|
22
22
|
async def request(connection, state: DtsState):
|
|
23
|
-
logger.info(f"DstWatchStateIO request")
|
|
24
23
|
DstWatchStateIO.connection = connection
|
|
25
24
|
key = f"1d0{state.value}"
|
|
26
25
|
await connection.request(key)
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
DstWatchStateIO.result
|
|
30
|
-
|
|
27
|
+
DstWatchStateIO.result = CancelableResult()
|
|
28
|
+
return DstWatchStateIO.result.get_result()
|
|
29
|
+
|
|
31
30
|
|
|
32
31
|
@staticmethod
|
|
33
32
|
async def send_to_watch(connection):
|
|
@@ -35,5 +34,4 @@ class DstWatchStateIO:
|
|
|
35
34
|
|
|
36
35
|
@staticmethod
|
|
37
36
|
def on_received(data):
|
|
38
|
-
logger.info(f"DstWatchStateIO onReceived")
|
|
39
37
|
DstWatchStateIO.result.set_result(data)
|
|
@@ -41,20 +41,16 @@ class EventsIO:
|
|
|
41
41
|
|
|
42
42
|
@staticmethod
|
|
43
43
|
async def request(connection, event_number):
|
|
44
|
-
logger.info(f"EventsIO request")
|
|
45
44
|
EventsIO.connection = connection
|
|
46
45
|
|
|
47
46
|
await connection.request("30{}".format(event_number)) # reminder title
|
|
48
47
|
await connection.request("31{}".format(event_number)) # reminder time
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
EventsIO.result
|
|
52
|
-
return EventsIO.result
|
|
49
|
+
EventsIO.result = CancelableResult()
|
|
50
|
+
return EventsIO.result.get_result()
|
|
53
51
|
|
|
54
52
|
@staticmethod
|
|
55
53
|
async def send_to_watch_set(message):
|
|
56
|
-
logger.info(f"EventsIO sendToWatchSet: {message}")
|
|
57
|
-
|
|
58
54
|
def reminder_title_from_json(reminder_json):
|
|
59
55
|
title_str = reminder_json.get("title")
|
|
60
56
|
return to_byte_array(title_str, 18)
|
|
@@ -342,7 +338,6 @@ class EventsIO:
|
|
|
342
338
|
|
|
343
339
|
reminder_json = json.loads(reminder_time_to_json(data[2:]))
|
|
344
340
|
reminder_json.update(EventsIO.title)
|
|
345
|
-
logger.info(reminder_json)
|
|
346
341
|
EventsIO.result.set_result(reminder_json)
|
|
347
342
|
|
|
348
343
|
@staticmethod
|
|
@@ -16,25 +16,20 @@ class SettingsIO:
|
|
|
16
16
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
async def request(connection):
|
|
19
|
-
logger.info(f"TimerIO request")
|
|
20
19
|
SettingsIO.connection = connection
|
|
21
20
|
await connection.request("13")
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
SettingsIO.result
|
|
25
|
-
return SettingsIO.result
|
|
22
|
+
SettingsIO.result = CancelableResult()
|
|
23
|
+
return SettingsIO.result.get_result()
|
|
26
24
|
|
|
27
25
|
@staticmethod
|
|
28
26
|
def send_to_watch(message):
|
|
29
|
-
logger.info(f"SettingsIO sendToWatch: {message}")
|
|
30
27
|
SettingsIO.connection.write(
|
|
31
28
|
0x000C, bytearray([CHARACTERISTICS["CASIO_SETTING_FOR_BASIC"]])
|
|
32
29
|
)
|
|
33
30
|
|
|
34
31
|
@staticmethod
|
|
35
32
|
async def send_to_watch_set(message):
|
|
36
|
-
logger.info(f"SettingsIO sendToWatchSet: {message}")
|
|
37
|
-
|
|
38
33
|
def encode(settings):
|
|
39
34
|
mask_24_hours = 0b00000001
|
|
40
35
|
MASK_BUTTON_TONE_OFF = 0b00000010
|
|
@@ -20,27 +20,23 @@ class TimeAdjustmentIO:
|
|
|
20
20
|
|
|
21
21
|
@staticmethod
|
|
22
22
|
async def request(connection):
|
|
23
|
-
logger.info(f"TimerIO request")
|
|
24
23
|
TimeAdjustmentIO.connection = connection
|
|
25
24
|
await connection.request("11")
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
TimeAdjustmentIO.result
|
|
29
|
-
|
|
26
|
+
TimeAdjustmentIO.result = CancelableResult()
|
|
27
|
+
return TimeAdjustmentIO.result.get_result()
|
|
28
|
+
|
|
30
29
|
|
|
31
30
|
@staticmethod
|
|
32
31
|
def send_to_watch(message):
|
|
33
|
-
logger.info(f"TimeAdjustmentIO sendToWatch: {message}")
|
|
34
32
|
TimeAdjustmentIO.connection.write(
|
|
35
33
|
0x000C, bytearray([CHARACTERISTICS["TIME_ADJUSTMENT"]])
|
|
36
34
|
)
|
|
37
35
|
|
|
38
36
|
@staticmethod
|
|
39
37
|
async def send_to_watch_set(message):
|
|
40
|
-
logger.info(f"TimeAdjustmentIO sendToWatchSet: {message}")
|
|
41
38
|
|
|
42
39
|
if TimeAdjustmentIO.original_value == None:
|
|
43
|
-
logging.error("Error: Must call get before set")
|
|
44
40
|
return ErrorIO.request("Error: Must call get before set")
|
|
45
41
|
|
|
46
42
|
time_adjustment = json.loads(message).get("timeAdjustment") == "True"
|
|
@@ -61,7 +57,6 @@ class TimeAdjustmentIO:
|
|
|
61
57
|
|
|
62
58
|
@staticmethod
|
|
63
59
|
def on_received(message):
|
|
64
|
-
logger.info(f"TimeAdjustmentIO onReceived: {message}")
|
|
65
60
|
TimeAdjustmentIO.original_value = to_hex_string(
|
|
66
61
|
message
|
|
67
62
|
) # save original message
|
|
@@ -3,17 +3,16 @@ import datetime
|
|
|
3
3
|
import json
|
|
4
4
|
import time
|
|
5
5
|
from typing import Any
|
|
6
|
-
from gshock_api.cancelable_result import CancelableResult
|
|
7
6
|
from gshock_api.logger import logger
|
|
8
7
|
|
|
9
8
|
from gshock_api.utils import to_compact_string, to_hex_string
|
|
10
9
|
from gshock_api.casio_constants import CasioConstants
|
|
10
|
+
import bleak
|
|
11
11
|
|
|
12
12
|
CHARACTERISTICS = CasioConstants.CHARACTERISTICS
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TimeIO:
|
|
16
|
-
result: CancelableResult = None
|
|
17
16
|
connection = None
|
|
18
17
|
|
|
19
18
|
@staticmethod
|
|
@@ -32,14 +31,19 @@ class TimeIO:
|
|
|
32
31
|
@staticmethod
|
|
33
32
|
async def send_to_watch_set(message):
|
|
34
33
|
date_time_ms = int(json.loads(message).get("value"))
|
|
35
|
-
logger.info("date_time_ms: {}".format(date_time_ms))
|
|
36
34
|
date_time = datetime.datetime.fromtimestamp(date_time_ms / 1000.0)
|
|
37
35
|
time_data = TimeEncoder.prepare_current_time(date_time)
|
|
38
36
|
time_command = to_hex_string(
|
|
39
37
|
bytearray([CHARACTERISTICS["CASIO_CURRENT_TIME"]]) + time_data
|
|
40
38
|
)
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
try:
|
|
40
|
+
await TimeIO.connection.write(0xE, to_compact_string(time_command))
|
|
41
|
+
except (Exception) as e:
|
|
42
|
+
# Ignore this exception if the LOWER-RIGHT button is pressed.
|
|
43
|
+
# In this case, the connection will be closed before the call completes with a response, and we get an EOFError.
|
|
44
|
+
# The call actually works, though.
|
|
45
|
+
|
|
46
|
+
logger.info(f"Error {e} ignored due to timeout limitation")
|
|
43
47
|
|
|
44
48
|
class TimeEncoder:
|
|
45
49
|
def prepare_current_time(date: datetime.datetime):
|
|
@@ -16,13 +16,12 @@ class TimerIO:
|
|
|
16
16
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
async def request(connection):
|
|
19
|
-
logger.info(f"TimerIO request")
|
|
20
19
|
TimerIO.connection = connection
|
|
21
20
|
await connection.request("18")
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
TimerIO.result
|
|
25
|
-
|
|
22
|
+
TimerIO.result = CancelableResult()
|
|
23
|
+
return TimerIO.result.get_result()
|
|
24
|
+
|
|
26
25
|
|
|
27
26
|
@staticmethod
|
|
28
27
|
async def send_to_watch(connection):
|
|
@@ -30,8 +29,6 @@ class TimerIO:
|
|
|
30
29
|
|
|
31
30
|
@staticmethod
|
|
32
31
|
async def send_to_watch_set(data):
|
|
33
|
-
logger.info(f"TimerIO sendToWatchSet: {data}")
|
|
34
|
-
|
|
35
32
|
def encode(seconds_str):
|
|
36
33
|
in_seconds = int(seconds_str)
|
|
37
34
|
hours = in_seconds // 3600
|
|
@@ -53,8 +50,6 @@ class TimerIO:
|
|
|
53
50
|
|
|
54
51
|
@staticmethod
|
|
55
52
|
def on_received(data):
|
|
56
|
-
logger.info(f"TimerIO onReceived")
|
|
57
|
-
|
|
58
53
|
def decode_value(data: str) -> str:
|
|
59
54
|
timer_int_array = data
|
|
60
55
|
|
|
@@ -19,13 +19,11 @@ class WatchConditionIO:
|
|
|
19
19
|
|
|
20
20
|
@staticmethod
|
|
21
21
|
async def request(connection):
|
|
22
|
-
logger.info(f"WatchConditionIO request")
|
|
23
22
|
WatchConditionIO.connection = connection
|
|
24
23
|
await connection.request("28")
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
WatchConditionIO.result
|
|
28
|
-
return WatchConditionIO.result
|
|
25
|
+
WatchConditionIO.result = CancelableResult()
|
|
26
|
+
return WatchConditionIO.result.get_result()
|
|
29
27
|
|
|
30
28
|
@staticmethod
|
|
31
29
|
async def send_to_watch(connection):
|
|
@@ -33,16 +31,11 @@ class WatchConditionIO:
|
|
|
33
31
|
|
|
34
32
|
@staticmethod
|
|
35
33
|
def on_received(data):
|
|
36
|
-
logger.info(f"WatchConditionIO onReceived")
|
|
37
|
-
|
|
38
34
|
def decode_value(data: str) -> WatchConditionIO.WatchConditionValue:
|
|
39
35
|
int_arr = list(map(int, data))
|
|
40
36
|
bytes_data = bytes(int_arr[1:])
|
|
41
37
|
|
|
42
38
|
if len(bytes_data) >= 2:
|
|
43
|
-
# Battery level between 15 and 20 for B2100 and between 12 and 19 for B5600. Scale accordingly to %
|
|
44
|
-
logger.info(f"battery level row value: {int(bytes_data[0])}")
|
|
45
|
-
|
|
46
39
|
battery_level_lower_limit = watch_info.batteryLevelLowerLimit
|
|
47
40
|
battery_level_upper_limit = watch_info.batteryLevelUpperLimit
|
|
48
41
|
|
|
@@ -15,9 +15,8 @@ class WatchNameIO:
|
|
|
15
15
|
WatchNameIO.connection = connection
|
|
16
16
|
await connection.request("23")
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
WatchNameIO.result
|
|
20
|
-
return WatchNameIO.result
|
|
18
|
+
WatchNameIO.result = CancelableResult()
|
|
19
|
+
return WatchNameIO.result.get_result()
|
|
21
20
|
|
|
22
21
|
@staticmethod
|
|
23
22
|
def on_received(data):
|
|
@@ -13,15 +13,13 @@ class WorldCitiesIO:
|
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
15
15
|
async def request(connection, cityNumber: int):
|
|
16
|
-
logger.info(f"DstWatchStateIO request")
|
|
17
16
|
WorldCitiesIO.connection = connection
|
|
18
17
|
key = "1f0{}".format(cityNumber)
|
|
19
18
|
|
|
20
19
|
await connection.request(key)
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
WorldCitiesIO.result
|
|
24
|
-
return WorldCitiesIO.result
|
|
21
|
+
WorldCitiesIO.result = CancelableResult()
|
|
22
|
+
return WorldCitiesIO.result.get_result()
|
|
25
23
|
|
|
26
24
|
@staticmethod
|
|
27
25
|
async def send_to_watch(connection):
|
|
@@ -29,5 +27,4 @@ class WorldCitiesIO:
|
|
|
29
27
|
|
|
30
28
|
@staticmethod
|
|
31
29
|
def on_received(data):
|
|
32
|
-
logger.info(f"WorldCitiesIO onReceived")
|
|
33
30
|
WorldCitiesIO.result.set_result(data)
|
|
@@ -72,7 +72,6 @@ class MessageDispatcher:
|
|
|
72
72
|
if key not in MessageDispatcher.data_received_messages:
|
|
73
73
|
logger.info(f"Unknown key: {key}")
|
|
74
74
|
else:
|
|
75
|
-
logger.info(f"Found key: {MessageDispatcher.data_received_messages[key]}")
|
|
76
75
|
MessageDispatcher.data_received_messages[key](data)
|
|
77
76
|
|
|
78
77
|
# Usage example
|
|
@@ -11,7 +11,6 @@ class Scanner:
|
|
|
11
11
|
|
|
12
12
|
async def scan(self, device_address=None) -> BLEDevice:
|
|
13
13
|
scanner = BleakScanner()
|
|
14
|
-
logger.info("Scanning for devices...")
|
|
15
14
|
|
|
16
15
|
if device_address is None:
|
|
17
16
|
while True:
|
|
@@ -19,7 +18,6 @@ class Scanner:
|
|
|
19
18
|
lambda d, ad: d.name and d.name.lower().startswith("casio"),
|
|
20
19
|
timeout=5 * 60.0,
|
|
21
20
|
)
|
|
22
|
-
logger.info(f"device: {device}")
|
|
23
21
|
if device is None:
|
|
24
22
|
continue
|
|
25
23
|
|
|
@@ -202,8 +202,6 @@ class WatchInfo:
|
|
|
202
202
|
if len(parts) > 1:
|
|
203
203
|
self.shortName = parts[1]
|
|
204
204
|
|
|
205
|
-
logger.info(f"============> self.shortName: ${self.shortName}")
|
|
206
|
-
|
|
207
205
|
# *** Order matters. Start with the longest shortName first. ***
|
|
208
206
|
if self.shortName.startswith("MSG"):
|
|
209
207
|
self.model = WATCH_MODEL.MSG
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
class CancelableResult:
|
|
5
|
-
def __init__(self, timeout: float = 5.0):
|
|
6
|
-
self._timeout = timeout
|
|
7
|
-
self._future: asyncio.Future[Any] = asyncio.Future()
|
|
8
|
-
|
|
9
|
-
async def get_result(self) -> Any:
|
|
10
|
-
try:
|
|
11
|
-
return await asyncio.wait_for(self._future, timeout=self._timeout)
|
|
12
|
-
except asyncio.TimeoutError:
|
|
13
|
-
if not self._future.done():
|
|
14
|
-
self._future.set_result('')
|
|
15
|
-
return await self._future
|
|
16
|
-
|
|
17
|
-
def set_result(self, result: Any) -> None:
|
|
18
|
-
if not self._future.done():
|
|
19
|
-
self._future.set_result(result)
|
|
20
|
-
|
|
21
|
-
# Example usage
|
|
22
|
-
async def main():
|
|
23
|
-
cancelable_result = CancelableResult(timeout=5)
|
|
24
|
-
|
|
25
|
-
# Simulate some async work that might or might not set a result
|
|
26
|
-
async def some_async_work():
|
|
27
|
-
await asyncio.sleep(3) # Change this to 6 to test the timeout
|
|
28
|
-
cancelable_result.set_result('Result after async work')
|
|
29
|
-
|
|
30
|
-
await asyncio.gather(some_async_work(), asyncio.sleep(6))
|
|
31
|
-
|
|
32
|
-
final_result = await cancelable_result.get_result()
|
|
33
|
-
print(f'Final result: "{final_result}"')
|
|
34
|
-
|
|
35
|
-
# Run the main coroutine
|
|
36
|
-
# asyncio.run(main())
|
|
37
|
-
|
|
38
|
-
cancelable_result = CancelableResult()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|