| Name | frame-sdk JSON |
| Version |
1.2.2
JSON |
| download |
| home_page | None |
| Summary | Python Developer SDK for Brilliant Frame glasses |
| upload_time | 2024-08-03 21:40:30 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.7 |
| 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. |
| 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"
}