Skip to main content

Python Developer SDK for Brilliant Frame glasses

Project description

Frame SDK for Python

The Python SDK for the Frame AI glasses from Brilliant Labs. View on PyPI.

Install

pip3 install frame-sdk

Documentation

Check out the docs for complete guidance on everything you can do with the Frame.

Relationship to frame-utilities-for-python

The frame-utilities-for-python package is for low-level communication with both Frame and Monocle devices and is a thin wrapper around the bluetooth connection, plus some internal tools that are used in the firmware preparation process. This frame-sdk package is a higher-level SDK that provides a more convenient way for developers to build apps for Frame.

It is recommended that you use this package for new projects, unless you have a specific need to use the lower-level frame-utilities-for-python package.

Examples

Here's a simple example of how to use the Frame SDK to display text, take a photo, and more.

import asyncio
from frame_sdk import Frame
from frame_sdk.display import Alignment, PaletteColors
from frame_sdk.camera import Quality, AutofocusType
import datetime

async def main():
    # the with statement handles the connection and disconnection to Frame
    async with Frame() as f:
        # you can access the lower-level bluetooth connection via f.bluetooth, although you shouldn't need to do this often
        print(f"Connected: {f.bluetooth.is_connected()}")

        # let's get the current battery level
        print(f"Frame battery: {await f.get_battery_level()}%")

        # let's write (or overwrite) the file greeting.txt with "Hello world".
        # You can provide a bytes object or convert a string with .encode()
        await f.files.write_file("greeting.txt", b"Hello world")

        # And now we read that file back.
        # Note that we should convert the bytearray to a string via the .decode() method.
        print((await f.files.read_file("greeting.txt")).decode())
        
        # run_lua will automatically handle scripts that are too long for the MTU, so you don't need to worry about it.
        # It will also automatically handle responses that are too long for the MTU automatically.
        await f.run_lua("frame.display.text('Hello world', 50, 100);frame.display.show()")

        # evaluate is equivalent to f.run_lua("print(\"1+2\"), await_print=True)
        # It will also automatically handle responses that are too long for the MTU automatically.
        print(await f.evaluate("1+2"))

        print("Tap the Frame to continue...")
        await f.display.show_text("Tap the Frame to take a photo", align=Alignment.MIDDLE_CENTER)
        await f.motion.wait_for_tap()

        # take a photo and save to disk
        await f.display.show_text("Taking photo...", align=Alignment.MIDDLE_CENTER)
        await f.camera.save_photo("frame-test-photo.jpg")
        await f.display.show_text("Photo saved!", align=Alignment.MIDDLE_CENTER, color=PaletteColors.GREEN)
        # or with more control
        await f.camera.save_photo("frame-test-photo-2.jpg", autofocus_seconds=3, quality=Quality.HIGH, autofocus_type=AutofocusType.CENTER_WEIGHTED)
        # or get the raw bytes
        photo_bytes = await f.camera.take_photo(autofocus_seconds=1)

        print("About to record until you stop talking")
        await f.display.show_text("Say something...", align=Alignment.MIDDLE_CENTER)
		# record audio to a file
        length = await f.microphone.save_audio_file("test-audio.wav")
        print(f"Recorded {length:01.1f} seconds: \"./test-audio.wav\"")
        await f.display.show_text(f"Recorded {length:01.1f} seconds", align=Alignment.MIDDLE_CENTER)
        await asyncio.sleep(3)

        # or get the audio directly in memory
        await f.display.show_text("Say something else...", align=Alignment.MIDDLE_CENTER)
        audio_data = await f.microphone.record_audio(max_length_in_seconds=10)
        await f.display.show_text(f"Playing back {len(audio_data) / f.microphone.sample_rate:01.1f} seconds of audio", align=Alignment.MIDDLE_CENTER)
        # you can play back the audio on your computer
        f.microphone.play_audio_background(audio_data)
        # or process it using other audio handling libraries, upload to a speech-to-text service, etc.

        print("Move around to track intensity of your motion")
        await f.display.show_text("Move around to track intensity of your motion", align=Alignment.MIDDLE_CENTER)
        intensity_of_motion = 0
        prev_direction = await f.motion.get_direction()
        for _ in range(10):
            await asyncio.sleep(0.1)
            direction = await f.motion.get_direction()
            intensity_of_motion = max(intensity_of_motion, (direction-prev_direction).amplitude())
            prev_direction = direction
        print(f"Intensity of motion: {intensity_of_motion:01.2f}")
        await f.display.show_text(f"Intensity of motion: {intensity_of_motion:01.2f}", align=Alignment.MIDDLE_CENTER)
        print("Tap the Frame to continue...")
        await f.motion.wait_for_tap()
		
        # Show the full palette
        width = 640 // 4
        height = 400 // 4
        for color in range(0, 16):
            tile_x = (color % 4)
            tile_y = (color // 4)
            await f.display.draw_rect(tile_x*width+1, tile_y*height+1, width, height, PaletteColors(color))
            await f.display.write_text(f"{color}", tile_x*width+width//2+1, tile_y*height+height//2+1)
        await f.display.show()

        print("Tap the Frame to continue...")
        await f.motion.wait_for_tap()

        # scroll some long text
        await f.display.scroll_text("Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you")

        # display battery indicator and time as a home screen
        batteryPercent = await f.get_battery_level()
        # select a battery fill color from the default palette based on level
        color = PaletteColors.RED if batteryPercent < 20 else PaletteColors.YELLOW if batteryPercent < 50 else PaletteColors.GREEN
        # specify the size of the battery indicator in the top-right
        batteryWidth = 150
        batteryHeight = 75
        # draw the endcap of the battery
        await f.display.draw_rect(640-32,40 + batteryHeight//2-8, 32, 16, PaletteColors.WHITE)
        # draw the battery outline
        await f.display.draw_rect_filled(640-16-batteryWidth, 40-8, batteryWidth+16, batteryHeight+16, PaletteColors.WHITE, 1, 15)
        # fill the battery based on level
        await f.display.draw_rect(640-8-batteryWidth, 40, int(batteryWidth * 0.01 * batteryPercent), batteryHeight, color)
        # write the battery level
        await f.display.write_text(f"{batteryPercent}%", 640-8-batteryWidth, 40, batteryWidth, batteryHeight, Alignment.MIDDLE_CENTER)
        # write the time and date in the center of the screen
        await f.display.write_text(datetime.datetime.now().strftime("%#I:%M %p\n%a, %B %d, %Y").lstrip("0"), align=Alignment.MIDDLE_CENTER)
        # now show what we've been drawing to the buffer
        await f.display.show()

        # set a wake screen via script, so when you tap to wake the frame, it shows the battery and time
        await f.run_on_wake("""frame.display.text('Battery: ' .. frame.battery_level() ..  '%', 10, 10);
                            if frame.time.utc() > 10000 then
                                local time_now = frame.time.date();
                                frame.display.text(time_now['hour'] .. ':' .. time_now['minute'], 300, 160);
                                frame.display.text(time_now['month'] .. '/' .. time_now['day'] .. '/' .. time_now['year'], 300, 220) 
                            end;
                            frame.display.show();
                            frame.sleep(10);
                            frame.display.text(' ',1,1);
                            frame.display.show();
                            frame.sleep()""")

        # tell frame to sleep after 10 seconds then clear the screen and go to sleep, without blocking for that
        await f.run_lua("frame.sleep(10);frame.display.text(' ',1,1);frame.display.show();frame.sleep()")

    print("disconnected")



asyncio.run(main())

Tests

To run the unit tests, ensure you have pytest installed:

pip3 install pytest

With a Frame device in range, run:

python3 -m pytest tests/*

Note that one of the audio playback tests fails on Windows.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

frame_sdk-1.2.2.tar.gz (497.7 kB view details)

Uploaded Source

Built Distribution

frame_sdk-1.2.2-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

File details

Details for the file frame_sdk-1.2.2.tar.gz.

File metadata

  • Download URL: frame_sdk-1.2.2.tar.gz
  • Upload date:
  • Size: 497.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.19

File hashes

Hashes for frame_sdk-1.2.2.tar.gz
Algorithm Hash digest
SHA256 1ee2225812b10be67afd902ade0ec512dfb37739cf3b8b1fd36bca71de75f411
MD5 7d0a30a34dd6c593f6ba6723b11f3cfc
BLAKE2b-256 f70b95c49f8c51b49d6a501f94771f92dc6c1fe254aab32a49f9e8a766b25553

See more details on using hashes here.

File details

Details for the file frame_sdk-1.2.2-py3-none-any.whl.

File metadata

  • Download URL: frame_sdk-1.2.2-py3-none-any.whl
  • Upload date:
  • Size: 27.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.19

File hashes

Hashes for frame_sdk-1.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 85105e7803f7ca4eb318a84aa6025d7476deb8424a5757b607920b88dbe9a18b
MD5 e2313623f9f9a5845d2afc8da3e3490a
BLAKE2b-256 1555c33537bcc04480be8b1ea1beeaed3797689a906668dd7a3c4cd4e4da13d3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page