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.
Files changed (82) hide show
  1. {gshock_api-2.0.30/src/gshock_api.egg-info → gshock_api-2.0.32}/PKG-INFO +17 -2
  2. gshock_api-2.0.32/pyproject.toml +290 -0
  3. gshock_api-2.0.32/setup.py +17 -0
  4. gshock_api-2.0.32/src/examples/api_tests.py +218 -0
  5. gshock_api-2.0.32/src/examples/app_notifications_tests.py +47 -0
  6. gshock_api-2.0.32/src/examples/args.py +28 -0
  7. gshock_api-2.0.32/src/examples/gshock_server.py +75 -0
  8. gshock_api-2.0.32/src/examples/health_test.py +15 -0
  9. gshock_api-2.0.32/src/gshock_api/__init__.py +9 -0
  10. gshock_api-2.0.32/src/gshock_api/alarms.py +140 -0
  11. gshock_api-2.0.32/src/gshock_api/always_connected_watch_filter.py +46 -0
  12. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/app_notification.py +15 -16
  13. gshock_api-2.0.32/src/gshock_api/cancelable_result.py +40 -0
  14. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/casio_constants.py +20 -11
  15. gshock_api-2.0.32/src/gshock_api/connection.py +160 -0
  16. gshock_api-2.0.32/src/gshock_api/event.py +202 -0
  17. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/exceptions.py +4 -2
  18. gshock_api-2.0.32/src/gshock_api/gshock_api.py +301 -0
  19. gshock_api-2.0.32/src/gshock_api/iolib/alarms_io.py +106 -0
  20. gshock_api-2.0.32/src/gshock_api/iolib/app_info_io.py +47 -0
  21. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/app_notification_io.py +3 -3
  22. gshock_api-2.0.32/src/gshock_api/iolib/button_pressed_io.py +79 -0
  23. gshock_api-2.0.32/src/gshock_api/iolib/connection_protocol.py +9 -0
  24. gshock_api-2.0.32/src/gshock_api/iolib/dst_for_world_cities_io.py +29 -0
  25. gshock_api-2.0.32/src/gshock_api/iolib/dst_watch_state_io.py +36 -0
  26. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/error_io.py +1 -1
  27. gshock_api-2.0.32/src/gshock_api/iolib/events_io.py +306 -0
  28. gshock_api-2.0.32/src/gshock_api/iolib/settings_io.py +120 -0
  29. gshock_api-2.0.32/src/gshock_api/iolib/time_adjustement_io.py +89 -0
  30. gshock_api-2.0.32/src/gshock_api/iolib/time_io.py +75 -0
  31. gshock_api-2.0.32/src/gshock_api/iolib/timer_io.py +60 -0
  32. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/unknown_io.py +2 -1
  33. gshock_api-2.0.32/src/gshock_api/iolib/watch_condition_io.py +56 -0
  34. gshock_api-2.0.32/src/gshock_api/iolib/watch_name_io.py +27 -0
  35. gshock_api-2.0.32/src/gshock_api/iolib/world_cities_io.py +29 -0
  36. gshock_api-2.0.32/src/gshock_api/logger.py +45 -0
  37. gshock_api-2.0.32/src/gshock_api/message_dispatcher.py +138 -0
  38. gshock_api-2.0.32/src/gshock_api/scanner.py +108 -0
  39. gshock_api-2.0.32/src/gshock_api/settings.py +18 -0
  40. gshock_api-2.0.32/src/gshock_api/utils.py +171 -0
  41. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/watch_info.py +134 -123
  42. {gshock_api-2.0.30 → gshock_api-2.0.32/src/gshock_api.egg-info}/PKG-INFO +17 -2
  43. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/SOURCES.txt +7 -1
  44. gshock_api-2.0.32/src/gshock_api.egg-info/entry_points.txt +2 -0
  45. gshock_api-2.0.32/src/gshock_api.egg-info/requires.txt +12 -0
  46. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/top_level.txt +1 -0
  47. {gshock_api-2.0.30 → gshock_api-2.0.32}/tests/test_code.py +3 -1
  48. gshock_api-2.0.30/pyproject.toml +0 -13
  49. gshock_api-2.0.30/setup.py +0 -17
  50. gshock_api-2.0.30/src/gshock_api/__init__.py +0 -14
  51. gshock_api-2.0.30/src/gshock_api/alarms.py +0 -132
  52. gshock_api-2.0.30/src/gshock_api/always_connected_watch_filter.py +0 -40
  53. gshock_api-2.0.30/src/gshock_api/cancelable_result.py +0 -23
  54. gshock_api-2.0.30/src/gshock_api/connection.py +0 -121
  55. gshock_api-2.0.30/src/gshock_api/event.py +0 -171
  56. gshock_api-2.0.30/src/gshock_api/gshock_api.py +0 -455
  57. gshock_api-2.0.30/src/gshock_api/iolib/alarms_io.py +0 -59
  58. gshock_api-2.0.30/src/gshock_api/iolib/app_info_io.py +0 -41
  59. gshock_api-2.0.30/src/gshock_api/iolib/button_pressed_io.py +0 -68
  60. gshock_api-2.0.30/src/gshock_api/iolib/dst_for_world_cities_io.py +0 -26
  61. gshock_api-2.0.30/src/gshock_api/iolib/dst_watch_state_io.py +0 -32
  62. gshock_api-2.0.30/src/gshock_api/iolib/events_io.py +0 -358
  63. gshock_api-2.0.30/src/gshock_api/iolib/settings_io.py +0 -129
  64. gshock_api-2.0.30/src/gshock_api/iolib/time_adjustement_io.py +0 -83
  65. gshock_api-2.0.30/src/gshock_api/iolib/time_io.py +0 -71
  66. gshock_api-2.0.30/src/gshock_api/iolib/timer_io.py +0 -62
  67. gshock_api-2.0.30/src/gshock_api/iolib/watch_condition_io.py +0 -52
  68. gshock_api-2.0.30/src/gshock_api/iolib/watch_name_io.py +0 -27
  69. gshock_api-2.0.30/src/gshock_api/iolib/world_cities_io.py +0 -27
  70. gshock_api-2.0.30/src/gshock_api/logger.py +0 -29
  71. gshock_api-2.0.30/src/gshock_api/mailsener.py +0 -20
  72. gshock_api-2.0.30/src/gshock_api/message_dispatcher.py +0 -82
  73. gshock_api-2.0.30/src/gshock_api/scanner.py +0 -59
  74. gshock_api-2.0.30/src/gshock_api/settings.py +0 -12
  75. gshock_api-2.0.30/src/gshock_api/utils.py +0 -101
  76. gshock_api-2.0.30/src/gshock_api.egg-info/requires.txt +0 -1
  77. {gshock_api-2.0.30 → gshock_api-2.0.32}/LICENSE.txt +0 -0
  78. {gshock_api-2.0.30 → gshock_api-2.0.32}/README.rst +0 -0
  79. {gshock_api-2.0.30 → gshock_api-2.0.32}/setup.cfg +0 -0
  80. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api/iolib/__init__.py +0 -0
  81. {gshock_api-2.0.30 → gshock_api-2.0.32}/src/gshock_api.egg-info/dependency_links.txt +0 -0
  82. {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.30
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: requests
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()