GeekMagicWeatherClockAPI 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

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.
@@ -4,196 +4,251 @@ from urllib.parse import quote
4
4
 
5
5
 
6
6
  class SmallTV:
7
- def __init__(self, ip: str):
7
+ """
8
+ HTTP client for controlling a GeekMagic SmallTV device.
9
+
10
+ All public methods return a standardized response dictionary::
11
+
12
+ {
13
+ "success": bool,
14
+ "status_code": int | None,
15
+ "response": str | None,
16
+ "error": str | None,
17
+ }
18
+ """
19
+
20
+ THEMES: dict[int, str] = {
21
+ 1: "Weather Clock Today",
22
+ 2: "Weather Forecast",
23
+ 3: "Photo Album",
24
+ 4: "Time Style 1",
25
+ 5: "Time Style 2",
26
+ 6: "Time Style 3",
27
+ 7: "Simple Weather Clock",
28
+ }
29
+
30
+ _THEME_LOOKUP: dict[str, int] = {} # populated lazily in __init__
31
+
32
+ def __init__(self, ip: str) -> None:
8
33
  self.ip = ip
9
34
  self.base_url = f"http://{ip}"
10
- self.THEMES = {
11
- 1: "Weather Clock Today",
12
- 2: "Weather Forecast",
13
- 3: "Photo Album",
14
- 4: "Time Style 1",
15
- 5: "Time Style 2",
16
- 6: "Time Style 3",
17
- 7: "Simple Weather Clock",
35
+
36
+ self._THEME_LOOKUP = {v.lower(): k for k, v in self.THEMES.items()}
37
+
38
+ self.session = requests.Session()
39
+ self.session.headers.update({"X-Requested-With": "XMLHttpRequest"})
40
+
41
+ # ------------------------------------------------------------------
42
+ # Internal helpers
43
+ # ------------------------------------------------------------------
44
+
45
+ @staticmethod
46
+ def _build_result(
47
+ success: bool,
48
+ status_code: int | None = None,
49
+ response: str | None = None,
50
+ error: str | None = None,
51
+ ) -> dict:
52
+ return {
53
+ "success": success,
54
+ "status_code": status_code,
55
+ "response": response,
56
+ "error": error,
18
57
  }
19
58
 
20
- def upload(self, file_path, retries=3):
59
+ def _get(self, endpoint: str, **kwargs) -> dict:
21
60
  """
22
- Upload a GIF/image to the SmallTV.
23
- Retries on malformed header responses from SmallTV firmware.
61
+ Perform a GET request against *endpoint* (relative path + query string).
62
+
63
+ Extra *kwargs* are forwarded to ``session.get()``.
64
+ """
65
+ url = f"{self.base_url}{endpoint}"
66
+ kwargs.setdefault("timeout", 10)
67
+
68
+ try:
69
+ r = self.session.get(url, **kwargs)
70
+ return self._build_result(
71
+ success=r.ok,
72
+ status_code=r.status_code,
73
+ response=r.text,
74
+ )
75
+ except requests.RequestException as exc:
76
+ return self._build_result(success=False, error=str(exc))
77
+ except Exception as exc:
78
+ return self._build_result(success=False, error=f"Unexpected error: {exc}")
79
+
80
+ def _post(self, endpoint: str, **kwargs) -> dict:
81
+ """
82
+ Perform a POST request against *endpoint* (relative path + query string).
83
+
84
+ Extra *kwargs* are forwarded to ``session.post()``.
24
85
  """
86
+ url = f"{self.base_url}{endpoint}"
87
+ kwargs.setdefault("timeout", 30)
25
88
 
89
+ try:
90
+ r = self.session.post(url, **kwargs)
91
+ return self._build_result(
92
+ success=r.ok,
93
+ status_code=r.status_code,
94
+ response=r.text,
95
+ )
96
+ except requests.exceptions.InvalidHeader as exc:
97
+ # SmallTV firmware occasionally returns malformed response headers.
98
+ return self._build_result(
99
+ success=False,
100
+ error=f"Malformed response headers from device: {exc}",
101
+ )
102
+ except requests.RequestException as exc:
103
+ return self._build_result(success=False, error=str(exc))
104
+ except Exception as exc:
105
+ return self._build_result(success=False, error=f"Unexpected error: {exc}")
106
+
107
+ # ------------------------------------------------------------------
108
+ # Public API
109
+ # ------------------------------------------------------------------
110
+
111
+ def upload(self, file_path, retries: int = 3) -> dict:
112
+ """
113
+ Upload a GIF/image to the SmallTV.
114
+
115
+ Retries up to *retries* times to work around malformed-header
116
+ responses emitted by some SmallTV firmware versions.
117
+
118
+ Returns a standardized result dict.
119
+ """
26
120
  file_path = Path(file_path)
27
121
 
28
122
  if not file_path.exists():
29
- raise FileNotFoundError(file_path)
123
+ return self._build_result(
124
+ success=False,
125
+ error=f"File not found: {file_path}",
126
+ )
30
127
 
31
128
  filename = file_path.name
32
129
 
33
- with open(file_path, "rb") as f:
34
- gif_data = f.read()
130
+ try:
131
+ gif_data = file_path.read_bytes()
132
+ except OSError as exc:
133
+ return self._build_result(success=False, error=str(exc))
35
134
 
36
- headers = {
37
- "X-Requested-With": "XMLHttpRequest"
38
- }
135
+ last_result: dict = {}
39
136
 
40
137
  for attempt in range(1, retries + 1):
41
- # Delete any partial/failed upload before retrying
138
+ # Clean up any partial upload from a previous failed attempt.
42
139
  if attempt > 1:
43
- print(f"Cleaning up failed upload, retrying... (attempt {attempt}/{retries})")
44
- try:
45
- self.delete(filename)
46
- except Exception:
47
- pass
140
+ self.delete(filename)
48
141
 
49
142
  files = {
50
143
  "update": (filename, gif_data, "image/gif"),
51
144
  "image": (filename, gif_data, "image/gif"),
52
145
  }
53
146
 
54
- try:
55
- r = requests.post(
56
- f"{self.base_url}/doUpload?dir=/image/",
57
- files=files,
58
- headers=headers,
59
- timeout=30
60
- )
61
- print("Upload Status:", r.status_code)
62
- return True
63
-
64
- except requests.exceptions.InvalidHeader:
65
- print(f"Upload failed (malformed headers from SmallTV firmware) — attempt {attempt}/{retries}")
66
- if attempt == retries:
67
- try:
68
- self.delete(filename)
69
- except Exception:
70
- pass
71
- raise RuntimeError(
72
- f"Upload of '{filename}' failed after {retries} attempts."
73
- )
147
+ result = self._post("/doUpload?dir=/image/", files=files)
148
+ last_result = result
74
149
 
75
- return False
150
+ if result["success"]:
151
+ return result
76
152
 
77
- def set_image(self, filename):
78
- """
79
- Set the currently displayed image.
80
- """
153
+ # Only retry on malformed-header errors (firmware quirk).
154
+ if result.get("error") and "malformed" not in result["error"].lower():
155
+ return result
81
156
 
82
- encoded_filename = quote(filename)
83
-
84
- r = requests.get(
85
- f"{self.base_url}/set?img=/image/{encoded_filename}",
86
- timeout=10
157
+ # All attempts exhausted — clean up and surface the last error.
158
+ self.delete(filename)
159
+ last_result["error"] = (
160
+ f"Upload of '{filename}' failed after {retries} attempt(s). "
161
+ f"Last error: {last_result.get('error')}"
87
162
  )
163
+ return last_result
88
164
 
89
- print("Set Status:", r.status_code)
90
-
91
- return r
92
-
93
- def set_theme(self, theme):
165
+ def set_image(self, filename: str) -> dict:
94
166
  """
95
- Set the SmallTV theme.
96
-
97
- Accepts:
98
- 1-7
99
- or
100
- "Weather Clock Today"
101
- "Weather Forecast"
102
- "Photo Album"
103
- "Time Style 1"
104
- "Time Style 2"
105
- "Time Style 3"
106
- "Simple Weather Clock"
167
+ Set the currently displayed image on the device.
107
168
  """
169
+ encoded = quote(filename)
170
+ return self._get(f"/set?img=/image/{encoded}")
108
171
 
109
- if isinstance(theme, str):
110
- theme_lookup = {
111
- v.lower(): k
112
- for k, v in self.THEMES.items()
113
- }
172
+ def set_theme(self, theme) -> dict:
173
+ """
174
+ Set the active SmallTV theme.
114
175
 
115
- theme_id = theme_lookup.get(theme.lower())
176
+ *theme* may be an integer (1–7) or one of the theme name strings:
116
177
 
178
+ * ``"Weather Clock Today"``
179
+ * ``"Weather Forecast"``
180
+ * ``"Photo Album"``
181
+ * ``"Time Style 1"``
182
+ * ``"Time Style 2"``
183
+ * ``"Time Style 3"``
184
+ * ``"Simple Weather Clock"``
185
+ """
186
+ if isinstance(theme, str):
187
+ theme_id = self._THEME_LOOKUP.get(theme.lower())
117
188
  if theme_id is None:
118
- raise ValueError(
119
- f"Unknown theme '{theme}'"
189
+ return self._build_result(
190
+ success=False,
191
+ error=f"Unknown theme '{theme}'. Valid themes: {list(self.THEMES.values())}",
120
192
  )
121
193
  else:
122
- theme_id = int(theme)
194
+ try:
195
+ theme_id = int(theme)
196
+ except (TypeError, ValueError) as exc:
197
+ return self._build_result(success=False, error=str(exc))
123
198
 
124
199
  if theme_id not in self.THEMES:
125
- raise ValueError(
126
- f"Theme must be between 1 and 7"
200
+ return self._build_result(
201
+ success=False,
202
+ error=f"Theme ID must be between 1 and 7, got {theme_id}.",
127
203
  )
128
204
 
129
- r = requests.get(
130
- f"{self.base_url}/set?theme={theme_id}",
131
- timeout=10
132
- )
133
-
134
- print(
135
- f"Theme set to {theme_id}: "
136
- f"{self.THEMES[theme_id]}"
137
- )
138
- print("Status:", r.status_code)
205
+ return self._get(f"/set?theme={theme_id}")
139
206
 
140
- return r
141
-
142
- def set_brightness(self, value):
207
+ def set_brightness(self, value) -> dict:
143
208
  """
144
- Set SmallTV brightness (0–100).
209
+ Set the display brightness (0–100).
145
210
  """
211
+ try:
212
+ value = int(value)
213
+ except (TypeError, ValueError) as exc:
214
+ return self._build_result(success=False, error=str(exc))
215
+
216
+ if not 0 <= value <= 100:
217
+ return self._build_result(
218
+ success=False,
219
+ error=f"Brightness must be between 0 and 100, got {value}.",
220
+ )
146
221
 
147
- value = int(value)
148
-
149
- if value < 0 or value > 100:
150
- raise ValueError("Brightness must be between 0 and 100")
151
-
152
- r = requests.get(
153
- f"{self.base_url}/set?brt={value}",
154
- timeout=10
155
- )
156
-
157
- print(f"Brightness set to {value}")
158
- print("Status:", r.status_code)
159
-
160
- return r
222
+ return self._get(f"/set?brt={value}")
161
223
 
162
- def delete(self, filename):
224
+ def delete(self, filename: str) -> dict:
163
225
  """
164
- Delete an image from the SmallTV.
226
+ Delete an image from the device.
165
227
  """
228
+ encoded = quote(filename)
229
+ return self._get(f"/delete?file=/image/{encoded}")
166
230
 
167
- encoded_filename = quote(filename)
168
-
169
- r = requests.get(
170
- f"{self.base_url}/delete?file=/image/{encoded_filename}",
171
- timeout=10
172
- )
173
-
174
- print("Delete Status:", r.status_code)
175
-
176
- return r
177
-
178
- def upload_and_set(self, file_path):
231
+ def upload_and_set(self, file_path) -> dict:
179
232
  """
180
233
  Upload a file and immediately display it.
181
- """
182
234
 
235
+ Returns the result of ``set_image`` on success, or the failed
236
+ ``upload`` result if the upload did not succeed.
237
+ """
183
238
  file_path = Path(file_path)
184
239
 
185
- self.upload(file_path)
240
+ upload_result = self.upload(file_path)
241
+ if not upload_result["success"]:
242
+ return upload_result
186
243
 
187
244
  return self.set_image(file_path.name)
188
245
 
189
- def replace(self, old_filename, new_file):
246
+ def replace(self, old_filename: str, new_file) -> dict:
190
247
  """
191
- Delete an old image, upload a new one, and display it.
192
- """
193
-
194
- try:
195
- self.delete(old_filename)
196
- except Exception:
197
- pass
248
+ Delete *old_filename*, upload *new_file*, and display it.
198
249
 
250
+ The delete step is best-effort; failure does not abort the operation.
251
+ Returns the result of ``upload_and_set``.
252
+ """
253
+ self.delete(old_filename)
199
254
  return self.upload_and_set(new_file)
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: GeekMagicWeatherClockAPI
3
+ Version: 0.1.1
4
+ Summary: Python wrapper for controlling the GeekMagic SmallTV
5
+ Project-URL: Homepage, https://github.com/eman225511/GeekMagicWeatherClockAPI
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: requests
10
+ Dynamic: license-file
11
+
12
+ # GeekMagicWeatherClockAPI
13
+
14
+ A Python wrapper for controlling the **GeekMagic SmallTV** over your local network. Supports uploading images/GIFs, switching themes, adjusting brightness, and managing files on the device.
15
+
16
+ [PyPI](https://pypi.org/project/GeekMagicWeatherClockAPI/)
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install GeekMagicWeatherClockAPI
24
+ ```
25
+
26
+ ## Requirements
27
+
28
+ - Python 3.10+
29
+ - [`requests`](https://pypi.org/project/requests/) (installed automatically)
30
+
31
+ ---
32
+
33
+ ## Setup
34
+
35
+ Find your SmallTV's local IP address (check your router's device list or the SmallTV's settings menu), then:
36
+
37
+ ```python
38
+ from GeekMagicWeatherClockAPI import SmallTV
39
+
40
+ tv = SmallTV("192.168.1.85")
41
+ ```
42
+
43
+ A persistent HTTP session is created automatically. All requests reuse this session, so there's no need to manage connections manually.
44
+
45
+ ---
46
+
47
+ ## Return values
48
+
49
+ Every method returns the same dictionary — no exceptions are raised for network or validation errors:
50
+
51
+ ```python
52
+ {
53
+ "success": bool, # True if the device responded successfully
54
+ "status_code": int | None, # HTTP status code, or None if the request never completed
55
+ "response": str | None, # Raw response body from the device
56
+ "error": str | None, # Human-readable error message on failure, else None
57
+ }
58
+ ```
59
+
60
+ ### Checking results
61
+
62
+ ```python
63
+ result = tv.set_brightness(50)
64
+
65
+ if result["success"]:
66
+ print("Done!")
67
+ else:
68
+ print("Something went wrong:", result["error"])
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Usage
74
+
75
+ ### Upload an image
76
+
77
+ ```python
78
+ tv.upload("my_animation.gif")
79
+
80
+ # Increase retries for flaky firmware responses
81
+ tv.upload("my_animation.gif", retries=5)
82
+ ```
83
+
84
+ ### Display an image
85
+
86
+ ```python
87
+ tv.set_image("my_animation.gif")
88
+ ```
89
+
90
+ ### Upload and immediately display
91
+
92
+ ```python
93
+ tv.upload_and_set("my_animation.gif")
94
+ ```
95
+
96
+ ### Replace an image
97
+
98
+ Deletes the old file, uploads the new one, and displays it — all in one call.
99
+
100
+ ```python
101
+ tv.replace("old_animation.gif", "new_animation.gif")
102
+ ```
103
+
104
+ ### Delete an image
105
+
106
+ ```python
107
+ tv.delete("my_animation.gif")
108
+ ```
109
+
110
+ ### Set brightness
111
+
112
+ Accepts a value from `0` to `100`.
113
+
114
+ ```python
115
+ tv.set_brightness(75)
116
+ ```
117
+
118
+ ### Set theme
119
+
120
+ Accepts either an integer (`1`–`7`) or the theme name as a string (case-insensitive).
121
+
122
+ ```python
123
+ tv.set_theme(3)
124
+ tv.set_theme("Photo Album")
125
+ ```
126
+
127
+ | ID | Theme Name |
128
+ |----|----------------------|
129
+ | 1 | Weather Clock Today |
130
+ | 2 | Weather Forecast |
131
+ | 3 | Photo Album |
132
+ | 4 | Time Style 1 |
133
+ | 5 | Time Style 2 |
134
+ | 6 | Time Style 3 |
135
+ | 7 | Simple Weather Clock |
136
+
137
+ ---
138
+
139
+ ## Examples
140
+
141
+ ### Basic setup and display
142
+
143
+ ```python
144
+ from GeekMagicWeatherClockAPI import SmallTV
145
+
146
+ tv = SmallTV("192.168.1.85")
147
+
148
+ tv.upload_and_set("spaceman.gif")
149
+ tv.set_brightness(30)
150
+ tv.set_theme("Weather Clock Today")
151
+ ```
152
+
153
+ ### Checking every result
154
+
155
+ ```python
156
+ tv = SmallTV("192.168.1.85")
157
+
158
+ for step, result in [
159
+ ("upload", tv.upload("clock.gif")),
160
+ ("display", tv.set_image("clock.gif")),
161
+ ("brightness", tv.set_brightness(60)),
162
+ ("theme", tv.set_theme(1)),
163
+ ]:
164
+ status = "OK" if result["success"] else f"FAILED — {result['error']}"
165
+ print(f"{step:12} {status}")
166
+ ```
167
+
168
+ ### Batch upload a folder of GIFs
169
+
170
+ ```python
171
+ from pathlib import Path
172
+
173
+ tv = SmallTV("192.168.1.85")
174
+
175
+ gifs = list(Path("./animations").glob("*.gif"))
176
+
177
+ for gif in gifs:
178
+ result = tv.upload(gif)
179
+ if result["success"]:
180
+ print(f"Uploaded: {gif.name}")
181
+ else:
182
+ print(f"Failed: {gif.name} — {result['error']}")
183
+ ```
184
+
185
+ ### Rotate through themes on a schedule
186
+
187
+ ```python
188
+ import time
189
+
190
+ tv = SmallTV("192.168.1.85")
191
+
192
+ themes = list(tv.THEMES.keys()) # [1, 2, 3, 4, 5, 6, 7]
193
+
194
+ for theme_id in themes:
195
+ result = tv.set_theme(theme_id)
196
+ if result["success"]:
197
+ print(f"Now showing: {tv.THEMES[theme_id]}")
198
+ time.sleep(10)
199
+ ```
200
+
201
+ ### Night mode — dim at a set hour
202
+
203
+ ```python
204
+ from datetime import datetime
205
+
206
+ tv = SmallTV("192.168.1.85")
207
+
208
+ hour = datetime.now().hour
209
+ brightness = 10 if 22 <= hour or hour < 7 else 80
210
+
211
+ result = tv.set_brightness(brightness)
212
+ print(f"Brightness set to {brightness}" if result["success"] else result["error"])
213
+ ```
214
+
215
+ ### Upload with retry and graceful fallback
216
+
217
+ ```python
218
+ tv = SmallTV("192.168.1.85")
219
+
220
+ result = tv.upload("hero.gif", retries=5)
221
+
222
+ if result["success"]:
223
+ tv.set_image("hero.gif")
224
+ else:
225
+ print("Upload failed after all retries:", result["error"])
226
+ # Fall back to a theme instead
227
+ tv.set_theme("Weather Clock Today")
228
+ ```
229
+
230
+ ### Swap a live image safely
231
+
232
+ ```python
233
+ tv = SmallTV("192.168.1.85")
234
+
235
+ # Removes "old.gif", uploads "new.gif", and displays it
236
+ result = tv.replace("old.gif", "new.gif")
237
+
238
+ if not result["success"]:
239
+ print("Replace failed:", result["error"])
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Notes
245
+
246
+ - The SmallTV firmware occasionally returns malformed `Content-Length` headers on upload responses. The `upload` method automatically retries and cleans up partial uploads when this happens.
247
+ - All methods return a structured dict and never raise — check `result["success"]` instead of wrapping calls in `try/except`.
248
+ - File management methods operate on the `/image/` directory on the device.
249
+ - The `THEMES` dict is a public class attribute and can be read directly to enumerate valid theme names: `SmallTV.THEMES`.
250
+
251
+ ---
252
+
253
+ > **Dev note:** on release, bump the version in `pyproject.toml` and run `python -m build` then `twine upload dist/*`.
@@ -0,0 +1,7 @@
1
+ GeekMagicWeatherClockAPI/__init__.py,sha256=sIM2Yj7PGpD0r8SrPGvoboz_USBJww7bQB-71qM7ZBc,49
2
+ GeekMagicWeatherClockAPI/core.py,sha256=FsMzJKNj3_r2nyGt82oV2LDKe9tu0w3Rw5Qamitq0Eg,8449
3
+ geekmagicweatherclockapi-0.1.1.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
4
+ geekmagicweatherclockapi-0.1.1.dist-info/METADATA,sha256=25p8KkZuC8V6Qvi3GhFQOl0917B6yQQ0IQR3EfxkQwQ,5935
5
+ geekmagicweatherclockapi-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ geekmagicweatherclockapi-0.1.1.dist-info/top_level.txt,sha256=3viXouZ_1SkVcqd-ChcYP3Nj1AoIoa2xFxm_XfsbPGQ,25
7
+ geekmagicweatherclockapi-0.1.1.dist-info/RECORD,,
@@ -1,125 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: GeekMagicWeatherClockAPI
3
- Version: 0.1.0
4
- Summary: Python wrapper for controlling the GeekMagic SmallTV
5
- Project-URL: Homepage, https://github.com/eman225511/GeekMagicWeatherClockAPI
6
- Requires-Python: >=3.7
7
- Description-Content-Type: text/markdown
8
- License-File: LICENSE
9
- Requires-Dist: requests
10
- Dynamic: license-file
11
-
12
- # GeekMagicWeatherClockAPI
13
-
14
- A simple Python wrapper for controlling the **GeekMagic SmallTV** over your local network. Supports uploading images/GIFs, switching themes, adjusting brightness, and managing files on the device.
15
-
16
- ---
17
-
18
- ## Installation
19
-
20
- ```bash
21
- pip install GeekMagicWeatherClockAPI
22
- ```
23
-
24
- ## Requirements
25
-
26
- - Python 3.7+
27
- - [`requests`](https://pypi.org/project/requests/) (installed automatically)
28
-
29
- ---
30
-
31
- ## Setup
32
-
33
- Find your SmallTV's local IP address (check your router's device list or the SmallTV's settings menu), then:
34
-
35
- ```python
36
- from GeekMagicWeatherClockAPI import SmallTV
37
-
38
- tv = SmallTV("192.168.1.85")
39
- ```
40
-
41
- ---
42
-
43
- ## Usage
44
-
45
- ### Upload an image
46
-
47
- ```python
48
- tv.upload("my_animation.gif")
49
- tv.upload("my_animation.gif", retries=5) # Custom retry count
50
- ```
51
-
52
- ### Display an image
53
-
54
- ```python
55
- tv.set_image("my_animation.gif")
56
- ```
57
-
58
- ### Upload and immediately display
59
-
60
- ```python
61
- tv.upload_and_set("my_animation.gif")
62
- ```
63
-
64
- ### Replace an image
65
-
66
- ```python
67
- tv.replace("old_animation.gif", "new_animation.gif")
68
- ```
69
-
70
- ### Delete an image
71
-
72
- ```python
73
- tv.delete("my_animation.gif")
74
- ```
75
-
76
- ### Set brightness
77
-
78
- Accepts a value from `0` to `100`.
79
-
80
- ```python
81
- tv.set_brightness(75)
82
- ```
83
-
84
- ### Set theme
85
-
86
- Accepts either an integer (`1`–`7`) or the theme name as a string (case-insensitive).
87
-
88
- ```python
89
- tv.set_theme(3)
90
- tv.set_theme("Photo Album")
91
- ```
92
-
93
- | ID | Theme Name |
94
- |----|----------------------|
95
- | 1 | Weather Clock Today |
96
- | 2 | Weather Forecast |
97
- | 3 | Photo Album |
98
- | 4 | Time Style 1 |
99
- | 5 | Time Style 2 |
100
- | 6 | Time Style 3 |
101
- | 7 | Simple Weather Clock |
102
-
103
- ---
104
-
105
- ## Full Example
106
-
107
- ```python
108
- from GeekMagicWeatherClockAPI import SmallTV
109
-
110
- tv = SmallTV("192.168.1.85")
111
-
112
- tv.upload_and_set("spaceman.gif")
113
- tv.set_brightness(30)
114
- tv.set_theme("Weather Clock Today")
115
- tv.replace("spaceman.gif", "new_image.gif")
116
- tv.delete("new_image.gif")
117
- ```
118
-
119
- ---
120
-
121
- ## Notes
122
-
123
- - The SmallTV firmware occasionally returns malformed `Content-Length` headers on upload responses. The `upload` method treats this as a failure and retries automatically.
124
- - All methods can raise `requests.exceptions.ConnectionError` if the device is unreachable.
125
- - File management methods operate on the `/image/` directory on the device.
@@ -1,7 +0,0 @@
1
- GeekMagicWeatherClockAPI/__init__.py,sha256=sIM2Yj7PGpD0r8SrPGvoboz_USBJww7bQB-71qM7ZBc,49
2
- GeekMagicWeatherClockAPI/core.py,sha256=FdqOxePm5qN2xBwQ9iEpfRBtHXj7cJ_rnUvGbmeg7i8,5269
3
- geekmagicweatherclockapi-0.1.0.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
4
- geekmagicweatherclockapi-0.1.0.dist-info/METADATA,sha256=ZcW-myU1Gjf9_o6svNkow4z3GInKWe-9nr_PEe_fCak,2704
5
- geekmagicweatherclockapi-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
- geekmagicweatherclockapi-0.1.0.dist-info/top_level.txt,sha256=3viXouZ_1SkVcqd-ChcYP3Nj1AoIoa2xFxm_XfsbPGQ,25
7
- geekmagicweatherclockapi-0.1.0.dist-info/RECORD,,