gshock-api 2.0.30__tar.gz → 2.0.32__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.30/src/gshock_api.egg-info → gshock_api-2.0.32}/PKG-INFO +17 -2
- gshock_api-2.0.32/pyproject.toml +290 -0
- gshock_api-2.0.32/setup.py +17 -0
- gshock_api-2.0.32/src/examples/api_tests.py +218 -0
- gshock_api-2.0.32/src/examples/app_notifications_tests.py +47 -0
- gshock_api-2.0.32/src/examples/args.py +28 -0
- gshock_api-2.0.32/src/examples/gshock_server.py +75 -0
- gshock_api-2.0.32/src/examples/health_test.py +15 -0
- gshock_api-2.0.32/src/gshock_api/__init__.py +9 -0
- gshock_api-2.0.32/src/gshock_api/alarms.py +140 -0
- gshock_api-2.0.32/src/gshock_api/always_connected_watch_filter.py +46 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/app_notification.py +15 -16
- gshock_api-2.0.32/src/gshock_api/cancelable_result.py +40 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/casio_constants.py +20 -11
- gshock_api-2.0.32/src/gshock_api/connection.py +160 -0
- gshock_api-2.0.32/src/gshock_api/event.py +202 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/exceptions.py +4 -2
- gshock_api-2.0.32/src/gshock_api/gshock_api.py +301 -0
- gshock_api-2.0.32/src/gshock_api/iolib/alarms_io.py +106 -0
- gshock_api-2.0.32/src/gshock_api/iolib/app_info_io.py +47 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/app_notification_io.py +3 -3
- gshock_api-2.0.32/src/gshock_api/iolib/button_pressed_io.py +79 -0
- gshock_api-2.0.32/src/gshock_api/iolib/connection_protocol.py +9 -0
- gshock_api-2.0.32/src/gshock_api/iolib/dst_for_world_cities_io.py +29 -0
- gshock_api-2.0.32/src/gshock_api/iolib/dst_watch_state_io.py +36 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/error_io.py +1 -1
- gshock_api-2.0.32/src/gshock_api/iolib/events_io.py +306 -0
- gshock_api-2.0.32/src/gshock_api/iolib/settings_io.py +120 -0
- gshock_api-2.0.32/src/gshock_api/iolib/time_adjustement_io.py +89 -0
- gshock_api-2.0.32/src/gshock_api/iolib/time_io.py +75 -0
- gshock_api-2.0.32/src/gshock_api/iolib/timer_io.py +60 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/unknown_io.py +2 -1
- gshock_api-2.0.32/src/gshock_api/iolib/watch_condition_io.py +56 -0
- gshock_api-2.0.32/src/gshock_api/iolib/watch_name_io.py +27 -0
- gshock_api-2.0.32/src/gshock_api/iolib/world_cities_io.py +29 -0
- gshock_api-2.0.32/src/gshock_api/logger.py +45 -0
- gshock_api-2.0.32/src/gshock_api/message_dispatcher.py +138 -0
- gshock_api-2.0.32/src/gshock_api/scanner.py +108 -0
- gshock_api-2.0.32/src/gshock_api/settings.py +18 -0
- gshock_api-2.0.32/src/gshock_api/utils.py +171 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/watch_info.py +134 -123
- {gshock_api-2.0.30 → gshock_api-2.0.32/src/gshock_api.egg-info}/PKG-INFO +17 -2
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/SOURCES.txt +7 -1
- gshock_api-2.0.32/src/gshock_api.egg-info/entry_points.txt +2 -0
- gshock_api-2.0.32/src/gshock_api.egg-info/requires.txt +12 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/top_level.txt +1 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/tests/test_code.py +3 -1
- gshock_api-2.0.30/pyproject.toml +0 -13
- gshock_api-2.0.30/setup.py +0 -17
- gshock_api-2.0.30/src/gshock_api/__init__.py +0 -14
- gshock_api-2.0.30/src/gshock_api/alarms.py +0 -132
- gshock_api-2.0.30/src/gshock_api/always_connected_watch_filter.py +0 -40
- gshock_api-2.0.30/src/gshock_api/cancelable_result.py +0 -23
- gshock_api-2.0.30/src/gshock_api/connection.py +0 -121
- gshock_api-2.0.30/src/gshock_api/event.py +0 -171
- gshock_api-2.0.30/src/gshock_api/gshock_api.py +0 -455
- gshock_api-2.0.30/src/gshock_api/iolib/alarms_io.py +0 -59
- gshock_api-2.0.30/src/gshock_api/iolib/app_info_io.py +0 -41
- gshock_api-2.0.30/src/gshock_api/iolib/button_pressed_io.py +0 -68
- gshock_api-2.0.30/src/gshock_api/iolib/dst_for_world_cities_io.py +0 -26
- gshock_api-2.0.30/src/gshock_api/iolib/dst_watch_state_io.py +0 -32
- gshock_api-2.0.30/src/gshock_api/iolib/events_io.py +0 -358
- gshock_api-2.0.30/src/gshock_api/iolib/settings_io.py +0 -129
- gshock_api-2.0.30/src/gshock_api/iolib/time_adjustement_io.py +0 -83
- gshock_api-2.0.30/src/gshock_api/iolib/time_io.py +0 -71
- gshock_api-2.0.30/src/gshock_api/iolib/timer_io.py +0 -62
- gshock_api-2.0.30/src/gshock_api/iolib/watch_condition_io.py +0 -52
- gshock_api-2.0.30/src/gshock_api/iolib/watch_name_io.py +0 -27
- gshock_api-2.0.30/src/gshock_api/iolib/world_cities_io.py +0 -27
- gshock_api-2.0.30/src/gshock_api/logger.py +0 -29
- gshock_api-2.0.30/src/gshock_api/mailsener.py +0 -20
- gshock_api-2.0.30/src/gshock_api/message_dispatcher.py +0 -82
- gshock_api-2.0.30/src/gshock_api/scanner.py +0 -59
- gshock_api-2.0.30/src/gshock_api/settings.py +0 -12
- gshock_api-2.0.30/src/gshock_api/utils.py +0 -101
- gshock_api-2.0.30/src/gshock_api.egg-info/requires.txt +0 -1
- {gshock_api-2.0.30 → gshock_api-2.0.32}/LICENSE.txt +0 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/README.rst +0 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/setup.cfg +0 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/__init__.py +0 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/dependency_links.txt +0 -0
- {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/not-zip-safe +0 -0
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gshock_api
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.32
|
|
4
4
|
Summary: Pythgon API for GShock Watches using BLE
|
|
5
5
|
Home-page: https://github.com/pyscaffold/pyscaffold/
|
|
6
6
|
Author: Ivo Zivkov
|
|
7
7
|
Author-email: Ivo Zivkov <izivkov@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Project-URL: Documentation, https://github.com/izivkov/gshock_api
|
|
8
10
|
Platform: any
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.12
|
|
9
14
|
Description-Content-Type: text/x-rst
|
|
10
15
|
License-File: LICENSE.txt
|
|
11
|
-
Requires-Dist:
|
|
16
|
+
Requires-Dist: pydantic>=2.0.0
|
|
17
|
+
Requires-Dist: types-requests
|
|
18
|
+
Requires-Dist: python-dotenv
|
|
19
|
+
Requires-Dist: typing-extensions>=4.12
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: basedpyright>=1.31.7; extra == "dev"
|
|
22
|
+
Requires-Dist: ruff; extra == "dev"
|
|
23
|
+
Requires-Dist: gitingest; extra == "dev"
|
|
24
|
+
Requires-Dist: poethepoet; extra == "dev"
|
|
25
|
+
Requires-Dist: radon; extra == "dev"
|
|
26
|
+
Requires-Dist: vulture; extra == "dev"
|
|
12
27
|
Dynamic: license-file
|
|
13
28
|
|
|
14
29
|
gshock_api
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# ULTRA-STRICT PYTHON PROJECT TEMPLATE
|
|
3
|
+
# Maximum strictness - TypeScript strict mode equivalent
|
|
4
|
+
# Tools: uv + ruff + basedpyright + pydantic v2
|
|
5
|
+
# Python 3.12+
|
|
6
|
+
# ============================================================
|
|
7
|
+
|
|
8
|
+
[build-system]
|
|
9
|
+
requires = ["setuptools>=61.0"]
|
|
10
|
+
build-backend = "setuptools.build_meta"
|
|
11
|
+
|
|
12
|
+
[project]
|
|
13
|
+
name = "gshock_api" # Change to your project name
|
|
14
|
+
version = "2.0.32"
|
|
15
|
+
description = "Pythgon API for GShock Watches using BLE"
|
|
16
|
+
authors = [{ name = "Ivo Zivkov", email = "izivkov@gmail.com" }]
|
|
17
|
+
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
|
|
21
|
+
readme = "README.rst"
|
|
22
|
+
requires-python = ">=3.12"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"pydantic>=2.0.0",
|
|
25
|
+
"types-requests",
|
|
26
|
+
"python-dotenv",
|
|
27
|
+
"typing-extensions>=4.12",
|
|
28
|
+
]
|
|
29
|
+
classifiers = [
|
|
30
|
+
"Development Status :: 4 - Beta",
|
|
31
|
+
"Programming Language :: Python :: 3",
|
|
32
|
+
]
|
|
33
|
+
urls = { Documentation = "https://github.com/izivkov/gshock_api" }
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
gshock_server = "examples.gshock_server:main"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"basedpyright>=1.31.7",
|
|
41
|
+
"ruff",
|
|
42
|
+
"gitingest",
|
|
43
|
+
"poethepoet",
|
|
44
|
+
"radon",
|
|
45
|
+
"vulture"
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
# ============================================================
|
|
52
|
+
# POE THE POET - Task Runner
|
|
53
|
+
# ============================================================
|
|
54
|
+
[tool.poe.tasks]
|
|
55
|
+
# Run with: poe format or uv run poe format
|
|
56
|
+
# Formats code, fixes issues, and type checks
|
|
57
|
+
format = [
|
|
58
|
+
{cmd = "ruff format ."},
|
|
59
|
+
{cmd = "ruff check . --fix"},
|
|
60
|
+
{cmd = "basedpyright --level error"}
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# Run with: poe check
|
|
64
|
+
# Lint and type check without fixing
|
|
65
|
+
check = [
|
|
66
|
+
{cmd = "ruff check ."},
|
|
67
|
+
{cmd = "basedpyright --level error"}
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Run with: poe lint or uv run poe lint
|
|
71
|
+
# Only linting, no type checking
|
|
72
|
+
lint = {cmd = "ruff check . --fix"}
|
|
73
|
+
|
|
74
|
+
# Run with: poe lint-unsafe or uv run poe lint-unsafe
|
|
75
|
+
# Lint with unsafe fixes enabled (more aggressive)
|
|
76
|
+
lint-unsafe = {cmd = "ruff check . --fix --unsafe-fixes"}
|
|
77
|
+
|
|
78
|
+
# Run with: poe metrics or uv run poe metrics
|
|
79
|
+
# Check code quality: dead code, complexity, and maintainability
|
|
80
|
+
metrics = [
|
|
81
|
+
{cmd = "vulture . --min-confidence 80"},
|
|
82
|
+
{cmd = "radon cc . -a -nb"},
|
|
83
|
+
{cmd = "radon mi . -nb"}
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
# Run with: poe quality or uv run poe quality
|
|
87
|
+
# Full quality check: format, lint, type check, and metrics
|
|
88
|
+
quality = [
|
|
89
|
+
{cmd = "ruff format ."},
|
|
90
|
+
{cmd = "ruff check . --fix"},
|
|
91
|
+
{cmd = "basedpyright --level error"},
|
|
92
|
+
{cmd = "vulture . --min-confidence 80 --exclude .venv,build,dist,docs,scripts,tests"},
|
|
93
|
+
{cmd = "radon cc . -a -nb"}
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
# ============================================================
|
|
97
|
+
# RUFF CONFIGURATION - MAXIMUM STRICTNESS
|
|
98
|
+
# ============================================================
|
|
99
|
+
[tool.ruff]
|
|
100
|
+
target-version = "py312"
|
|
101
|
+
line-length = 88
|
|
102
|
+
indent-width = 4
|
|
103
|
+
fix = true
|
|
104
|
+
show-fixes = true
|
|
105
|
+
|
|
106
|
+
[tool.ruff.lint]
|
|
107
|
+
# Comprehensive rule set for strict checking
|
|
108
|
+
select = [
|
|
109
|
+
"E", # pycodestyle errors
|
|
110
|
+
"F", # pyflakes
|
|
111
|
+
"I", # isort
|
|
112
|
+
"UP", # pyupgrade
|
|
113
|
+
"B", # flake8-bugbear
|
|
114
|
+
"C4", # flake8-comprehensions
|
|
115
|
+
"T20", # flake8-print (no print statements)
|
|
116
|
+
"SIM", # flake8-simplify
|
|
117
|
+
"N", # pep8-naming
|
|
118
|
+
"Q", # flake8-quotes
|
|
119
|
+
"RUF", # Ruff-specific rules
|
|
120
|
+
"ASYNC", # flake8-async
|
|
121
|
+
"S", # flake8-bandit (security)
|
|
122
|
+
"PTH", # flake8-use-pathlib
|
|
123
|
+
"ERA", # eradicate (commented-out code)
|
|
124
|
+
"PL", # pylint
|
|
125
|
+
"PERF", # perflint (performance)
|
|
126
|
+
"ANN", # flake8-annotations
|
|
127
|
+
"ARG", # flake8-unused-arguments
|
|
128
|
+
"RET", # flake8-return
|
|
129
|
+
"TCH", # flake8-type-checking
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
ignore = [
|
|
133
|
+
"E501", # Line too long (formatter handles this)
|
|
134
|
+
"S603", # subprocess without shell=True (too strict)
|
|
135
|
+
"S607", # Starting a process with a partial path (too strict)
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
# Per-file ignores
|
|
139
|
+
[tool.ruff.lint.per-file-ignores]
|
|
140
|
+
"__init__.py" = [
|
|
141
|
+
"F401", # Allow unused imports in __init__.py
|
|
142
|
+
]
|
|
143
|
+
"tests/**/*.py" = [
|
|
144
|
+
"S101", # Allow assert in tests
|
|
145
|
+
"PLR2004", # Allow magic values in tests
|
|
146
|
+
"ANN", # Don't require annotations in tests
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
[tool.ruff.lint.isort]
|
|
150
|
+
known-first-party = ["gshock_api"] # CHANGE THIS
|
|
151
|
+
combine-as-imports = true
|
|
152
|
+
force-sort-within-sections = true
|
|
153
|
+
|
|
154
|
+
[tool.ruff.lint.pydocstyle]
|
|
155
|
+
convention = "google"
|
|
156
|
+
|
|
157
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
158
|
+
strict = true
|
|
159
|
+
|
|
160
|
+
[tool.ruff.format]
|
|
161
|
+
quote-style = "double"
|
|
162
|
+
indent-style = "space"
|
|
163
|
+
skip-magic-trailing-comma = false
|
|
164
|
+
line-ending = "auto"
|
|
165
|
+
|
|
166
|
+
# ============================================================
|
|
167
|
+
# Basedpyright CONFIGURATION - MAXIMUM STRICTNESS
|
|
168
|
+
# TypeScript strict mode equivalent
|
|
169
|
+
# ============================================================
|
|
170
|
+
[tool.basedpyright]
|
|
171
|
+
pythonVersion = "3.12"
|
|
172
|
+
typeCheckingMode = "strict"
|
|
173
|
+
|
|
174
|
+
# ============================================================
|
|
175
|
+
# IMPORT AND MODULE CHECKS
|
|
176
|
+
# ============================================================
|
|
177
|
+
reportMissingImports = true
|
|
178
|
+
reportMissingTypeStubs = true # Stricter: require type stubs
|
|
179
|
+
reportUndefinedVariable = true
|
|
180
|
+
reportAssertAlwaysTrue = true
|
|
181
|
+
reportInvalidStringEscapeSequence = true
|
|
182
|
+
|
|
183
|
+
# ============================================================
|
|
184
|
+
# STRICT NULL SAFETY (like TS strictNullChecks)
|
|
185
|
+
# ============================================================
|
|
186
|
+
reportOptionalSubscript = true
|
|
187
|
+
reportOptionalMemberAccess = true
|
|
188
|
+
reportOptionalCall = true
|
|
189
|
+
reportOptionalIterable = true
|
|
190
|
+
reportOptionalContextManager = true
|
|
191
|
+
reportOptionalOperand = true
|
|
192
|
+
reportAny = true # Prohibir Any completamente
|
|
193
|
+
reportExplicitAny = true
|
|
194
|
+
reportIgnoreCommentWithoutRule = false # No permitir # type: ignore sin código
|
|
195
|
+
reportPrivateLocalImportUsage = true
|
|
196
|
+
reportUnnecessaryTypeAnnotation = true # Detectar anotaciones redundantes
|
|
197
|
+
|
|
198
|
+
# ============================================================
|
|
199
|
+
# TYPE COMPLETENESS (like TS noImplicitAny + strictFunctionTypes)
|
|
200
|
+
# ============================================================
|
|
201
|
+
reportMissingParameterType = true
|
|
202
|
+
reportMissingTypeArgument = true
|
|
203
|
+
reportUnknownParameterType = true
|
|
204
|
+
reportUnknownLambdaType = true
|
|
205
|
+
reportUnknownArgumentType = true # STRICT: Enable (can be noisy)
|
|
206
|
+
reportUnknownVariableType = true # STRICT: Enable (can be noisy)
|
|
207
|
+
reportUnknownMemberType = true # STRICT: Enable (can be noisy)
|
|
208
|
+
reportUntypedFunctionDecorator = true
|
|
209
|
+
reportUntypedClassDecorator = true
|
|
210
|
+
reportUntypedBaseClass = true
|
|
211
|
+
reportUntypedNamedTuple = true
|
|
212
|
+
|
|
213
|
+
# ============================================================
|
|
214
|
+
# CLASS AND INHERITANCE CHECKS
|
|
215
|
+
# ============================================================
|
|
216
|
+
reportIncompatibleMethodOverride = true
|
|
217
|
+
reportIncompatibleVariableOverride = true
|
|
218
|
+
reportInconsistentConstructor = true
|
|
219
|
+
reportUninitializedInstanceVariable = true
|
|
220
|
+
reportOverlappingOverload = true
|
|
221
|
+
reportMissingSuperCall = true # STRICT: Enable
|
|
222
|
+
|
|
223
|
+
# ============================================================
|
|
224
|
+
# CODE QUALITY (like TS noUnusedLocals + noUnusedParameters)
|
|
225
|
+
# ============================================================
|
|
226
|
+
reportPrivateUsage = true
|
|
227
|
+
reportConstantRedefinition = true
|
|
228
|
+
reportInvalidStubStatement = true
|
|
229
|
+
reportIncompleteStub = true
|
|
230
|
+
reportUnsupportedDunderAll = true
|
|
231
|
+
reportUnusedClass = "error" # STRICT: Error instead of warning
|
|
232
|
+
reportUnusedFunction = "error" # STRICT: Error instead of warning
|
|
233
|
+
reportUnusedVariable = "error" # STRICT: Error instead of warning
|
|
234
|
+
reportUnusedImport = "error" # STRICT: Error instead of warning
|
|
235
|
+
reportDuplicateImport = "error" # STRICT: Error instead of warning
|
|
236
|
+
|
|
237
|
+
# ============================================================
|
|
238
|
+
# UNNECESSARY CODE DETECTION
|
|
239
|
+
# ============================================================
|
|
240
|
+
reportUnnecessaryIsInstance = "error" # STRICT: Error
|
|
241
|
+
reportUnnecessaryCast = "error" # STRICT: Error
|
|
242
|
+
reportUnnecessaryComparison = "error" # STRICT: Error
|
|
243
|
+
reportUnnecessaryContains = "error" # STRICT: Error
|
|
244
|
+
reportUnnecessaryTypeIgnoreComment = "error" # STRICT: Error
|
|
245
|
+
|
|
246
|
+
# ============================================================
|
|
247
|
+
# FUNCTION/METHOD SIGNATURE STRICTNESS
|
|
248
|
+
# ============================================================
|
|
249
|
+
reportGeneralTypeIssues = true
|
|
250
|
+
reportPropertyTypeMismatch = true
|
|
251
|
+
reportFunctionMemberAccess = true
|
|
252
|
+
reportCallInDefaultInitializer = true
|
|
253
|
+
reportImplicitStringConcatenation = true # STRICT: Enable
|
|
254
|
+
|
|
255
|
+
# ============================================================
|
|
256
|
+
# ADDITIONAL STRICT CHECKS (Progressive Enhancement)
|
|
257
|
+
# ============================================================
|
|
258
|
+
reportImplicitOverride = true # STRICT: Require @override decorator (Python 3.12+)
|
|
259
|
+
reportShadowedImports = true # STRICT: Detect shadowed imports
|
|
260
|
+
reportDeprecated = "warning" # Warn on deprecated usage
|
|
261
|
+
|
|
262
|
+
# ============================================================
|
|
263
|
+
# ADDITIONAL TYPE CHECKS
|
|
264
|
+
# ============================================================
|
|
265
|
+
reportImportCycles = "warning"
|
|
266
|
+
|
|
267
|
+
# ============================================================
|
|
268
|
+
# EXCLUSIONS
|
|
269
|
+
# ============================================================
|
|
270
|
+
exclude = [
|
|
271
|
+
"**/__pycache__",
|
|
272
|
+
"**/node_modules",
|
|
273
|
+
".git",
|
|
274
|
+
".mypy_cache",
|
|
275
|
+
".pyright_cache",
|
|
276
|
+
".ruff_cache",
|
|
277
|
+
".pytest_cache",
|
|
278
|
+
".venv",
|
|
279
|
+
"venv",
|
|
280
|
+
"env",
|
|
281
|
+
"logs",
|
|
282
|
+
"output",
|
|
283
|
+
"data",
|
|
284
|
+
"build",
|
|
285
|
+
"dist",
|
|
286
|
+
"*.egg-info",
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
venvPath = "."
|
|
290
|
+
venv = ".venv"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from setuptools import find_packages, setup
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="gshock_api",
|
|
5
|
+
version="2.0.31",
|
|
6
|
+
package_dir={"": "src"},
|
|
7
|
+
packages=find_packages(where="src"),
|
|
8
|
+
install_requires=[
|
|
9
|
+
"pytz",
|
|
10
|
+
"bleak>=1.0.1",
|
|
11
|
+
],
|
|
12
|
+
entry_points={
|
|
13
|
+
"console_scripts": [
|
|
14
|
+
"gshock_server=examples.gshock_server:main",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import pytz
|
|
10
|
+
|
|
11
|
+
from gshock_api.always_connected_watch_filter import (
|
|
12
|
+
always_connected_watch_filter as watch_filter,
|
|
13
|
+
)
|
|
14
|
+
from gshock_api.app_notification import AppNotification, NotificationType
|
|
15
|
+
from gshock_api.connection import Connection
|
|
16
|
+
from gshock_api.event import Event, RepeatPeriod, create_event_date
|
|
17
|
+
from gshock_api.exceptions import GShockConnectionError
|
|
18
|
+
from gshock_api.gshock_api import GshockAPI
|
|
19
|
+
from gshock_api.logger import logger
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def main(argv: Sequence[str]) -> None:
|
|
23
|
+
await run_api_tests(argv)
|
|
24
|
+
|
|
25
|
+
def prompt() -> None:
|
|
26
|
+
logger.info(
|
|
27
|
+
"========================================================================"
|
|
28
|
+
)
|
|
29
|
+
logger.info(
|
|
30
|
+
"Press and hold lower-left button on your watch for 3 seconds to start..."
|
|
31
|
+
)
|
|
32
|
+
logger.info(
|
|
33
|
+
"========================================================================"
|
|
34
|
+
)
|
|
35
|
+
logger.info("")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def run_api_tests(argv: Sequence[str]) -> None: # noqa: PLR0915
|
|
39
|
+
prompt()
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
logger.info("Waiting for connection...")
|
|
43
|
+
connection = Connection()
|
|
44
|
+
await connection.connect(watch_filter.connection_filter)
|
|
45
|
+
logger.info("Connected...")
|
|
46
|
+
|
|
47
|
+
api = GshockAPI(connection)
|
|
48
|
+
|
|
49
|
+
app_info = await api.get_app_info()
|
|
50
|
+
logger.info(f"app info: {app_info}")
|
|
51
|
+
|
|
52
|
+
pressed_button = await api.get_pressed_button()
|
|
53
|
+
logger.info(f"pressed button: {pressed_button}")
|
|
54
|
+
|
|
55
|
+
watch_name = await api.get_watch_name()
|
|
56
|
+
logger.info(f"got watch name: {watch_name}")
|
|
57
|
+
|
|
58
|
+
await api.set_time(time.time() + 10 * 60)
|
|
59
|
+
|
|
60
|
+
alarms = await api.get_alarms()
|
|
61
|
+
logger.info(f"alarms: {alarms}")
|
|
62
|
+
|
|
63
|
+
alarms[3]["enabled"] = True
|
|
64
|
+
alarms[3]["hour"] = 7
|
|
65
|
+
alarms[3]["minute"] = 25
|
|
66
|
+
alarms[3]["enabled"] = False
|
|
67
|
+
await api.set_alarms(alarms)
|
|
68
|
+
|
|
69
|
+
seconds = await api.get_timer()
|
|
70
|
+
logger.info(f"timer: {seconds} seconds")
|
|
71
|
+
|
|
72
|
+
await api.set_timer(seconds + 10)
|
|
73
|
+
time_adjstment = await api.get_time_adjustment()
|
|
74
|
+
logger.info(f"time_adjstment: {time_adjstment}")
|
|
75
|
+
|
|
76
|
+
await api.set_time_adjustment(time_adjustement=True, minutes_after_hour=10)
|
|
77
|
+
|
|
78
|
+
condition = await api.get_watch_condition()
|
|
79
|
+
logger.info(f"condition: {condition}")
|
|
80
|
+
|
|
81
|
+
settings_local = await api.get_basic_settings()
|
|
82
|
+
logger.info(f"settings: {settings_local}")
|
|
83
|
+
|
|
84
|
+
settings_local["button_tone"] = True
|
|
85
|
+
settings_local["language"] = "Russian"
|
|
86
|
+
settings_local["time_format"] = "24h"
|
|
87
|
+
|
|
88
|
+
await api.set_settings(settings_local)
|
|
89
|
+
settings_local = await api.get_basic_settings()
|
|
90
|
+
await app_notifications(api)
|
|
91
|
+
|
|
92
|
+
# Create a single event
|
|
93
|
+
tz = pytz.timezone("America/Toronto")
|
|
94
|
+
dt = datetime.now()
|
|
95
|
+
utc_timestamp = dt.timestamp()
|
|
96
|
+
event_date = create_event_date(utc_timestamp, tz)
|
|
97
|
+
event_date_str = json.dumps(event_date.__dict__)
|
|
98
|
+
event_json_str = (
|
|
99
|
+
"""{"title":"Test Event", "time":{"selected":\""""
|
|
100
|
+
+ str(False)
|
|
101
|
+
+ """\", "enabled":\""""
|
|
102
|
+
+ str(True)
|
|
103
|
+
+ """\", "repeat_period":\""""
|
|
104
|
+
+ str(RepeatPeriod.WEEKLY)
|
|
105
|
+
+ """\","days_of_week":\""""
|
|
106
|
+
+ "MONDAY"
|
|
107
|
+
+ """\", "start_date":"""
|
|
108
|
+
+ event_date_str
|
|
109
|
+
+ """, "end_date":"""
|
|
110
|
+
+ event_date_str
|
|
111
|
+
+ """}}"""
|
|
112
|
+
)
|
|
113
|
+
Event().create_event(json.loads(event_json_str))
|
|
114
|
+
logger.info(f"Created event: {event_json_str}")
|
|
115
|
+
|
|
116
|
+
reminders = await api.get_reminders()
|
|
117
|
+
for reminder in reminders:
|
|
118
|
+
logger.info(f"reminder: {reminder}")
|
|
119
|
+
|
|
120
|
+
reminders[3]["title"] = "Test Event"
|
|
121
|
+
|
|
122
|
+
await api.set_reminders(reminders)
|
|
123
|
+
|
|
124
|
+
except GShockConnectionError as e:
|
|
125
|
+
logger.info(f"Connection problem: {e}")
|
|
126
|
+
|
|
127
|
+
input("Hit any key to disconnect")
|
|
128
|
+
|
|
129
|
+
await connection.disconnect()
|
|
130
|
+
logger.info("--- END OF TESTS ---")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
async def run_api_tests_notifications() -> None:
|
|
134
|
+
prompt()
|
|
135
|
+
|
|
136
|
+
connection = Connection()
|
|
137
|
+
await connection.connect()
|
|
138
|
+
|
|
139
|
+
api = GshockAPI(connection)
|
|
140
|
+
|
|
141
|
+
await app_notifications(api)
|
|
142
|
+
|
|
143
|
+
input("Hit any key to disconnect")
|
|
144
|
+
|
|
145
|
+
await connection.disconnect()
|
|
146
|
+
logger.info("--- END OF TESTS ---")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def app_notifications(api: GshockAPI) -> None:
|
|
150
|
+
AppNotification(
|
|
151
|
+
type=NotificationType.CALENDAR,
|
|
152
|
+
timestamp="20231001T121000",
|
|
153
|
+
app="Calendar",
|
|
154
|
+
title="This is a very long Meeting with Team",
|
|
155
|
+
text=" 9:20 - 10:15 AM",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
AppNotification(
|
|
159
|
+
type=NotificationType.CALENDAR,
|
|
160
|
+
timestamp="20250516T233000",
|
|
161
|
+
app="Calendar",
|
|
162
|
+
title="Full day event 3",
|
|
163
|
+
text="Tomorrow",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
email_notification2 = AppNotification(
|
|
167
|
+
type=NotificationType.EMAIL_SMS,
|
|
168
|
+
timestamp="20250516T211520",
|
|
169
|
+
app="Gmail",
|
|
170
|
+
title="me",
|
|
171
|
+
text="""[translate:彼女はピア
|
|
172
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
173
|
+
彼女はピア
|
|
174
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
175
|
+
彼女はピア
|
|
176
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
177
|
+
]""",
|
|
178
|
+
short_text="""[translate:彼女はピア
|
|
179
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
180
|
+
彼女はピア
|
|
181
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
182
|
+
彼女はピア
|
|
183
|
+
彼女はピアノを弾いたり、絵を描くのが好きです。リア充です
|
|
184
|
+
]""",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
AppNotification(
|
|
188
|
+
type=NotificationType.EMAIL_SMS,
|
|
189
|
+
timestamp="20250516T211520",
|
|
190
|
+
app="Gmail",
|
|
191
|
+
title="me",
|
|
192
|
+
text="[translate:الساعة\n]",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
AppNotification(
|
|
196
|
+
type=NotificationType.EMAIL,
|
|
197
|
+
timestamp="20231001T120000",
|
|
198
|
+
app="EmailApp",
|
|
199
|
+
title="Ivo",
|
|
200
|
+
short_text="""And this is a short message up to 40 chars""",
|
|
201
|
+
text="This is the message up to 193 characters, combined up to 206 characters",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
await api.send_app_notification(email_notification2)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def convert_time_string_to_epoch(time_string: str) -> Optional[float]:
|
|
208
|
+
try:
|
|
209
|
+
time_object = datetime.strptime(time_string, "%H:%M:%S")
|
|
210
|
+
timestamp = time_object.timestamp()
|
|
211
|
+
return timestamp
|
|
212
|
+
except ValueError:
|
|
213
|
+
logger.info("Invalid time format. Please use the format HH:MM:SS.")
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
if __name__ == "__main__":
|
|
218
|
+
asyncio.run(main(sys.argv[1:]))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from gshock_api.iolib.app_notification_io import AppNotificationIO
|
|
2
|
+
|
|
3
|
+
bufferSMS = "fdfffffffffef9cdcfcdcacfcaceccabcec7cfcdcdcef7ffb29a8c8c9e989a8cf1ffd7cbcec9d6dfc7ccccd2cdcfc8c7ffffe6ffab97968cdf968cdf9edf8c96928f939adf929a8c8c9e989adf"
|
|
4
|
+
bufferSMS_2 = "fcfffffffffef9cdcfcdcacfcaceccabcec7cfcdcac6f7ffb29a8c8c9e989a8cf1ffd7cbcec9d6dfc7ccccd2cdcfc8c7fffff4ffbe91908b979a8ddf90919a"
|
|
5
|
+
bufferGmail = "fffffffffffef9cdcfcdcacfcaceceabcfc8cbcccecffaffb8929e9693fdff929affff05ffab97968cdf968cdf9edf899a8d86df93909198df8c8a9d959a9c8bd1dfb29e86899adf9a899a91df8b9090df93909198d1dfbd8a8bdf979a8d9adf968bdf968cd1d1d1f5a8979e8bdf968cdf968bc0f5b6df8b97969194df889adf9c9e91df9b90df9d9a8b8b9a8ddf8b979e91df8b979adf909999969c969e93dfbc9e8c9690dfb8d2ac97909c94dfbe8f8fdedfab97968cdf9e8f8fdf8f8d9089969b9a8cdf8b979adf999093939088969198df9a878b8d9edf999a9e8b8a8d9a8cc5f5ac9a8b8cdf889e8b9c97d88cdf8d9a9296919b"
|
|
6
|
+
bufferGmailJapanese = "fefffffffffef9cdcfcdcacfcacec9abcdcececacdcffaffb8929e9693fdff929affff9aff1a42431a5a4c1c7e501c7c6b1c7d5df51a42431a5a4c1c7e501c7c6b1c7d5d1c7c711c7d6d1a43411c7e7b1c7e601c7d751c7f7e184a4a1c7d6d1970701c7e701c7e511c7e731a5a421c7e721c7e581c7e661c7f7d1c7c551c7d5d1a7a7a1c7e581c7e66f5"
|
|
7
|
+
bufferCalendar = "f7fffffffffefacdcfcdcacfcaceceabcdcdcecfcfcff7ffbc9e939a919b9e8debff1d7f711d7f55b0919ad28b96929a1d7f531d7f71ffffe1ff1d7f711d7f55cecfc5cbcfdf1d7f6cdfcecec5cbcfdfafb21d7f531d7f71"
|
|
8
|
+
bufferCalendar2 = "f9fffffffffefacdcfcdcacfcaceceabcdcdcfc6cfcbf7ffbc9e939a919b9e8ddaff1d7f711d7f55b290919b9e86dfba899a8d86df889a9a94df99908d9a899a8d1d7f531d7f71ffffe1ff1d7f711d7f55cecfc5cccfdf1d7f6cdfcecec5cccfdfafb21d7f531d7f71"
|
|
9
|
+
bufferAllDayEvent = "fdfffffffffefacdcfcdcacfcacec9abcdcccccfcfcff7ffbc9e939a919b9e8de3ff1d7f711d7f55b98a9393df9b9e86df9a899a918bdfcc1d7f531d7f71ffffebff1d7f711d7f55ab9092908d8d90881d7f531d7f71" # noqa: N816
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def format_hex_string(hexstr):
|
|
13
|
+
"""Format a hex string into space-separated bytes."""
|
|
14
|
+
return " ".join(hexstr[i : i + 2] for i in range(0, len(hexstr), 2))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Example usage
|
|
18
|
+
input_hex_2 = AppNotificationIO.xor_decode_buffer(bufferGmailJapanese).hex()
|
|
19
|
+
buf = bytes.fromhex(input_hex_2)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
notification = AppNotificationIO.decode_notification_packet(buf)
|
|
23
|
+
print(notification)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
rebuilt_buf = AppNotificationIO.encode_notification_packet(notification)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def compare_buffers(buf1: bytes, buf2: bytes):
|
|
30
|
+
min_len = min(len(buf1), len(buf2))
|
|
31
|
+
for i in range(min_len):
|
|
32
|
+
if buf1[i] != buf2[i]:
|
|
33
|
+
print(f"Difference at byte {i}:")
|
|
34
|
+
print(f" buf1: {buf1[i]:02x}")
|
|
35
|
+
print(f" buf2: {buf2[i]:02x}")
|
|
36
|
+
print(f" Context buf1: ... {buf1[max(0,i-5):i+5].hex()} ...")
|
|
37
|
+
print(f" Context buf2: ... {buf2[max(0,i-5):i+5].hex()} ...")
|
|
38
|
+
break
|
|
39
|
+
else:
|
|
40
|
+
if len(buf1) != len(buf2):
|
|
41
|
+
print(f"Buffers have different lengths: {len(buf1)} vs {len(buf2)}")
|
|
42
|
+
else:
|
|
43
|
+
print("No differences found.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Usage:
|
|
47
|
+
compare_buffers(rebuilt_buf, buf)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Args:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.parse_and_store(sys.argv[1:])
|
|
8
|
+
|
|
9
|
+
def parse_and_store(self, args):
|
|
10
|
+
parser = argparse.ArgumentParser(description="Parser")
|
|
11
|
+
|
|
12
|
+
parser.add_argument(
|
|
13
|
+
"--fine-adjustment-secs",
|
|
14
|
+
type=int,
|
|
15
|
+
choices=range(-10, 11),
|
|
16
|
+
default=0,
|
|
17
|
+
help="Fine adjustment in seconds to add/subtract when setting time (-10 to 10)"
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"-l", "--log_level", default="INFO", help="Sets log level", required=False
|
|
21
|
+
)
|
|
22
|
+
self.args = parser.parse_args(args)
|
|
23
|
+
|
|
24
|
+
def get(self):
|
|
25
|
+
return self.args
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
args = Args()
|