Merge pull request #5 from FoamyGuy/implement_portalbase
Some checks failed
Build CI / test (push) Has been cancelled
Some checks failed
Build CI / test (push) Has been cancelled
Adding PortalBase support
This commit is contained in:
commit
7de2ec3d14
9 changed files with 635 additions and 13 deletions
|
|
@ -15,7 +15,7 @@ Implementation Notes
|
|||
|
||||
**Hardware:**
|
||||
|
||||
* `Adafruit Fruit Jam <url>`_"
|
||||
* `Adafruit Fruit Jam <https://www.adafruit.com/product/6200>`_
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
|
|
@ -29,13 +29,314 @@ Implementation Notes
|
|||
__version__ = "0.0.0+auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FruitJam.git"
|
||||
|
||||
import gc
|
||||
import os
|
||||
import time
|
||||
|
||||
import board
|
||||
import busio
|
||||
import supervisor
|
||||
import terminalio
|
||||
from adafruit_esp32spi import adafruit_esp32spi
|
||||
from adafruit_portalbase import PortalBase
|
||||
from digitalio import DigitalInOut
|
||||
|
||||
from adafruit_fruitjam.graphics import Graphics
|
||||
from adafruit_fruitjam.network import CONTENT_IMAGE, CONTENT_JSON, CONTENT_TEXT, Network
|
||||
from adafruit_fruitjam.peripherals import Peripherals
|
||||
|
||||
|
||||
class FruitJam:
|
||||
def __init__(self):
|
||||
class FruitJam(PortalBase):
|
||||
"""Class representing the Adafruit Fruit Jam.
|
||||
|
||||
:param url: The URL of your data source. Defaults to ``None``.
|
||||
:param headers: The headers for authentication, typically used by Azure API's.
|
||||
:param json_path: The list of json traversal to get data out of. Can be list of lists for
|
||||
multiple data points. Defaults to ``None`` to not use json.
|
||||
:param regexp_path: The list of regexp strings to get data out (use a single regexp group). Can
|
||||
be list of regexps for multiple data points. Defaults to ``None`` to not
|
||||
use regexp.
|
||||
:param convert_image: Determine whether or not to use the AdafruitIO image converter service.
|
||||
Set as False if your image is already resized. Defaults to True.
|
||||
:param default_bg: The path to your default background image file or a hex color.
|
||||
Defaults to 0x000000.
|
||||
:param status_neopixel: The pin for the status NeoPixel. Use ``board.NEOPIXEL`` for the on-board
|
||||
NeoPixel. Defaults to ``None``, not the status LED
|
||||
:param str text_font: The path to your font file for your data text display.
|
||||
:param text_position: The position of your extracted text on the display in an (x, y) tuple.
|
||||
Can be a list of tuples for when there's a list of json_paths, for example
|
||||
:param text_color: The color of the text, in 0xRRGGBB format. Can be a list of colors for when
|
||||
there's multiple texts. Defaults to ``None``.
|
||||
:param text_wrap: Whether or not to wrap text (for long text data chunks). Defaults to
|
||||
``False``, no wrapping.
|
||||
:param text_maxlen: The max length of the text for text wrapping. Defaults to 0.
|
||||
:param text_transform: A function that will be called on the text before display
|
||||
:param int text_scale: The factor to scale the default size of the text by
|
||||
:param json_transform: A function or a list of functions to call with the parsed JSON.
|
||||
Changes and additions are permitted for the ``dict`` object.
|
||||
:param image_json_path: The JSON traversal path for a background image to display. Defaults to
|
||||
``None``.
|
||||
:param image_resize: What size to resize the image we got from the json_path, make this a tuple
|
||||
of the width and height you want. Defaults to ``None``.
|
||||
:param image_position: The position of the image on the display as an (x, y) tuple. Defaults to
|
||||
``None``.
|
||||
:param image_dim_json_path: The JSON traversal path for the original dimensions of image tuple.
|
||||
Used with fetch(). Defaults to ``None``.
|
||||
:param success_callback: A function we'll call if you like, when we fetch data successfully.
|
||||
Defaults to ``None``.
|
||||
:param str caption_text: The text of your caption, a fixed text not changed by the data we get.
|
||||
Defaults to ``None``.
|
||||
:param str caption_font: The path to the font file for your caption. Defaults to ``None``.
|
||||
:param caption_position: The position of your caption on the display as an (x, y) tuple.
|
||||
Defaults to ``None``.
|
||||
:param caption_color: The color of your caption. Must be a hex value, e.g. ``0x808000``.
|
||||
:param image_url_path: The HTTP traversal path for a background image to display.
|
||||
Defaults to ``None``.
|
||||
:param esp: A passed ESP32 object, Can be used in cases where the ESP32 chip needs to be used
|
||||
before calling the pyportal class. Defaults to ``None``.
|
||||
:param busio.SPI external_spi: A previously declared spi object. Defaults to ``None``.
|
||||
:param debug: Turn on debug print outs. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
def __init__( # noqa: PLR0912,PLR0913,Too many branches,Too many arguments in function definition
|
||||
self,
|
||||
*,
|
||||
url=None,
|
||||
headers=None,
|
||||
json_path=None,
|
||||
regexp_path=None,
|
||||
convert_image=True,
|
||||
default_bg=0x000000,
|
||||
status_neopixel=None,
|
||||
text_font=terminalio.FONT,
|
||||
text_position=None,
|
||||
text_color=0x808080,
|
||||
text_wrap=False,
|
||||
text_maxlen=0,
|
||||
text_transform=None,
|
||||
text_scale=1,
|
||||
json_transform=None,
|
||||
image_json_path=None,
|
||||
image_resize=None,
|
||||
image_position=None,
|
||||
image_dim_json_path=None,
|
||||
caption_text=None,
|
||||
caption_font=None,
|
||||
caption_position=None,
|
||||
caption_color=0x808080,
|
||||
image_url_path=None,
|
||||
success_callback=None,
|
||||
esp=None,
|
||||
external_spi=None,
|
||||
debug=False,
|
||||
secrets_data=None,
|
||||
):
|
||||
graphics = Graphics(
|
||||
default_bg=default_bg,
|
||||
debug=debug,
|
||||
)
|
||||
self._default_bg = default_bg
|
||||
|
||||
spi = board.SPI()
|
||||
|
||||
if image_json_path or image_url_path:
|
||||
if debug:
|
||||
print("Init image path")
|
||||
if not image_position:
|
||||
image_position = (0, 0) # default to top corner
|
||||
if not image_resize:
|
||||
image_resize = (
|
||||
self.display.width,
|
||||
self.display.height,
|
||||
) # default to full screen
|
||||
|
||||
if esp is None:
|
||||
esp32_cs = DigitalInOut(board.ESP_CS)
|
||||
esp32_ready = DigitalInOut(board.ESP_BUSY)
|
||||
esp32_reset = DigitalInOut(board.ESP_RESET)
|
||||
spi = board.SPI()
|
||||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
|
||||
|
||||
self.peripherals = Peripherals()
|
||||
|
||||
network = Network(
|
||||
status_neopixel=self.peripherals.neopixels
|
||||
if status_neopixel is None
|
||||
else status_neopixel,
|
||||
esp=esp,
|
||||
external_spi=spi,
|
||||
extract_values=False,
|
||||
convert_image=convert_image,
|
||||
image_url_path=image_url_path,
|
||||
image_json_path=image_json_path,
|
||||
image_resize=image_resize,
|
||||
image_position=image_position,
|
||||
image_dim_json_path=image_dim_json_path,
|
||||
debug=debug,
|
||||
)
|
||||
self.url = url
|
||||
|
||||
super().__init__(
|
||||
network,
|
||||
graphics,
|
||||
url=url,
|
||||
headers=headers,
|
||||
json_path=json_path,
|
||||
regexp_path=regexp_path,
|
||||
json_transform=json_transform,
|
||||
success_callback=success_callback,
|
||||
debug=debug,
|
||||
)
|
||||
|
||||
# Convenience Shortcuts for compatibility
|
||||
|
||||
# self.sd_check = self.peripherals.sd_check
|
||||
# self.play_file = self.peripherals.play_file
|
||||
# self.stop_play = self.peripherals.stop_play
|
||||
|
||||
self.image_converter_url = self.network.image_converter_url
|
||||
self.wget = self.network.wget
|
||||
# self.show_QR = self.graphics.qrcode
|
||||
# self.hide_QR = self.graphics.hide_QR
|
||||
|
||||
if default_bg is not None:
|
||||
self.graphics.set_background(default_bg)
|
||||
|
||||
if self._debug:
|
||||
print("Init caption")
|
||||
if caption_font:
|
||||
self._caption_font = self._load_font(caption_font)
|
||||
self.set_caption(caption_text, caption_position, caption_color)
|
||||
|
||||
if text_font:
|
||||
if text_position is not None and isinstance(text_position[0], (list, tuple)):
|
||||
num = len(text_position)
|
||||
if not text_wrap:
|
||||
text_wrap = [0] * num
|
||||
if not text_maxlen:
|
||||
text_maxlen = [0] * num
|
||||
if not text_transform:
|
||||
text_transform = [None] * num
|
||||
if not isinstance(text_scale, (list, tuple)):
|
||||
text_scale = [text_scale] * num
|
||||
else:
|
||||
num = 1
|
||||
text_position = (text_position,)
|
||||
text_color = (text_color,)
|
||||
text_wrap = (text_wrap,)
|
||||
text_maxlen = (text_maxlen,)
|
||||
text_transform = (text_transform,)
|
||||
text_scale = (text_scale,)
|
||||
for i in range(num):
|
||||
self.add_text(
|
||||
text_position=text_position[i],
|
||||
text_font=text_font,
|
||||
text_color=text_color[i],
|
||||
text_wrap=text_wrap[i],
|
||||
text_maxlen=text_maxlen[i],
|
||||
text_transform=text_transform[i],
|
||||
text_scale=text_scale[i],
|
||||
)
|
||||
else:
|
||||
self._text_font = None
|
||||
self._text = None
|
||||
|
||||
gc.collect()
|
||||
|
||||
def set_caption(self, caption_text, caption_position, caption_color):
|
||||
"""A caption. Requires setting ``caption_font`` in init!
|
||||
|
||||
:param caption_text: The text of the caption.
|
||||
:param caption_position: The position of the caption text.
|
||||
:param caption_color: The color of your caption text. Must be a hex value, e.g.
|
||||
``0x808000``.
|
||||
"""
|
||||
if self._debug:
|
||||
print("Setting caption to", caption_text)
|
||||
|
||||
if (not caption_text) or (not self._caption_font) or (not caption_position):
|
||||
return # nothing to do!
|
||||
|
||||
index = self.add_text(
|
||||
text_position=caption_position,
|
||||
text_font=self._caption_font,
|
||||
text_color=caption_color,
|
||||
is_data=False,
|
||||
)
|
||||
self.set_text(caption_text, index)
|
||||
|
||||
def fetch(self, refresh_url=None, timeout=10, force_content_type=None): # noqa: PLR0912 Too many branches
|
||||
"""Fetch data from the url we initialized with, perfom any parsing,
|
||||
and display text or graphics. This function does pretty much everything
|
||||
Optionally update the URL
|
||||
"""
|
||||
|
||||
if refresh_url:
|
||||
self.url = refresh_url
|
||||
|
||||
response = self.network.fetch(self.url, headers=self._headers, timeout=timeout)
|
||||
|
||||
json_out = None
|
||||
if not force_content_type:
|
||||
content_type = self.network.check_response(response)
|
||||
else:
|
||||
content_type = force_content_type
|
||||
json_path = self._json_path
|
||||
|
||||
if content_type == CONTENT_JSON:
|
||||
if json_path is not None:
|
||||
# Drill down to the json path and set json_out as that node
|
||||
if isinstance(json_path, (list, tuple)) and (
|
||||
not json_path or not isinstance(json_path[0], (list, tuple))
|
||||
):
|
||||
json_path = (json_path,)
|
||||
try:
|
||||
gc.collect()
|
||||
json_out = response.json()
|
||||
if self._debug:
|
||||
print(json_out)
|
||||
gc.collect()
|
||||
except ValueError: # failed to parse?
|
||||
print("Couldn't parse json: ", response.text)
|
||||
raise
|
||||
except MemoryError:
|
||||
supervisor.reload()
|
||||
if content_type == CONTENT_IMAGE:
|
||||
try:
|
||||
filename, position = self.network.process_image(
|
||||
json_out, self.peripherals.sd_check()
|
||||
)
|
||||
if filename and position is not None:
|
||||
self.graphics.set_background(filename, position)
|
||||
except ValueError as error:
|
||||
print("Error displaying cached image. " + error.args[0])
|
||||
if self._default_bg is not None:
|
||||
self.graphics.set_background(self._default_bg)
|
||||
except KeyError as error:
|
||||
print("Error finding image data. '" + error.args[0] + "' not found.")
|
||||
self.set_background(self._default_bg)
|
||||
|
||||
if content_type == CONTENT_JSON:
|
||||
values = self.network.process_json(json_out, json_path)
|
||||
elif content_type == CONTENT_TEXT:
|
||||
values = self.network.process_text(response.text, self._regexp_path)
|
||||
|
||||
# if we have a callback registered, call it now
|
||||
if self._success_callback:
|
||||
self._success_callback(values)
|
||||
|
||||
self._fill_text_labels(values)
|
||||
# Clean up
|
||||
json_out = None
|
||||
response = None
|
||||
gc.collect()
|
||||
|
||||
if len(values) == 1:
|
||||
values = values[0]
|
||||
|
||||
return values
|
||||
|
||||
@property
|
||||
def neopixels(self):
|
||||
return self.peripherals.neopixels
|
||||
|
|
|
|||
68
adafruit_fruitjam/graphics.py
Normal file
68
adafruit_fruitjam/graphics.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams, written for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
"""
|
||||
`adafruit_fruitjam.graphics`
|
||||
================================================================================
|
||||
|
||||
Graphics Helper library for the Adafruit Fruit Jam.
|
||||
|
||||
* Author(s): Melissa LeBlanc-Williams, Tim Cocks
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
**Hardware:**
|
||||
|
||||
* `Adafruit Fruit Jam <https://www.adafruit.com/product/6200>`_
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Adafruit CircuitPython firmware for the supported boards:
|
||||
https://github.com/adafruit/circuitpython/releases
|
||||
|
||||
"""
|
||||
|
||||
import supervisor
|
||||
from adafruit_portalbase.graphics import GraphicsBase
|
||||
|
||||
from adafruit_fruitjam.peripherals import request_display_config
|
||||
|
||||
__version__ = "0.0.0+auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FruitJam.git"
|
||||
|
||||
|
||||
class Graphics(GraphicsBase):
|
||||
"""Graphics Helper library for the Adafruit Fruit Jam.
|
||||
|
||||
:param default_bg: The path to your default background image file or a hex color.
|
||||
Defaults to 0x000000.
|
||||
:param int width: The total width of the display(s) in Pixels. Defaults to 64.
|
||||
:param int height: The total height of the display(s) in Pixels. Defaults to 32.
|
||||
:param int bit_depth: The number of bits per color channel. Defaults to 2.
|
||||
:param list alt_addr_pins: An alternate set of address pins to use. Defaults to None
|
||||
:param string color_order: A string containing the letter "R", "G", and "B" in the
|
||||
order you want. Defaults to "RGB"
|
||||
:param bool Serpentine: Used when panels are arranged in a serpentine pattern rather
|
||||
than a Z-pattern. Defaults to True.
|
||||
:param int tiles_rows: Used to indicate the number of rows the panels are arranged in.
|
||||
Defaults to 1.
|
||||
:param debug: Turn on debug print outs. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
**kwargs,
|
||||
):
|
||||
default_bg = 0x000000
|
||||
debug = False
|
||||
if "default_bg" in kwargs:
|
||||
default_bg = kwargs.pop("default_bg")
|
||||
if "debug" in kwargs:
|
||||
debug = kwargs.pop("debug")
|
||||
|
||||
if supervisor.runtime.display is None:
|
||||
request_display_config(640, 480)
|
||||
super().__init__(supervisor.runtime.display, default_bg=default_bg, debug=debug)
|
||||
212
adafruit_fruitjam/network.py
Normal file
212
adafruit_fruitjam/network.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams, written for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
"""
|
||||
`adafruit_fruitjam.network`
|
||||
================================================================================
|
||||
|
||||
CircuitPython PortalBase network driver for Adafruit Fruit Jam.
|
||||
|
||||
* Author(s): Limor Fried, Kevin J. Walters, Melissa LeBlanc-Williams, Tim Cocks
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
**Hardware:**
|
||||
|
||||
* `Adafruit Fruit Jam <https://www.adafruit.com/product/6200>`_
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Adafruit CircuitPython firmware for the supported boards:
|
||||
https://github.com/adafruit/circuitpython/releases
|
||||
|
||||
"""
|
||||
|
||||
import gc
|
||||
|
||||
import microcontroller
|
||||
import neopixel
|
||||
from adafruit_portalbase.network import (
|
||||
CONTENT_IMAGE,
|
||||
CONTENT_JSON,
|
||||
CONTENT_TEXT,
|
||||
NetworkBase,
|
||||
)
|
||||
from adafruit_portalbase.wifi_coprocessor import WiFi
|
||||
|
||||
__version__ = "0.0.0+auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FruitJam.git"
|
||||
|
||||
# you'll need to pass in an io username, width, height, format (bit depth), io key, and then url!
|
||||
IMAGE_CONVERTER_SERVICE = (
|
||||
"https://io.adafruit.com/api/v2/%s/integrations/image-formatter?"
|
||||
"x-aio-key=%s&width=%d&height=%d&output=BMP%d&url=%s"
|
||||
)
|
||||
|
||||
|
||||
class Network(NetworkBase):
|
||||
"""CircuitPython PortalBase network driver for Adafruit Fruit Jam.
|
||||
|
||||
:param status_neopixel: The pin for the status NeoPixel. Use ``board.NEOPIXEL`` for the on-board
|
||||
NeoPixel. Defaults to ``None``, not the status LED. Or pass an
|
||||
instantiated NeoPixel object.
|
||||
:param esp: A passed ESP32 object, Can be used in cases where the ESP32 chip needs to be used
|
||||
before calling the fruitjam class. Defaults to ``None``.
|
||||
:param busio.SPI external_spi: A previously declared spi object. Defaults to ``None``.
|
||||
:param bool extract_values: If true, single-length fetched values are automatically extracted
|
||||
from lists and tuples. Defaults to ``True``.
|
||||
:param debug: Turn on debug print outs. Defaults to False.
|
||||
:param convert_image: Determine whether or not to use the AdafruitIO image converter service.
|
||||
Set as False if your image is already resized. Defaults to True.
|
||||
:param image_url_path: The HTTP traversal path for a background image to display.
|
||||
Defaults to ``None``.
|
||||
:param image_json_path: The JSON traversal path for a background image to display. Defaults to
|
||||
``None``.
|
||||
:param image_resize: What size to resize the image we got from the json_path, make this a tuple
|
||||
of the width and height you want. Defaults to ``None``.
|
||||
:param image_position: The position of the image on the display as an (x, y) tuple. Defaults to
|
||||
``None``.
|
||||
:param image_dim_json_path: The JSON traversal path for the original dimensions of image tuple.
|
||||
Used with fetch(). Defaults to ``None``.
|
||||
|
||||
"""
|
||||
|
||||
def __init__( # noqa: PLR0913 Too many arguments in function definition
|
||||
self,
|
||||
*,
|
||||
status_neopixel=None,
|
||||
esp=None,
|
||||
external_spi=None,
|
||||
extract_values=True,
|
||||
debug=False,
|
||||
convert_image=True,
|
||||
image_url_path=None,
|
||||
image_json_path=None,
|
||||
image_resize=None,
|
||||
image_position=None,
|
||||
image_dim_json_path=None,
|
||||
):
|
||||
print(f"status_neopixel", status_neopixel)
|
||||
if isinstance(status_neopixel, microcontroller.Pin):
|
||||
status_led = neopixel.NeoPixel(status_neopixel, 1, brightness=0.2)
|
||||
elif isinstance(status_neopixel, neopixel.NeoPixel):
|
||||
status_led = status_neopixel
|
||||
else:
|
||||
status_led = None
|
||||
|
||||
wifi = WiFi(status_led=status_led, esp=esp, external_spi=external_spi)
|
||||
|
||||
super().__init__(
|
||||
wifi,
|
||||
extract_values=extract_values,
|
||||
debug=debug,
|
||||
)
|
||||
|
||||
self._convert_image = convert_image
|
||||
self._image_json_path = image_json_path
|
||||
self._image_url_path = image_url_path
|
||||
self._image_resize = image_resize
|
||||
self._image_position = image_position
|
||||
self._image_dim_json_path = image_dim_json_path
|
||||
gc.collect()
|
||||
|
||||
@property
|
||||
def ip_address(self):
|
||||
"""Return the IP Address nicely formatted"""
|
||||
return self._wifi.esp.pretty_ip(self._wifi.esp.ip_address)
|
||||
|
||||
def image_converter_url(self, image_url, width, height, color_depth=16):
|
||||
"""Generate a converted image url from the url passed in,
|
||||
with the given width and height. aio_username and aio_key must be
|
||||
set in secrets."""
|
||||
try:
|
||||
aio_username = self._get_setting("AIO_USERNAME")
|
||||
aio_key = self._get_setting("AIO_KEY")
|
||||
except KeyError as error:
|
||||
raise KeyError(
|
||||
"\n\nOur image converter service require a login/password to rate-limit. "
|
||||
"Please register for a free adafruit.io account and place the user/key in "
|
||||
"your secrets file under 'aio_username' and 'aio_key'"
|
||||
) from error
|
||||
|
||||
return IMAGE_CONVERTER_SERVICE % (
|
||||
aio_username,
|
||||
aio_key,
|
||||
width,
|
||||
height,
|
||||
color_depth,
|
||||
image_url,
|
||||
)
|
||||
|
||||
def process_image(self, json_data, sd_card=False): # noqa: PLR0912 Too many branches
|
||||
"""
|
||||
Process image content
|
||||
|
||||
:param json_data: The JSON data that we can pluck values from
|
||||
:param bool sd_card: Whether or not we have an SD card inserted
|
||||
|
||||
"""
|
||||
filename = None
|
||||
position = None
|
||||
image_url = None
|
||||
|
||||
if self._image_url_path:
|
||||
image_url = self._image_url_path
|
||||
|
||||
if self._image_json_path:
|
||||
image_url = self.json_traverse(json_data, self._image_json_path)
|
||||
|
||||
iwidth = 0
|
||||
iheight = 0
|
||||
if self._image_dim_json_path:
|
||||
iwidth = int(self.json_traverse(json_data, self._image_dim_json_path[0]))
|
||||
iheight = int(self.json_traverse(json_data, self._image_dim_json_path[1]))
|
||||
print("image dim:", iwidth, iheight)
|
||||
|
||||
if image_url:
|
||||
print("original URL:", image_url)
|
||||
if self._convert_image:
|
||||
if iwidth < iheight:
|
||||
image_url = self.image_converter_url(
|
||||
image_url,
|
||||
int(self._image_resize[1] * self._image_resize[1] / self._image_resize[0]),
|
||||
self._image_resize[1],
|
||||
)
|
||||
else:
|
||||
image_url = self.image_converter_url(
|
||||
image_url, self._image_resize[0], self._image_resize[1]
|
||||
)
|
||||
|
||||
print("convert URL:", image_url)
|
||||
# convert image to bitmap and cache
|
||||
# print("**not actually wgetting**")
|
||||
filename = "/cache.bmp"
|
||||
chunk_size = 4096 # default chunk size is 12K (for QSPI)
|
||||
if sd_card:
|
||||
filename = "/sd" + filename
|
||||
chunk_size = 512 # current bug in big SD writes -> stick to 1 block
|
||||
try:
|
||||
self.wget(image_url, filename, chunk_size=chunk_size)
|
||||
except OSError as error:
|
||||
raise OSError(
|
||||
"""\n\nNo writable filesystem found for saving datastream.
|
||||
Insert an SD card or set internal filesystem to be unsafe by
|
||||
setting 'disable_concurrent_write_protection' in the mount options in boot.py"""
|
||||
) from error
|
||||
except RuntimeError as error:
|
||||
raise RuntimeError("wget didn't write a complete file") from error
|
||||
if iwidth < iheight:
|
||||
pwidth = int(self._image_resize[1] * self._image_resize[1] / self._image_resize[0])
|
||||
position = (
|
||||
self._image_position[0] + int((self._image_resize[0] - pwidth) / 2),
|
||||
self._image_position[1],
|
||||
)
|
||||
else:
|
||||
position = self._image_position
|
||||
|
||||
image_url = None
|
||||
gc.collect()
|
||||
|
||||
return filename, position
|
||||
|
|
@ -15,7 +15,7 @@ Implementation Notes
|
|||
|
||||
**Hardware:**
|
||||
|
||||
* `Adafruit Fruit Jam <url>`_"
|
||||
* `Adafruit Fruit Jam <https://www.adafruit.com/product/6200>`_
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,14 @@ extensions = [
|
|||
# Uncomment the below if you use native CircuitPython modules such as
|
||||
# digitalio, micropython and busio. List the modules you use. Without it, the
|
||||
# autodoc module docs will fail to generate with a warning.
|
||||
autodoc_mock_imports = ["displayio", "supervisor", "framebufferio", "picodvi", "audiobusio"]
|
||||
autodoc_mock_imports = [
|
||||
"displayio",
|
||||
"supervisor",
|
||||
"framebufferio",
|
||||
"picodvi",
|
||||
"audiobusio",
|
||||
"terminalio",
|
||||
]
|
||||
|
||||
autodoc_preserve_defaults = True
|
||||
|
||||
|
|
|
|||
12
examples/fruitjam_displaycheck.py
Normal file
12
examples/fruitjam_displaycheck.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import supervisor
|
||||
|
||||
from adafruit_fruitjam.peripherals import request_display_config
|
||||
|
||||
print(f"Display is None ? {supervisor.runtime.display is None}")
|
||||
print(f"size: {supervisor.runtime.display.width}, {supervisor.runtime.display.height}")
|
||||
request_display_config(360, 200)
|
||||
print(f"size: {supervisor.runtime.display.width}, {supervisor.runtime.display.height}")
|
||||
|
|
@ -1,12 +1,27 @@
|
|||
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2025 Tim Cocks, written for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import supervisor
|
||||
# NOTE: Make sure you've created your settings.toml file before running this example
|
||||
# https://learn.adafruit.com/adafruit-pyportal/create-your-settings-toml-file
|
||||
|
||||
from adafruit_fruitjam.peripherals import request_display_config
|
||||
from adafruit_fruitjam import FruitJam
|
||||
|
||||
print(f"Display is None ? {supervisor.runtime.display is None}")
|
||||
print(f"size: {supervisor.runtime.display.width}, {supervisor.runtime.display.height}")
|
||||
request_display_config(360, 200)
|
||||
print(f"size: {supervisor.runtime.display.width}, {supervisor.runtime.display.height}")
|
||||
# Set a data source URL
|
||||
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
|
||||
|
||||
# Create the PyPortal object
|
||||
fruitjam = FruitJam(url=TEXT_URL, text_position=(10, 20))
|
||||
fruitjam.neopixels.brightness = 0.1
|
||||
|
||||
# Go get that data
|
||||
print("Fetching text from", TEXT_URL)
|
||||
data = fruitjam.fetch()
|
||||
|
||||
# Print out what we got
|
||||
print("-" * 40)
|
||||
print(data)
|
||||
print("-" * 40)
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -7,3 +7,8 @@ Adafruit-Blinka
|
|||
adafruit-circuitpython-busdevice
|
||||
adafruit-circuitpython-tlv320
|
||||
adafruit-circuitpython-neopixel
|
||||
adafruit-circuitpython-portalbase
|
||||
adafruit-circuitpython-esp32spi
|
||||
adafruit-circuitpython-requests
|
||||
adafruit-circuitpython-bitmap-font
|
||||
adafruit-circuitpython-display-text
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ ignore = [
|
|||
"PLR2004", # magic-value-comparison
|
||||
"UP030", # format literals
|
||||
"PLW1514", # unspecified-encoding
|
||||
"PLR0914", # Too many locals
|
||||
"PLR0915", # Too many statements
|
||||
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue