Cat Facts JSON

Having a play around with the Pimoroni Cat Facts example code, and noticed there’s also cat breeds data available too.

"""
Get a cat fact from t'internet!
You will need to add your wireless SSID and password to secrets.py (and save this file to your Pico)
"""

import requests
from time import sleep



# request = requests.get('http://catfact.ninja/fact').json()
# fact = request['fact']
# print('Cat fact!')
# print(fact)


request = requests.get('http://catfact.ninja/breeds').json()
breed = request['data']['breed']
for x in breed:
  print(x)
#print('Cat breed :')
#print(fact)

Error reads: TypeError: list indices must be integers or slices, not str

Change breed = request['data']['breed']
to breed = request['data']
and I get all the JSON data

{'breed': 'Abyssinian', 'country': 'Ethiopia', 'origin': 'Natural/Standard', 'coat': 'Short', 'pattern': 'Ticked'}
{'breed': 'Aegean', 'country': 'Greece', 'origin': 'Natural/Standard', 'coat': 'Semi-long', 'pattern': 'Bi- or tri-colored'}
{'breed': 'American Curl', 'country': 'United States', 'origin': 'Mutation', 'coat': 'Short/Long', 'pattern': 'All'}
{'breed': 'American Bobtail', 'country': 'United States', 'origin': 'Mutation', 'coat': 'Short/Long', 'pattern': 'All'}

How do I code the python to get just breed names, or any other attribute?

Thank you.

The request['data'] output appears to be a list of dicts, one for each breed, so you can step through the list and extract the 'breed' entry from each dict:

request = requests.get('http://catfact.ninja/breeds').json()
data = request['data']
for i in range(len(data)):
    print(data[i]['breed'])

Thanks for that, it set me off and I then found enumerate

request = requests.get('http://catfact.ninja/breeds').json()
data = request['data']
for idx, val in enumerate(data):
    print(f"Index: {idx}, Value: {val}")    

You could extract the Breed or any other information as well:

request = requests.get('http://catfact.ninja/breeds').json()
for idx, val in enumerate(request['data']):
    print(f"Index: {idx}, Breed: {val['breed']}, Origin: {val['origin']}")
1 Like

What an interesting site. Useful data for trying out my new PRESTO

# Test WIFI network DEMO on PRESTO - using json()
# Tony Goodhew 30th Dec 2024
# Uses site http://catfact.ninja
"""
Get a cat fact from t'internet!
You will need to add your wireless SSID and password to secrets.py (and save this file to your Pico)
"""

import time
from random import randint
from presto import Presto
import network
import rp2
import requests
from secrets import WIFI_SSID, WIFI_PASSWORD
rp2.country("GB")
# Setup for the Presto display
presto = Presto(full_res=True,ambient_light=True)
display = presto.display

WIDTH, HEIGHT = display.get_bounds()

# Create some colours
BLUE = display.create_pen(20,0,255)
WHITE = display.create_pen(255, 255, 255)
RED = display.create_pen(255,0,0)
ORANGE = display.create_pen(245, 165, 4)
GREEN = display.create_pen(0,255,0)
PINK = display.create_pen(250, 125, 180)
CYAN = display.create_pen(0,255,255)
MAGENTA = display.create_pen(255,0,255)
BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)

colours = [RED,PINK,YELLOW,ORANGE,GREEN,CYAN,BLUE,MAGENTA]

def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()
    presto.update()

display.set_font("bitmap8")
clean()
display.set_pen(RED)
display.text("Cat Facts",70,200,460,8)
presto.update()
time.sleep(2)

touch = presto.touch # Activate touch

clean()

# connect to wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
while wlan.isconnected() is False:
    print('Waiting for connection...')
    sleep(1)

running = True
while running:
    clean()
    request = requests.get('http://catfact.ninja/fact').json()
    fact = request['fact']
    print('Cat fact!')
    print(fact)
    sz = 4
    if len(fact) > 250:
        sz = 3
    display.set_pen(colours[randint(1,7)])
    display.text(fact,5,20,469,sz)
    presto.update()
    
    # Time delay loop - Cannot use time.sleep()
    # Touching the screen halts the program
    deadline = time.ticks_ms() + 10000
    while time.ticks_ms() < deadline:
        touch.poll()
        if touch.state:
            clean()
            presto.update()
            running = False # Reset flag to finish main loop
            deadline = 0    # Stops this loop
        
print("\nTerminated by screen touch")

This updated version has some improvements:
1 Read time and text size coincide better with the length of the ‘fact’
2 Apostrophes have been improved but there are still a few ‘strange’ characters left.
3 Text length in bottom right corner.

# Cat Facts - Test WIFI network DEMO on PRESTO - using json()
# Tony Goodhew 31th Dec 2024 - vers 2
# Uses site http://catfact.ninja
# HALT program by touching the screen

"""
Get a cat fact from t'internet!
You will need to add your wireless SSID and password to secrets.py (and save this file to your Pico)
"""

import time
from random import randint
from presto import Presto
import network
import rp2
import requests
from secrets import WIFI_SSID, WIFI_PASSWORD
rp2.country("GB")
# Setup for the Presto display
presto = Presto(full_res=True,ambient_light=True)
display = presto.display

WIDTH, HEIGHT = display.get_bounds()

# Create some colours
BLUE = display.create_pen(20,0,255)
WHITE = display.create_pen(255, 255, 255)
RED = display.create_pen(255,0,0)
ORANGE = display.create_pen(245, 165, 4)
GREEN = display.create_pen(0,255,0)
PINK = display.create_pen(250, 125, 180)
CYAN = display.create_pen(0,255,255)
MAGENTA = display.create_pen(255,0,255)
BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)
GREY = display.create_pen(70,70,70)

colours = [RED,PINK,YELLOW,ORANGE,GREEN,CYAN,BLUE,MAGENTA]

def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()
    presto.update()

def cleanup(s):
    n = ""
    l = len(s)
    for p in range(l):
        c = s[p]
#        print(c, str(ord(c)))
        if ord(c) == 8217:
            c = "'"
        n = n + c
    return n

display.set_font("bitmap8")
clean()
display.set_pen(RED)
display.text("Cat Facts",70,200,460,8)
display.set_pen(BLUE)
display.text("Data from: http://catfact.ninja/fact",70,400,480,2)
display.text("Tony Goodhew, Leicester UK",120,450,480,2)
presto.update()
time.sleep(2)

touch = presto.touch # Activate touch

# connect to wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
while wlan.isconnected() is False:
    print('Waiting for connection...')
    time.sleep(1)

request = requests.get('http://catfact.ninja/fact').json()
fact = request['fact']

running = True
while running:
    clean()    
    fact = cleanup(fact)
    print(fact)
    sz = 4
    charno = len(fact)
    delay = charno *60
    if charno > 300:
        sz = 3

    display.set_pen(colours[randint(1,7)])
    display.text(fact,5,20,469,sz)
    display.set_pen(GREY)
    display.text(str(charno),450,455,100,2)
    presto.update()
    request = requests.get('http://catfact.ninja/fact').json()
    fact = request['fact']
    
    # Time delay loop - Cannot use time.sleep()
    # Touching the screen halts the program
    deadline = time.ticks_ms() + delay
    while time.ticks_ms() < deadline:
        touch.poll()
        if touch.state:
            clean()
            presto.update()
            running = False # Reset flag to finish main loop
            deadline = 0    # Stops this loop
        
print("\nTerminated by screen touch")

Happy New Year!

GENIUS!

One day, I shall try and make my LCD GFX do something similar.
At the moment, it’s showing a new fact every 60 seconds against a random background colour.

"""
Get a cat fact from t'internet!
You will need to add your wireless SSID and password to secrets.py (and save this file to your Pico)
"""
from network_manager import NetworkManager
import WIFI_CONFIG
import uasyncio
import network
import requests
import urequests
import time
import gfx_pack
from gfx_pack import GfxPack , SWITCH_A, SWITCH_B, SWITCH_C, SWITCH_D, SWITCH_E
import json
from urandom import *
import random


SWITCH_A = 0
SWITCH_B = 1
SWITCH_C = 2
SWITCH_D = 3
SWITCH_E = 4


board = gfx_pack.GfxPack()
gp = GfxPack()
display = gp.display
#display = board.display
sys_status = "Starting"
print(sys_status)
WIDTH, HEIGHT = display.get_bounds()
display.set_backlight(0.1)  # turn on the white component of the backlight


def display_status():
    global sys_status
    display.set_pen(0)
    display.clear()
    display.set_pen(15)
    display.text(sys_status, 0, 0, WIDTH, 1)
    display.update()



def status_handler(mode, status, ip):
    # reports wifi connection status
    global sys_status
    print(mode, status, ip)
    sys_status = 'Mode: {0} Connected: {1} IP: {2}'.format(mode, status, ip)
    display_status()
    #display.update()
    print('Connecting to wifi...')
    if status is not None:
        if status:
            print('Wifi connection successful!')
            time.sleep(0.2)
            #display.clear()
        else:
            print('Wifi connection failed!')



try:
    network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
    uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
except Exception as e:
    print(f'Wifi connection failed! {e}')

