GeekMagicWeatherClockAPI 0.1.0__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.
@@ -0,0 +1,3 @@
1
+ from .core import SmallTV
2
+
3
+ __all__ = ["SmallTV"]
@@ -0,0 +1,199 @@
1
+ import requests
2
+ from pathlib import Path
3
+ from urllib.parse import quote
4
+
5
+
6
+ class SmallTV:
7
+ def __init__(self, ip: str):
8
+ self.ip = ip
9
+ 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",
18
+ }
19
+
20
+ def upload(self, file_path, retries=3):
21
+ """
22
+ Upload a GIF/image to the SmallTV.
23
+ Retries on malformed header responses from SmallTV firmware.
24
+ """
25
+
26
+ file_path = Path(file_path)
27
+
28
+ if not file_path.exists():
29
+ raise FileNotFoundError(file_path)
30
+
31
+ filename = file_path.name
32
+
33
+ with open(file_path, "rb") as f:
34
+ gif_data = f.read()
35
+
36
+ headers = {
37
+ "X-Requested-With": "XMLHttpRequest"
38
+ }
39
+
40
+ for attempt in range(1, retries + 1):
41
+ # Delete any partial/failed upload before retrying
42
+ 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
48
+
49
+ files = {
50
+ "update": (filename, gif_data, "image/gif"),
51
+ "image": (filename, gif_data, "image/gif"),
52
+ }
53
+
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
+ )
74
+
75
+ return False
76
+
77
+ def set_image(self, filename):
78
+ """
79
+ Set the currently displayed image.
80
+ """
81
+
82
+ encoded_filename = quote(filename)
83
+
84
+ r = requests.get(
85
+ f"{self.base_url}/set?img=/image/{encoded_filename}",
86
+ timeout=10
87
+ )
88
+
89
+ print("Set Status:", r.status_code)
90
+
91
+ return r
92
+
93
+ def set_theme(self, theme):
94
+ """
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"
107
+ """
108
+
109
+ if isinstance(theme, str):
110
+ theme_lookup = {
111
+ v.lower(): k
112
+ for k, v in self.THEMES.items()
113
+ }
114
+
115
+ theme_id = theme_lookup.get(theme.lower())
116
+
117
+ if theme_id is None:
118
+ raise ValueError(
119
+ f"Unknown theme '{theme}'"
120
+ )
121
+ else:
122
+ theme_id = int(theme)
123
+
124
+ if theme_id not in self.THEMES:
125
+ raise ValueError(
126
+ f"Theme must be between 1 and 7"
127
+ )
128
+
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)
139
+
140
+ return r
141
+
142
+ def set_brightness(self, value):
143
+ """
144
+ Set SmallTV brightness (0–100).
145
+ """
146
+
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
161
+
162
+ def delete(self, filename):
163
+ """
164
+ Delete an image from the SmallTV.
165
+ """
166
+
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):
179
+ """
180
+ Upload a file and immediately display it.
181
+ """
182
+
183
+ file_path = Path(file_path)
184
+
185
+ self.upload(file_path)
186
+
187
+ return self.set_image(file_path.name)
188
+
189
+ def replace(self, old_filename, new_file):
190
+ """
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
198
+
199
+ return self.upload_and_set(new_file)
@@ -0,0 +1,125 @@
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.
@@ -0,0 +1,7 @@
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,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ GeekMagicWeatherClockAPI