frame-sdk


Nameframe-sdk JSON
Version 1.2.2 PyPI version JSON
download
home_pageNone
SummaryPython Developer SDK for Brilliant Frame glasses
upload_time2024-08-03 21:40:30
maintainerNone
docs_urlNone
authorNone
requires_python>=3.7
licenseMIT License Copyright (c) 2024 Roger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Frame SDK for Python
The Python SDK for the Frame AI glasses from [Brilliant Labs](https://github.com/brilliantlabsAR).  View on [PyPI](https://pypi.org/project/frame-sdk/).

## Install

```sh
pip3 install frame-sdk
```

## Documentation

Check out [the docs](https://docs.brilliant.xyz/frame/building-apps/) for complete guidance on everything you can do with the Frame.

## Relationship to `frame-utilities-for-python`

The [`frame-utilities-for-python`](https://github.com/brilliantlabsAR/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.

```python
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:

```sh
pip3 install pytest
```

With a Frame device in range, run:

```sh
python3 -m pytest tests/*
```

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

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "frame-sdk",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "Roger Pincombe <pip@betechie.com>, Brilliant Labs <info@brilliant.xyz>",
    "download_url": "https://files.pythonhosted.org/packages/f7/0b/95c49f8c51b49d6a501f94771f92dc6c1fe254aab32a49f9e8a766b25553/frame_sdk-1.2.2.tar.gz",
    "platform": null,
    "description": "# Frame SDK for Python\nThe Python SDK for the Frame AI glasses from [Brilliant Labs](https://github.com/brilliantlabsAR).  View on [PyPI](https://pypi.org/project/frame-sdk/).\n\n## Install\n\n```sh\npip3 install frame-sdk\n```\n\n## Documentation\n\nCheck out [the docs](https://docs.brilliant.xyz/frame/building-apps/) for complete guidance on everything you can do with the Frame.\n\n## Relationship to `frame-utilities-for-python`\n\nThe [`frame-utilities-for-python`](https://github.com/brilliantlabsAR/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.\n\nIt 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.\n\n## Examples\n\nHere's a simple example of how to use the Frame SDK to display text, take a photo, and more.\n\n```python\nimport asyncio\nfrom frame_sdk import Frame\nfrom frame_sdk.display import Alignment, PaletteColors\nfrom frame_sdk.camera import Quality, AutofocusType\nimport datetime\n\nasync def main():\n    # the with statement handles the connection and disconnection to Frame\n    async with Frame() as f:\n        # you can access the lower-level bluetooth connection via f.bluetooth, although you shouldn't need to do this often\n        print(f\"Connected: {f.bluetooth.is_connected()}\")\n\n        # let's get the current battery level\n        print(f\"Frame battery: {await f.get_battery_level()}%\")\n\n        # let's write (or overwrite) the file greeting.txt with \"Hello world\".\n        # You can provide a bytes object or convert a string with .encode()\n        await f.files.write_file(\"greeting.txt\", b\"Hello world\")\n\n        # And now we read that file back.\n        # Note that we should convert the bytearray to a string via the .decode() method.\n        print((await f.files.read_file(\"greeting.txt\")).decode())\n        \n        # run_lua will automatically handle scripts that are too long for the MTU, so you don't need to worry about it.\n        # It will also automatically handle responses that are too long for the MTU automatically.\n        await f.run_lua(\"frame.display.text('Hello world', 50, 100);frame.display.show()\")\n\n        # evaluate is equivalent to f.run_lua(\"print(\\\"1+2\\\"), await_print=True)\n        # It will also automatically handle responses that are too long for the MTU automatically.\n        print(await f.evaluate(\"1+2\"))\n\n        print(\"Tap the Frame to continue...\")\n        await f.display.show_text(\"Tap the Frame to take a photo\", align=Alignment.MIDDLE_CENTER)\n        await f.motion.wait_for_tap()\n\n        # take a photo and save to disk\n        await f.display.show_text(\"Taking photo...\", align=Alignment.MIDDLE_CENTER)\n        await f.camera.save_photo(\"frame-test-photo.jpg\")\n        await f.display.show_text(\"Photo saved!\", align=Alignment.MIDDLE_CENTER, color=PaletteColors.GREEN)\n        # or with more control\n        await f.camera.save_photo(\"frame-test-photo-2.jpg\", autofocus_seconds=3, quality=Quality.HIGH, autofocus_type=AutofocusType.CENTER_WEIGHTED)\n        # or get the raw bytes\n        photo_bytes = await f.camera.take_photo(autofocus_seconds=1)\n\n        print(\"About to record until you stop talking\")\n        await f.display.show_text(\"Say something...\", align=Alignment.MIDDLE_CENTER)\n\t\t# record audio to a file\n        length = await f.microphone.save_audio_file(\"test-audio.wav\")\n        print(f\"Recorded {length:01.1f} seconds: \\\"./test-audio.wav\\\"\")\n        await f.display.show_text(f\"Recorded {length:01.1f} seconds\", align=Alignment.MIDDLE_CENTER)\n        await asyncio.sleep(3)\n\n        # or get the audio directly in memory\n        await f.display.show_text(\"Say something else...\", align=Alignment.MIDDLE_CENTER)\n        audio_data = await f.microphone.record_audio(max_length_in_seconds=10)\n        await f.display.show_text(f\"Playing back {len(audio_data) / f.microphone.sample_rate:01.1f} seconds of audio\", align=Alignment.MIDDLE_CENTER)\n        # you can play back the audio on your computer\n        f.microphone.play_audio_background(audio_data)\n        # or process it using other audio handling libraries, upload to a speech-to-text service, etc.\n\n        print(\"Move around to track intensity of your motion\")\n        await f.display.show_text(\"Move around to track intensity of your motion\", align=Alignment.MIDDLE_CENTER)\n        intensity_of_motion = 0\n        prev_direction = await f.motion.get_direction()\n        for _ in range(10):\n            await asyncio.sleep(0.1)\n            direction = await f.motion.get_direction()\n            intensity_of_motion = max(intensity_of_motion, (direction-prev_direction).amplitude())\n            prev_direction = direction\n        print(f\"Intensity of motion: {intensity_of_motion:01.2f}\")\n        await f.display.show_text(f\"Intensity of motion: {intensity_of_motion:01.2f}\", align=Alignment.MIDDLE_CENTER)\n        print(\"Tap the Frame to continue...\")\n        await f.motion.wait_for_tap()\n\t\t\n        # Show the full palette\n        width = 640 // 4\n        height = 400 // 4\n        for color in range(0, 16):\n            tile_x = (color % 4)\n            tile_y = (color // 4)\n            await f.display.draw_rect(tile_x*width+1, tile_y*height+1, width, height, PaletteColors(color))\n            await f.display.write_text(f\"{color}\", tile_x*width+width//2+1, tile_y*height+height//2+1)\n        await f.display.show()\n\n        print(\"Tap the Frame to continue...\")\n        await f.motion.wait_for_tap()\n\n        # scroll some long text\n        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\")\n\n        # display battery indicator and time as a home screen\n        batteryPercent = await f.get_battery_level()\n        # select a battery fill color from the default palette based on level\n        color = PaletteColors.RED if batteryPercent < 20 else PaletteColors.YELLOW if batteryPercent < 50 else PaletteColors.GREEN\n        # specify the size of the battery indicator in the top-right\n        batteryWidth = 150\n        batteryHeight = 75\n        # draw the endcap of the battery\n        await f.display.draw_rect(640-32,40 + batteryHeight//2-8, 32, 16, PaletteColors.WHITE)\n        # draw the battery outline\n        await f.display.draw_rect_filled(640-16-batteryWidth, 40-8, batteryWidth+16, batteryHeight+16, PaletteColors.WHITE, 1, 15)\n        # fill the battery based on level\n        await f.display.draw_rect(640-8-batteryWidth, 40, int(batteryWidth * 0.01 * batteryPercent), batteryHeight, color)\n        # write the battery level\n        await f.display.write_text(f\"{batteryPercent}%\", 640-8-batteryWidth, 40, batteryWidth, batteryHeight, Alignment.MIDDLE_CENTER)\n        # write the time and date in the center of the screen\n        await f.display.write_text(datetime.datetime.now().strftime(\"%#I:%M %p\\n%a, %B %d, %Y\").lstrip(\"0\"), align=Alignment.MIDDLE_CENTER)\n        # now show what we've been drawing to the buffer\n        await f.display.show()\n\n        # set a wake screen via script, so when you tap to wake the frame, it shows the battery and time\n        await f.run_on_wake(\"\"\"frame.display.text('Battery: ' .. frame.battery_level() ..  '%', 10, 10);\n                            if frame.time.utc() > 10000 then\n                                local time_now = frame.time.date();\n                                frame.display.text(time_now['hour'] .. ':' .. time_now['minute'], 300, 160);\n                                frame.display.text(time_now['month'] .. '/' .. time_now['day'] .. '/' .. time_now['year'], 300, 220) \n                            end;\n                            frame.display.show();\n                            frame.sleep(10);\n                            frame.display.text(' ',1,1);\n                            frame.display.show();\n                            frame.sleep()\"\"\")\n\n        # tell frame to sleep after 10 seconds then clear the screen and go to sleep, without blocking for that\n        await f.run_lua(\"frame.sleep(10);frame.display.text(' ',1,1);frame.display.show();frame.sleep()\")\n\n    print(\"disconnected\")\n\n\n\nasyncio.run(main())\n\n```\n\n## Tests\n\nTo run the unit tests, ensure you have pytest installed:\n\n```sh\npip3 install pytest\n```\n\nWith a Frame device in range, run:\n\n```sh\npython3 -m pytest tests/*\n```\n\nNote that one of the audio playback tests fails on Windows.",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2024 Roger  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "Python Developer SDK for Brilliant Frame glasses",
    "version": "1.2.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/OkGoDoIt/frame-sdk-python/issues",
        "Homepage": "https://github.com/OkGoDoIt/frame-sdk-python"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1555c33537bcc04480be8b1ea1beeaed3797689a906668dd7a3c4cd4e4da13d3",
                "md5": "e2313623f9f9a5845d2afc8da3e3490a",
                "sha256": "85105e7803f7ca4eb318a84aa6025d7476deb8424a5757b607920b88dbe9a18b"
            },
            "downloads": -1,
            "filename": "frame_sdk-1.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e2313623f9f9a5845d2afc8da3e3490a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 27177,
            "upload_time": "2024-08-03T21:40:27",
            "upload_time_iso_8601": "2024-08-03T21:40:27.206186Z",
            "url": "https://files.pythonhosted.org/packages/15/55/c33537bcc04480be8b1ea1beeaed3797689a906668dd7a3c4cd4e4da13d3/frame_sdk-1.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f70b95c49f8c51b49d6a501f94771f92dc6c1fe254aab32a49f9e8a766b25553",
                "md5": "7d0a30a34dd6c593f6ba6723b11f3cfc",
                "sha256": "1ee2225812b10be67afd902ade0ec512dfb37739cf3b8b1fd36bca71de75f411"
            },
            "downloads": -1,
            "filename": "frame_sdk-1.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "7d0a30a34dd6c593f6ba6723b11f3cfc",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 497664,
            "upload_time": "2024-08-03T21:40:30",
            "upload_time_iso_8601": "2024-08-03T21:40:30.093118Z",
            "url": "https://files.pythonhosted.org/packages/f7/0b/95c49f8c51b49d6a501f94771f92dc6c1fe254aab32a49f9e8a766b25553/frame_sdk-1.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-03 21:40:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "OkGoDoIt",
    "github_project": "frame-sdk-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "frame-sdk"
}
        
Elapsed time: 0.27700s