while True:
    URLF = ('https://catfact.ninja/fact?max_length=180')
    print(URLF)
    URLB = ('https://catfact.ninja/breeds')
    # print(URLB)

    # open the json file
    print(f'Requesting URLF: {URLF}')
    f = urequests.get(URLF)

    # open the json data
    ff = f.json()

    print('Data obtained!')
    f.close()
    kf = ff['fact']
    print("Fact length is ", len(kf))
    if len(kf) > 50:
        sz = 0.3
    else:
        sz = 2
    
    

# request = requests.get('http://catfact.ninja/breeds?limit=111').json()
# data = request['data']    
# for idx, val in enumerate(data):
#    print(f"Index: {idx}, Value: {val}")


    col1 = (random.randint(0,255))
    print("col1 ", col1)
    col2 = (random.randint(0,255))
    print("col2 ", col2)
    col3 = (random.randint(0,255))
    print("col3 ", col3)
    

    print('kat fact',kf)
    display.set_pen(0)
    display.clear()
    display.update()
    display.set_pen(15)
    board.set_backlight(col1,col2,col3)  # Random Backlight Colour
    display.text("Cat Fact! ", 0, 0, WIDTH, 0.5)
    display.text((kf), 0, 8, WIDTH, (sz))
    display.set_pen(0)
    display.update()
    
    countdown = 60
    for i in (range(countdown)):
        time.sleep(1)
        print(i)   ####  <---- To do - get the countdown on the LCD!  ####
    print("Time's up!")
    #time.sleep(60)
    

Edited it to get it working on my PRESTO. Nice job.

I’ve used uasyncio what does it do?

It’s a pity the Cat Fact data contain escape code and ‘funny bits’.

uasyncio - something to do with networking?

Yes, the junk chars are annoying.

Ideally I’d like to make use of the buttons on the GFX LCD to refresh the display, but no joy there yet.

Asyncio is used for multitasking.

Here is a free book - Using Asyncio in Python

I need to study this - not found I needed it yet - what have I been missing?

MicroPython Video here:
How to Use Asyncio in MicroPython (Raspberry Pi Pico) | Digi-Key Electronics

This looks easier!

1 Like

I think I’ve managed to remove the annoying characters in the Cat Facts data which I think were caused by entering the data from a wordprocessor rather than a text editor. There were left and right handed quote characters in the mix with escape codes. Here is the updated program:

# Cat Facts - Test WIFI network DEMO on PRESTO - using json()
# Tony Goodhew 31st Jan 2025 - vers 3
# Uses site http://catfact.ninja
# HALT program by touching the screen

"""
Get a cat fact from t'internet!
You will need to add your wireless SSID and password to secrets.py (and save this file to your Pico)
"""

import time
from random import randint
from presto import Presto
import network
import rp2
import requests
from secrets import WIFI_SSID, WIFI_PASSWORD
rp2.country("GB")
# Setup for the Presto display
presto = Presto(full_res=True,ambient_light=True)
display = presto.display

WIDTH, HEIGHT = display.get_bounds()

# Create some colours
BLUE = display.create_pen(20,0,255)
WHITE = display.create_pen(255, 255, 255)
RED = display.create_pen(255,0,0)
ORANGE = display.create_pen(245, 165, 4)
GREEN = display.create_pen(0,255,0)
PINK = display.create_pen(250, 125, 180)
CYAN = display.create_pen(0,255,255)
MAGENTA = display.create_pen(255,0,255)
BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)
GREY = display.create_pen(70,70,70)

colours = [RED,PINK,YELLOW,ORANGE,GREEN,CYAN,BLUE,MAGENTA]

def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()
    presto.update()
    
def no_escape(txt):
    running = True
    while running:
        x = txt.find("\m")
#        print(x)
        if x == -1:
            running = False
            break
        else:
            txt = txt[:x]+txt[x+2:]
    return txt

def cleanup(s):
#    print(s)
    n = ""
    l = len(s)
    for p in range(l):
        c = s[p]
#        print(c, str(ord(c)))
        if (ord(c) == 8217):
            c = "'"
        elif (ord(c) == 8220) or (ord(c) == 8221):
            c = '"'
        n = n + c
    return n

display.set_font("bitmap8")
clean()
display.set_pen(RED)
display.text("Cat Facts",70,200,460,8)
display.set_pen(BLUE)
display.text("Data from: http://catfact.ninja/fact",70,400,480,2)
display.text("Tony Goodhew, Leicester UK",120,450,480,2)
presto.update()
time.sleep(2)

touch = presto.touch # Activate touch

# connect to wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
while wlan.isconnected() is False:
    print('Waiting for connection...')
    time.sleep(1)

