Compare commits

...

7 commits

Author SHA1 Message Date
foamyguy
e44ae3524b
Merge pull request #3088 from FoamyGuy/fruitjam_irc
Fruit Jam IRC app
2025-07-29 11:12:57 -05:00
foamyguy
f8c2b61fea more pylint 2025-07-29 10:30:35 -05:00
foamyguy
f5f0805802 pylint and format 2025-07-29 10:21:40 -05:00
foamyguy
b036cc6252 icon and metadata for FJOS 2025-07-29 10:01:06 -05:00
foamyguy
d22c263f6f fruit jam IRC app 2025-07-29 09:37:52 -05:00
foamyguy
895120fd8c
Merge pull request #3087 from FoamyGuy/magtag_updates
Change deprecated splash usage to root_group
2025-07-28 16:02:31 -05:00
foamyguy
989db55caf change deprecated splash usage to root_group 2025-07-25 16:46:08 -05:00
54 changed files with 928 additions and 107 deletions

Binary file not shown.

View file

@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
from os import getenv
from displayio import Group
from terminalio import FONT
import supervisor
import audiocore
import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_color_terminal import ColorTerminal
from adafruit_fruitjam.peripherals import Peripherals
from curses_irc_client import run_irc_client
# Configuration - modify these values as needed
IRC_CONFIG = {
"server": "irc.libera.chat", # Example: irc.libera.chat, irc.freenode.net
# "port": 6667, # 6667 - clear text
"port": 6697, # 6697 - TLS encrypted
"username": "",
"channel": "#adafruit-fruit-jam",
}
if IRC_CONFIG["username"] == "":
raise ValueError("username must be set in IRC_CONFIG")
main_group = Group()
display = supervisor.runtime.display
font_bb = FONT.get_bounding_box()
screen_size = (display.width // font_bb[0], display.height // font_bb[1])
terminal = ColorTerminal(FONT, screen_size[0], screen_size[1])
main_group.append(terminal.tilegrid)
display.root_group = main_group
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(ssid, password)
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("IRC Configuration:")
print(f"Server: {IRC_CONFIG['server']}:{IRC_CONFIG['port']}")
print(f"Nickname: {IRC_CONFIG['username']}")
print(f"Channel: {IRC_CONFIG['channel']}")
print("-" * 40)
fruit_jam_peripherals = Peripherals()
beep_wave = audiocore.WaveFile("beep.wav")
run_irc_client(
esp,
IRC_CONFIG,
terminal,
terminal.tilegrid,
audio_interface=fruit_jam_peripherals.audio,
beep_wave=beep_wave,
)

View file

@ -0,0 +1,249 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import adafruit_dang as curses
from irc_client import IRCClient
ANSI_BLACK_ON_GREY = chr(27) + "[30;100m"
ANSI_RESET = chr(27) + "[0m"
class Window:
"""
Terminal Window class that supports basic scrolling.
"""
def __init__(self, n_rows, n_cols, row=0, col=0):
self.n_rows = n_rows
self.n_cols = n_cols
self.row = row
self.col = col
@property
def bottom(self):
return self.row + self.n_rows - 1
def up(self, cursor): # pylint: disable=invalid-name
if cursor.row == self.row - 1 and self.row > 0:
self.row -= 1
def down(self, buffer, cursor):
if cursor.row == self.bottom + 1 and self.bottom < len(buffer) - 1:
self.row += 1
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
n_pages = cursor.col // (self.n_cols - right_margin)
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
def translate(self, cursor):
return cursor.row - self.row, cursor.col - self.col
def irc_client_main(
stdscr,
radio,
irc_config,
terminal_tilegrid=None,
audio_interface=None,
beep_wave=None,
):
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
"""
Main curses IRC client application loop.
"""
irc_client = IRCClient(
radio, irc_config, audio_interface=audio_interface, beep_wave=beep_wave
)
irc_client.connect()
# irc_client.join()
window = Window(terminal_tilegrid.height, terminal_tilegrid.width)
stdscr.erase()
img = [None] * window.n_rows
status_bar = {
"user_message": None,
"user_message_shown_time": 0,
}
cur_row_index = 0
user_input = ""
def show_user_message(message):
"""
Show a status message to the user
"""
status_bar["user_message"] = message + (
" " * (window.n_cols - 1 - len(message))
)
status_bar["user_message_shown_time"] = time.monotonic()
def setline(row, line):
"""
Set a line of text in the terminal window.
"""
if img[row] == line:
return
img[row] = line
line += " " * (window.n_cols - len(line) - 1)
stdscr.addstr(row, 0, line)
def get_page(row_index):
"""
Get a page of messages from the message buffer.
"""
page_len = window.n_rows - 2
page_start = max((len(irc_client.message_buffer) + row_index) - page_len, 0)
page_end = page_start + page_len
page = irc_client.message_buffer[page_start:page_end]
return page
# pylint: disable=too-many-nested-blocks
try:
# main application loop
while True:
lastrow = 0
lines_added = irc_client.update()
cur_page = get_page(cur_row_index)
if lines_added > 0 and len(cur_page) < window.n_rows - 2:
cur_row_index = max(cur_row_index - lines_added, 0)
cur_page = get_page(cur_row_index)
for row, line in enumerate(cur_page):
lastrow = row
setline(row, line)
for row in range(lastrow + 1, window.n_rows - 2):
setline(row, "")
user_input_row = window.n_rows - 2
if user_input:
setline(user_input_row, user_input)
else:
setline(user_input_row, " " * (window.n_cols - 1))
user_message_row = terminal_tilegrid.height - 1
if status_bar["user_message"] is None:
message = f" {irc_config['username']} | {irc_config['server']} | {irc_config['channel']}" # pylint: disable=line-too-long
message += " " * (terminal_tilegrid.width - len(message) - 1)
line = f"{ANSI_BLACK_ON_GREY}{message}{ANSI_RESET}"
else:
line = f"{ANSI_BLACK_ON_GREY}{status_bar['user_message']}{ANSI_RESET}"
if status_bar["user_message_shown_time"] + 3.0 < time.monotonic():
status_bar["user_message"] = None
setline(user_message_row, line)
# read from the keyboard
k = stdscr.getkey()
if k is not None:
if len(k) == 1 and " " <= k <= "~":
user_input += k
elif k == "\n": # enter key pressed
if not user_input.startswith("/"):
print(f"sending: {user_input}")
irc_client.send_message(user_input)
user_input = ""
else: # slash commands
parts = user_input.split(" ", 1)
if parts[0] in {"/j", "/join"}:
if len(parts) >= 2 and parts[1] != "":
if parts[1] != irc_client.config["channel"]:
irc_client.join(parts[1])
user_input = ""
else:
show_user_message("Already in channel")
user_input = ""
else:
show_user_message(
"Invalid /join arg. Use: /join <channel>"
)
user_input = ""
elif parts[0] == "/msg":
to_user, message_to_send = parts[1].split(" ", 1)
irc_client.send_dm(to_user, message_to_send)
user_input = ""
elif parts[0] == "/beep":
to_user = parts[1]
message_to_send = "*Beep*\x07"
irc_client.send_dm(to_user, message_to_send)
user_input = ""
elif parts[0] == "/op":
user_to_op = parts[1]
irc_client.op(user_to_op)
user_input = ""
elif parts[0] == "/deop":
user_to_op = parts[1]
irc_client.deop(user_to_op)
user_input = ""
elif parts[0] == "/kick":
user_to_kick = parts[1]
irc_client.kick(user_to_kick)
user_input = ""
elif parts[0] == "/ban":
user_to_ban = parts[1]
irc_client.ban(user_to_ban)
user_input = ""
elif parts[0] == "/unban":
user_to_unban = parts[1]
irc_client.unban(user_to_unban)
user_input = ""
elif parts[0] == "/whois":
user_to_check = parts[1]
irc_client.whois(user_to_check)
user_input = ""
elif k in ("KEY_BACKSPACE", "\x7f", "\x08"):
user_input = user_input[:-1]
elif k == "KEY_UP":
page_len = window.n_rows - 2
if len(irc_client.message_buffer) > page_len:
page_start = (
len(irc_client.message_buffer) + cur_row_index
) - page_len
if page_start > 0:
cur_row_index -= 1
elif k == "KEY_DOWN":
if cur_row_index < 0:
cur_row_index += 1
elif k == "KEY_PGUP":
page_len = window.n_rows - 2
if len(irc_client.message_buffer) > page_len:
page_start = (
len(irc_client.message_buffer) + cur_row_index
) - page_len
if page_start > 0:
cur_row_index -= 6
elif k == "KEY_PGDN":
if cur_row_index <= 0:
cur_row_index = cur_row_index + 6
else:
print(f"unknown key: {k}")
except KeyboardInterrupt as exc:
irc_client.disconnect()
raise KeyboardInterrupt from exc
def run_irc_client(
radio, irc_config, terminal, terminal_tilegrid, audio_interface=None, beep_wave=None
):
"""
Entry point to run the curses IRC client application.
"""
return curses.custom_terminal_wrapper(
terminal,
irc_client_main,
radio,
irc_config,
terminal_tilegrid,
audio_interface,
beep_wave,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,493 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import adafruit_connection_manager
ANSI_ESCAPE_CODES = [
chr(27) + "[30m",
chr(27) + "[31m",
chr(27) + "[32m",
chr(27) + "[33m",
chr(27) + "[34m",
chr(27) + "[35m",
chr(27) + "[36m",
]
ANSI_RESET = chr(27) + "[0m"
class IRCClient:
"""
Handles interaction with IRC Server and makes incoming messages available.
:param radio: The network radio to connect with.
:param dict irc_config: Dictionary containing IRC configration for
server, port, username and channel.
:param audio_interface: Optional interface to play audio from for beep messages
:param beep_wave: Optional wave file to use for beep messages
:param int max_line_length: Maximum characters per line to format messages into.
"""
def __init__(
self,
radio,
irc_config,
audio_interface=None,
beep_wave=None,
max_line_length=120,
):
self.radio = radio
self.config = irc_config
required = {"username", "server", "channel"}
for key in required:
if key not in self.config:
raise ValueError(
f"missing required config key. Required keys are: {required}"
)
if "port" not in self.config:
self.config["port"] = 6667
if "timeout" not in self.config:
self.config["timeout"] = 120
self.pool = adafruit_connection_manager.get_radio_socketpool(radio)
self.connection_manager = adafruit_connection_manager.get_connection_manager(
self.pool
)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(radio)
print(f"Connecting to {self.config['server']}:{self.config['port']}...")
self.socket = self.connection_manager.get_socket(
self.config["server"],
self.config["port"],
"",
timeout=0.01,
is_ssl=True,
ssl_context=ssl_context,
)
print("Connected")
# color to use for next unique username
self.next_color_index = 4
# map of unique usernames to color
self.user_color_map = {}
# buffer for incoming data until it's a full line
self.line_buffer = ""
# buffer for full incoming chat messages
self.message_buffer = []
# whether to show whois reply message on screen
self.show_whois_reply = False
self.audio_interface = audio_interface
if audio_interface is not None:
self.beep_wave = beep_wave
self.max_line_length = max_line_length
def connect(self):
"""
Connect to IRC Server
"""
# Send nick and user info
self.socket.send(f"NICK {self.config['username']}\r\n".encode("utf-8"))
self.socket.send(
f"USER {self.config['username']} 0 * :{self.config['username']}\r\n".encode(
"utf-8"
)
)
def disconnect(self):
"""
Disconnect from IRC Server
"""
self.socket.send("QUIT :Goodbye\r\n".encode("utf-8"))
self.socket.close()
def readlines(self):
"""
Read incoming data from the socket and return a list of lines read.
"""
lines = []
# Receive data
data = self.socket.recv(4096).decode("utf-8")
if not data:
raise RuntimeError("Connection closed by server")
self.line_buffer += data
# Process complete lines
while "\r\n" in self.line_buffer:
line, self.line_buffer = self.line_buffer.split("\r\n", 1)
if line:
lines.append(line)
return lines
def update(self):
"""
Check for udpates from the server. Main loop of the program should call this.
"""
updated_display_lines = 0
try:
lines = self.readlines()
for line in lines:
updated_display_lines += self.process_message(line)
except OSError as e:
# no data before timeout
# print(e)
if "ETIMEDOUT" not in str(e):
raise RuntimeError(e) from e
# raise RuntimeError("Connection timed out")
return updated_display_lines
def send_message(self, message):
"""
Send a message to the channel that the user is in.
"""
irc_command = f"PRIVMSG {self.config['channel']} :{message}\r\n"
self.socket.send(irc_command.encode("utf-8"))
self.process_message(
f":{self.config['username']}!~{self.config['username']}@localhost "
+ irc_command[:-2]
)
def send_dm(self, to_user, message):
"""
Send a direct message to a specified user.
"""
irc_command = f"PRIVMSG {to_user} :{message}\r\n"
self.socket.send(irc_command.encode("utf-8"))
color = self.get_color_for_user(to_user)
self.message_buffer.append(f"DM out: <{color}{to_user}{ANSI_RESET}> {message}")
def op(self, user):
"""
Make specified user an operator in the channel that the user is in.
You must already be an operator to grant operator privilege.
"""
op_cmd = f"MODE {self.config['channel']} +o {user}\r\n"
self.socket.send(op_cmd.encode("utf-8"))
def deop(self, user):
"""
Remove operator privilege from the specified user for this channel.
"""
deop_cmd = f"MODE {self.config['channel']} -o {user}\r\n"
self.socket.send(deop_cmd.encode("utf-8"))
def kick(self, user):
"""
Kick a specified user from the channel.
"""
kick_cmd = f"KICK {self.config['channel']} {user}\r\n"
self.socket.send(kick_cmd.encode("utf-8"))
def get_technical_name(self, nickname):
"""
Get the full technical name of a user given a nickname
"""
start_time = time.monotonic()
whois_cmd = f"WHOIS {nickname}\r\n"
self.socket.send(whois_cmd.encode("utf-8"))
whois_resp_lines = None
while whois_resp_lines is None and start_time + 3.0 > time.monotonic():
try:
whois_resp_lines = self.readlines()
except OSError as e:
if "ETIMEDOUT" in str(e):
whois_resp_lines = None
else:
raise RuntimeError(e) from e
if whois_resp_lines is None:
return None
for line in whois_resp_lines:
line = line.lstrip("\0")
parts = line.split(" ", 2)
if len(parts) >= 2:
command = parts[1]
if command != "311":
self.process_message(line)
continue
whois_response = parts[2].split(" ", 1)[1]
response_parts = whois_response.split(" ")
technical_name = f"*!{response_parts[1]}@{response_parts[2]}"
return technical_name
return None
def ban(self, user):
"""
Ban the specified user from the channel
"""
technical_name = self.get_technical_name(user)
if technical_name is not None:
ban_cmd = f"MODE {self.config['channel']} +b {technical_name}\r\n"
self.socket.send(ban_cmd.encode("utf-8"))
else:
self.message_buffer.append(
f"{ANSI_RESET} Error: failed whois lookup for ban"
)
def unban(self, user):
"""
Unban the specified user from the channel
"""
technical_name = self.get_technical_name(user)
if technical_name is not None:
ban_cmd = f"MODE {self.config['channel']} -b {technical_name}\r\n"
self.socket.send(ban_cmd.encode("utf-8"))
else:
self.message_buffer.append(
f"{ANSI_RESET} Error: failed whois lookup for unban"
)
def whois(self, user):
"""
Run a whois query on the specified user
"""
self.show_whois_reply = True
whois_cmd = f"WHOIS {user}\r\n"
self.socket.send(whois_cmd.encode("utf-8"))
def leave_channel(self):
"""
Leave the channel
"""
self.socket.send(f"PART {self.config['channel']}\r\n".encode("utf-8"))
def join(self, new_channel=None):
"""
Join the specified channel. This will leave the prior channel.
"""
if new_channel is not None and new_channel != self.config["channel"]:
self.leave_channel()
self.config["channel"] = new_channel
print(f"Joining channel {self.config['channel']}...")
self.socket.send(f"JOIN {self.config['channel']}\r\n".encode("utf-8"))
self.message_buffer.append(f"{ANSI_RESET}* Joined {self.config['channel']} *")
def get_color_for_user(self, username):
"""
Get the color to use for the specified username
"""
if username not in self.user_color_map:
self.user_color_map[username] = self.next_color_index
self.next_color_index += 1
if self.next_color_index > 6:
self.next_color_index = 1
return ANSI_ESCAPE_CODES[self.user_color_map[username]]
@staticmethod
def split_string_chunks(s, chunk_size):
"""
Split a string into chunks of specified size.
"""
chunks = []
for i in range(0, len(s), chunk_size):
chunks.append(s[i : i + chunk_size])
return chunks
def process_message(self, message):
# pylint: disable=too-many-branches, too-many-statements
"""
Process an incoming IRC message
:param message: The message that came from the IRC server.
:return lines_added: The number of lines added to the display
"""
# pylint: disable=too-many-locals
lines_added = 0
message = message.lstrip("\x00")
print(f"RAW: {message.encode('utf-8')}")
# Handle PING messages (keep connection alive)
if message.startswith("PING"):
pong_response = message.replace("PING", "PONG")
self.socket.send(f"{pong_response}\r\n".encode("utf-8"))
print("Responded to PING")
return 0
# Parse IRC message format: :prefix COMMAND params
parts = message.split(" ", 2)
# pylint: disable=too-many-nested-blocks
if len(parts) >= 2:
command = parts[1]
try:
command_num = int(command)
except ValueError:
command_num = None
# End of MOTD - now we can join the channel
if command in {"376", "422"}: # 422 is "no MOTD"
# join channel
self.join()
# Welcome messages (001-004 are standard welcome messages)
elif command in [
"001",
"002",
"003",
"004",
"251",
"252",
"253",
"254",
"255",
"265",
"266",
"375",
"372",
]:
if len(parts) >= 3:
welcome_text = parts[2]
if welcome_text.startswith(":"):
welcome_text = welcome_text[1:]
print(
f"'{welcome_text[0:11]}' startswith '{self.config['username']}' ? {welcome_text.startswith(self.config['username'])}" # pylint: disable=line-too-long
)
if welcome_text.startswith(self.config["username"]):
welcome_text = welcome_text.replace(
self.config["username"], "", 1
)
# terminal.write(f"WELCOME: {welcome_text}\n")
self.message_buffer.append(f"{welcome_text}")
lines_added += 1
print(f"WELCOME: {welcome_text}")
# Channel messages
elif command == "PRIVMSG":
if len(parts) >= 3:
# Extract sender nickname
sender = parts[0]
if sender.startswith(":"):
sender = sender[1:]
if "!" in sender:
sender = sender.split("!")[0]
# Extract message content
message_content = parts[2]
inc_channel, inc_message = message_content.split(" ", 1)
message_content = inc_message[1:]
if "*beep*" in message_content:
if (
self.audio_interface is not None
and not self.audio_interface.playing
):
print("playing beep")
self.audio_interface.play(self.beep_wave)
# print(f"is playing: {self.audio_interface.playing}")
while self.audio_interface.playing:
pass
print(f"message_content: {message_content.encode('utf-8')}")
color = self.get_color_for_user(sender)
if inc_channel == self.config["channel"]:
full_line = f"<{color}{sender}{ANSI_RESET}> {message_content}"
if len(full_line) < self.max_line_length:
self.message_buffer.append(full_line)
lines_added += 1
else:
chunks = self.split_string_chunks(
full_line, self.max_line_length
)
for chunk in chunks:
self.message_buffer.append(f"{ANSI_RESET}{chunk}")
lines_added += 1
elif inc_channel == self.config["username"]:
self.message_buffer.append(
f"DM in: <{color}{sender}{ANSI_RESET}> {message_content}"
)
lines_added += 1
print(f"<{sender}> {message_content}")
# Join confirmations
elif command == "JOIN":
sender = parts[0]
if sender.startswith(":"):
sender = sender[1:]
if "!" in sender:
sender = sender.split("!")[0]
if len(parts) >= 3:
joined_channel = parts[2]
if joined_channel.startswith(":"):
joined_channel = joined_channel[1:]
print(f"*** {sender} joined {joined_channel}")
# error messages
elif command_num is not None and 400 <= command_num <= 553:
# message codes: https://www.alien.net.au/irc/irc2numerics.html
self.message_buffer.append(f"{ANSI_RESET}{command} {parts[2]}")
lines_added += 1
# whois reply
elif self.show_whois_reply and command == "311":
whois_response = parts[2].split(" ", 1)[1]
self.message_buffer.append(f"{ANSI_RESET}{whois_response}")
lines_added += 1
self.show_whois_reply = False
# Mode messages
elif command == "MODE":
action_user = parts[0].split("!", 1)[0][1:]
mode_msg_parts = parts[2].split(" ", 2)
if len(mode_msg_parts) >= 3:
channel, mode, target_user = ( # pylint: disable=unused-variable
mode_msg_parts
)
action_user_color = self.get_color_for_user(action_user)
target_user_color = self.get_color_for_user(target_user)
self.message_buffer.append(
f"{action_user_color}{action_user}{ANSI_RESET} sets mode {mode} on {target_user_color}{target_user}{ANSI_RESET}" # pylint: disable=line-too-long
)
lines_added += 1
# Part messages
elif command == "PART":
sender = parts[0]
if sender.startswith(":"):
sender = sender[1:]
if "!" in sender:
sender = sender.split("!")[0]
if len(parts) >= 3:
left_channel = parts[2]
print(f"*** {sender} left {left_channel}")
# Quit messages
elif command == "QUIT":
sender = parts[0]
if sender.startswith(":"):
sender = sender[1:]
if "!" in sender:
sender = sender.split("!")[0]
quit_message = ""
if len(parts) >= 3:
quit_message = parts[2]
if quit_message.startswith(":"):
quit_message = quit_message[1:]
print(f"*** {sender} quit ({quit_message})")
return lines_added

View file

@ -0,0 +1,4 @@
{
"title": "IRC Client",
"icon": "icon.bmp"
}

View file

@ -56,7 +56,7 @@ icon2 = displayio.TileGrid(bitmap5, pixel_shader=palette5, x = 2, y = 2)
group = displayio.Group()
# adding start-up bitmap to group
group.append(tile_grid)
funhouse.splash.append(group)
funhouse.graphics.root_group.append(group)
# text for fume data
fume_text = funhouse.add_text(
text=" ",
@ -74,7 +74,7 @@ fan_text = funhouse.add_text(
text_font="fonts/Arial-Bold-24.pcf",
)
# showing graphics
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
# state machines
run = False # state if main code is running

View file

@ -65,10 +65,10 @@ pres_label = funhouse.add_text(
)
# Now display the splash to draw all labels at once
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
status = Circle(229, 10, 10, fill=0xFF0000, outline=0x880000)
funhouse.splash.append(status)
funhouse.graphics.root_group.append(status)
def connected(client, _userdata, _result, _payload):
status.fill = 0x00FF00

View file

@ -77,7 +77,7 @@ time_label = funhouse.add_text(
text=trip_time, text_scale=2, text_position=(30, 25), text_color=0x606060
)
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
# Turn on the light
print("Turning on light...")

View file

@ -31,7 +31,7 @@ mail_label = funhouse.add_text(
)
reset_label = funhouse.add_text(text="reset", text_position=(3, 70), text_color=GRAY)
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
def send_io_data(mail_value):

View file

@ -82,10 +82,10 @@ countdown_label = funhouse.add_text(
text_color=0xFFFF00,
text_font="fonts/Arial-Bold-24.pcf",
)
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
status = Circle(229, 10, 10, fill=0xFF0000, outline=0x880000)
funhouse.splash.append(status)
funhouse.graphics.root_group.append(status)
# Initialize a new MQTT Client object
if USE_MQTT:

View file

@ -77,10 +77,10 @@ level_label = funhouse.add_text(
text_color=0xFFFF00,
text_font="fonts/Arial-Bold-24.pcf",
)
funhouse.display.root_group = funhouse.splash
funhouse.display.root_group = funhouse.graphics.root_group
status = Circle(229, 10, 10, fill=0xFF0000, outline=0x880000)
funhouse.splash.append(status)
funhouse.graphics.root_group.append(status)
# Initialize a new MQTT Client object
funhouse.network.init_mqtt(

View file

@ -65,8 +65,8 @@ progress_bar_1 = ProgressBar(
BAR_X, 95, BAR_WIDTH, BAR_HEIGHT, 1.0, bar_color=0x999999, outline_color=0x000000
)
magtag.graphics.splash.append(progress_bar)
magtag.graphics.splash.append(progress_bar_1)
magtag.graphics.root_group.append(progress_bar)
magtag.graphics.root_group.append(progress_bar_1)
magtag.graphics.set_background("/bmps/background.bmp")

View file

@ -40,7 +40,7 @@ for i in range(list_len):
# Add button labels at the bottom of the screen
BUTTON_TEXT_IDX = list_len
magtag.graphics.splash.append(Rect(0, magtag.graphics.display.height - 14,
magtag.graphics.root_group.append(Rect(0, magtag.graphics.display.height - 14,
magtag.graphics.display.width,
magtag.graphics.display.height, fill=0x0))
magtag.add_text(

View file

@ -32,7 +32,7 @@ graphics = Graphics(auto_refresh=False)
display = graphics.display
background = Rect(0, 0, 296, 128, fill=0xFFFFFF)
graphics.splash.append(background)
graphics.root_group.append(background)
label_overview_text = Label(
font_large,
@ -42,18 +42,18 @@ label_overview_text = Label(
color=0x000000,
text="Authorize this device with Google:",
)
graphics.splash.append(label_overview_text)
graphics.root_group.append(label_overview_text)
label_verification_url = Label(font_small, x=0, y=40, line_spacing=0.75, color=0x000000)
graphics.splash.append(label_verification_url)
graphics.root_group.append(label_verification_url)
label_user_code = Label(font_small, x=0, y=80, color=0x000000, line_spacing=0.75)
graphics.splash.append(label_user_code)
graphics.root_group.append(label_user_code)
label_qr_code = Label(
font_small, x=0, y=100, color=0x000000, text="Or scan the QR code:"
)
graphics.splash.append(label_qr_code)
graphics.root_group.append(label_qr_code)
# Set scope(s) of access required by the API you're using
scopes = ["https://www.googleapis.com/auth/calendar.readonly"]
@ -85,7 +85,7 @@ label_verification_url.text = (
label_user_code.text = "2. Enter code: %s" % google_auth.user_code
graphics.qrcode(google_auth.verification_url.encode(), qr_size=2, x=240, y=70)
graphics.display.root_group = graphics.splash
graphics.display.root_group = graphics.root_group
display.refresh()
# Poll Google's authorization server
@ -98,9 +98,9 @@ print("Add the following lines to your settings.toml file:")
print(f'google_access_token="{google_auth.access_token}"')
print(f'google_refresh_token="{google_auth.refresh_token}"')
graphics.splash.pop()
graphics.splash.pop()
graphics.splash.pop()
graphics.root_group.pop()
graphics.root_group.pop()
graphics.root_group.pop()
label_overview_text.text = "Successfully Authenticated!"
label_verification_url.text = (

View file

@ -205,7 +205,7 @@ magtag.set_background(0xFFFFFF)
# Add the header
line_header = Line(0, 30, 320, 30, color=0x000000)
magtag.splash.append(line_header)
magtag.graphics.root_group.append(line_header)
label_header = magtag.add_text(
text_font="fonts/Arial-18.pcf",

View file

@ -60,7 +60,7 @@ MAGTAG.add_text(
# Add 14-pixel-tall black bar at bottom of display. It's a distinct layer
# (not just background) to appear on top of name list if it runs long.
MAGTAG.graphics.splash.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.root_group.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.display.width,
MAGTAG.graphics.display.height, fill=0x0))

View file

@ -69,7 +69,7 @@ MAGTAG.add_text(
# Add 14-pixel-tall black bar at bottom of display. It's a distinct layer
# (not just background) to appear on top of task list if it runs long.
MAGTAG.graphics.splash.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.root_group.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.display.width,
MAGTAG.graphics.display.height, fill=0x0))

View file

@ -75,7 +75,7 @@ progress_bar = ProgressBar(
BAR_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT, 1.0, bar_color=0x999999, outline_color=0x000000
)
magtag.graphics.splash.append(progress_bar)
magtag.graphics.root_group.append(progress_bar)
timestamp = None

View file

@ -53,7 +53,7 @@ progress_bar = ProgressBar(
BAR_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT, 1.0, bar_color=0x999999, outline_color=0x000000
)
magtag.graphics.splash.append(progress_bar)
magtag.graphics.root_group.append(progress_bar)
try:

View file

@ -78,7 +78,7 @@ MAGTAG.add_text(
# Add 14-pixel-tall black bar at bottom of display. It's a distinct layer
# (not just background) to appear on top of produce list if it runs long.
MAGTAG.graphics.splash.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.root_group.append(Rect(0, MAGTAG.graphics.display.height - 14,
MAGTAG.graphics.display.width,
MAGTAG.graphics.display.height, fill=0x0))

View file

@ -130,12 +130,12 @@ station_info.anchored_position = (158, 126)
# ----------------------------
# Add all the graphic layers
# ----------------------------
magtag.splash.append(tide_tg)
magtag.splash.append(grid_overlay)
magtag.splash.append(plot_y_labels)
magtag.splash.append(tide_info)
magtag.splash.append(date_label)
magtag.splash.append(station_info)
magtag.graphics.root_group.append(tide_tg)
magtag.graphics.root_group.append(grid_overlay)
magtag.graphics.root_group.append(plot_y_labels)
magtag.graphics.root_group.append(tide_info)
magtag.graphics.root_group.append(date_label)
magtag.graphics.root_group.append(station_info)
# /////////////////////////////////////////////////////////////////////////

View file

@ -344,9 +344,9 @@ future_banners = [
make_banner(x=210, y=81),
]
magtag.splash.append(today_banner)
magtag.graphics.root_group.append(today_banner)
for future_banner in future_banners:
magtag.splash.append(future_banner)
magtag.graphics.root_group.append(future_banner)
# ===========
# M A I N

View file

@ -283,9 +283,9 @@ future_banners = [
make_banner(x=210, y=102),
]
magtag.splash.append(today_banner)
magtag.graphics.root_group.append(today_banner)
for future_banner in future_banners:
magtag.splash.append(future_banner)
magtag.graphics.root_group.append(future_banner)
# ===========
# M A I N

View file

@ -280,9 +280,9 @@ future_banners = [
make_banner(x=210, y=102),
]
magtag.splash.append(today_banner)
magtag.graphics.root_group.append(today_banner)
for future_banner in future_banners:
magtag.splash.append(future_banner)
magtag.graphics.root_group.append(future_banner)
# ===========
# M A I N

View file

@ -136,8 +136,8 @@ def create_text_areas(configs):
def clear_splash():
for _ in range(len(pyportal.splash) - 1):
pyportal.splash.pop()
for _ in range(len(pyportal.root_group) - 1):
pyportal.root_group.pop()
def touch_in_button(t, b):
@ -337,8 +337,8 @@ class Time_State(State):
def enter(self):
self.adjust_backlight_based_on_light(force=True)
for ta in self.text_areas:
pyportal.splash.append(ta)
pyportal.splash.append(self.weather_icon)
pyportal.root_group.append(ta)
pyportal.root_group.append(self.weather_icon)
if snooze_time:
# CircuitPython 6 & 7 compatible
if self.snooze_file:
@ -348,7 +348,7 @@ class Time_State(State):
icon_sprite = displayio.TileGrid(icon, pixel_shader=icon.pixel_shader)
self.snooze_icon.append(icon_sprite)
pyportal.splash.append(self.snooze_icon)
pyportal.root_group.append(self.snooze_icon)
if alarm_enabled:
self.text_areas[1].text = '%2d:%02d' % (alarm_hour, alarm_minute)
else:
@ -532,7 +532,7 @@ class Setting_State(State):
pyportal.set_background(self.background)
for ta in self.text_areas:
pyportal.splash.append(ta)
pyportal.root_group.append(ta)
if alarm_enabled:
self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute) # set time textarea
else:

View file

@ -54,6 +54,6 @@ while True:
names_textarea.x = names_position[0]
names_textarea.y = names_position[1]
names_textarea.color = names_color
pyportal.splash.append(names_textarea)
pyportal.root_group.append(names_textarea)
time.sleep(30) # wait 30 seconds to read it
pyportal.splash.pop()
pyportal.root_group.pop()

View file

@ -53,7 +53,7 @@ pyportal = PyPortal(default_bg=BACKGROUND_FILE,
text_color=0xFFFFFF)
circle = Circle(WIDTH - 8, HEIGHT - 7, 5, fill=0)
pyportal.splash.append(circle)
pyportal.root_group.append(circle)
loopcount = 0
errorcount = 0
while True:

View file

@ -47,7 +47,7 @@ for pos in (days_position, hours_position, minutes_position):
textarea.x = pos[0]
textarea.y = pos[1]
textarea.color = text_color
pyportal.splash.append(textarea)
pyportal.root_group.append(textarea)
text_areas.append(textarea)
refresh_time = None

View file

@ -38,7 +38,7 @@ pyportal = PyPortal(url=DATA_SOURCE,
default_bg=0x000000)
gfx = electioncal_graphics.Electioncal_Graphics(pyportal.splash, am_pm=True)
gfx = electioncal_graphics.Electioncal_Graphics(pyportal.root_group, am_pm=True)
display_refresh = None
while True:
# only query the online time once per hour (and on first run)

View file

@ -47,7 +47,7 @@ for pos in (days_position, hours_position, minutes_position):
textarea.x = pos[0]
textarea.y = pos[1]
textarea.color = text_color
pyportal.splash.append(textarea)
pyportal.root_group.append(textarea)
text_areas.append(textarea)
refresh_time = None

View file

@ -47,7 +47,7 @@ for pos in (years_position, days_position, hours_position, minutes_position):
textarea.x = pos[0]
textarea.y = pos[1]
textarea.color = text_color
pyportal.splash.append(textarea)
pyportal.root_group.append(textarea)
text_areas.append(textarea)
refresh_time = None

View file

@ -26,16 +26,16 @@ font_large.load_glyphs(glyphs)
label_overview_text = Label(
font_large, x=0, y=45, text="To authorize this device with Google:"
)
graphics.splash.append(label_overview_text)
graphics.root_group.append(label_overview_text)
label_verification_url = Label(font_small, x=0, y=100, line_spacing=1)
graphics.splash.append(label_verification_url)
graphics.root_group.append(label_verification_url)
label_user_code = Label(font_small, x=0, y=150)
graphics.splash.append(label_user_code)
graphics.root_group.append(label_user_code)
label_qr_code = Label(font_small, x=0, y=190, text="Or scan the QR code:")
graphics.splash.append(label_qr_code)
graphics.root_group.append(label_qr_code)
# Set scope(s) of access required by the API you're using
scopes = ["https://www.googleapis.com/auth/calendar.readonly"]
@ -71,7 +71,7 @@ label_user_code.text = "2. Enter code: %s" % google_auth.user_code
# Create a QR code
graphics.qrcode(google_auth.verification_url.encode(), qr_size=2, x=170, y=165)
graphics.display.root_group = graphics.splash
graphics.display.root_group = graphics.root_group
# Poll Google's authorization server
print("Waiting for browser authorization...")
@ -85,9 +85,9 @@ print("Add the following lines to your settings.toml file:")
print(f'GOOGLE_ACCESS_TOKEN = "{google_auth.access_token}"')
print(f'GOOGLE_REFRESH_TOKEN = "{google_auth.refresh_token}"')
# Remove QR code and code/verification labels
graphics.splash.pop()
graphics.splash.pop()
graphics.splash.pop()
graphics.root_group.pop()
graphics.root_group.pop()
graphics.root_group.pop()
label_overview_text.text = "Successfully Authenticated!"
label_verification_url.text = (

View file

@ -202,7 +202,7 @@ font_events = "fonts/Arial-14.pcf"
# Add the header
line_header = Line(0, 50, 320, 50, color=0x000000)
pyportal.splash.append(line_header)
pyportal.root_group.append(line_header)
label_header = pyportal.add_text(
text_font="fonts/Arial-18.pcf",

View file

@ -33,7 +33,7 @@ for peg in pegs:
style=Button.RECT,
fill_color=None, outline_color=0x5C3C15,
name=peg['label'])
pyportal.splash.append(button.group)
pyportal.root_group.append(button.group)
buttons.append(button)
note_select = None

View file

@ -86,7 +86,7 @@ countdown_text = Label(big_font)
countdown_text.x = 25
countdown_text.y = 120
countdown_text.color = 0x7942a0
pyportal.splash.append(countdown_text)
pyportal.root_group.append(countdown_text)
refresh_time = None

View file

@ -41,7 +41,7 @@ for i, c in enumerate(icons_pal):
if c == 0xFFFF00:
icons_pal.make_transparent(i)
storm_icons = displayio.Group()
pyportal.splash.append(storm_icons)
pyportal.root_group.append(storm_icons)
STORM_CLASS = ("TD", "TS", "HU")
# setup info label
@ -53,7 +53,7 @@ info_update = Label(
)
info_update.anchor_point = (0.0, 1.0)
info_update.anchored_position = (10, board.DISPLAY.height - 10)
pyportal.splash.append(info_update)
pyportal.root_group.append(info_update)
# these are need for lat/lon to screen x/y mapping
VIRTUAL_WIDTH = board.DISPLAY.width * 360 / (LON_RANGE[1] - LON_RANGE[0])

View file

@ -43,21 +43,21 @@ pyportal.get_local_time()
# Date and time label
date_label = Label(FONT, text="0000-00-00", color=DATE_COLOR, x=165, y=223)
time_label = Label(FONT, text="00:00:00", color=TIME_COLOR, x=240, y=223)
pyportal.splash.append(date_label)
pyportal.splash.append(time_label)
pyportal.root_group.append(date_label)
pyportal.root_group.append(time_label)
# ISS trail
trail_bitmap = displayio.Bitmap(3, 3, 1)
trail_palette = displayio.Palette(1)
trail_palette[0] = TRAIL_COLOR
trail = displayio.Group()
pyportal.splash.append(trail)
pyportal.root_group.append(trail)
# ISS location marker
marker = displayio.Group()
for r in range(MARK_SIZE - MARK_THICKNESS, MARK_SIZE):
marker.append(Circle(0, 0, r, outline=MARK_COLOR))
pyportal.splash.append(marker)
pyportal.root_group.append(marker)
def get_location(width=WIDTH, height=HEIGHT):
"""Fetch current lat/lon, convert to (x, y) tuple scaled to width/height."""

View file

@ -47,7 +47,7 @@ display = board.DISPLAY
display.rotation = 270
# instantiate the openweather_graphics class
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.splash, am_pm=True, celsius=False)
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.root_group, am_pm=True, celsius=False)
# time keeping for refreshing screen icons and weather information
localtile_refresh = None

View file

@ -45,7 +45,7 @@ time_textarea = Label(big_font)
time_textarea.x = 0
time_textarea.y = 130
time_textarea.color = 0xFF0000
pyportal.splash.append(time_textarea)
pyportal.root_group.append(time_textarea)
# To help us know if we've changed the times, print them out!
gremlin_hour, gremlin_min = gremlin_time[3:5]

View file

@ -63,7 +63,7 @@ for spot in spots:
style=Button.SHADOWROUNDRECT,
fill_color=spot['color'], outline_color=0x222222,
name=spot['label'])
pyportal.splash.append(button)
pyportal.root_group.append(button)
buttons.append(button)
mode = 0

View file

@ -47,7 +47,7 @@ pyportal = PyPortal(url=DATA_SOURCE,
status_neopixel=board.NEOPIXEL,
default_bg=0x000000)
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.splash, am_pm=True, celsius=False)
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.root_group, am_pm=True, celsius=False)
localtile_refresh = None
weather_refresh = None

View file

@ -68,13 +68,13 @@ font_small.load_glyphs(b'abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123
label_day = label.Label(font_large, color=LABEL_DAY_COLOR)
label_day.x = board.DISPLAY.width // 7
label_day.y = 80
pyportal.splash.append(label_day)
pyportal.root_group.append(label_day)
# Set up label for the time
label_time = label.Label(font_small, color=LABEL_TIME_COLOR)
label_time.x = board.DISPLAY.width // 4
label_time.y = 150
pyportal.splash.append(label_time)
pyportal.root_group.append(label_time)
refresh_time = None
while True:

View file

@ -109,13 +109,13 @@ font_small.load_glyphs(b'abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123
label_month = label.Label(font_large, color=LABEL_DAY_COLOR)
label_month.x = board.DISPLAY.width // 10
label_month.y = 80
pyportal.splash.append(label_month)
pyportal.root_group.append(label_month)
# Set up label for the time
label_time = label.Label(font_small, color=LABEL_TIME_COLOR)
label_time.x = board.DISPLAY.width // 3
label_time.y = 150
pyportal.splash.append(label_time)
pyportal.root_group.append(label_time)
refresh_time = None
while True:

View file

@ -104,11 +104,11 @@ class Switch(object):
self.switch.direction = digitalio.Direction.OUTPUT
rect = RoundRect(SWITCHX, SWITCHY, 31, 60, 16, outline=SWITCH_COLOR,
fill=SWITCH_FILL_COLOR, stroke=3)
my_pyportal.splash.append(rect)
my_pyportal.root_group.append(rect)
self.circle_on = Circle(SWITCHX + 15, SWITCHY + 16, 10, fill=SWITCH_FILL_COLOR)
my_pyportal.splash.append(self.circle_on)
my_pyportal.root_group.append(self.circle_on)
self.circle_off = Circle(SWITCHX + 15, SWITCHY + 42, 10, fill=DISPLAY_COLOR)
my_pyportal.splash.append(self.circle_off)
my_pyportal.root_group.append(self.circle_off)
# turn switch on or off
def enable(self, enable):
@ -149,7 +149,7 @@ class Clock(object):
self.text_areas = create_text_areas(text_area_configs)
self.text_areas[2].text = "starting..."
for ta in self.text_areas:
self.pyportal.splash.append(ta)
self.pyportal.root_group.append(ta)
def adjust_backlight(self, force=False):
"""Check light level. Adjust the backlight and background image if it's dark."""

View file

@ -61,7 +61,7 @@ DATE_LABEL = Label(date_font, text="0000-00-00 00:00:00", color=DATE_COLOR, x=75
# Add all the labels to the display
for label in HI_LABELS + LO_LABELS + [DATE_LABEL]:
pyportal.splash.append(label)
pyportal.root_group.append(label)
def get_tide_info():
"""Fetch JSON tide time info and return it."""

View file

@ -67,7 +67,7 @@ palette.make_transparent(0)
# Setup tide plot bitmap
tide_plot = displayio.Bitmap(WIDTH, HEIGHT, 3)
pyportal.splash.append(displayio.TileGrid(tide_plot, pixel_shader=palette))
pyportal.root_group.append(displayio.TileGrid(tide_plot, pixel_shader=palette))
# Setup font used for date and time
date_font = bitmap_font.load_font(cwd+"/fonts/mono-bold-8.bdf")
@ -75,7 +75,7 @@ date_font.load_glyphs(b'1234567890-')
# Setup date label
date_label = Label(date_font, text="0000-00-00", color=DATE_COLOR, x=7, y=14)
pyportal.splash.append(date_label)
pyportal.root_group.append(date_label)
if board.board_id == "pyportal_titano":
x_pos = 394
@ -84,14 +84,14 @@ else:
# Setup time label
time_label = Label(date_font, text="00:00:00", color=TIME_COLOR, x=x_pos, y=14)
pyportal.splash.append(time_label)
pyportal.root_group.append(time_label)
# Setup current time marker
time_marker_bitmap = displayio.Bitmap(MARK_SIZE, MARK_SIZE, 3)
for pixel in range(MARK_SIZE * MARK_SIZE):
time_marker_bitmap[pixel] = 2
time_marker = displayio.TileGrid(time_marker_bitmap, pixel_shader=palette, x=-MARK_SIZE, y=-MARK_SIZE)
pyportal.splash.append(time_marker)
pyportal.root_group.append(time_marker)
def get_tide_data():
"""Fetch JSON tide data and return parsed results in a list."""

View file

@ -51,7 +51,7 @@ DATE_LABEL = Label(date_font, text="0000-00-00 00:00:00", color=DATE_COLOR, x=75
# Add all the labels to the display
for label in HI_LABELS + LO_LABELS + [DATE_LABEL]:
pyportal.graphics.splash.append(label)
pyportal.graphics.root_group.append(label)
def get_tide_info():

View file

@ -47,7 +47,7 @@ palette.make_transparent(0)
# Setup tide plot bitmap
tide_plot = displayio.Bitmap(WIDTH, HEIGHT, 3)
pyportal.graphics.splash.append(displayio.TileGrid(tide_plot, pixel_shader=palette))
pyportal.graphics.root_group.append(displayio.TileGrid(tide_plot, pixel_shader=palette))
# Setup font used for date and time
date_font = bitmap_font.load_font("/fonts/mono-bold-8.bdf")
@ -55,11 +55,11 @@ date_font.load_glyphs(b"1234567890-")
# Setup date label
date_label = Label(date_font, text="0000-00-00", color=DATE_COLOR, x=7, y=14)
pyportal.graphics.splash.append(date_label)
pyportal.graphics.root_group.append(date_label)
# Setup time label
time_label = Label(date_font, text="00:00:00", color=TIME_COLOR, x=234, y=14)
pyportal.graphics.splash.append(time_label)
pyportal.graphics.root_group.append(time_label)
# Setup current time marker
time_marker_bitmap = displayio.Bitmap(MARK_SIZE, MARK_SIZE, 3)
@ -67,7 +67,7 @@ time_marker_bitmap.fill(2)
time_marker = displayio.TileGrid(
time_marker_bitmap, pixel_shader=palette, x=-MARK_SIZE, y=-MARK_SIZE
)
pyportal.graphics.splash.append(time_marker)
pyportal.graphics.root_group.append(time_marker)
def get_tide_data():

View file

@ -133,7 +133,7 @@ alarm_checks = [None, alarms['bed'],alarms['breakfast'],alarms['lunch'],alarms['
alarm_gfx = [group_trash, group_bed, group_eat, group_eat, group_eat]
# allows for the openweather_graphics to show
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.splash, am_pm=True, celsius=False)
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.root_group, am_pm=True, celsius=False)
# state machines
localtile_refresh = None
@ -250,13 +250,13 @@ while True:
print("pressed dismiss button")
dismissed = True
alarm = False
display.root_group = pyportal.splash
display.root_group = pyportal.root_group
touched = time.monotonic()
mode = mode
if not switch_snooze.value and not phys_snooze:
phys_snooze = True
print("pressed snooze button")
display.root_group = pyportal.splash
display.root_group = pyportal.root_group
snoozed = True
alarm = False
touched = time.monotonic()
@ -271,7 +271,7 @@ while True:
if touch:
if snooze_buttons[button_mode].contains(touch) and not touch_button_snooze:
print("Touched snooze")
display.root_group = pyportal.splash
display.root_group = pyportal.root_group
touch_button_snooze = True
snoozed = True
alarm = False
@ -281,7 +281,7 @@ while True:
print("Touched dismiss")
dismissed = True
alarm = False
display.root_group = pyportal.splash
display.root_group = pyportal.root_group
touch_button_dismiss = True
touched = time.monotonic()
mode = mode

View file

@ -121,12 +121,12 @@ pyportal = PyPortal(url=DATA_SOURCE,
pyportal.preload_font() # speed things up by preloading font
pyportal.splash.append(loading_text_area) #loading...
pyportal.splash.append(q_text_area)
pyportal.splash.append(reveal_text_area)
pyportal.splash.append(timer_text_area)
pyportal.root_group.append(loading_text_area) #loading...
pyportal.root_group.append(q_text_area)
pyportal.root_group.append(reveal_text_area)
pyportal.root_group.append(timer_text_area)
for textarea in ans_text_areas:
pyportal.splash.append(textarea)
pyportal.root_group.append(textarea)
while True:
# Load new question when screen is touched

View file

@ -80,7 +80,7 @@ pyportal = PyPortal(url=url,
caption_font=CAPTION_FONT_FILE)
canvas = displayio.Group()
pyportal.splash.append(canvas)
pyportal.root_group.append(canvas)
bar_font = bitmap_font.load_font(BAR_FONT_FILE)
while True:

View file

@ -84,9 +84,9 @@ light_on_time_position = (15,220)
light_on_time_textarea = Label(info_font, color=light_on_time_color,
x=light_on_time_position[0], y=light_on_time_position[1])
pyportal.splash.append(time_textarea)
pyportal.splash.append(wakeup_time_textarea)
pyportal.splash.append(light_on_time_textarea)
pyportal.root_group.append(time_textarea)
pyportal.root_group.append(wakeup_time_textarea)
pyportal.root_group.append(light_on_time_textarea)
while True:
try:

View file

@ -48,7 +48,7 @@ for pos in (days_position, hours_position, minutes_position):
textarea.x = pos[0]
textarea.y = pos[1]
textarea.color = text_color
pyportal.splash.append(textarea)
pyportal.root_group.append(textarea)
text_areas.append(textarea)
refresh_time = None