Skip to content

Commit d2ad31d

Browse files
authored
Merge pull request #3215 from adafruit/xteink_demos
adding xteink x4 demos
2 parents 0aa3df0 + 3e34f8d commit d2ad31d

File tree

12 files changed

+10314
-0
lines changed

12 files changed

+10314
-0
lines changed
32.2 KB
Binary file not shown.
375 KB
Binary file not shown.
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
# pylint: disable=redefined-outer-name, eval-used, wrong-import-order, unsubscriptable-object
5+
6+
"""
7+
Xteink X4 Weather Display Demo
8+
Based on MagTag Weather by Carter Nelson
9+
"""
10+
11+
import time
12+
import os
13+
import board
14+
import alarm
15+
import displayio
16+
import adafruit_imageload
17+
import ssl
18+
import wifi
19+
import socketpool
20+
import adafruit_requests
21+
from adafruit_bitmap_font import bitmap_font
22+
from adafruit_display_text import label
23+
import gc
24+
25+
gc.collect()
26+
27+
display = board.DISPLAY
28+
display.rotation = 270
29+
DISPLAY_WIDTH = display.width
30+
31+
try:
32+
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
33+
except TypeError:
34+
print("Could not find WiFi info. Check your settings.toml file!")
35+
raise
36+
37+
# --| USER CONFIG |--------------------------
38+
LAT = 40.7128 # latitude
39+
LON = -74.0060 # longitude
40+
TMZ = "America/New_York" # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
41+
METRIC = False # set to True for metric units
42+
CITY = "New York, NY" # optional
43+
# -------------------------------------------
44+
45+
pool = socketpool.SocketPool(wifi.radio)
46+
requests = adafruit_requests.Session(pool, ssl.create_default_context())
47+
URL = f"https://api.open-meteo.com/v1/forecast?latitude={LAT}&longitude={LON}&"
48+
URL += "daily=weather_code,temperature_2m_max,temperature_2m_min"
49+
URL += ",sunrise,sunset"
50+
URL += "&timeformat=unixtime"
51+
URL += f"&timezone={TMZ}"
52+
gc.collect()
53+
resp_data = requests.get(URL)
54+
#resp_data = get_forecast()
55+
print("got url")
56+
forecast_data = resp_data.json()
57+
58+
# ----------------------------
59+
# Define various assets
60+
# ----------------------------
61+
gc.collect()
62+
font_file = "/fonts/Arial-Bold-24.bdf"
63+
font = bitmap_font.load_font(font_file)
64+
BACKGROUND_BMP = "/bmps/weather_bg_vert.bmp"
65+
ICONS_LARGE_FILE = "/bmps/weather-icons.bmp"
66+
DAYS = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
67+
MONTHS = (
68+
"January",
69+
"February",
70+
"March",
71+
"April",
72+
"May",
73+
"June",
74+
"July",
75+
"August",
76+
"September",
77+
"October",
78+
"November",
79+
"December",
80+
)
81+
82+
# Weather Code Information from https://open-meteo.com/en/docs
83+
# Code Description
84+
# 0 Clear sky
85+
# 1, 2, 3 Mainly clear, partly cloudy, and overcast
86+
# 45, 48 Fog and depositing rime fog
87+
# 51, 53, 55 Drizzle: Light, moderate, and dense intensity
88+
# 56, 57 Freezing Drizzle: Light and dense intensity
89+
# 61, 63, 65 Rain: Slight, moderate and heavy intensity
90+
# 66, 67 Freezing Rain: Light and heavy intensity
91+
# 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity
92+
# 77 Snow grains
93+
# 80, 81, 82 Rain showers: Slight, moderate, and violent
94+
# 85, 86 Snow showers slight and heavy
95+
# 95 * Thunderstorm: Slight or moderate
96+
# 96, 99 * Thunderstorm with slight and heavy hail
97+
98+
# Map the above WMO codes to index of icon in 3x3 spritesheet
99+
WMO_CODE_TO_ICON = (
100+
(0,), # 0 = sunny
101+
(1,), # 1 = partly sunny/cloudy
102+
(2, 3, 45, 48,), # 2 = cloudy/very cloudy/fog
103+
(61, 63, 65, 51, 53, 55, 80, 81, 82), # 4 = rain/showers
104+
(95, 96, 99), # 6 = storms
105+
(56, 57, 66, 67, 71, 73, 75, 77, 85, 86), # 7 = snow
106+
)
107+
108+
# ----------------------------
109+
# Backgrounnd bitmap
110+
# ----------------------------
111+
gc.collect()
112+
splash = displayio.Group()
113+
bitmap = displayio.OnDiskBitmap(BACKGROUND_BMP)
114+
tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
115+
splash.append(tile_grid)
116+
display.root_group = splash
117+
print("got background")
118+
119+
# ----------------------------
120+
# Weather icons sprite sheet
121+
# ----------------------------
122+
gc.collect()
123+
icons_large_bmp, icons_large_pal = adafruit_imageload.load(ICONS_LARGE_FILE)
124+
print("got icon sheet")
125+
126+
# /////////////////////////////////////////////////////////////////////////
127+
# helper functions
128+
129+
def temperature_text(tempC):
130+
if METRIC:
131+
return "{:3.0f}C".format(tempC)
132+
else:
133+
return "{:3.0f}F".format(32.0 + 1.8 * tempC)
134+
135+
136+
def update_today(data):
137+
"""Update today weather info."""
138+
# date text
139+
s = data["daily"]["time"][0] + data["utc_offset_seconds"]
140+
t = time.localtime(s)
141+
today_day.text = "{}".format(
142+
DAYS[t.tm_wday].upper())
143+
print(today_day.text)
144+
today_date.text = "{} {}, {}".format(
145+
MONTHS[t.tm_mon - 1].upper(), t.tm_mday, t.tm_year
146+
)
147+
# weather icon
148+
w = data["daily"]["weather_code"][0]
149+
today_icon[0] = next(i for i, t in enumerate(WMO_CODE_TO_ICON) if w in t)
150+
# temperatures
151+
today_temp.text = f"H: {temperature_text(data['daily']['temperature_2m_max'][0])} "
152+
today_temp.text += f"L: {temperature_text(data['daily']['temperature_2m_min'][0])}"
153+
# sunrise/set
154+
sr = time.localtime(data["daily"]["sunrise"][0] + data["utc_offset_seconds"])
155+
ss = time.localtime(data["daily"]["sunset"][0] + data["utc_offset_seconds"])
156+
today_sunrise.text = "{:2d}:{:02d} AM".format(sr.tm_hour, sr.tm_min)
157+
today_sunset.text = "{:2d}:{:02d} PM".format(ss.tm_hour - 12, ss.tm_min)
158+
159+
# ===========
160+
# U I
161+
# ===========
162+
print("making ui")
163+
today_day = label.Label(font, text="?" * 30, color=0x000000)
164+
today_day.anchor_point = (0.5, 0)
165+
today_day.anchored_position = (DISPLAY_WIDTH / 2, 106)
166+
today_date = label.Label(font, text="?" * 30, color=0x000000)
167+
today_date.anchor_point = (0.5, 0)
168+
today_date.anchored_position = (DISPLAY_WIDTH / 2, 140)
169+
170+
location_name = label.Label(font, color=0x000000)
171+
if CITY:
172+
location_name.text = f"{CITY[:16]}"
173+
else:
174+
location_name.text = f"({LAT},{LON})"
175+
location_name.anchor_point = (0.5, 0)
176+
location_name.anchored_position = (DISPLAY_WIDTH / 2, 210)
177+
178+
today_icon = displayio.TileGrid(
179+
icons_large_bmp,
180+
pixel_shader=icons_large_pal,
181+
x=203,
182+
y=275,
183+
width=1,
184+
height=1,
185+
tile_width=74,
186+
tile_height=74,
187+
)
188+
today_icon.x = int(DISPLAY_WIDTH / 2 - today_icon.tile_width / 2)
189+
190+
today_temp = label.Label(font, text="H: +100F", color=0x000000)
191+
today_temp.anchor_point = (0, 0)
192+
today_temp.anchored_position = (163, 415)
193+
194+
today_sunrise = label.Label(font, text="12:12 PM", color=0x000000)
195+
today_sunrise.anchor_point = (0, 0)
196+
today_sunrise.anchored_position = (202, 520)
197+
198+
today_sunset = label.Label(font, text="12:12 PM", color=0x000000)
199+
today_sunset.anchor_point = (0, 0)
200+
today_sunset.anchored_position = (202, 614)
201+
today_banner = displayio.Group()
202+
today_banner.append(today_day)
203+
today_banner.append(today_date)
204+
today_banner.append(location_name)
205+
today_banner.append(today_icon)
206+
today_banner.append(today_temp)
207+
today_banner.append(today_sunrise)
208+
today_banner.append(today_sunset)
209+
210+
display.root_group.append(today_banner)
211+
212+
# ===========
213+
# M A I N
214+
# ===========
215+
gc.collect()
216+
print("Updating...")
217+
update_today(forecast_data)
218+
219+
print("Refreshing...")
220+
time.sleep(display.time_to_refresh + 1)
221+
display.refresh()
222+
time.sleep(display.time_to_refresh + 1)
223+
224+
print("Sleeping...")
225+
wake_alarm = alarm.wake_alarm
226+
pin_alarm = alarm.pin.PinAlarm(pin=board.BUTTON, value=False, pull=True)
227+
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
228+
# entire code will run again

0 commit comments

Comments
 (0)