request = requests.get('http://catfact.ninja/fact').json()
fact = request['fact']

running = True
while running:
    clean()    
    fact = cleanup(fact)
    print(fact)
    sz = 4
    charno = len(fact)
    delay = charno *60
    if charno > 300:
        sz = 3

    display.set_pen(colours[randint(1,7)])
    display.text(fact,5,20,469,sz)
    display.set_pen(GREY)
    display.text(str(charno),450,455,100,2)
    presto.update()
    request = requests.get('http://catfact.ninja/fact').json()
    fact = request['fact']
    
    # Time delay loop - Cannot use time.sleep()
    # Touching the screen halts the program
    deadline = time.ticks_ms() + delay
    while time.ticks_ms() < deadline:
        touch.poll()
        if touch.state:
            clean()
            presto.update()
            running = False # Reset flag to finish main loop
            deadline = 0    # Stops this loop
        
print("\nTerminated by screen touch")

@Richard238, Have you managed to use the buttons on your GFX LCD? What do you want them to do?

Thanks, I’ll watch that YT vid when I get a moment.

Not, yet, I’m a bit putt off by the example button test script doing almost nothing whatsoever.
All this does is say “Button A pressed” on a red background. (The button has not been pressed.)

# This example shows you a simple, non-interrupt way of reading GFX Pack's buttons with a loop that checks to see if buttons are pressed.

import time
from gfx_pack import GfxPack, SWITCH_A, SWITCH_B, SWITCH_C, SWITCH_D, SWITCH_E

gp = GfxPack()
display = gp.display

WIDTH, HEIGHT = display.get_bounds()
display.set_backlight(0)  # turn off the white component of the backlight


# sets up a handy function we can call to clear the screen
def clear():
    display.set_pen(0)
    display.clear()
    display.set_pen(15)


# set up
display.set_font("bitmap8")

while True:
    if gp.switch_pressed(SWITCH_A):                       # if a button press is detected...                                          # clear to black
        gp.set_backlight(255, 0, 0, 0)                    # red, green, blue, white
        clear()
        display.text("Button A pressed", 0, 0, WIDTH, 2)  # display some text on the screen
        display.update()                                  # update the display
        time.sleep(1)
    elif gp.switch_pressed(SWITCH_B):
        gp.set_backlight(255, 125, 0, 0)
        clear()
        display.text("Button B pressed", 0, 0, WIDTH, 2)
        display.update()
        time.sleep(1)
    elif gp.switch_pressed(SWITCH_C):
        gp.set_backlight(0, 255, 0, 0)
        clear()
        display.text("Button C pressed", 0, 0, WIDTH, 2)
        display.update()
        time.sleep(1)
    elif gp.switch_pressed(SWITCH_D):
        gp.set_backlight(0, 0, 255, 0)
        clear()
        display.text("Button D pressed", 0, 0, WIDTH, 2)
        display.update()
        time.sleep(1)
    elif gp.switch_pressed(SWITCH_E):
        gp.set_backlight(255, 0, 255, 0)
        clear()
        display.text("Button E pressed", 0, 0, WIDTH, 2)
        display.update()
        time.sleep(1)
    else:
        gp.set_backlight(0, 0, 0, 125)
        clear()
        display.set_pen(15)
        display.text("Press any button!", 0, 0, WIDTH, 2)
        display.update()
    time.sleep(0.01)  # this number is how frequently the Pico checks for button presses

I’d like them to teach me how to use buttons ;-)
Skip to next fact?
Press C for Cat facts?
Press D for dog facts? - I added dog facts yesterday.
Press to change BG colour?

That’s not a wishlist, just ideas so I can learn how to use buttons, especially since most Pimoroni boards have buttons it’d be great to learn to use them.

Just change the indented code under each if or elif. Each time you press a button that piece of code will run.

But nothing happens when I press any of the buttons.
It says A is pressed straight away, even though none of the buttons have been pressed.

This is a basic buttons test using a Pico 2 W ,which does not allow pull-downs on input pins. I’ve run it on a Display 2 240x320 as I do not have a GFX LCD, but these switch pins are the same.

# Basic buttons on  PICO 2 W
# Tony Goodhew = 2nd Jan 2024
from machine import Pin
import time

swA = Pin(12, Pin.IN, Pin.PULL_UP)
swB = Pin(13, Pin.IN, Pin.PULL_UP)
swX = Pin(14, Pin.IN, Pin.PULL_UP)
swY = Pin(15, Pin.IN, Pin.PULL_UP)

while True:
    print(swA.value(),swB.value(),swX.value(),swY.value())
    time.sleep(0.3)

These give a ZERO when pressed.

It may be that the code you are using has the Pins setup with pull-downs and go HIGH when pressed. You cannot see that easily with PimoroniGraphics running.

OK, I’ll try that when I get chance - back at work today so Pi time is once again limited.

With a couple of tweaks to add the fifth button, all good. Thank you!

# Basic buttons on  PICO 2 W
# Tony Goodhew = 2nd Jan 2024   <----2025! ;-)
from machine import Pin
import time

swA = Pin(12, Pin.IN, Pin.PULL_UP)
swB = Pin(13, Pin.IN, Pin.PULL_UP)
swX = Pin(14, Pin.IN, Pin.PULL_UP)
swY = Pin(15, Pin.IN, Pin.PULL_UP)
swE = Pin(22, Pin.IN, Pin.PULL_UP)

while True:
    print(swA.value(),swB.value(),swX.value(),swY.value(),swE.value())
    time.sleep(0.3)

I’ll try applying that to the sample button test when I get a moment.

You can also use interrupts. This is for a Display 2 but should be easy to convert your display.

# Interrupt example using the Pimoroni Display 2
# Tony Goodhew 23 Sept 2023
from machine import Pin
import time
import gc

from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2, PEN_RGB332

from pimoroni import RGBLED

display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, pen_type=PEN_RGB332, rotate=0)
display.set_backlight(1.0)
display.set_font("bitmap8") 

led = RGBLED(6, 7, 8)
led.set_rgb(0,0,0)   #Turn off the RGBLED

WHITE = display.create_pen(255, 255, 255)
BLACK = display.create_pen(0, 0, 0)
RED = display.create_pen(200, 0, 0)
BLUE = display.create_pen(0, 0, 200)
GREEN = display.create_pen(0, 200, 0)

COL = WHITE
delta = 1

led = Pin(25, Pin.OUT)
swA = Pin(12, Pin.IN, Pin.PULL_UP)
swB = Pin(13, Pin.IN, Pin.PULL_UP)
swX = Pin(14, Pin.IN, Pin.PULL_UP)
swY = Pin(15, Pin.IN, Pin.PULL_UP)


def sw_handlerA(pin):
    time.sleep(0.05) # debounce
    global COL
    COL = display.create_pen(200, 0, 0) # RED
    
def sw_handlerB(pin):
    time.sleep(0.05) # debounce
    global COL
    COL = display.create_pen(0, 200, 0) # GREEN
    
def sw_handlerX(pin):
    time.sleep(0.05) # debounce
    global COL
    COL = display.create_pen(0, 0, 200) # BLUE
    
def sw_handlerY(pin):
    time.sleep(0.05) # debounce
    global COL
    global delta
    COL = display.create_pen(200, 200, 0) # YELLOW
    delta = delta * -1 # Reverse count direction

swA.irq(trigger = machine.Pin.IRQ_FALLING, handler = sw_handlerA)
swB.irq(trigger = machine.Pin.IRQ_FALLING, handler = sw_handlerB)
swX.irq(trigger = machine.Pin.IRQ_FALLING, handler = sw_handlerX)
swY.irq(trigger = machine.Pin.IRQ_FALLING, handler = sw_handlerY)

count = 0

# The following loop runs flat out - NO time.sleep()
while True:
    display.set_pen(BLACK)
    display.clear()
    count = count + delta
    display.set_pen(COL)
    display.text(str(count), 10,10,300,6)
    display.update()

Where I do this: display.set_pen(COL)
you could change the background colour

Have fun!

Swapped Pico v2 from GFX LCD to Pico Enviro+ (Currently on winter leave from the greenhouse!)
Swapped Pico v1 from Pico Enviro+ to GFX LCD

Buttons on GFX LCD OK - becasue it’s on Pico v1
Buttons on Enviro+ working just fine, despite now being on Pico v2

OK, why?

The answer I found is to not use pin but to use button

### from machine import Pin  <--- Not required for buttons
from pimoroni import Button  #<---Add this for buttons
import time

swA = Button(12, invert=True)
#swA = Pin(12, Pin.IN, Pin.PULL_UP)
swB = Button(13, invert=True)
swX = Button(14, invert=True)
swY = Button(15, invert=True)

while True:
    if swA.is_pressed:
        print("You pressed button A")
        print()
        time.sleep(0.4)
    elif swB.is_pressed:
        print("You pressed button B")
        print()
        time.sleep(0.4)
    elif swX.is_pressed:
        print("You pressed button X")
        print()
        time.sleep(0.4)
    elif swY.is_pressed:
        print("You pressed button Y")
        print()
        time.sleep(0.4)