Showing preview only (411K chars total). Download the full file or copy to clipboard to get everything.
Repository: martinohanlon/BlueDot
Branch: master
Commit: 208163eeeae2
Files: 148
Total size: 374.3 KB
Directory structure:
gitextract_s_m_z04y/
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── PRIVACYPOLICY.MD
├── README.rst
├── bluedot/
│ ├── __init__.py
│ ├── app.py
│ ├── btcomm.py
│ ├── colors.py
│ ├── constants.py
│ ├── dot.py
│ ├── exceptions.py
│ ├── interactions.py
│ ├── mock.py
│ ├── threads.py
│ └── utils.py
├── clients/
│ ├── android/
│ │ ├── .gitignore
│ │ ├── README.rst
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ ├── release/
│ │ │ │ ├── app-release.aab
│ │ │ │ ├── app-release.apk
│ │ │ │ └── output-metadata.json
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── com/
│ │ │ │ └── stuffaboutcode/
│ │ │ │ └── bluedot/
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── com/
│ │ │ │ │ └── stuffaboutcode/
│ │ │ │ │ ├── bluedot/
│ │ │ │ │ │ ├── BluetoothChatService.java
│ │ │ │ │ │ ├── Button.java
│ │ │ │ │ │ ├── Constants.java
│ │ │ │ │ │ ├── Devices.java
│ │ │ │ │ │ ├── DynamicMatrix.java
│ │ │ │ │ │ └── SettingsActivity.java
│ │ │ │ │ └── logger/
│ │ │ │ │ ├── Log.java
│ │ │ │ │ ├── LogFragment.java
│ │ │ │ │ ├── LogNode.java
│ │ │ │ │ ├── LogView.java
│ │ │ │ │ ├── LogWrapper.java
│ │ │ │ │ └── MessageOnlyLogFilter.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── round_button.xml
│ │ │ │ ├── layout/
│ │ │ │ │ ├── activity_button.xml
│ │ │ │ │ ├── activity_devices.xml
│ │ │ │ │ └── settings_activity.xml
│ │ │ │ ├── menu/
│ │ │ │ │ └── settings_menu.xml
│ │ │ │ ├── values/
│ │ │ │ │ ├── arrays.xml
│ │ │ │ │ ├── attrs.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ └── xml/
│ │ │ │ └── root_preferences.xml
│ │ │ └── test/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── stuffaboutcode/
│ │ │ └── bluedot/
│ │ │ └── ExampleUnitTest.java
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ └── python/
│ └── README.rst
├── docs/
│ ├── Makefile
│ ├── bluedotandroidapp.rst
│ ├── bluedotpythonapp.rst
│ ├── btcommapi.rst
│ ├── build.rst
│ ├── changelog.rst
│ ├── conf.py
│ ├── dotapi.rst
│ ├── examples/
│ │ ├── bt_enumerate.py
│ │ ├── bt_pair_button.py
│ │ ├── bt_pairing.py
│ │ ├── camera.py
│ │ ├── dpad.py
│ │ ├── dpad_layout.py
│ │ ├── goodbye_world.py
│ │ ├── hello_event.py
│ │ ├── hello_world.py
│ │ ├── joypad.py
│ │ ├── led1.py
│ │ ├── led2.py
│ │ ├── led3.py
│ │ ├── looks_border.py
│ │ ├── looks_color.py
│ │ ├── looks_square.py
│ │ ├── looks_visible.py
│ │ ├── many_buttons.py
│ │ ├── many_buttons_random_colors.py
│ │ ├── mock_app.py
│ │ ├── mock_script.py
│ │ ├── multiple_dots.py
│ │ ├── robot1.py
│ │ ├── robot2.py
│ │ ├── robot3.py
│ │ ├── rotation.py
│ │ ├── shout_hello.py
│ │ ├── slider_centre.py
│ │ ├── slider_dimmer.py
│ │ ├── slider_left_right.py
│ │ ├── swipe1.py
│ │ ├── swipe2.py
│ │ ├── swipe_direction.py
│ │ ├── swipe_speed_angle.py
│ │ ├── two_buttons.py
│ │ ├── two_buttons_gap.py
│ │ └── two_buttons_two_events.py
│ ├── gettingstarted.rst
│ ├── index.rst
│ ├── mockapi.rst
│ ├── pairpiandroid.rst
│ ├── pairpipi.rst
│ ├── protocol.rst
│ └── recipes.rst
├── examples/
│ ├── adapter_details.py
│ ├── click_wheel.py
│ ├── client_connects.py
│ ├── client_debug.py
│ ├── color_changer.py
│ ├── connect_multiple_apps.py
│ ├── dot_changer.py
│ ├── dot_debug.py
│ ├── dot_single_button_debug.py
│ ├── double_press.py
│ ├── dpad.py
│ ├── dpad_layout.py
│ ├── hello_world.py
│ ├── joypad.py
│ ├── many_buttons.py
│ ├── matrix_of_dots.py
│ ├── mock_app_debug.py
│ ├── mock_btcomm_debug.py
│ ├── mock_debug.py
│ ├── remote_camera.py
│ ├── server_debug.py
│ ├── simple_robot.py
│ ├── simple_variable_robot.py
│ ├── slider_centre.py
│ ├── slider_horizontal.py
│ ├── slider_led_dimmer.py
│ ├── source_robot.py
│ ├── swipe_debug.py
│ ├── threaded_callbacks.py
│ ├── two_buttons.py
│ └── wait_for_events.py
├── setup.py
└── tests/
├── test_blue_dot.py
├── test_bluetooth_adapter.py
└── test_bluetooth_comms.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [martinohanlon]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Having trouble with BlueDot?
title: ''
labels: ''
assignees: ''
---
Fill in the details or delete as appropriate :)
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
or
Run this program
```python
```
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**System (please complete the following information):**
- OS: [e.g. Raspbian]
- Version [e.g. Buster]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Got an idea for BlueDot?
title: ''
labels: ''
assignees: ''
---
**Describe the feature you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .gitignore
================================================
*.py[cdo]
pythonhosted/
.pytest_cache/
# Editor detritus
*.vim
*.swp
tags
.vscode
.idea
# Packaging detritus
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
coverage
.coverage
.tox
.cache
# Generated documentation
docs/_build
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Martin O'Hanlon
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.
================================================
FILE: PRIVACYPOLICY.MD
================================================
# BlueDot Privacy Policy
The BlueDot Python library and Android app do not store your personal data. That is it!
There is nowhere to store your data, no database, no server. Even if Rick Astley asked [really really nicely](https://www.youtube.com/watch?v=dQw4w9WgXcQ) to see your data it couldn't be given it to him.
For the purposes of utmost transparency... The android app will store 2 items of non-personal data, [preferences for using the default port and a preferred port](https://github.com/martinohanlon/BlueDot/blob/master/clients/android/app/src/main/java/com/stuffaboutcode/bluedot/SettingsActivity.java) (if you change them from the default) in a [shared preferences file](https://developer.android.com/training/data-storage/shared-preferences).
================================================
FILE: README.rst
================================================
Blue Dot
========
|pypibadge| |docsbadge|
.. raw:: html
<iframe allowtransparency="true" style="background-color: white;" src="https://github.com/sponsors/martinohanlon/button" title="Sponsor martinohanlon" height="35" width="116" style="border: 0;"></iframe>
Blue Dot allows you to control your Raspberry Pi projects wirelessly - it's a Bluetooth remote and zero boiler plate (super simple to use :) Python library.
|bluedotfeature|
|bluedotapp| |bluedotmanybuttons| |bluedotpython|
Created by `Martin O'Hanlon`_ (`@martinohanlon`_, `stuffaboutco.de`_).
Getting Started
---------------
`Install and usage`_ is really simple:
1. Install the Python library::
sudo pip3 install bluedot
2. Get the `Android Blue Dot app`_ or use the `Python Blue Dot app`_
3. Pair your Raspberry Pi
4. Write some code::
from bluedot import BlueDot
bd = BlueDot()
bd.wait_for_press()
print("You pressed the blue dot!")
5. Press the Blue Dot
See the `getting started`_ guide to 'get started'!
More
----
Blue Dot is more than just one `button_`. You can create as many buttons as you want and change their appearance to create your own controller.
|bluedotjoypad|
Every `button`_ is also a `joystick`_. You can tell if a button was pressed in the middle, on the top, bottom, left or right. You can easily create a `BlueDot controlled Robot`_.
Why be restricted by such vague positions like top and bottom though: you can get the exact (x, y) position or even the angle and distance from centre where the button was pressed.
Its not all about when the button was pressed either - pressed, released or moved they all work.
A button can be any colour, square, given give or hidden!
You can press it, `slide it`_, `swipe it`_, `rotate it`_ - one blue circle can do a lot!
Even more
---------
The `online documentation`_ describes how to use Blue Dot and the `Python library`_ including `Recipes`_ and ideas.
Status
------
Production - under active development. Be sure to raise an `issue`_ if you have a feature request or experience problems.
.. _Martin O'Hanlon: https://github.com/martinohanlon
.. _stuffaboutco.de: http://stuffaboutco.de
.. _@martinohanlon: https://twitter.com/martinohanlon
.. _getting started: http://bluedot.readthedocs.io/en/latest/gettingstarted.html
.. _Install and usage: http://bluedot.readthedocs.io/en/latest/gettingstarted.html
.. _online documentation: http://bluedot.readthedocs.io/en/latest/
.. _Python library: http://bluedot.readthedocs.io/en/latest/dotapi.html
.. _examples: https://github.com/martinohanlon/BlueDot/tree/master/examples
.. _Recipes: http://bluedot.readthedocs.io/en/latest/recipes.html
.. _Android Blue Dot app: http://play.google.com/store/apps/details?id=com.stuffaboutcode.bluedot
.. _Python Blue Dot app: http://bluedot.readthedocs.io/en/latest/bluedotpythonapp.html
.. _issue: https://github.com/martinohanlon/bluedot/issues
.. _BlueDot controlled Robot: https://youtu.be/eW9oEPySF58
.. _joystick: http://bluedot.readthedocs.io/en/latest/recipes.html#joystick
.. _button: http://bluedot.readthedocs.io/en/latest/recipes.html#button
.. _slide it: http://bluedot.readthedocs.io/en/latest/recipes.html#slider
.. _swipe it: http://bluedot.readthedocs.io/en/latest/recipes.html#swiping
.. _rotate it: http://bluedot.readthedocs.io/en/latest/recipes.html#rotating
.. |bluedotapp| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotandroid_small.png
:height: 270 px
:width: 144 px
:scale: 100 %
:alt: blue dot app
.. |bluedotpython| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotpython.png
:height: 247 px
:width: 294 px
:scale: 100 %
:alt: blue dot python app
.. |bluedotjoypad| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/layout_joypad_smaller.png
:height: 147 px
:width: 294 px
:scale: 100 %
:alt: blue dot app as a joy pad controller
.. |bluedotmanybuttons| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/layout_many_buttons_smaller.png
:height: 270 px
:width: 144 px
:scale: 100 %
:alt: blue dot app with 10 buttons in a 2x5 grid
.. |bluedotfeature| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/blue_dot_feature_small.png
:height: 247 px
:width: 506 px
:scale: 100 %
:alt: blue dot feature
.. |pypibadge| image:: https://badge.fury.io/py/bluedot.svg
:target: https://badge.fury.io/py/bluedot
:alt: Latest Version
.. |docsbadge| image:: https://readthedocs.org/projects/bluedot/badge/
:target: https://readthedocs.org/projects/bluedot/
:alt: Docs
================================================
FILE: bluedot/__init__.py
================================================
from .dot import BlueDot, BlueDotButton
from .interactions import BlueDotInteraction, BlueDotPosition, BlueDotRotation, BlueDotSwipe
from .colors import COLORS
from .mock import MockBlueDot
================================================
FILE: bluedot/app.py
================================================
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
from argparse import ArgumentParser
import pygame
import sys
from .btcomm import BluetoothAdapter, BluetoothClient
from .constants import PROTOCOL_VERSION
from .colors import BLUE, GRAY43, GRAY86, RED, parse_color
DEFAULTSIZE = (320, 240)
BORDER = 7
FONT = "monospace"
FONTSIZE = 18
FONTPAD = 3
CLIENT_NAME = "Blue Dot Python app"
BORDER_THICKNESS = 0.025
class BlueDotClient:
def __init__(self, device, server, port, fullscreen, width, height):
self._device = device
self._server = server
self._port = 1 if port is None else port
self._fullscreen = fullscreen
#init pygame
pygame.init()
#load font
self._font = pygame.font.SysFont(FONT, FONTSIZE)
#setup the screen
#set the screen caption
pygame.display.set_caption("Blue Dot")
#create the screen
screenflags = 0
if fullscreen:
screenflags = pygame.FULLSCREEN
if width == None and height == None:
display_info = pygame.display.Info()
width = display_info.current_w
height = display_info.current_h
if width == None: width = DEFAULTSIZE[0]
if height == None: height = DEFAULTSIZE[1]
self._screen = pygame.display.set_mode((width, height), screenflags)
self._width = width
self._height = height
self._run()
pygame.quit()
def _run(self):
# has a server been specified? If so connected directly
if self._server:
button_screen = ButtonScreen(self._screen, self._font, self._device, self._server, self._port, self._width, self._height)
button_screen.run()
else:
# start the devices screen
devices_screen = DevicesScreen(self._screen, self._font, self._device, self._port, self._width, self._height)
devices_screen.run()
class BlueDotScreen:
def __init__(self, screen, font, width, height):
self.screen = screen
self.font = font
self.width = width
self.height = height
# setup screen attributes
self.frame_rect = pygame.Rect(BORDER, BORDER, self.width - (BORDER * 2) - FONTSIZE - FONTPAD, self.height - (BORDER * 2))
self.close_rect = pygame.Rect(self.width - FONTSIZE - FONTPAD - BORDER, BORDER, FONTSIZE + FONTPAD, FONTSIZE + FONTPAD)
self.draw_screen()
def draw_screen(self):
# set the screen background
self.screen.fill(GRAY86.rgb)
self.draw_close_button()
def draw_close_button(self):
# draw close button
pygame.draw.rect(self.screen, BLUE.rgb, self.close_rect, 2)
pygame.draw.line(self.screen, BLUE.rgb,
(self.close_rect[0], self.close_rect[1]),
(self.close_rect[0] + self.close_rect[2], self.close_rect[1] + self.close_rect[3]),
1)
pygame.draw.line(self.screen, BLUE.rgb,
(self.close_rect[0], self.close_rect[1] + self.close_rect[3]),
(self.close_rect[0] + self.close_rect[2], self.close_rect[1]),
1)
def draw_error(self, e):
message = "Error: {}".format(e)
print(message)
self.draw_status_message(message, colour = RED.rgb)
def draw_status_message(self, message, colour = BLUE.rgb):
self.screen.fill(GRAY86.rgb, self.frame_rect)
self.draw_close_button()
self.draw_text(message, colour, self.frame_rect.height / 2, border = True, border_pad = FONTPAD)
pygame.display.update()
def draw_text(self, text, colour, start_y, antiaalias=False, background=None, border=False, border_width=1, border_pad=0):
rect = pygame.Rect(self.frame_rect)
y = rect.top + start_y + border_pad
lineSpacing = -2
# get the height of the font
fontHeight = self.font.size("Tg")[1]
while text:
i = 1
# determine if the row of text will be outside our area
if y + fontHeight > rect.bottom:
break
# determine maximum width of line
while self.font.size(text[:i])[0] < (rect.width - (border_pad * 2)) and i < len(text):
i += 1
# if we've wrapped the text, then adjust the wrap to the last word
if i < len(text):
i = text.rfind(" ", 0, i) + 1
# render the line and blit it to the surface
if background:
image = self.font.render(text[:i], 1, colour, background)
image.set_colorkey(background)
else:
image = self.font.render(text[:i], antiaalias, colour)
self.screen.blit(image, (rect.left + border_pad, y))
y += fontHeight + lineSpacing + border_pad
# remove the text we just blitted
text = text[i:]
#return the rect the text was drawn in
rect.top = rect.top + start_y
rect.height = y - start_y
if border:
pygame.draw.rect(self.screen, colour, rect, border_width)
return rect
class DevicesScreen(BlueDotScreen):
def __init__(self, screen, font, device, port, width, height):
self.bt_adapter = BluetoothAdapter(device = device)
self.port = port
super(DevicesScreen, self).__init__(screen, font, width, height)
# self.draw_screen()
def draw_screen(self):
self.device_rects = []
super(DevicesScreen, self).draw_screen()
#title
title_rect = self.draw_text("Connect", RED.rgb, 0)
y = title_rect.bottom
for d in self.bt_adapter.paired_devices:
device_rect = self.draw_text("{} ({})".format(d[1], d[0]), BLUE.rgb, y, border = True, border_pad = FONTPAD)
self.device_rects.append(pygame.Rect(device_rect))
y = device_rect.bottom
def run(self):
clock = pygame.time.Clock()
running = True
while running:
clock.tick(50)
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
#has a device been clicked?
for d in range(len(self.device_rects)):
if self.device_rects[d].collidepoint(pos):
# show the button
self.draw_status_message("Connecting")
button_screen = ButtonScreen(self.screen, self.font, self.bt_adapter.device, self.bt_adapter.paired_devices[d][0], self.port, self.width, self.height)
button_screen.run()
#redraw the screen
self.draw_screen()
if self.close_rect.collidepoint(pos):
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.type == pygame.QUIT:
running = False
pygame.display.update()
class ButtonScreen(BlueDotScreen):
def __init__(self, screen, font, device, server, port, width, height):
self.device = device
self.server = server
self.port = port
self._data_buffer = ""
self.last_x = 0
self.last_y = 0
self._colour = BLUE
self._border = False
self._square = False
self._visible = True
self._pressed = False
super(ButtonScreen, self).__init__(screen, font, width, height)
def draw_screen(self):
super(ButtonScreen, self).draw_screen()
# work out dot position
self.dot_centre = (int(self.frame_rect.top + (self.frame_rect.width / 2)), int(self.frame_rect.left + (self.frame_rect.height / 2)))
if self.frame_rect.width > self.frame_rect.height:
self.dot_rect = pygame.Rect(self.frame_rect.left + int((self.frame_rect.width - self.frame_rect.height) / 2), self.frame_rect.top, self.frame_rect.height, self.frame_rect.height)
self.dot_radius = int(self.dot_rect.height / 2)
else:
self.dot_rect = pygame.Rect(self.frame_rect.left, self.frame_rect.top + int((self.frame_rect.height - self.frame_rect.width) / 2), self.frame_rect.width, self.frame_rect.width)
self.dot_radius = int(self.dot_rect.width / 2)
self.border_width = max(int(self.dot_rect.width * BORDER_THICKNESS), 1)
self.border_height = max(int(self.dot_rect.height * BORDER_THICKNESS), 1)
self._draw_dot()
def _draw_dot(self):
# clear the dot
pygame.draw.rect(
self.screen,
GRAY86.rgb,
(
self.dot_rect.left - self.border_width,
self.dot_rect.top - self.border_height,
self.dot_rect.width + (self.border_width * 2),
self.dot_rect.height + (self.border_height * 2),
)
)
colour = self._colour if not self._pressed else self._colour.get_adjusted_color(0.85)
# draw the dot
if self._square:
if self._visible:
pygame.draw.rect(self.screen, colour.rgb, self.dot_rect)
if self._border:
pygame.draw.rect(self.screen, GRAY43.rgb, self.dot_rect, max(int(self.dot_radius * BORDER_THICKNESS), 1))
else:
if self._visible:
pygame.draw.ellipse(self.screen, colour.rgb, self.dot_rect)
if self._border:
pygame.draw.ellipse(self.screen, GRAY43.rgb, self.dot_rect, max(int(self.dot_radius * BORDER_THICKNESS), 1))
def _process(self, op, pos):
if self.bt_client.connected:
x = (pos[0] - self.dot_centre[0]) / float(self.dot_radius)
x = round(x, 4)
y = ((pos[1] - self.dot_centre[1]) / float(self.dot_radius)) * -1
y = round(y, 4)
message = "{},0,0,{},{}\n".format(op, x, y)
if op == 2:
if x != self.last_x or y != self.last_y:
self._send_message(message)
else:
self._send_message(message)
self.last_x = x
self.last_y = y
else:
self.draw_error("Blue Dot not connected")
def _send_protocol_version(self):
if self.bt_client.connected:
self._send_message("3,{},{}\n".format(PROTOCOL_VERSION, CLIENT_NAME))
def _send_message(self, message):
try:
self.bt_client.send(message)
except:
e = str(sys.exc_info()[1])
self.draw_error(e)
def _data_received(self, data):
# add the data received to the buffer
self._data_buffer += data
# get any full commands ended by \n
last_command = self._data_buffer.rfind("\n")
if last_command != -1:
commands = self._data_buffer[:last_command].split("\n")
# remove the processed commands from the buffer
self._data_buffer = self._data_buffer[last_command + 1:]
self._process_commands(commands)
def _process_commands(self, commands):
for command in commands:
params = command.split(",")
invalid_command = False
if len(params) == 7:
if params[0] == "4":
# currently the python blue dot client only supports 1 button
if params[5] != "1" or params[6] != "1":
print("Error - The BlueDot python client only supports a single button.")
self._colour = parse_color(params[1])
self._square = True if params[2] == "1" else False
self._border = True if params[3] == "1" else False
self._visible = True if params[4] == "1" else False
self._draw_dot()
elif params[0] == "5":
if params[5] == "0" and params[6] == "0":
self._colour = parse_color(params[1])
self._square = True if params[2] == "1" else False
self._border = True if params[3] == "1" else False
self._visible = True if params[4] == "1" else False
self._draw_dot()
else:
invalid_command = True
if invalid_command:
print("Error - Invalid message received '{}'".format(command))
def run(self):
self._connect()
self._send_protocol_version()
clock = pygame.time.Clock()
pygame.event.clear()
self._pressed = False
running = True
while running:
clock.tick(50)
ev = pygame.event.get()
for event in ev:
# handle mouse
if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.MOUSEBUTTONUP or (event.type == pygame.MOUSEMOTION and self._pressed):
pos = pygame.mouse.get_pos()
#circle clicked?
if self.dot_rect.collidepoint(pos):
if event.type == pygame.MOUSEBUTTONDOWN:
self._pressed = True
self._draw_dot()
self._process(1, pos)
elif event.type == pygame.MOUSEBUTTONUP:
self._pressed = False
self._draw_dot()
self._process(0, pos)
elif event.type == pygame.MOUSEMOTION:
self._process(2, pos)
#close clicked?
if self.close_rect.collidepoint(pos):
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if event.type == pygame.QUIT:
running = False
pygame.display.update()
self.bt_client.disconnect()
def _connect(self):
self.bt_client = BluetoothClient(self.server, self._data_received, port = self.port, device = self.device, auto_connect = False)
try:
self.bt_client.connect()
except:
e = str(sys.exc_info()[1])
self.draw_error(e)
def main():
#read command line options
parser = ArgumentParser(description="Blue Dot Python App")
parser.add_argument("--device", help="The name of the bluetooth device to use (default is hci0)")
parser.add_argument("--server", help="The name or mac address of the bluedot server")
parser.add_argument("--port", help="The port number to use when connecting (default is 1)", type=int)
parser.add_argument("--fullscreen", help="Fullscreen app", action="store_true")
parser.add_argument("--width", type=int, help="A custom screen width (default is {})".format(DEFAULTSIZE[0]))
parser.add_argument("--height", type=int, help="A customer screen height (default is {})".format(DEFAULTSIZE[1]))
args = parser.parse_args()
#start the blue dot client
blue_dot_client = BlueDotClient(args.device, args.server, args.port, args.fullscreen, args.width, args.height)
if __name__ == "__main__":
main()
================================================
FILE: bluedot/btcomm.py
================================================
from __future__ import unicode_literals
import socket
import sys
import errno
from .utils import (
register_spp,
get_mac,
get_adapter_powered_status,
get_adapter_discoverable_status,
get_adapter_pairable_status,
get_paired_devices,
device_pairable,
device_discoverable,
device_powered,
)
from .threads import WrapThread
BLUETOOTH_TIMEOUT = 0.01
class BluetoothAdapter:
"""
Represents and allows interaction with a Bluetooth Adapter.
The following example will get the Bluetooth adapter, print its powered status
and any paired devices::
a = BluetoothAdapter()
print("Powered = {}".format(a.powered))
print(a.paired_devices)
:param str device:
The Bluetooth device to be used, the default is "hci0", if your device
only has 1 Bluetooth adapter this shouldn't need to be changed.
"""
def __init__(self, device = "hci0"):
self._device = device
self._address = get_mac(self._device)
self._pairing_thread = None
@property
def device(self):
"""
The Bluetooth device name. This defaults to "hci0".
"""
return self._device
@property
def address(self):
"""
The `MAC address`_ of the Bluetooth adapter.
.. _MAC address: https://en.wikipedia.org/wiki/MAC_address
"""
return self._address
@property
def powered(self):
"""
Set to ``True`` to power on the Bluetooth adapter.
Depending on how Bluetooth has been powered down, you may need to use
:command:`rfkill` to unblock Bluetooth to give permission to bluez to power on Bluetooth::
sudo rfkill unblock bluetooth
"""
return get_adapter_powered_status(self._device)
@powered.setter
def powered(self, value):
device_powered(self._device, value)
@property
def discoverable(self):
"""
Set to ``True`` to make the Bluetooth adapter discoverable.
"""
return get_adapter_discoverable_status(self._device)
@discoverable.setter
def discoverable(self, value):
device_discoverable(self._device, value)
@property
def pairable(self):
"""
Set to ``True`` to make the Bluetooth adapter pairable.
"""
return get_adapter_pairable_status(self._device)
@pairable.setter
def pairable(self, value):
device_pairable(self._device, value)
@property
def paired_devices(self):
"""
Returns a sequence of devices paired with this adapater
:code:`[(mac_address, name), (mac_address, name), ...]`::
a = BluetoothAdapter()
devices = a.paired_devices
for d in devices:
device_address = d[0]
device_name = d[1]
"""
return get_paired_devices(self._device)
def allow_pairing(self, timeout = 60):
"""
Put the adapter into discoverable and pairable mode.
:param int timeout:
The time in seconds the adapter will remain pairable. If set to ``None``
the device will be discoverable and pairable indefinetly.
"""
#if a pairing thread is already running, stop it and restart
if self._pairing_thread:
if self._pairing_thread.is_alive:
self._pairing_thread.stop()
#make the adapter pairable
self.pairable = True
self.discoverable = True
if timeout != None:
#start the pairing thread
self._pairing_thread = WrapThread(target=self._expire_pairing, args=(timeout, ))
self._pairing_thread.start()
def _expire_pairing(self, timeout):
#wait till the timeout or the thread is stopped
self._pairing_thread.stopping.wait(timeout)
self.discoverable = False
self.pairable = False
class BluetoothServer:
"""
Creates a Bluetooth server which will allow connections and accept incoming
RFCOMM serial data.
When data is received by the server it is passed to a callback function
which must be specified at initiation.
The following example will create a Bluetooth server which will wait for a
connection and print any data it receives and send it back to the client::
from bluedot.btcomm import BluetoothServer
from signal import pause
def data_received(data):
print(data)
s.send(data)
s = BluetoothServer(data_received)
pause()
:param data_received_callback:
A function reference should be passed, this function will be called when
data is received by the server. The function should accept a single parameter
which when called will hold the data received. Set to ``None`` if received
data is not required.
:param bool auto_start:
If ``True`` (the default), the Bluetooth server will be automatically started
on initialisation, if ``False``, the method ``start`` will need to be called
before connections will be accepted.
:param str device:
The Bluetooth device the server should use, the default is "hci0", if
your device only has 1 Bluetooth adapter this shouldn't need to be changed.
:param int port:
The Bluetooth port the server should use, the default is 1.
:param str encoding:
The encoding standard to be used when sending and receiving byte data. The default is
"utf-8". If set to ``None`` no encoding is done and byte data types should be used.
:param bool power_up_device:
If ``True``, the Bluetooth device will be powered up (if required) when the
server starts. The default is ``False``.
Depending on how Bluetooth has been powered down, you may need to use :command:`rfkill`
to unblock Bluetooth to give permission to bluez to power on Bluetooth::
sudo rfkill unblock bluetooth
:param when_client_connects:
A function reference which will be called when a client connects. If ``None``
(the default), no notification will be given when a client connects
:param when_client_disconnects:
A function reference which will be called when a client disconnects. If ``None``
(the default), no notification will be given when a client disconnects
"""
def __init__(self,
data_received_callback,
auto_start = True,
device = "hci0",
port = 1,
encoding = "utf-8",
power_up_device = False,
when_client_connects = None,
when_client_disconnects = None):
self._setup_adapter(device)
self._data_received_callback = data_received_callback
self._port = port
self._encoding = encoding
self._power_up_device = power_up_device
self._when_client_connects = when_client_connects
self._when_client_disconnects = when_client_disconnects
self._running = False
self._client_connected = False
self._server_sock = None
self._client_info = None
self._client_sock = None
self._conn_thread = None
if auto_start:
self.start()
@property
def device(self):
"""
The Bluetooth device the server is using. This defaults to "hci0".
"""
return self.adapter.device
@property
def adapter(self):
"""
A :class:`BluetoothAdapter` object which represents the Bluetooth device
the server is using.
"""
return self._adapter
@property
def port(self):
"""
The port the server is using. This defaults to 1.
"""
return self._port
@property
def encoding(self):
"""
The encoding standard the server is using. This defaults to "utf-8".
"""
return self._encoding
@property
def running(self):
"""
Returns a ``True`` if the server is running.
"""
return self._running
@property
def server_address(self):
"""
The `MAC address`_ of the device the server is using.
.. _MAC address: https://en.wikipedia.org/wiki/MAC_address
"""
return self.adapter.address
@property
def client_address(self):
"""
The `MAC address`_ of the client connected to the server. Returns
``None`` if no client is connected.
.. _MAC address: https://en.wikipedia.org/wiki/MAC_address
"""
if self._client_info:
return self._client_info[0]
else:
return None
@property
def client_connected(self):
"""
Returns ``True`` if a client is connected.
"""
return self._client_connected
@property
def data_received_callback(self):
"""
Sets or returns the function which is called when data is received by the server.
The function should accept a single parameter which when called will hold
the data received. Set to ``None`` if received data is not required.
"""
return self._data_received_callback
@data_received_callback.setter
def data_received_callback(self, value):
self._data_received_callback = value
@property
def when_client_connects(self):
"""
Sets or returns the function which is called when a client connects.
"""
return self._when_client_connects
@when_client_connects.setter
def when_client_connects(self, value):
self._when_client_connects = value
@property
def when_client_disconnects(self):
"""
Sets or returns the function which is called when a client disconnects.
"""
return self._when_client_disconnects
@when_client_disconnects.setter
def when_client_disconnects(self, value):
self._when_client_disconnects = value
def start(self):
"""
Starts the Bluetooth server if its not already running. The server needs to be started before
connections can be made.
"""
if not self._running:
if self._power_up_device:
self.adapter.powered = True
if not self.adapter.powered:
raise Exception("Bluetooth device {} is turned off".format(self.adapter.device))
#register the serial port profile with Bluetooth
register_spp(self._port)
#start Bluetooth server
#open the Bluetooth socket
self._server_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
self._server_sock.settimeout(BLUETOOTH_TIMEOUT)
try:
self._server_sock.bind((self.server_address, self.port))
except (socket.error, OSError) as e:
if e.errno == errno.EADDRINUSE:
print("Bluetooth address {} is already in use - is the server already running?".format(self.server_address))
raise e
self._server_sock.listen(1)
#wait for client connection
self._conn_thread = WrapThread(target=self._wait_for_connection)
self._conn_thread.start()
self._running = True
def stop(self):
"""
Stops the Bluetooth server if its running.
"""
if self._running:
if self._conn_thread:
self._conn_thread.stop()
self._conn_thread = None
def send(self, data):
"""
Send data to a connected Bluetooth client
:param str data:
The data to be sent.
"""
# print(data)
if self._client_connected:
if self._encoding is not None:
data = data.encode(self._encoding)
try:
self._send_data(data)
except IOError as e:
self._handle_bt_error(e)
def _send_data(self, data):
"""
Send raw data to the client.
:param bytes data:
The data to be sent.
"""
self._client_sock.sendall(data)
def disconnect_client(self):
"""
Disconnects the client if connected. Returns `True` if a client was disconnected.
"""
if self._client_connected:
self._client_connected = False
# call the callback
if self.when_client_disconnects:
WrapThread(target=self.when_client_disconnects).start()
return True
else:
return False
def _setup_adapter(self, device):
self._adapter = BluetoothAdapter(device)
def _wait_for_connection(self):
#keep going until the server is stopped
while not self._conn_thread.stopping.is_set():
#wait for connection
self._client_connected = False
while not self._conn_thread.stopping.is_set():
try:
# accept() will timeout after BLUETOOTH_TIMEOUT seconds
self._client_sock, self._client_info = self._server_sock.accept()
self._client_connected = True
break
except socket.timeout as e:
self._handle_bt_error(e)
#did a client connect?
if self._client_connected:
#call the call back
if self.when_client_connects:
WrapThread(target=self.when_client_connects).start()
#read data
self._read()
#server has been stopped
self._server_sock.close()
self._server_sock = None
self._running = False
def _read(self):
#read until the server is stopped or the client disconnects
while self._client_connected:
#read data from Bluetooth socket
try:
data = self._client_sock.recv(1024, socket.MSG_DONTWAIT)
except IOError as e:
self._handle_bt_error(e)
data = b""
if data:
if self._data_received_callback:
if self._encoding:
data = data.decode(self._encoding)
self.data_received_callback(data)
if self._conn_thread.stopping.wait(BLUETOOTH_TIMEOUT):
break
#close the client socket
self._client_sock.close()
self._client_sock = None
self._client_info = None
self._client_connected = False
def _handle_bt_error(self, bt_error):
assert isinstance(bt_error, IOError)
#'timed out' is caused by the wait_for_connection loop
if isinstance(bt_error, socket.timeout):
pass
#'resource unavailable' is when data cannot be read because there is nothing in the buffer
elif bt_error.errno == errno.EAGAIN:
pass
#'connection reset' is caused when the client disconnects
elif bt_error.errno == errno.ECONNRESET:
self.disconnect_client()
#'conection timeout' is caused when the server can no longer connect to read from the client
# (perhaps the client has gone out of range)
elif bt_error.errno == errno.ETIMEDOUT:
self.disconnect_client()
else:
raise bt_error
class BluetoothClient():
"""
Creates a Bluetooth client which can send data to a server using RFCOMM Serial Data.
The following example will create a Bluetooth client which will connect to a paired
device called "raspberrypi", send "helloworld" and print any data is receives::
from bluedot.btcomm import BluetoothClient
from signal import pause
def data_received(data):
print(data)
c = BluetoothClient("raspberrypi", data_received)
c.send("helloworld")
pause()
:param str server:
The server name ("raspberrypi") or server MAC address
("11:11:11:11:11:11") to connect to. The server must be a paired device.
:param data_received_callback:
A function reference should be passed, this function will be called when
data is received by the client. The function should accept a single parameter
which when called will hold the data received. Set to ``None`` if data
received is not required.
:param int port:
The Bluetooth port the client should use, the default is 1.
:param str device:
The Bluetooth device to be used, the default is "hci0", if your device
only has 1 Bluetooth adapter this shouldn't need to be changed.
:param str encoding:
The encoding standard to be used when sending and receiving byte data. The default is
"utf-8". If set to ``None`` no encoding is done and byte data types should be used.
:param bool power_up_device:
If ``True``, the Bluetooth device will be powered up (if required) when the
server starts. The default is ``False``.
Depending on how Bluetooth has been powered down, you may need to use :command:`rfkill`
to unblock Bluetooth to give permission to Bluez to power on Bluetooth::
sudo rfkill unblock bluetooth
:param bool auto_connect:
If ``True`` (the default), the Bluetooth client will automatically try
to connect to the server at initialisation, if ``False``, the
:meth:`connect` method will need to be called.
"""
def __init__(self,
server,
data_received_callback,
port = 1,
device = "hci0",
encoding = "utf-8",
power_up_device = False,
auto_connect = True):
self._server = server
self._data_received_callback = data_received_callback
self._port = port
self._power_up_device = power_up_device
self._encoding = encoding
self._setup_adapter(device)
self._connected = False
self._client_sock = None
self._conn_thread = None
if auto_connect:
self.connect()
@property
def device(self):
"""
The Bluetooth device the client is using. This defaults to "hci0".
"""
return self.adapter.device
@property
def server(self):
"""
The server name ("raspberrypi") or server `MAC address`_
("11:11:11:11:11:11") to connect to.
.. _MAC address: https://en.wikipedia.org/wiki/MAC_address
"""
return self._server
@property
def port(self):
"""
The port the client is using. This defaults to 1.
"""
return self._port
@property
def adapter(self):
"""
A :class:`BluetoothAdapter` object which represents the Bluetooth
device the client is using.
"""
return self._adapter
@property
def encoding(self):
"""
The encoding standard the client is using. The default is "utf-8".
"""
return self._encoding
@property
def client_address(self):
"""
The MAC address of the device being used.
"""
return self.adapter.address
@property
def connected(self):
"""
Returns ``True`` when connected.
"""
return self._connected
@property
def data_received_callback(self):
"""
Sets or returns the function which is called when data is received by the client.
The function should accept a single parameter which when called will hold
the data received. Set to ``None`` if data received is not required.
"""
return self._data_received_callback
@data_received_callback.setter
def data_received_callback(self, value):
self._data_received_callback = value
def connect(self):
"""
Connect to a Bluetooth server.
"""
if not self._connected:
if self._power_up_device:
self.adapter.powered = True
if not self.adapter.powered:
raise Exception("Bluetooth device {} is turned off".format(self.adapter.device))
#try and find the server name or MAC address in the paired devices list
server_mac = None
for device in self.adapter.paired_devices:
if self._server == device[0] or self._server == device[1]:
server_mac = device[0]
break
if server_mac == None:
raise Exception("Server {} not found in paired devices".format(self._server))
#create a socket
self._client_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
self._client_sock.bind((self.adapter.address, self._port))
self._client_sock.connect((server_mac, self._port))
self._connected = True
self._conn_thread = WrapThread(target=self._read)
self._conn_thread.start()
def disconnect(self):
"""
Disconnect from a Bluetooth server.
"""
if self._connected:
#stop the connection thread
if self._conn_thread:
self._conn_thread.stop()
self._conn_thread = None
#close the socket
try:
self._client_sock.close()
finally:
self._client_sock = None
self._connected = False
def send(self, data):
"""
Send data to a Bluetooth server.
:param str data:
The data to be sent.
"""
if self._connected:
if self._encoding is not None:
data = data.encode(self._encoding)
try:
self._send_data(data)
except IOError as e:
self._handle_bt_error(e)
def _send_data(self, data):
"""
Send raw data to the client.
:param bytes data:
The data to be sent.
"""
self._client_sock.sendall(data)
def _read(self):
#read until the client is stopped or the client disconnects
while self._connected:
#read data from Bluetooth socket
try:
data = self._client_sock.recv(1024, socket.MSG_DONTWAIT)
except IOError as e:
self._handle_bt_error(e)
data = b""
if data:
#print("received [%s]" % data)
if self._data_received_callback:
if self._encoding:
data = data.decode(self._encoding)
self.data_received_callback(data)
if self._conn_thread.stopping.wait(BLUETOOTH_TIMEOUT):
break
def _setup_adapter(self, device):
self._adapter = BluetoothAdapter(device)
def _handle_bt_error(self, bt_error):
assert isinstance(bt_error, IOError)
#'resource unavailable' is when data cannot be read because there is nothing in the buffer
if bt_error.errno == errno.EAGAIN:
pass
#'connection reset' is caused when the client disconnects
elif bt_error.errno == errno.ECONNRESET:
self._connected = False
#'conection timeout' is caused when the server can no longer connect to read from the client
# (perhaps the client has gone out of range)
elif bt_error.errno == errno.ETIMEDOUT:
self._connected = False
else:
raise bt_error
================================================
FILE: bluedot/colors.py
================================================
# color codes obtained from https://www.webucator.com/blog/2015/03/python-color-constants-module/
class Color:
"""
Represents a color within bluedot. Used to change the color of the dot.
Color objects are immutable.
:param int red:
The red value of the color `0 - 255`. Default is `255`.
:param int green:
The green value of the color `0 - 255`. Default is `255`.
:param int green:
The blue value of the color `0 - 255`. Default is `255`.
:param int green:
The alpha value of the color `0 - 255`. `0` is transparent. Default
is `255`.
"""
def __init__(self, red = 255, green = 255, blue = 255, alpha = 255):
self._red = red
self._green = green
self._blue = blue
self._alpha = alpha
@property
def red(self):
"""
Returns the red value of the color.
"""
return self._red
@property
def green(self):
"""
Returns the green value of the color.
"""
return self._green
@property
def blue(self):
"""
Returns the blue value of the color.
"""
return self._blue
@property
def alpha(self):
"""
Returns the alpha value of the color.
"""
return self._alpha
@property
def rgb(self):
"""
Returns a tuple of `(red, green, blue)` values.
"""
return (self._red, self._green, self._blue)
@property
def rgba(self):
"""
Returns a tuple of `(red, green, blue, alpha)` values.
"""
return (self._red, self._green, self._blue, self._alpha)
@property
def str_rgb(self):
"""
Returns a string of red, green, blue hex values in the format
`#rrggbb`.
"""
return '#%02x%02x%02x' % (self._red, self._green, self._blue)
@property
def str_rgba(self):
"""
Returns a string of red, green, blue, alpha hex values in the format
`#rrggbbaa`.
"""
return '#%02x%02x%02x%02x' % (self._red, self._green, self._blue, self._alpha)
@property
def str_argb(self):
"""
Returns a string of alpha, red, green, blue hex values in the format
`#aarrggbb`.
"""
return '#%02x%02x%02x%02x' % (self._alpha, self._red, self._green, self._blue)
def get_adjusted_color(self, factor):
"""
Returns a new Color object based on this Color adjusted by a factor
:param float factor:
The value to adjust this color by.
"""
return Color(self.red * factor, self.green * factor, self.blue * factor)
def __eq__(self, other):
other = parse_color(other)
return self._red == other._red and self._green == other._green and self._blue == other._blue and self._alpha == other._alpha
def __str__(self):
return self.str_rgba
ALICEBLUE = Color(240, 248, 255)
ANTIQUEWHITE = Color(250, 235, 215)
ANTIQUEWHITE1 = Color(255, 239, 219)
ANTIQUEWHITE2 = Color(238, 223, 204)
ANTIQUEWHITE3 = Color(205, 192, 176)
ANTIQUEWHITE4 = Color(139, 131, 120)
AQUA = Color(0, 255, 255)
AQUAMARINE1 = Color(127, 255, 212)
AQUAMARINE2 = Color(118, 238, 198)
AQUAMARINE3 = Color(102, 205, 170)
AQUAMARINE4 = Color(69, 139, 116)
AZURE1 = Color(240, 255, 255)
AZURE2 = Color(224, 238, 238)
AZURE3 = Color(193, 205, 205)
AZURE4 = Color(131, 139, 139)
BANANA = Color(227, 207, 87)
BEIGE = Color(245, 245, 220)
BISQUE1 = Color(255, 228, 196)
BISQUE2 = Color(238, 213, 183)
BISQUE3 = Color(205, 183, 158)
BISQUE4 = Color(139, 125, 107)
BLACK = Color(0, 0, 0)
BLANCHEDALMOND = Color(255, 235, 205)
BLUE = Color(0, 0, 255)
BLUE2 = Color(0, 0, 238)
BLUE3 = Color(0, 0, 205)
BLUE4 = Color(0, 0, 139)
BLUEVIOLET = Color(138, 43, 226)
BRICK = Color(156, 102, 31)
BROWN = Color(165, 42, 42)
BROWN1 = Color(255, 64, 64)
BROWN2 = Color(238, 59, 59)
BROWN3 = Color(205, 51, 51)
BROWN4 = Color(139, 35, 35)
BURLYWOOD = Color(222, 184, 135)
BURLYWOOD1 = Color(255, 211, 155)
BURLYWOOD2 = Color(238, 197, 145)
BURLYWOOD3 = Color(205, 170, 125)
BURLYWOOD4 = Color(139, 115, 85)
BURNTSIENNA = Color(138, 54, 15)
BURNTUMBER = Color(138, 51, 36)
CADETBLUE = Color(95, 158, 160)
CADETBLUE1 = Color(152, 245, 255)
CADETBLUE2 = Color(142, 229, 238)
CADETBLUE3 = Color(122, 197, 205)
CADETBLUE4 = Color(83, 134, 139)
CADMIUMORANGE = Color(255, 97, 3)
CADMIUMYELLOW = Color(255, 153, 18)
CARROT = Color(237, 145, 33)
CHARTREUSE1 = Color(127, 255, 0)
CHARTREUSE2 = Color(118, 238, 0)
CHARTREUSE3 = Color(102, 205, 0)
CHARTREUSE4 = Color(69, 139, 0)
CHOCOLATE = Color(210, 105, 30)
CHOCOLATE1 = Color(255, 127, 36)
CHOCOLATE2 = Color(238, 118, 33)
CHOCOLATE3 = Color(205, 102, 29)
CHOCOLATE4 = Color(139, 69, 19)
COBALT = Color(61, 89, 171)
COBALTGREEN = Color(61, 145, 64)
COLDGREY = Color(128, 138, 135)
CORAL = Color(255, 127, 80)
CORAL1 = Color(255, 114, 86)
CORAL2 = Color(238, 106, 80)
CORAL3 = Color(205, 91, 69)
CORAL4 = Color(139, 62, 47)
CORNFLOWERBLUE = Color(100, 149, 237)
CORNSILK1 = Color(255, 248, 220)
CORNSILK2 = Color(238, 232, 205)
CORNSILK3 = Color(205, 200, 177)
CORNSILK4 = Color(139, 136, 120)
CRIMSON = Color(220, 20, 60)
CYAN1 = Color(0, 238, 238)
CYAN2 = Color(0, 205, 205)
CYAN3 = Color(0, 139, 139)
DARKGOLDENROD = Color(184, 134, 11)
DARKGOLDENROD1 = Color(255, 185, 15)
DARKGOLDENROD2 = Color(238, 173, 14)
DARKGOLDENROD3 = Color(205, 149, 12)
DARKGOLDENROD4 = Color(139, 101, 8)
DARKGRAY = Color(169, 169, 169)
DARKGREEN = Color(0, 100, 0)
DARKKHAKI = Color(189, 183, 107)
DARKOLIVEGREEN = Color(85, 107, 47)
DARKOLIVEGREEN1 = Color(202, 255, 112)
DARKOLIVEGREEN2 = Color(188, 238, 104)
DARKOLIVEGREEN3 = Color(162, 205, 90)
DARKOLIVEGREEN4 = Color(110, 139, 61)
DARKORANGE = Color(255, 140, 0)
DARKORANGE1 = Color(255, 127, 0)
DARKORANGE2 = Color(238, 118, 0)
DARKORANGE3 = Color(205, 102, 0)
DARKORANGE4 = Color(139, 69, 0)
DARKORCHID = Color(153, 50, 204)
DARKORCHID1 = Color(191, 62, 255)
DARKORCHID2 = Color(178, 58, 238)
DARKORCHID3 = Color(154, 50, 205)
DARKORCHID4 = Color(104, 34, 139)
DARKSALMON = Color(233, 150, 122)
DARKSEAGREEN = Color(143, 188, 143)
DARKSEAGREEN1 = Color(193, 255, 193)
DARKSEAGREEN2 = Color(180, 238, 180)
DARKSEAGREEN3 = Color(155, 205, 155)
DARKSEAGREEN4 = Color(105, 139, 105)
DARKSLATEBLUE = Color(72, 61, 139)
DARKSLATEGRAY = Color(47, 79, 79)
DARKSLATEGRAY1 = Color(151, 255, 255)
DARKSLATEGRAY2 = Color(141, 238, 238)
DARKSLATEGRAY3 = Color(121, 205, 205)
DARKSLATEGRAY4 = Color(82, 139, 139)
DARKTURQUOISE = Color(0, 206, 209)
DARKVIOLET = Color(148, 0, 211)
DEEPPINK1 = Color(255, 20, 147)
DEEPPINK2 = Color(238, 18, 137)
DEEPPINK3 = Color(205, 16, 118)
DEEPPINK4 = Color(139, 10, 80)
DEEPSKYBLUE1 = Color(0, 191, 255)
DEEPSKYBLUE2 = Color(0, 178, 238)
DEEPSKYBLUE3 = Color(0, 154, 205)
DEEPSKYBLUE4 = Color(0, 104, 139)
DIMGRAY = Color(105, 105, 105)
DODGERBLUE1 = Color(30, 144, 255)
DODGERBLUE2 = Color(28, 134, 238)
DODGERBLUE3 = Color(24, 116, 205)
DODGERBLUE4 = Color(16, 78, 139)
EGGSHELL = Color(252, 230, 201)
EMERALDGREEN = Color(0, 201, 87)
FIREBRICK = Color(178, 34, 34)
FIREBRICK1 = Color(255, 48, 48)
FIREBRICK2 = Color(238, 44, 44)
FIREBRICK3 = Color(205, 38, 38)
FIREBRICK4 = Color(139, 26, 26)
FLESH = Color(255, 125, 64)
FLORALWHITE = Color(255, 250, 240)
FORESTGREEN = Color(34, 139, 34)
GAINSBORO = Color(220, 220, 220)
GHOSTWHITE = Color(248, 248, 255)
GOLD1 = Color(255, 215, 0)
GOLD2 = Color(238, 201, 0)
GOLD3 = Color(205, 173, 0)
GOLD4 = Color(139, 117, 0)
GOLDENROD = Color(218, 165, 32)
GOLDENROD1 = Color(255, 193, 37)
GOLDENROD2 = Color(238, 180, 34)
GOLDENROD3 = Color(205, 155, 29)
GOLDENROD4 = Color(139, 105, 20)
GRAY = Color(128, 128, 128)
GRAY1 = Color(3, 3, 3)
GRAY10 = Color(26, 26, 26)
GRAY11 = Color(28, 28, 28)
GRAY12 = Color(31, 31, 31)
GRAY13 = Color(33, 33, 33)
GRAY14 = Color(36, 36, 36)
GRAY15 = Color(38, 38, 38)
GRAY16 = Color(41, 41, 41)
GRAY17 = Color(43, 43, 43)
GRAY18 = Color(46, 46, 46)
GRAY19 = Color(48, 48, 48)
GRAY2 = Color(5, 5, 5)
GRAY20 = Color(51, 51, 51)
GRAY21 = Color(54, 54, 54)
GRAY22 = Color(56, 56, 56)
GRAY23 = Color(59, 59, 59)
GRAY24 = Color(61, 61, 61)
GRAY25 = Color(64, 64, 64)
GRAY26 = Color(66, 66, 66)
GRAY27 = Color(69, 69, 69)
GRAY28 = Color(71, 71, 71)
GRAY29 = Color(74, 74, 74)
GRAY3 = Color(8, 8, 8)
GRAY30 = Color(77, 77, 77)
GRAY31 = Color(79, 79, 79)
GRAY32 = Color(82, 82, 82)
GRAY33 = Color(84, 84, 84)
GRAY34 = Color(87, 87, 87)
GRAY35 = Color(89, 89, 89)
GRAY36 = Color(92, 92, 92)
GRAY37 = Color(94, 94, 94)
GRAY38 = Color(97, 97, 97)
GRAY39 = Color(99, 99, 99)
GRAY4 = Color(10, 10, 10)
GRAY40 = Color(102, 102, 102)
GRAY42 = Color(107, 107, 107)
GRAY43 = Color(110, 110, 110)
GRAY44 = Color(112, 112, 112)
GRAY45 = Color(115, 115, 115)
GRAY46 = Color(117, 117, 117)
GRAY47 = Color(120, 120, 120)
GRAY48 = Color(122, 122, 122)
GRAY49 = Color(125, 125, 125)
GRAY5 = Color(13, 13, 13)
GRAY50 = Color(127, 127, 127)
GRAY51 = Color(130, 130, 130)
GRAY52 = Color(133, 133, 133)
GRAY53 = Color(135, 135, 135)
GRAY54 = Color(138, 138, 138)
GRAY55 = Color(140, 140, 140)
GRAY56 = Color(143, 143, 143)
GRAY57 = Color(145, 145, 145)
GRAY58 = Color(148, 148, 148)
GRAY59 = Color(150, 150, 150)
GRAY6 = Color(15, 15, 15)
GRAY60 = Color(153, 153, 153)
GRAY61 = Color(156, 156, 156)
GRAY62 = Color(158, 158, 158)
GRAY63 = Color(161, 161, 161)
GRAY64 = Color(163, 163, 163)
GRAY65 = Color(166, 166, 166)
GRAY66 = Color(168, 168, 168)
GRAY67 = Color(171, 171, 171)
GRAY68 = Color(173, 173, 173)
GRAY69 = Color(176, 176, 176)
GRAY7 = Color(18, 18, 18)
GRAY70 = Color(179, 179, 179)
GRAY71 = Color(181, 181, 181)
GRAY72 = Color(184, 184, 184)
GRAY73 = Color(186, 186, 186)
GRAY74 = Color(189, 189, 189)
GRAY75 = Color(191, 191, 191)
GRAY76 = Color(194, 194, 194)
GRAY77 = Color(196, 196, 196)
GRAY78 = Color(199, 199, 199)
GRAY79 = Color(201, 201, 201)
GRAY8 = Color(20, 20, 20)
GRAY80 = Color(204, 204, 204)
GRAY81 = Color(207, 207, 207)
GRAY82 = Color(209, 209, 209)
GRAY83 = Color(212, 212, 212)
GRAY84 = Color(214, 214, 214)
GRAY85 = Color(217, 217, 217)
GRAY86 = Color(219, 219, 219)
GRAY87 = Color(222, 222, 222)
GRAY88 = Color(224, 224, 224)
GRAY89 = Color(227, 227, 227)
GRAY9 = Color(23, 23, 23)
GRAY90 = Color(229, 229, 229)
GRAY91 = Color(232, 232, 232)
GRAY92 = Color(235, 235, 235)
GRAY93 = Color(237, 237, 237)
GRAY94 = Color(240, 240, 240)
GRAY95 = Color(242, 242, 242)
GRAY97 = Color(247, 247, 247)
GRAY98 = Color(250, 250, 250)
GRAY99 = Color(252, 252, 252)
GREEN = Color(0, 128, 0)
GREEN1 = Color(0, 255, 0)
GREEN2 = Color(0, 238, 0)
GREEN3 = Color(0, 205, 0)
GREEN4 = Color(0, 139, 0)
GREENYELLOW = Color(173, 255, 47)
HONEYDEW1 = Color(240, 255, 240)
HONEYDEW2 = Color(224, 238, 224)
HONEYDEW3 = Color(193, 205, 193)
HONEYDEW4 = Color(131, 139, 131)
HOTPINK = Color(255, 105, 180)
HOTPINK1 = Color(255, 110, 180)
HOTPINK2 = Color(238, 106, 167)
HOTPINK3 = Color(205, 96, 144)
HOTPINK4 = Color(139, 58, 98)
INDIANRED = Color(176, 23, 31)
INDIANRED = Color(205, 92, 92)
INDIANRED1 = Color(255, 106, 106)
INDIANRED2 = Color(238, 99, 99)
INDIANRED3 = Color(205, 85, 85)
INDIANRED4 = Color(139, 58, 58)
INDIGO = Color(75, 0, 130)
IVORY1 = Color(255, 255, 240)
IVORY2 = Color(238, 238, 224)
IVORY3 = Color(205, 205, 193)
IVORY4 = Color(139, 139, 131)
IVORYBLACK = Color(41, 36, 33)
KHAKI = Color(240, 230, 140)
KHAKI1 = Color(255, 246, 143)
KHAKI2 = Color(238, 230, 133)
KHAKI3 = Color(205, 198, 115)
KHAKI4 = Color(139, 134, 78)
LAVENDER = Color(230, 230, 250)
LAVENDERBLUSH1 = Color(255, 240, 245)
LAVENDERBLUSH2 = Color(238, 224, 229)
LAVENDERBLUSH3 = Color(205, 193, 197)
LAVENDERBLUSH4 = Color(139, 131, 134)
LAWNGREEN = Color(124, 252, 0)
LEMONCHIFFON1 = Color(255, 250, 205)
LEMONCHIFFON2 = Color(238, 233, 191)
LEMONCHIFFON3 = Color(205, 201, 165)
LEMONCHIFFON4 = Color(139, 137, 112)
LIGHTBLUE = Color(173, 216, 230)
LIGHTBLUE1 = Color(191, 239, 255)
LIGHTBLUE2 = Color(178, 223, 238)
LIGHTBLUE3 = Color(154, 192, 205)
LIGHTBLUE4 = Color(104, 131, 139)
LIGHTCORAL = Color(240, 128, 128)
LIGHTCYAN1 = Color(224, 255, 255)
LIGHTCYAN2 = Color(209, 238, 238)
LIGHTCYAN3 = Color(180, 205, 205)
LIGHTCYAN4 = Color(122, 139, 139)
LIGHTGOLDENROD1 = Color(255, 236, 139)
LIGHTGOLDENROD2 = Color(238, 220, 130)
LIGHTGOLDENROD3 = Color(205, 190, 112)
LIGHTGOLDENROD4 = Color(139, 129, 76)
LIGHTGOLDENRODYELLOW = Color(250, 250, 210)
LIGHTGREY = Color(211, 211, 211)
LIGHTPINK = Color(255, 182, 193)
LIGHTPINK1 = Color(255, 174, 185)
LIGHTPINK2 = Color(238, 162, 173)
LIGHTPINK3 = Color(205, 140, 149)
LIGHTPINK4 = Color(139, 95, 101)
LIGHTSALMON1 = Color(255, 160, 122)
LIGHTSALMON2 = Color(238, 149, 114)
LIGHTSALMON3 = Color(205, 129, 98)
LIGHTSALMON4 = Color(139, 87, 66)
LIGHTSEAGREEN = Color(32, 178, 170)
LIGHTSKYBLUE = Color(135, 206, 250)
LIGHTSKYBLUE1 = Color(176, 226, 255)
LIGHTSKYBLUE2 = Color(164, 211, 238)
LIGHTSKYBLUE3 = Color(141, 182, 205)
LIGHTSKYBLUE4 = Color(96, 123, 139)
LIGHTSLATEBLUE = Color(132, 112, 255)
LIGHTSLATEGRAY = Color(119, 136, 153)
LIGHTSTEELBLUE = Color(176, 196, 222)
LIGHTSTEELBLUE1 = Color(202, 225, 255)
LIGHTSTEELBLUE2 = Color(188, 210, 238)
LIGHTSTEELBLUE3 = Color(162, 181, 205)
LIGHTSTEELBLUE4 = Color(110, 123, 139)
LIGHTYELLOW1 = Color(255, 255, 224)
LIGHTYELLOW2 = Color(238, 238, 209)
LIGHTYELLOW3 = Color(205, 205, 180)
LIGHTYELLOW4 = Color(139, 139, 122)
LIMEGREEN = Color(50, 205, 50)
LINEN = Color(250, 240, 230)
MAGENTA = Color(255, 0, 255)
MAGENTA2 = Color(238, 0, 238)
MAGENTA3 = Color(205, 0, 205)
MAGENTA4 = Color(139, 0, 139)
MANGANESEBLUE = Color(3, 168, 158)
MAROON = Color(128, 0, 0)
MAROON1 = Color(255, 52, 179)
MAROON2 = Color(238, 48, 167)
MAROON3 = Color(205, 41, 144)
MAROON4 = Color(139, 28, 98)
MEDIUMORCHID = Color(186, 85, 211)
MEDIUMORCHID1 = Color(224, 102, 255)
MEDIUMORCHID2 = Color(209, 95, 238)
MEDIUMORCHID3 = Color(180, 82, 205)
MEDIUMORCHID4 = Color(122, 55, 139)
MEDIUMPURPLE = Color(147, 112, 219)
MEDIUMPURPLE1 = Color(171, 130, 255)
MEDIUMPURPLE2 = Color(159, 121, 238)
MEDIUMPURPLE3 = Color(137, 104, 205)
MEDIUMPURPLE4 = Color(93, 71, 139)
MEDIUMSEAGREEN = Color(60, 179, 113)
MEDIUMSLATEBLUE = Color(123, 104, 238)
MEDIUMSPRINGGREEN = Color(0, 250, 154)
MEDIUMTURQUOISE = Color(72, 209, 204)
MEDIUMVIOLETRED = Color(199, 21, 133)
MELON = Color(227, 168, 105)
MIDNIGHTBLUE = Color(25, 25, 112)
MINT = Color(189, 252, 201)
MINTCREAM = Color(245, 255, 250)
MISTYROSE1 = Color(255, 228, 225)
MISTYROSE2 = Color(238, 213, 210)
MISTYROSE3 = Color(205, 183, 181)
MISTYROSE4 = Color(139, 125, 123)
MOCCASIN = Color(255, 228, 181)
NAVAJOWHITE1 = Color(255, 222, 173)
NAVAJOWHITE2 = Color(238, 207, 161)
NAVAJOWHITE3 = Color(205, 179, 139)
NAVAJOWHITE4 = Color(139, 121, 94)
NAVY = Color(0, 0, 128)
OLDLACE = Color(253, 245, 230)
OLIVE = Color(128, 128, 0)
OLIVEDRAB = Color(107, 142, 35)
OLIVEDRAB1 = Color(192, 255, 62)
OLIVEDRAB2 = Color(179, 238, 58)
OLIVEDRAB3 = Color(154, 205, 50)
OLIVEDRAB4 = Color(105, 139, 34)
ORANGE = Color(255, 128, 0)
ORANGE1 = Color(255, 165, 0)
ORANGE2 = Color(238, 154, 0)
ORANGE3 = Color(205, 133, 0)
ORANGE4 = Color(139, 90, 0)
ORANGERED1 = Color(255, 69, 0)
ORANGERED2 = Color(238, 64, 0)
ORANGERED3 = Color(205, 55, 0)
ORANGERED4 = Color(139, 37, 0)
ORCHID = Color(218, 112, 214)
ORCHID1 = Color(255, 131, 250)
ORCHID2 = Color(238, 122, 233)
ORCHID3 = Color(205, 105, 201)
ORCHID4 = Color(139, 71, 137)
PALEGOLDENROD = Color(238, 232, 170)
PALEGREEN = Color(152, 251, 152)
PALEGREEN1 = Color(154, 255, 154)
PALEGREEN2 = Color(144, 238, 144)
PALEGREEN3 = Color(124, 205, 124)
PALEGREEN4 = Color(84, 139, 84)
PALETURQUOISE1 = Color(187, 255, 255)
PALETURQUOISE2 = Color(174, 238, 238)
PALETURQUOISE3 = Color(150, 205, 205)
PALETURQUOISE4 = Color(102, 139, 139)
PALEVIOLETRED = Color(219, 112, 147)
PALEVIOLETRED1 = Color(255, 130, 171)
PALEVIOLETRED2 = Color(238, 121, 159)
PALEVIOLETRED3 = Color(205, 104, 137)
PALEVIOLETRED4 = Color(139, 71, 93)
PAPAYAWHIP = Color(255, 239, 213)
PEACHPUFF1 = Color(255, 218, 185)
PEACHPUFF2 = Color(238, 203, 173)
PEACHPUFF3 = Color(205, 175, 149)
PEACHPUFF4 = Color(139, 119, 101)
PEACOCK = Color(51, 161, 201)
PINK = Color(255, 192, 203)
PINK1 = Color(255, 181, 197)
PINK2 = Color(238, 169, 184)
PINK3 = Color(205, 145, 158)
PINK4 = Color(139, 99, 108)
PLUM = Color(221, 160, 221)
PLUM1 = Color(255, 187, 255)
PLUM2 = Color(238, 174, 238)
PLUM3 = Color(205, 150, 205)
PLUM4 = Color(139, 102, 139)
POWDERBLUE = Color(176, 224, 230)
PURPLE = Color(128, 0, 128)
PURPLE1 = Color(155, 48, 255)
PURPLE2 = Color(145, 44, 238)
PURPLE3 = Color(125, 38, 205)
PURPLE4 = Color(85, 26, 139)
RASPBERRY = Color(135, 38, 87)
RAWSIENNA = Color(199, 97, 20)
RED = Color(255, 0, 0)
RED1 = Color(238, 0, 0)
RED2 = Color(205, 0, 0)
RED3 = Color(139, 0, 0)
RED4 = Color(125, 0, 0)
ROSYBROWN = Color(188, 143, 143)
ROSYBROWN1 = Color(255, 193, 193)
ROSYBROWN2 = Color(238, 180, 180)
ROSYBROWN3 = Color(205, 155, 155)
ROSYBROWN4 = Color(139, 105, 105)
ROYALBLUE = Color(65, 105, 225)
ROYALBLUE1 = Color(72, 118, 255)
ROYALBLUE2 = Color(67, 110, 238)
ROYALBLUE3 = Color(58, 95, 205)
ROYALBLUE4 = Color(39, 64, 139)
SALMON = Color(250, 128, 114)
SALMON1 = Color(255, 140, 105)
SALMON2 = Color(238, 130, 98)
SALMON3 = Color(205, 112, 84)
SALMON4 = Color(139, 76, 57)
SANDYBROWN = Color(244, 164, 96)
SAPGREEN = Color(48, 128, 20)
SEAGREEN1 = Color(84, 255, 159)
SEAGREEN2 = Color(78, 238, 148)
SEAGREEN3 = Color(67, 205, 128)
SEAGREEN4 = Color(46, 139, 87)
SEASHELL1 = Color(255, 245, 238)
SEASHELL2 = Color(238, 229, 222)
SEASHELL3 = Color(205, 197, 191)
SEASHELL4 = Color(139, 134, 130)
SEPIA = Color(94, 38, 18)
SGIBEET = Color(142, 56, 142)
SGIBRIGHTGRAY = Color(197, 193, 170)
SGICHARTREUSE = Color(113, 198, 113)
SGIDARKGRAY = Color(85, 85, 85)
SGIGRAY12 = Color(30, 30, 30)
SGIGRAY16 = Color(40, 40, 40)
SGIGRAY32 = Color(81, 81, 81)
SGIGRAY36 = Color(91, 91, 91)
SGIGRAY52 = Color(132, 132, 132)
SGIGRAY56 = Color(142, 142, 142)
SGIGRAY72 = Color(183, 183, 183)
SGIGRAY76 = Color(193, 193, 193)
SGIGRAY92 = Color(234, 234, 234)
SGIGRAY96 = Color(244, 244, 244)
SGILIGHTBLUE = Color(125, 158, 192)
SGILIGHTGRAY = Color(170, 170, 170)
SGIOLIVEDRAB = Color(142, 142, 56)
SGISALMON = Color(198, 113, 113)
SGISLATEBLUE = Color(113, 113, 198)
SGITEAL = Color(56, 142, 142)
SIENNA = Color(160, 82, 45)
SIENNA1 = Color(255, 130, 71)
SIENNA2 = Color(238, 121, 66)
SIENNA3 = Color(205, 104, 57)
SIENNA4 = Color(139, 71, 38)
SILVER = Color(192, 192, 192)
SKYBLUE = Color(135, 206, 235)
SKYBLUE1 = Color(135, 206, 255)
SKYBLUE2 = Color(126, 192, 238)
SKYBLUE3 = Color(108, 166, 205)
SKYBLUE4 = Color(74, 112, 139)
SLATEBLUE = Color(106, 90, 205)
SLATEBLUE1 = Color(131, 111, 255)
SLATEBLUE2 = Color(122, 103, 238)
SLATEBLUE3 = Color(105, 89, 205)
SLATEBLUE4 = Color(71, 60, 139)
SLATEGRAY = Color(112, 128, 144)
SLATEGRAY1 = Color(198, 226, 255)
SLATEGRAY2 = Color(185, 211, 238)
SLATEGRAY3 = Color(159, 182, 205)
SLATEGRAY4 = Color(108, 123, 139)
SNOW1 = Color(255, 250, 250)
SNOW2 = Color(238, 233, 233)
SNOW3 = Color(205, 201, 201)
SNOW4 = Color(139, 137, 137)
SPRINGGREEN = Color(0, 255, 127)
SPRINGGREEN1 = Color(0, 238, 118)
SPRINGGREEN2 = Color(0, 205, 102)
SPRINGGREEN3 = Color(0, 139, 69)
STEELBLUE = Color(70, 130, 180)
STEELBLUE1 = Color(99, 184, 255)
STEELBLUE2 = Color(92, 172, 238)
STEELBLUE3 = Color(79, 148, 205)
STEELBLUE4 = Color(54, 100, 139)
TAN = Color(210, 180, 140)
TAN1 = Color(255, 165, 79)
TAN2 = Color(238, 154, 73)
TAN3 = Color(205, 133, 63)
TAN4 = Color(139, 90, 43)
TEAL = Color(0, 128, 128)
THISTLE = Color(216, 191, 216)
THISTLE1 = Color(255, 225, 255)
THISTLE2 = Color(238, 210, 238)
THISTLE3 = Color(205, 181, 205)
THISTLE4 = Color(139, 123, 139)
TOMATO1 = Color(255, 99, 71)
TOMATO2 = Color(238, 92, 66)
TOMATO3 = Color(205, 79, 57)
TOMATO4 = Color(139, 54, 38)
TURQUOISE = Color(64, 224, 208)
TURQUOISE1 = Color(0, 245, 255)
TURQUOISE2 = Color(0, 229, 238)
TURQUOISE3 = Color(0, 197, 205)
TURQUOISE4 = Color(0, 134, 139)
TURQUOISEBLUE = Color(0, 199, 140)
VIOLET = Color(238, 130, 238)
VIOLETRED = Color(208, 32, 144)
VIOLETRED1 = Color(255, 62, 150)
VIOLETRED2 = Color(238, 58, 140)
VIOLETRED3 = Color(205, 50, 120)
VIOLETRED4 = Color(139, 34, 82)
WARMGREY = Color(128, 128, 105)
WHEAT = Color(245, 222, 179)
WHEAT1 = Color(255, 231, 186)
WHEAT2 = Color(238, 216, 174)
WHEAT3 = Color(205, 186, 150)
WHEAT4 = Color(139, 126, 102)
WHITE = Color(255, 255, 255)
WHITESMOKE = Color(245, 245, 245)
WHITESMOKE = Color(245, 245, 245)
YELLOW = Color(255, 255, 0)
YELLOW1 = Color(255, 255, 0)
YELLOW2 = Color(238, 238, 0)
YELLOW3 = Color(205, 205, 0)
YELLOW4 = Color(139, 139, 0)
COLORS = {}
# Add colors to colors dictionary
COLORS["aliceblue"] = ALICEBLUE
COLORS["antiquewhite"] = ANTIQUEWHITE
COLORS["antiquewhite1"] = ANTIQUEWHITE1
COLORS["antiquewhite2"] = ANTIQUEWHITE2
COLORS["antiquewhite3"] = ANTIQUEWHITE3
COLORS["antiquewhite4"] = ANTIQUEWHITE4
COLORS["aqua"] = AQUA
COLORS["aquamarine"] = AQUAMARINE1
COLORS["aquamarine1"] = AQUAMARINE1
COLORS["aquamarine2"] = AQUAMARINE2
COLORS["aquamarine3"] = AQUAMARINE3
COLORS["aquamarine4"] = AQUAMARINE4
COLORS["azure"] = AZURE1
COLORS["azure1"] = AZURE1
COLORS["azure2"] = AZURE2
COLORS["azure3"] = AZURE3
COLORS["azure4"] = AZURE4
COLORS["banana"] = BANANA
COLORS["beige"] = BEIGE
COLORS["bisque"] = BISQUE1
COLORS["bisque1"] = BISQUE1
COLORS["bisque2"] = BISQUE2
COLORS["bisque3"] = BISQUE3
COLORS["bisque4"] = BISQUE4
COLORS["black"] = BLACK
COLORS["blanchedalmond"] = BLANCHEDALMOND
COLORS["blue"] = BLUE
COLORS["blue1"] = BLUE
COLORS["blue2"] = BLUE2
COLORS["blue3"] = BLUE3
COLORS["blue4"] = BLUE4
COLORS["blueviolet"] = BLUEVIOLET
COLORS["brick"] = BRICK
COLORS["brown"] = BROWN
COLORS["brown1"] = BROWN1
COLORS["brown2"] = BROWN2
COLORS["brown3"] = BROWN3
COLORS["brown4"] = BROWN4
COLORS["burlywood"] = BURLYWOOD
COLORS["burlywood1"] = BURLYWOOD1
COLORS["burlywood2"] = BURLYWOOD2
COLORS["burlywood3"] = BURLYWOOD3
COLORS["burlywood4"] = BURLYWOOD4
COLORS["burntsienna"] = BURNTSIENNA
COLORS["burntumber"] = BURNTUMBER
COLORS["cadetblue"] = CADETBLUE
COLORS["cadetblue1"] = CADETBLUE1
COLORS["cadetblue2"] = CADETBLUE2
COLORS["cadetblue3"] = CADETBLUE3
COLORS["cadetblue4"] = CADETBLUE4
COLORS["cadmiumorange"] = CADMIUMORANGE
COLORS["cadmiumyellow"] = CADMIUMYELLOW
COLORS["carrot"] = CARROT
COLORS["chartreuse"] = CHARTREUSE1
COLORS["chartreuse1"] = CHARTREUSE1
COLORS["chartreuse2"] = CHARTREUSE2
COLORS["chartreuse3"] = CHARTREUSE3
COLORS["chartreuse4"] = CHARTREUSE4
COLORS["chocolate"] = CHOCOLATE
COLORS["chocolate1"] = CHOCOLATE1
COLORS["chocolate2"] = CHOCOLATE2
COLORS["chocolate3"] = CHOCOLATE3
COLORS["chocolate4"] = CHOCOLATE4
COLORS["cobalt"] = COBALT
COLORS["cobaltgreen"] = COBALTGREEN
COLORS["coldgrey"] = COLDGREY
COLORS["coral"] = CORAL
COLORS["coral1"] = CORAL1
COLORS["coral2"] = CORAL2
COLORS["coral3"] = CORAL3
COLORS["coral4"] = CORAL4
COLORS["cornflowerblue"] = CORNFLOWERBLUE
COLORS["cornsilk"] = CORNSILK1
COLORS["cornsilk1"] = CORNSILK1
COLORS["cornsilk2"] = CORNSILK2
COLORS["cornsilk3"] = CORNSILK3
COLORS["cornsilk4"] = CORNSILK4
COLORS["crimson"] = CRIMSON
COLORS["cyan"] = CYAN1
COLORS["cyan1"] = CYAN1
COLORS["cyan2"] = CYAN2
COLORS["cyan3"] = CYAN3
COLORS["darkgoldenrod"] = DARKGOLDENROD
COLORS["darkgoldenrod1"] = DARKGOLDENROD1
COLORS["darkgoldenrod2"] = DARKGOLDENROD2
COLORS["darkgoldenrod3"] = DARKGOLDENROD3
COLORS["darkgoldenrod4"] = DARKGOLDENROD4
COLORS["darkgray"] = DARKGRAY
COLORS["darkgreen"] = DARKGREEN
COLORS["darkkhaki"] = DARKKHAKI
COLORS["darkolivegreen"] = DARKOLIVEGREEN
COLORS["darkolivegreen1"] = DARKOLIVEGREEN1
COLORS["darkolivegreen2"] = DARKOLIVEGREEN2
COLORS["darkolivegreen3"] = DARKOLIVEGREEN3
COLORS["darkolivegreen4"] = DARKOLIVEGREEN4
COLORS["darkorange"] = DARKORANGE
COLORS["darkorange1"] = DARKORANGE1
COLORS["darkorange2"] = DARKORANGE2
COLORS["darkorange3"] = DARKORANGE3
COLORS["darkorange4"] = DARKORANGE4
COLORS["darkorchid"] = DARKORCHID
COLORS["darkorchid1"] = DARKORCHID1
COLORS["darkorchid2"] = DARKORCHID2
COLORS["darkorchid3"] = DARKORCHID3
COLORS["darkorchid4"] = DARKORCHID4
COLORS["darksalmon"] = DARKSALMON
COLORS["darkseagreen"] = DARKSEAGREEN
COLORS["darkseagreen1"] = DARKSEAGREEN1
COLORS["darkseagreen2"] = DARKSEAGREEN2
COLORS["darkseagreen3"] = DARKSEAGREEN3
COLORS["darkseagreen4"] = DARKSEAGREEN4
COLORS["darkslateblue"] = DARKSLATEBLUE
COLORS["darkslategray"] = DARKSLATEGRAY
COLORS["darkslategray1"] = DARKSLATEGRAY1
COLORS["darkslategray2"] = DARKSLATEGRAY2
COLORS["darkslategray3"] = DARKSLATEGRAY3
COLORS["darkslategray4"] = DARKSLATEGRAY4
COLORS["darkturquoise"] = DARKTURQUOISE
COLORS["darkviolet"] = DARKVIOLET
COLORS["deeppink"] = DEEPPINK1
COLORS["deeppink1"] = DEEPPINK1
COLORS["deeppink2"] = DEEPPINK2
COLORS["deeppink3"] = DEEPPINK3
COLORS["deeppink4"] = DEEPPINK4
COLORS["deepskyblue"] = DEEPSKYBLUE1
COLORS["deepskyblue1"] = DEEPSKYBLUE1
COLORS["deepskyblue2"] = DEEPSKYBLUE2
COLORS["deepskyblue3"] = DEEPSKYBLUE3
COLORS["deepskyblue4"] = DEEPSKYBLUE4
COLORS["dimgray"] = DIMGRAY
COLORS["dodgerblue"] = DODGERBLUE1
COLORS["dodgerblue1"] = DODGERBLUE1
COLORS["dodgerblue2"] = DODGERBLUE2
COLORS["dodgerblue3"] = DODGERBLUE3
COLORS["dodgerblue4"] = DODGERBLUE4
COLORS["eggshell"] = EGGSHELL
COLORS["emeraldgreen"] = EMERALDGREEN
COLORS["firebrick"] = FIREBRICK
COLORS["firebrick1"] = FIREBRICK1
COLORS["firebrick2"] = FIREBRICK2
COLORS["firebrick3"] = FIREBRICK3
COLORS["firebrick4"] = FIREBRICK4
COLORS["flesh"] = FLESH
COLORS["floralwhite"] = FLORALWHITE
COLORS["forestgreen"] = FORESTGREEN
COLORS["gainsboro"] = GAINSBORO
COLORS["ghostwhite"] = GHOSTWHITE
COLORS["gold"] = GOLD1
COLORS["gold1"] = GOLD1
COLORS["gold2"] = GOLD2
COLORS["gold3"] = GOLD3
COLORS["gold4"] = GOLD4
COLORS["goldenrod"] = GOLDENROD
COLORS["goldenrod1"] = GOLDENROD1
COLORS["goldenrod2"] = GOLDENROD2
COLORS["goldenrod3"] = GOLDENROD3
COLORS["goldenrod4"] = GOLDENROD4
COLORS["gray"] = GRAY
COLORS["gray1"] = GRAY1
COLORS["gray10"] = GRAY10
COLORS["gray11"] = GRAY11
COLORS["gray12"] = GRAY12
COLORS["gray13"] = GRAY13
COLORS["gray14"] = GRAY14
COLORS["gray15"] = GRAY15
COLORS["gray16"] = GRAY16
COLORS["gray17"] = GRAY17
COLORS["gray18"] = GRAY18
COLORS["gray19"] = GRAY19
COLORS["gray2"] = GRAY2
COLORS["gray20"] = GRAY20
COLORS["gray21"] = GRAY21
COLORS["gray22"] = GRAY22
COLORS["gray23"] = GRAY23
COLORS["gray24"] = GRAY24
COLORS["gray25"] = GRAY25
COLORS["gray26"] = GRAY26
COLORS["gray27"] = GRAY27
COLORS["gray28"] = GRAY28
COLORS["gray29"] = GRAY29
COLORS["gray3"] = GRAY3
COLORS["gray30"] = GRAY30
COLORS["gray31"] = GRAY31
COLORS["gray32"] = GRAY32
COLORS["gray33"] = GRAY33
COLORS["gray34"] = GRAY34
COLORS["gray35"] = GRAY35
COLORS["gray36"] = GRAY36
COLORS["gray37"] = GRAY37
COLORS["gray38"] = GRAY38
COLORS["gray39"] = GRAY39
COLORS["gray4"] = GRAY4
COLORS["gray40"] = GRAY40
COLORS["gray42"] = GRAY42
COLORS["gray43"] = GRAY43
COLORS["gray44"] = GRAY44
COLORS["gray45"] = GRAY45
COLORS["gray46"] = GRAY46
COLORS["gray47"] = GRAY47
COLORS["gray48"] = GRAY48
COLORS["gray49"] = GRAY49
COLORS["gray5"] = GRAY5
COLORS["gray50"] = GRAY50
COLORS["gray51"] = GRAY51
COLORS["gray52"] = GRAY52
COLORS["gray53"] = GRAY53
COLORS["gray54"] = GRAY54
COLORS["gray55"] = GRAY55
COLORS["gray56"] = GRAY56
COLORS["gray57"] = GRAY57
COLORS["gray58"] = GRAY58
COLORS["gray59"] = GRAY59
COLORS["gray6"] = GRAY6
COLORS["gray60"] = GRAY60
COLORS["gray61"] = GRAY61
COLORS["gray62"] = GRAY62
COLORS["gray63"] = GRAY63
COLORS["gray64"] = GRAY64
COLORS["gray65"] = GRAY65
COLORS["gray66"] = GRAY66
COLORS["gray67"] = GRAY67
COLORS["gray68"] = GRAY68
COLORS["gray69"] = GRAY69
COLORS["gray7"] = GRAY7
COLORS["gray70"] = GRAY70
COLORS["gray71"] = GRAY71
COLORS["gray72"] = GRAY72
COLORS["gray73"] = GRAY73
COLORS["gray74"] = GRAY74
COLORS["gray75"] = GRAY75
COLORS["gray76"] = GRAY76
COLORS["gray77"] = GRAY77
COLORS["gray78"] = GRAY78
COLORS["gray79"] = GRAY79
COLORS["gray8"] = GRAY8
COLORS["gray80"] = GRAY80
COLORS["gray81"] = GRAY81
COLORS["gray82"] = GRAY82
COLORS["gray83"] = GRAY83
COLORS["gray84"] = GRAY84
COLORS["gray85"] = GRAY85
COLORS["gray86"] = GRAY86
COLORS["gray87"] = GRAY87
COLORS["gray88"] = GRAY88
COLORS["gray89"] = GRAY89
COLORS["gray9"] = GRAY9
COLORS["gray90"] = GRAY90
COLORS["gray91"] = GRAY91
COLORS["gray92"] = GRAY92
COLORS["gray93"] = GRAY93
COLORS["gray94"] = GRAY94
COLORS["gray95"] = GRAY95
COLORS["gray97"] = GRAY97
COLORS["gray98"] = GRAY98
COLORS["gray99"] = GRAY99
COLORS["green"] = GREEN
COLORS["green1"] = GREEN1
COLORS["green2"] = GREEN2
COLORS["green3"] = GREEN3
COLORS["green4"] = GREEN4
COLORS["greenyellow"] = GREENYELLOW
COLORS["honeydew"] = HONEYDEW1
COLORS["honeydew1"] = HONEYDEW1
COLORS["honeydew2"] = HONEYDEW2
COLORS["honeydew3"] = HONEYDEW3
COLORS["honeydew4"] = HONEYDEW4
COLORS["hotpink"] = HOTPINK
COLORS["hotpink1"] = HOTPINK1
COLORS["hotpink2"] = HOTPINK2
COLORS["hotpink3"] = HOTPINK3
COLORS["hotpink4"] = HOTPINK4
COLORS["indianred"] = INDIANRED
COLORS["indianred"] = INDIANRED
COLORS["indianred1"] = INDIANRED1
COLORS["indianred2"] = INDIANRED2
COLORS["indianred3"] = INDIANRED3
COLORS["indianred4"] = INDIANRED4
COLORS["indigo"] = INDIGO
COLORS["ivory"] = IVORY1
COLORS["ivory1"] = IVORY1
COLORS["ivory2"] = IVORY2
COLORS["ivory3"] = IVORY3
COLORS["ivory4"] = IVORY4
COLORS["ivoryblack"] = IVORYBLACK
COLORS["khaki"] = KHAKI
COLORS["khaki1"] = KHAKI1
COLORS["khaki2"] = KHAKI2
COLORS["khaki3"] = KHAKI3
COLORS["khaki4"] = KHAKI4
COLORS["lavender"] = LAVENDER
COLORS["lavenderblush"] = LAVENDERBLUSH1
COLORS["lavenderblush1"] = LAVENDERBLUSH1
COLORS["lavenderblush2"] = LAVENDERBLUSH2
COLORS["lavenderblush3"] = LAVENDERBLUSH3
COLORS["lavenderblush4"] = LAVENDERBLUSH4
COLORS["lawngreen"] = LAWNGREEN
COLORS["lemonchiffon"] = LEMONCHIFFON1
COLORS["lemonchiffon1"] = LEMONCHIFFON1
COLORS["lemonchiffon2"] = LEMONCHIFFON2
COLORS["lemonchiffon3"] = LEMONCHIFFON3
COLORS["lemonchiffon4"] = LEMONCHIFFON4
COLORS["lightblue"] = LIGHTBLUE
COLORS["lightblue1"] = LIGHTBLUE1
COLORS["lightblue2"] = LIGHTBLUE2
COLORS["lightblue3"] = LIGHTBLUE3
COLORS["lightblue4"] = LIGHTBLUE4
COLORS["lightcoral"] = LIGHTCORAL
COLORS["lightcyan"] = LIGHTCYAN1
COLORS["lightcyan1"] = LIGHTCYAN1
COLORS["lightcyan2"] = LIGHTCYAN2
COLORS["lightcyan3"] = LIGHTCYAN3
COLORS["lightcyan4"] = LIGHTCYAN4
COLORS["lightgoldenrod"] = LIGHTGOLDENROD1
COLORS["lightgoldenrod1"] = LIGHTGOLDENROD1
COLORS["lightgoldenrod2"] = LIGHTGOLDENROD2
COLORS["lightgoldenrod3"] = LIGHTGOLDENROD3
COLORS["lightgoldenrod4"] = LIGHTGOLDENROD4
COLORS["lightgoldenrodyellow"] = LIGHTGOLDENRODYELLOW
COLORS["lightgrey"] = LIGHTGREY
COLORS["lightpink"] = LIGHTPINK
COLORS["lightpink1"] = LIGHTPINK1
COLORS["lightpink2"] = LIGHTPINK2
COLORS["lightpink3"] = LIGHTPINK3
COLORS["lightpink4"] = LIGHTPINK4
COLORS["lightsalmon"] = LIGHTSALMON1
COLORS["lightsalmon1"] = LIGHTSALMON1
COLORS["lightsalmon2"] = LIGHTSALMON2
COLORS["lightsalmon3"] = LIGHTSALMON3
COLORS["lightsalmon4"] = LIGHTSALMON4
COLORS["lightseagreen"] = LIGHTSEAGREEN
COLORS["lightskyblue"] = LIGHTSKYBLUE
COLORS["lightskyblue1"] = LIGHTSKYBLUE1
COLORS["lightskyblue2"] = LIGHTSKYBLUE2
COLORS["lightskyblue3"] = LIGHTSKYBLUE3
COLORS["lightskyblue4"] = LIGHTSKYBLUE4
COLORS["lightslateblue"] = LIGHTSLATEBLUE
COLORS["lightslategray"] = LIGHTSLATEGRAY
COLORS["lightsteelblue"] = LIGHTSTEELBLUE
COLORS["lightsteelblue1"] = LIGHTSTEELBLUE1
COLORS["lightsteelblue2"] = LIGHTSTEELBLUE2
COLORS["lightsteelblue3"] = LIGHTSTEELBLUE3
COLORS["lightsteelblue4"] = LIGHTSTEELBLUE4
COLORS["lightyellow"] = LIGHTYELLOW1
COLORS["lightyellow1"] = LIGHTYELLOW1
COLORS["lightyellow2"] = LIGHTYELLOW2
COLORS["lightyellow3"] = LIGHTYELLOW3
COLORS["lightyellow4"] = LIGHTYELLOW4
COLORS["limegreen"] = LIMEGREEN
COLORS["linen"] = LINEN
COLORS["magenta"] = MAGENTA
COLORS["magenta1"] = MAGENTA2
COLORS["magenta2"] = MAGENTA2
COLORS["magenta3"] = MAGENTA3
COLORS["magenta4"] = MAGENTA4
COLORS["manganeseblue"] = MANGANESEBLUE
COLORS["maroon"] = MAROON
COLORS["maroon1"] = MAROON1
COLORS["maroon2"] = MAROON2
COLORS["maroon3"] = MAROON3
COLORS["maroon4"] = MAROON4
COLORS["mediumorchid"] = MEDIUMORCHID
COLORS["mediumorchid1"] = MEDIUMORCHID1
COLORS["mediumorchid2"] = MEDIUMORCHID2
COLORS["mediumorchid3"] = MEDIUMORCHID3
COLORS["mediumorchid4"] = MEDIUMORCHID4
COLORS["mediumpurple"] = MEDIUMPURPLE
COLORS["mediumpurple1"] = MEDIUMPURPLE1
COLORS["mediumpurple2"] = MEDIUMPURPLE2
COLORS["mediumpurple3"] = MEDIUMPURPLE3
COLORS["mediumpurple4"] = MEDIUMPURPLE4
COLORS["mediumseagreen"] = MEDIUMSEAGREEN
COLORS["mediumslateblue"] = MEDIUMSLATEBLUE
COLORS["mediumspringgreen"] = MEDIUMSPRINGGREEN
COLORS["mediumturquoise"] = MEDIUMTURQUOISE
COLORS["mediumvioletred"] = MEDIUMVIOLETRED
COLORS["melon"] = MELON
COLORS["midnightblue"] = MIDNIGHTBLUE
COLORS["mint"] = MINT
COLORS["mintcream"] = MINTCREAM
COLORS["mistyrose"] = MISTYROSE1
COLORS["mistyrose1"] = MISTYROSE1
COLORS["mistyrose2"] = MISTYROSE2
COLORS["mistyrose3"] = MISTYROSE3
COLORS["mistyrose4"] = MISTYROSE4
COLORS["moccasin"] = MOCCASIN
COLORS["navajowhite"] = NAVAJOWHITE1
COLORS["navajowhite1"] = NAVAJOWHITE1
COLORS["navajowhite2"] = NAVAJOWHITE2
COLORS["navajowhite3"] = NAVAJOWHITE3
COLORS["navajowhite4"] = NAVAJOWHITE4
COLORS["navy"] = NAVY
COLORS["oldlace"] = OLDLACE
COLORS["olive"] = OLIVE
COLORS["olivedrab"] = OLIVEDRAB
COLORS["olivedrab1"] = OLIVEDRAB1
COLORS["olivedrab2"] = OLIVEDRAB2
COLORS["olivedrab3"] = OLIVEDRAB3
COLORS["olivedrab4"] = OLIVEDRAB4
COLORS["orange"] = ORANGE
COLORS["orange1"] = ORANGE1
COLORS["orange2"] = ORANGE2
COLORS["orange3"] = ORANGE3
COLORS["orange4"] = ORANGE4
COLORS["orangered"] = ORANGERED1
COLORS["orangered1"] = ORANGERED1
COLORS["orangered2"] = ORANGERED2
COLORS["orangered3"] = ORANGERED3
COLORS["orangered4"] = ORANGERED4
COLORS["orchid"] = ORCHID
COLORS["orchid1"] = ORCHID1
COLORS["orchid2"] = ORCHID2
COLORS["orchid3"] = ORCHID3
COLORS["orchid4"] = ORCHID4
COLORS["palegoldenrod"] = PALEGOLDENROD
COLORS["palegreen"] = PALEGREEN
COLORS["palegreen1"] = PALEGREEN1
COLORS["palegreen2"] = PALEGREEN2
COLORS["palegreen3"] = PALEGREEN3
COLORS["palegreen4"] = PALEGREEN4
COLORS["paleturquoise1"] = PALETURQUOISE1
COLORS["paleturquoise2"] = PALETURQUOISE2
COLORS["paleturquoise3"] = PALETURQUOISE3
COLORS["paleturquoise4"] = PALETURQUOISE4
COLORS["palevioletred"] = PALEVIOLETRED
COLORS["palevioletred1"] = PALEVIOLETRED1
COLORS["palevioletred2"] = PALEVIOLETRED2
COLORS["palevioletred3"] = PALEVIOLETRED3
COLORS["palevioletred4"] = PALEVIOLETRED4
COLORS["papayawhip"] = PAPAYAWHIP
COLORS["peachpuff1"] = PEACHPUFF1
COLORS["peachpuff2"] = PEACHPUFF2
COLORS["peachpuff3"] = PEACHPUFF3
COLORS["peachpuff4"] = PEACHPUFF4
COLORS["peacock"] = PEACOCK
COLORS["pink"] = PINK
COLORS["pink1"] = PINK1
COLORS["pink2"] = PINK2
COLORS["pink3"] = PINK3
COLORS["pink4"] = PINK4
COLORS["plum"] = PLUM
COLORS["plum1"] = PLUM1
COLORS["plum2"] = PLUM2
COLORS["plum3"] = PLUM3
COLORS["plum4"] = PLUM4
COLORS["powderblue"] = POWDERBLUE
COLORS["purple"] = PURPLE
COLORS["purple1"] = PURPLE1
COLORS["purple2"] = PURPLE2
COLORS["purple3"] = PURPLE3
COLORS["purple4"] = PURPLE4
COLORS["raspberry"] = RASPBERRY
COLORS["rawsienna"] = RAWSIENNA
COLORS["red"] = RED
COLORS["red1"] = RED1
COLORS["red2"] = RED2
COLORS["red3"] = RED3
COLORS["red4"] = RED4
COLORS["rosybrown"] = ROSYBROWN
COLORS["rosybrown1"] = ROSYBROWN1
COLORS["rosybrown2"] = ROSYBROWN2
COLORS["rosybrown3"] = ROSYBROWN3
COLORS["rosybrown4"] = ROSYBROWN4
COLORS["royalblue"] = ROYALBLUE
COLORS["royalblue1"] = ROYALBLUE1
COLORS["royalblue2"] = ROYALBLUE2
COLORS["royalblue3"] = ROYALBLUE3
COLORS["royalblue4"] = ROYALBLUE4
COLORS["salmon"] = SALMON
COLORS["salmon1"] = SALMON1
COLORS["salmon2"] = SALMON2
COLORS["salmon3"] = SALMON3
COLORS["salmon4"] = SALMON4
COLORS["sandybrown"] = SANDYBROWN
COLORS["sapgreen"] = SAPGREEN
COLORS["seagreen1"] = SEAGREEN1
COLORS["seagreen2"] = SEAGREEN2
COLORS["seagreen3"] = SEAGREEN3
COLORS["seagreen4"] = SEAGREEN4
COLORS["seashell1"] = SEASHELL1
COLORS["seashell2"] = SEASHELL2
COLORS["seashell3"] = SEASHELL3
COLORS["seashell4"] = SEASHELL4
COLORS["sepia"] = SEPIA
COLORS["sgibeet"] = SGIBEET
COLORS["sgibrightgray"] = SGIBRIGHTGRAY
COLORS["sgichartreuse"] = SGICHARTREUSE
COLORS["sgidarkgray"] = SGIDARKGRAY
COLORS["sgigray12"] = SGIGRAY12
COLORS["sgigray16"] = SGIGRAY16
COLORS["sgigray32"] = SGIGRAY32
COLORS["sgigray36"] = SGIGRAY36
COLORS["sgigray52"] = SGIGRAY52
COLORS["sgigray56"] = SGIGRAY56
COLORS["sgigray72"] = SGIGRAY72
COLORS["sgigray76"] = SGIGRAY76
COLORS["sgigray92"] = SGIGRAY92
COLORS["sgigray96"] = SGIGRAY96
COLORS["sgilightblue"] = SGILIGHTBLUE
COLORS["sgilightgray"] = SGILIGHTGRAY
COLORS["sgiolivedrab"] = SGIOLIVEDRAB
COLORS["sgisalmon"] = SGISALMON
COLORS["sgislateblue"] = SGISLATEBLUE
COLORS["sgiteal"] = SGITEAL
COLORS["sienna"] = SIENNA
COLORS["sienna1"] = SIENNA1
COLORS["sienna2"] = SIENNA2
COLORS["sienna3"] = SIENNA3
COLORS["sienna4"] = SIENNA4
COLORS["silver"] = SILVER
COLORS["skyblue"] = SKYBLUE
COLORS["skyblue1"] = SKYBLUE1
COLORS["skyblue2"] = SKYBLUE2
COLORS["skyblue3"] = SKYBLUE3
COLORS["skyblue4"] = SKYBLUE4
COLORS["slateblue"] = SLATEBLUE
COLORS["slateblue1"] = SLATEBLUE1
COLORS["slateblue2"] = SLATEBLUE2
COLORS["slateblue3"] = SLATEBLUE3
COLORS["slateblue4"] = SLATEBLUE4
COLORS["slategray"] = SLATEGRAY
COLORS["slategray1"] = SLATEGRAY1
COLORS["slategray2"] = SLATEGRAY2
COLORS["slategray3"] = SLATEGRAY3
COLORS["slategray4"] = SLATEGRAY4
COLORS["snow"] = SNOW1
COLORS["snow1"] = SNOW1
COLORS["snow2"] = SNOW2
COLORS["snow3"] = SNOW3
COLORS["snow4"] = SNOW4
COLORS["springgreen"] = SPRINGGREEN
COLORS["springgreen1"] = SPRINGGREEN1
COLORS["springgreen2"] = SPRINGGREEN2
COLORS["springgreen3"] = SPRINGGREEN3
COLORS["steelblue"] = STEELBLUE
COLORS["steelblue1"] = STEELBLUE1
COLORS["steelblue2"] = STEELBLUE2
COLORS["steelblue3"] = STEELBLUE3
COLORS["steelblue4"] = STEELBLUE4
COLORS["tan"] = TAN
COLORS["tan1"] = TAN1
COLORS["tan2"] = TAN2
COLORS["tan3"] = TAN3
COLORS["tan4"] = TAN4
COLORS["teal"] = TEAL
COLORS["thistle"] = THISTLE
COLORS["thistle1"] = THISTLE1
COLORS["thistle2"] = THISTLE2
COLORS["thistle3"] = THISTLE3
COLORS["thistle4"] = THISTLE4
COLORS["tomato"] = TOMATO1
COLORS["tomato1"] = TOMATO1
COLORS["tomato2"] = TOMATO2
COLORS["tomato3"] = TOMATO3
COLORS["tomato4"] = TOMATO4
COLORS["turquoise"] = TURQUOISE
COLORS["turquoise1"] = TURQUOISE1
COLORS["turquoise2"] = TURQUOISE2
COLORS["turquoise3"] = TURQUOISE3
COLORS["turquoise4"] = TURQUOISE4
COLORS["turquoiseblue"] = TURQUOISEBLUE
COLORS["violet"] = VIOLET
COLORS["violetred"] = VIOLETRED
COLORS["violetred1"] = VIOLETRED1
COLORS["violetred2"] = VIOLETRED2
COLORS["violetred3"] = VIOLETRED3
COLORS["violetred4"] = VIOLETRED4
COLORS["warmgrey"] = WARMGREY
COLORS["wheat"] = WHEAT
COLORS["wheat1"] = WHEAT1
COLORS["wheat2"] = WHEAT2
COLORS["wheat3"] = WHEAT3
COLORS["wheat4"] = WHEAT4
COLORS["white"] = WHITE
COLORS["whitesmoke"] = WHITESMOKE
COLORS["whitesmoke"] = WHITESMOKE
COLORS["yellow"] = YELLOW
COLORS["yellow1"] = YELLOW1
COLORS["yellow2"] = YELLOW2
COLORS["yellow3"] = YELLOW3
COLORS["yellow4"] = YELLOW4
def parse_color(value):
if value is not None:
# is it a Color object?
if isinstance(value, Color):
return value
# is the color a string
elif isinstance(value, str):
# strip the color of white space
value = value.strip()
# if it starts with a # check it is a valid color
if value[0] == "#":
# check its format
if len(value) != 7 and len(value) != 9:
raise ValueError("{} is not a valid # color, it must be in the format #rrggbb or #rrggbbaa".format(value))
else:
# add the alpha if required
if len(value) == 7:
value = value + "ff"
hex_values = (value[1:3], value[3:5], value[5:7], value[7:9])
# check hex values are between 00 and ff
int_values = []
for hex_color in hex_values:
try:
int_color = int(hex_color, 16)
int_values.append(int_color)
except:
raise ValueError("{} is not a valid value, it must be hex 00 - ff".format(hex_color))
if not (0 <= int_color <= 255):
raise ValueError("{} is not a valid color value, it must be 00 - ff".format(hex_color))
return Color(red=int_values[0], green=int_values[1], blue=int_values[2], alpha=int_values[3])
else:
# does the color exist in the dictionary of colors
if value.lower() in COLORS:
return COLORS[value.lower()]
else:
raise ValueError("'{}' is not a valid color value.")
# if the color is not a string or a Color object, maybe its a list or tuple - try and convert it
else:
# get the number of colors and check it is iterable
try:
no_of_colors = len(value)
except:
raise ValueError("A color must be a Color object, string or list of values (red, green, blue) or (alpha, red, green, blue)")
if not (3 <= no_of_colors <= 4):
raise ValueError("A color must contain 3 or 4 values (red, green, blue) or (red, green, blue, alpha)")
# check the color values are between 0 and 255
for c in value:
if not (0 <= c <= 255):
raise ValueError("{} is not a valid color value, it must be 0 - 255")
if no_of_colors == 3:
return Color(red=value[0], green=value[1], blue=value[2])
else:
return Color(red=value[0], green=value[1], blue=value[2], alpha=value[3])
================================================
FILE: bluedot/constants.py
================================================
PROTOCOL_VERSION = 2
CHECK_PROTOCOL_TIMEOUT = 2
================================================
FILE: bluedot/dot.py
================================================
from __future__ import division
import sys
import warnings
from time import sleep, time
from threading import Event
from inspect import getfullargspec
from .btcomm import BluetoothServer
from .threads import WrapThread
from .constants import PROTOCOL_VERSION, CHECK_PROTOCOL_TIMEOUT
from .interactions import BlueDotInteraction, BlueDotPosition, BlueDotRotation, BlueDotSwipe
from .colors import parse_color, BLUE
from .exceptions import ButtonDoesNotExist
class Dot:
"""
The internal base class for the implementation of a "button" or "buttons".
"""
def __init__(self, color, square, border, visible):
self._color = color
self._square = square
self._border = border
self._visible = visible
self._is_pressed_event = Event()
self._is_released_event = Event()
self._is_moved_event = Event()
self._is_swiped_event = Event()
self._is_double_pressed_event = Event()
self._when_pressed = None
self._when_pressed_background = False
self._when_double_pressed = None
self._when_double_pressed_background = False
self._when_released = None
self._when_released_background = False
self._when_moved = None
self._when_moved_background = False
self._when_swiped = None
self._when_swiped_background = False
self._when_rotated = None
self._when_rotated_background = False
self._is_pressed = False
self._position = None
self._double_press_time = 0.3
self._rotation_segments = 8
@property
def is_pressed(self):
"""
Returns ``True`` if the button is pressed (or held).
"""
return self._is_pressed
@property
def value(self):
"""
Returns a 1 if ``.is_pressed``, 0 if not.
"""
return 1 if self.is_pressed else 0
@property
def values(self):
"""
Returns an infinite generator constantly yielding the current value.
"""
while True:
yield self.value
@property
def position(self):
"""
Returns an instance of :class:`BlueDotPosition` representing the
current or last position the button was pressed, held or
released.
.. note::
If the button is released (and inactive), :attr:`position` will
return the position where it was released, until it is pressed
again. If the button has never been pressed :attr:`position` will
return ``None``.
"""
return self._position
@property
def when_pressed(self):
"""
Sets or returns the function which is called when the button is pressed.
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotPosition` will be returned representing where the button was pressed.
The following example will print a message to the screen when the button is pressed::
from bluedot import BlueDot
def dot_was_pressed():
print("The button was pressed")
bd = BlueDot()
bd.when_pressed = dot_was_pressed
This example shows how the position of where the button was pressed can be obtained::
from bluedot import BlueDot
def dot_was_pressed(pos):
print("The button was pressed at pos x={} y={}".format(pos.x, pos.y))
bd = BlueDot()
bd.when_pressed = dot_was_pressed
The function will be run in the same thread and block, to run in a separate
thread use `set_when_pressed(function, background=True)`
"""
return self._when_pressed
@when_pressed.setter
def when_pressed(self, value):
self.set_when_pressed(value)
def set_when_pressed(self, callback, background=False):
"""
Sets the function which is called when the button is pressed.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_pressed = callback
self._when_pressed_background = background
@property
def when_double_pressed(self):
"""
Sets or returns the function which is called when the button is double pressed.
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotPosition` will be returned representing where the button was
pressed the second time.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_double_pressed(function, background=True)`
.. note::
The double press event is fired before the 2nd press event e.g. events would be
appear in the order, pressed, released, double pressed, pressed.
"""
return self._when_double_pressed
@when_double_pressed.setter
def when_double_pressed(self, value):
self.set_when_double_pressed(value)
def set_when_double_pressed(self, callback, background=False):
"""
Sets the function which is called when the button is double pressed.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_double_pressed = callback
self._when_double_pressed_background = background
@property
def double_press_time(self):
"""
Sets or returns the time threshold in seconds for a double press. Defaults to 0.3.
"""
return self._double_press_time
@double_press_time.setter
def double_press_time(self, value):
self._double_press_time = value
@property
def when_released(self):
"""
Sets or returns the function which is called when the button is released.
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotPosition` will be returned representing where the button was held
when it was released.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_released(function, background=True)`
"""
return self._when_released
@when_released.setter
def when_released(self, value):
self.set_when_released(value)
def set_when_released(self, callback, background=False):
"""
Sets the function which is called when the button is released.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_released = callback
self._when_released_background = background
@property
def when_moved(self):
"""
Sets or returns the function which is called when the position the button is pressed is moved.
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotPosition` will be returned representing the new position of where the
Blue Dot is held.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_moved(function, background=True)`
"""
return self._when_moved
@when_moved.setter
def when_moved(self, value):
self.set_when_moved(value)
def set_when_moved(self, callback, background=False):
"""
Sets the function which is called when the position the button is pressed is moved.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_moved = callback
self._when_moved_background = background
@property
def when_swiped(self):
"""
Sets or returns the function which is called when the button is swiped.
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotSwipe` will be returned representing the how the button was
swiped.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_swiped(function, background=True)`
"""
return self._when_swiped
@when_swiped.setter
def when_swiped(self, value):
self.set_when_swiped(value)
def set_when_swiped(self, callback, background=False):
"""
Sets the function which is called when the position the button is swiped.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_swiped = callback
self._when_swiped_background = background
@property
def rotation_segments(self):
"""
Sets or returns the number of virtual segments the button is split into for rotating.
Defaults to 8.
"""
return self._rotation_segments
@rotation_segments.setter
def rotation_segments(self, value):
self._rotation_segments = value
@property
def when_rotated(self):
"""
Sets or returns the function which is called when the button is rotated (like an
iPod clock wheel).
The function should accept 0 or 1 parameters, if the function accepts 1 parameter an
instance of :class:`BlueDotRotation` will be returned representing how the button was
rotated.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_rotated(function, background=True)`
"""
return self._when_rotated
@when_rotated.setter
def when_rotated(self, value):
self.set_when_rotated(value)
def set_when_rotated(self, callback, background=False):
"""
Sets the function which is called when the position the button is rotated (like an
iPod clock wheel).
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_rotated = callback
self._when_rotated_background = background
@property
def color(self):
"""
Sets or returns the color of the dot. Defaults to BLUE.
An instance of :class:`.colors.Color` is returned.
Value can be set as a :class:`.colors.Color` object, a hex color value
in the format `#rrggbb` or `#rrggbbaa`, a tuple of `(red, green, blue)`
or `(red, green, blue, alpha)` values between `0` & `255` or a text
description of the color, e.g. "red".
A dictionary of available colors can be obtained from `bluedot.COLORS`.
"""
return self._color
@color.setter
def color(self, value):
self._color = parse_color(value)
@property
def square(self):
"""
When set to `True` the 'dot' is made square. Default is `False`.
"""
return self._square
@square.setter
def square(self, value):
self._square = value
@property
def border(self):
"""
When set to `True` adds a border to the dot. Default is `False`.
"""
return self._border
@border.setter
def border(self, value):
self._border = value
@property
def visible(self):
"""
When set to `False` the dot will be hidden. Default is `True`.
.. note::
Events (press, release, moved) are still sent from the dot
when it is not visible.
"""
return self._visible
@visible.setter
def visible(self, value):
self._visible = value
def wait_for_press(self, timeout = None):
"""
Waits until a Blue Dot is pressed.
Returns ``True`` if the button was pressed.
:param float timeout:
Number of seconds to wait for a Blue Dot to be pressed, if ``None``
(the default), it will wait indefinetly.
"""
return self._is_pressed_event.wait(timeout)
def wait_for_double_press(self, timeout = None):
"""
Waits until a Blue Dot is double pressed.
Returns ``True`` if the button was double pressed.
:param float timeout:
Number of seconds to wait for a Blue Dot to be double pressed, if ``None``
(the default), it will wait indefinetly.
"""
return self._is_double_pressed_event.wait(timeout)
def wait_for_release(self, timeout = None):
"""
Waits until a Blue Dot is released.
Returns ``True`` if the button was released.
:param float timeout:
Number of seconds to wait for a Blue Dot to be released, if ``None``
(the default), it will wait indefinetly.
"""
return self._is_released_event.wait(timeout)
def wait_for_move(self, timeout = None):
"""
Waits until the position where the button is pressed is moved.
Returns ``True`` if the position pressed on the button was moved.
:param float timeout:
Number of seconds to wait for the position that the button
is pressed to move, if ``None`` (the default), it will wait indefinetly.
"""
return self._is_moved_event.wait(timeout)
def wait_for_swipe(self, timeout = None):
"""
Waits until the button is swiped.
Returns ``True`` if the button was swiped.
:param float timeout:
Number of seconds to wait for the button to be swiped, if ``None``
(the default), it will wait indefinetly.
"""
return self._is_swiped_event.wait(timeout)
def press(self, position):
"""
Processes any "pressed" events associated with this dot.
:param BlueDotPosition position:
The BlueDotPosition where the dot was pressed.
"""
self._position = position
self._is_pressed = True
self._is_pressed_event.set()
self._is_pressed_event.clear()
self._process_callback(self.when_pressed, position, self._when_pressed_background)
def release(self, position):
"""
Processes any "released" events associated with this dot.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
self._position = position
self._is_pressed = False
self._is_released_event.set()
self._is_released_event.clear()
self._process_callback(self.when_released, position, self._when_released_background)
def move(self, position):
"""
Processes any "released" events associated with this dot.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
self._is_moved_event.set()
self._is_moved_event.clear()
self._process_callback(self.when_moved, position, self._when_moved_background)
def double_press(self, position):
"""
Processes any "double press" events associated with this dot.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
self._is_double_pressed_event.set()
self._is_double_pressed_event.clear()
self._process_callback(self.when_double_pressed, position, self._when_double_pressed_background)
def swipe(self, swipe):
"""
Processes any "swipe" events associated with this dot.
:param BlueDotSwipe swipe:
The BlueDotSwipe representing how the dot was swiped.
"""
self._is_swiped_event.set()
self._is_swiped_event.clear()
self._process_callback(self.when_swiped, swipe, self._when_swiped_background)
def rotate(self, rotation):
"""
Processes any "rotation" events associated with this dot.
:param BlueDotRotation rotation:
The BlueDotRotation representing how the dot was rotated.
"""
# print("rotating - when_rotated {}")
self._process_callback(self.when_rotated, rotation, self._when_rotated_background)
def _process_callback(self, callback, arg, background):
if callback:
args_expected = getfullargspec(callback).args
no_args_expected = len(args_expected)
if len(args_expected) > 0:
# if someone names the first arg of a class function to something
# other than self, this will fail! or if they name the first argument
# of a non class function to self this will fail!
if args_expected[0] == "self":
no_args_expected -= 1
if no_args_expected == 0:
call_back_t = WrapThread(target=callback)
else:
call_back_t = WrapThread(target=callback, args=(arg, ))
call_back_t.start()
# if this callback is not running in the background wait for it
if not background:
call_back_t.join()
class BlueDotButton(Dot):
"""
Represents a single button on the button client applications. It keeps
tracks of when and where the button has been pressed and processes any
events.
This class is intended for use via :class:`BlueDot` and should not be
instantiated "manually".
A button can be interacted with individually via :class:`BlueDot` by
stating its position in the grid e.g. ::
from bluedot import BlueDot
bd = BlueDot()
first_button = bd[0,0].wait_for_press
first_button.wait_for_press()
print("The first button was pressed")
:param BlueDot bd:
The BlueDot object this button belongs too.
:param int col:
The column position for this button in the grid.
:param int col:
The row position for this button in the grid.
:param string color
The color of the button.
Can be set as a :class:`.colors.Color` object, a hex color value
in the format `#rrggbb` or `#rrggbbaa`, a tuple of `(red, green, blue)`
or `(red, green, blue, alpha)` values between `0` & `255` or a text
description of the color, e.g. "red".
A dictionary of available colors can be obtained from `bluedot.COLORS`.
:param bool square:
When set to `True` the button is made square.
:param bool border:
When set to `True` adds a border to the button.
:param bool visible:
When set to `False` the button will be hidden.
"""
def __init__(self, bd, col, row, color, square, border, visible):
self._bd = bd
self.col = col
self.row = row
self._interaction = None
# setup the "dot"
super().__init__(color, square, border, visible)
@property
def color(self):
return super(BlueDotButton, self.__class__).color.fget(self)
@color.setter
def color(self, value):
super(BlueDotButton, self.__class__).color.fset(self, value)
self._send_config()
@property
def square(self):
return super(BlueDotButton, self.__class__).square.fget(self)
@square.setter
def square(self, value):
super(BlueDotButton, self.__class__).square.fset(self, value)
self._send_config()
@property
def border(self):
return super(BlueDotButton, self.__class__).border.fget(self)
@border.setter
def border(self, value):
super(BlueDotButton, self.__class__).border.fset(self, value)
self._send_config()
@property
def visible(self):
return super(BlueDotButton, self.__class__).visible.fget(self)
@visible.setter
def visible(self, value):
super(BlueDotButton, self.__class__).visible.fset(self, value)
self._send_config()
@property
def modified(self):
"""
Returns `True` if the button's appearance has been modified [is
different] from the default.
"""
return not (
self.color == self._bd.color and
self.visible == self._bd.visible and
self.border == self._bd.border and
self.square == self._bd.square
)
@property
def interaction(self):
"""
Returns an instance of :class:`BlueDotInteraction` representing the
current or last interaction with the button.
.. note::
If the button is released (and inactive), :attr:`interaction`
will return the interaction when it was released, until it is
pressed again. If the button has never been pressed
:attr:`interaction` will return ``None``.
"""
return self._interaction
def press(self, position):
"""
Processes any "pressed" events associated with this button.
:param BlueDotPosition position:
The BlueDotPosition where the dot was pressed.
"""
super().press(position)
# create new interaction
self._interaction = BlueDotInteraction(position)
def release(self, position):
"""
Processes any "released" events associated with this button.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
super().release(position)
self._interaction.released(position)
def move(self, position):
"""
Processes any "released" events associated with this button.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
super().move(position)
self._interaction.moved(position)
def is_double_press(self, position):
"""
Returns True if the position passed represents a double press.
i.e. The last interaction was the button was to release it, and
the time to press is less than the double_press_time.
:param BlueDotPosition position:
The BlueDotPosition where the Dot was pressed.
"""
double_press = False
#was there a previous interaction
if self._interaction:
# was the previous interaction complete (i.e. had it been released)
if not self._interaction.active:
# was it less than the time threshold (0.3 seconds)
if self._interaction.duration < self._double_press_time:
#was the dot pressed again in less than the threshold
if time() - self._interaction.released_position.time < self._double_press_time:
double_press = True
return double_press
def get_swipe(self):
"""
Returns an instance of :class:`BlueDotSwipe` if the last interaction
with the button was a swipe. Returns `None` if the button was not
swiped.
"""
swipe = BlueDotSwipe(self.interaction)
if swipe.valid:
return swipe
def get_rotation(self):
"""
Returns an instance of :class:`BlueDotRotation` if the last interaction
with the button was a rotation. Returns `None` if the button was not
rotated.
"""
# only bother checking to see if its a rotation if `when_rotated`
# as been set. Performance thang!
if self.when_rotated or self._bd.when_rotated:
rotation = BlueDotRotation(self._interaction, self._rotation_segments)
if rotation.valid:
return rotation
def _build_config_msg(self):
return "5,{},{},{},{},{},{}\n".format(
self.color,
int(self.square),
int(self.border),
int(self.visible),
self.col,
self.row
)
def _send_config(self):
if self._bd.is_connected:
self._bd._server.send(self._build_config_msg())
class BlueDot(Dot):
"""
Interacts with a Blue Dot client application, communicating when and where a
button has been pressed, released or held.
This class starts an instance of :class:`.btcomm.BluetoothServer`
which manages the connection with the Blue Dot client.
This class is intended for use with a Blue Dot client application.
The following example will print a message when the Blue Dot button is pressed::
from bluedot import BlueDot
bd = BlueDot()
bd.wait_for_press()
print("The button was pressed")
Multiple buttons can be created, by changing the number of columns and rows. Each button can be referenced using its [col, row]::
bd = BlueDot(cols=2, rows=2)
bd[0,0].wait_for_press()
print("Top left button pressed")
bd[1,1].wait_for_press()
print("Bottom right button pressed")
:param str device:
The Bluetooth device the server should use, the default is "hci0", if
your device only has 1 Bluetooth adapter this shouldn't need to be changed.
:param int port:
The Bluetooth port the server should use, the default is 1, and under
normal use this should never need to change.
:param bool auto_start_server:
If ``True`` (the default), the Bluetooth server will be automatically
started on initialisation; if ``False``, the method :meth:`start` will
need to be called before connections will be accepted.
:param bool power_up_device:
If ``True``, the Bluetooth device will be powered up (if required) when the
server starts. The default is ``False``.
Depending on how Bluetooth has been powered down, you may need to use :command:`rfkill`
to unblock Bluetooth to give permission to bluez to power on Bluetooth::
sudo rfkill unblock bluetooth
:param bool print_messages:
If ``True`` (the default), server status messages will be printed stating
when the server has started and when clients connect / disconnect.
:param int cols:
The number of columns in the grid of buttons. Defaults to ``1``.
:param int rows:
The number of rows in the grid of buttons. Defaults to ``1``.
"""
def __init__(self,
device = "hci0",
port = 1,
auto_start_server = True,
power_up_device = False,
print_messages = True,
cols = 1,
rows = 1):
self._data_buffer = ""
self._device = device
self._port = port
self._power_up_device = power_up_device
self._print_messages = print_messages
self._check_protocol_event = Event()
self._is_connected_event = Event()
self._when_client_connects = None
self._when_client_connects_background = False
self._when_client_disconnects = None
self._when_client_disconnects_background = False
# setup the main "dot"
super().__init__(BLUE, False, False, True)
# setup the grid
self._buttons = {}
self.resize(cols, rows)
self._create_server()
if auto_start_server:
self.start()
@property
def buttons(self):
"""
A list of :class:`BlueDotButton` objects in the "grid".
"""
return self._buttons.values()
@property
def cols(self):
"""
Sets or returns the number of columns in the grid of buttons.
"""
return self._cols
@cols.setter
def cols(self, value):
self.resize(value, self._rows)
@property
def rows(self):
"""
Sets or returns the number of rows in the grid of buttons.
"""
return self._rows
@rows.setter
def rows(self, value):
self.resize(self._cols, value)
@property
def device(self):
"""
The Bluetooth device the server is using. This defaults to "hci0".
"""
return self._device
@property
def port(self):
"""
The port the server is using. This defaults to 1.
"""
return self._port
@property
def server(self):
"""
The :class:`.btcomm.BluetoothServer` instance that is being used to communicate
with clients.
"""
return self._server
@property
def adapter(self):
"""
The :class:`.btcomm.BluetoothAdapter` instance that is being used.
"""
return self._server.adapter
@property
def paired_devices(self):
"""
Returns a sequence of devices paired with this adapter
:code:`[(mac_address, name), (mac_address, name), ...]`::
bd = BlueDot()
devices = bd.paired_devices
for d in devices:
device_address = d[0]
device_name = d[1]
"""
return self._server.adapter.paired_devices
@property
def print_messages(self):
"""
When set to ``True`` messages relating to the status of the Bluetooth server
will be printed.
"""
return self._print_messages
@print_messages.setter
def print_messages(self, value):
self._print_messages = value
@property
def running(self):
"""
Returns a ``True`` if the server is running.
"""
return self._server.running
@property
def is_connected(self):
"""
Returns ``True`` if a Blue Dot client is connected.
"""
return self._is_connected_event.is_set()
@property
def is_pressed(self):
"""
Returns ``True`` if the button is pressed (or held).
.. note::
If there are multiple buttons, if any button is pressed, `True`
will be returned.
"""
for button in self.buttons:
if button._is_pressed:
return True
return False
@property
def interaction(self):
"""
Returns an instance of :class:`BlueDotInteraction` representing the
current or last interaction with the Blue Dot.
.. note::
If the Blue Dot is released (and inactive), :attr:`interaction`
will return the interaction when it was released, until it is
pressed again. If the Blue Dot has never been pressed
:attr:`interaction` will return ``None``.
If there are multiple buttons, the interaction will only be
returned for button [0,0]
.. deprecated:: 2.0.0
"""
return self._get_button((0,0)).interaction
@property
def rotation_segments(self):
"""
Sets or returns the number of virtual segments the button is split into for rotating.
Defaults to 8.
.. note::
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).rotation_segments.fget(self)
@rotation_segments.setter
def rotation_segments(self, value):
super(BlueDot, self.__class__).rotation_segments.fset(self, value)
for button in self.buttons:
button.rotation_segments = value
@property
def double_press_time(self):
"""
Sets or returns the time threshold in seconds for a double press. Defaults to 0.3.
.. note::
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).double_press_time.fget(self)
@double_press_time.setter
def double_press_time(self, value):
super(BlueDot, self.__class__).double_press_time.fset(self, value)
for button in self.buttons:
button.double_press_time = value
@property
def color(self):
"""
Sets or returns the color of the button. Defaults to BLUE.
An instance of :class:`.colors.Color` is returned.
Value can be set as a :class:`.colors.Color` object, a hex color value
in the format `#rrggbb` or `#rrggbbaa`, a tuple of `(red, green, blue)`
or `(red, green, blue, alpha)` values between `0` & `255` or a text
description of the color, e.g. "red".
A dictionary of available colors can be obtained from `bluedot.COLORS`.
.. note::
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).color.fget(self)
@color.setter
def color(self, value):
super(BlueDot, self.__class__).color.fset(self, value)
for button in self.buttons:
button.color = value
@property
def square(self):
"""
When set to `True` the 'dot' is made square. Default is `False`.
.. note::
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).square.fget(self)
@square.setter
def square(self, value):
super(BlueDot, self.__class__).square.fset(self, value)
for button in self.buttons:
button.square = value
@property
def border(self):
"""
When set to `True` adds a border to the dot. Default is `False`.
.. note::
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).border.fget(self)
@border.setter
def border(self, value):
super(BlueDot, self.__class__).border.fset(self, value)
for button in self.buttons:
button.border = value
@property
def visible(self):
"""
When set to `False` the dot will be hidden. Default is `True`.
.. note::
Events (press, release, moved) are still sent from the dot
when it is not visible.
If there are multiple buttons in the grid, the 'default' value
will be returned and when set all buttons will be updated.
"""
return super(BlueDot, self.__class__).visible.fget(self)
@visible.setter
def visible(self, value):
super(BlueDot, self.__class__).visible.fset(self, value)
for button in self.buttons:
button.visible = value
@property
def when_client_connects(self):
"""
Sets or returns the function which is called when a Blue Dot
application connects.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_client_connects(function, background=True)`
"""
return self._when_client_connects
@when_client_connects.setter
def when_client_connects(self, value):
self.set_when_client_connects(value)
def set_when_client_connects(self, callback, background=False):
"""
Sets the function which is called when a Blue Dot connects.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_client_connects = callback
self._when_client_connects_background = background
@property
def when_client_disconnects(self):
"""
Sets or returns the function which is called when a Blue Dot disconnects.
The function will be run in the same thread and block, to run in a separate
thread use `set_when_client_disconnects(function, background=True)`
"""
return self._when_client_disconnects
@when_client_disconnects.setter
def when_client_disconnects(self, value):
self.set_when_client_disconnects(value)
def set_when_client_disconnects(self, callback, background=False):
"""
Sets the function which is called when a Blue Dot disconnects.
:param Callable callback:
The function to call, setting to `None` will stop the callback.
:param bool background:
If set to `True` the function will be run in a separate thread
and it will return immediately. The default is `False`.
"""
self._when_client_disconnects = callback
self._when_client_disconnects_background = background
def wait_for_connection(self, timeout = None):
"""
Waits until a Blue Dot client connects.
Returns ``True`` if a client connects.
:param float timeout:
Number of seconds to wait for a wait connections, if ``None`` (the default),
it will wait indefinetly for a connection from a Blue Dot client.
"""
return self._is_connected_event.wait(timeout)
def start(self):
"""
Start the :class:`.btcomm.BluetoothServer` if it is not already
running. By default the server is started at initialisation.
"""
self._server.start()
self._print_message("Server started {}".format(self.server.server_address))
self._print_message("Waiting for connection")
def _create_server(self):
self._server = BluetoothServer(
self._data_received,
when_client_connects = self._client_connected,
when_client_disconnects = self._client_disconnected,
device = self.device,
port = self.port,
power_up_device = self._power_up_device,
auto_start = False)
def stop(self):
"""
Stop the Bluetooth server.
"""
self._server.stop()
def allow_pairing(self, timeout = 60):
"""
Allow a Bluetooth device to pair with your Raspberry Pi by putting
the adapter into discoverable and pairable mode.
:param int timeout:
The time in seconds the adapter will remain pairable. If set to ``None``
the device will be discoverable and pairable indefinetly.
"""
self.server.adapter.allow_pairing(timeout = timeout)
def resize(self, cols, rows):
"""
Resizes the grid of buttons.
:param int cols:
The number of columns in the grid of buttons.
:param int rows:
The number of rows in the grid of buttons.
.. note::
Existing buttons will retain their state (color, border, etc) when
resized. New buttons will be created with the default values set
by the :class:`BlueDot`.
"""
self._cols = cols
self._rows = rows
# create new buttons
new_buttons = {}
for c in range(cols):
for r in range(rows):
# if button already exist, reuse it
if (c,r) in self._buttons.keys():
new_buttons[c,r] = self._buttons[c,r]
else:
new_buttons[c,r] = BlueDotButton(self, c, r, self._color, self._square, self._border, self._visible)
self._buttons = new_buttons
self._send_bluedot_config()
def _get_button(self, key):
try:
return self._buttons[key]
except KeyError:
raise ButtonDoesNotExist("The button `{}` does not exist".format(key))
def _client_connected(self):
self._is_connected_event.set()
self._print_message("Client connected {}".format(self.server.client_address))
self._send_bluedot_config()
if self.when_client_connects:
self._process_callback(self.when_client_connects, None, self._when_client_connects_background)
# wait for the protocol version to be checked.
if not self._check_protocol_event.wait(CHECK_PROTOCOL_TIMEOUT):
self._print_message("Protocol version not received from client - do you need to update the client to the latest version?")
self._server.disconnect_client()
def _client_disconnected(self):
self._is_connected_event.clear()
self._check_protocol_event.clear()
self._print_message("Client disconnected")
if self.when_client_disconnects:
self._process_callback(self.when_client_disconnects, None, self._when_client_disconnects_background)
def _data_received(self, data):
#add the data received to the buffer
self._data_buffer += data
#get any full commands ended by \n
last_command = self._data_buffer.rfind("\n")
if last_command != -1:
commands = self._data_buffer[:last_command].split("\n")
#remove the processed commands from the buffer
self._data_buffer = self._data_buffer[last_command + 1:]
self._process_commands(commands)
def _process_commands(self, commands):
for command in commands:
# debug - print each command
# print(command)
operation = command.split(",")[0]
params = command.split(",")[1:]
# dot change operation?
if operation in ["0", "1", "2"]:
position = None
try:
button, position = self._parse_interaction_msg(operation, params)
self._position = position
except ValueError:
# warn about the occasional corrupt command
warnings.warn("Data received which could not be parsed.\n{}".format(command))
except ButtonDoesNotExist:
# data received for a button which could not be found
warnings.warn("Data received for a button which does not exist.\n{}".format(command))
else:
# dot released
if operation == "0":
self._process_release(button, position)
# dot pressed
elif operation == "1":
self._process_press(button, position)
# dot pressed position moved
elif operation == "2":
self._process_move(button, position)
# protocol check
elif operation == "3":
self._check_protocol_version(params[0], params[1])
else:
# operation not identified...
warnings.warn("Data received for an unknown operation.\n{}".format(command))
def _parse_interaction_msg(self, operation, params):
"""
Parses an interaction (press, move, release) message and returns
the component parts
"""
# parse message
col = int(params[0])
row = int(params[1])
position = BlueDotPosition(col, row, params[2], params[3])
button = self._get_button((col, row))
return button, position
def _process_press(self, button, position):
# was the button double pressed?
if button.is_double_press(position):
self.double_press(position)
button.double_press(position)
# set the blue dot and button as pressed
self.press(position)
button.press(position)
def _process_move(self, button, position):
# set the blue dot as moved
self.move(position)
# set the button as moved
button.move(position)
# was it a rotation
rotation = button.get_rotation()
if rotation is not None:
self.rotate(rotation)
button.rotate(rotation)
def _process_release(self, button, position):
# set the blue dot as released
self.release(position)
# set the button as released
button.release(position)
# was it a swipe?
swipe = button.get_swipe()
if swipe is not None:
self.swipe(swipe)
button.swipe(swipe)
def _check_protocol_version(self, protocol_version, client_name):
try:
version_no = int(protocol_version)
except ValueError:
raise ValueError("protocol version number must be numeric, received {}.".format(protocol_version))
self._check_protocol_event.set()
if version_no != PROTOCOL_VERSION:
msg = "Client '{}' was using protocol version {}, bluedot python library is using version {}. "
if version_no > PROTOCOL_VERSION:
msg += "Update the bluedot python library, using 'sudo pip3 --upgrade install bluedot'."
msg = msg.format(client_name, protocol_version, PROTOCOL_VERSION)
else:
msg += "Update the {}."
msg = msg.format(client_name, protocol_version, PROTOCOL_VERSION, client_name)
self._server.disconnect_client()
print(msg)
# called whenever the BlueDot configuration is changed or a client connects
def _send_bluedot_config(self):
if self.is_connected:
self._server.send(
"4,{},{},{},{},{},{}\n".format(
self._color.str_rgba,
int(self._square),
int(self._border),
int(self._visible),
self._cols,
self._rows
)
)
# send the configuration for the individual buttons
button_config_msg = ""
for button in self.buttons:
if button.modified:
button_config_msg += button._build_config_msg()
if button_config_msg != "":
self._server.send(button_config_msg)
def _print_message(self, message):
if self.print_messages:
print(message)
def __getitem__(self, key):
return self._get_button(key)
================================================
FILE: bluedot/exceptions.py
================================================
class ButtonDoesNotExist(Exception):
pass
================================================
FILE: bluedot/interactions.py
================================================
from time import time
from math import atan2, degrees, hypot
class BlueDotPosition:
"""
Represents a position of where the blue dot is pressed, released or held.
:param float x:
The x position of the Blue Dot, 0 being centre, -1 being far left
and 1 being far right.
:param float y:
The y position of the Blue Dot, 0 being centre, -1 being at the
bottom and 1 being at the top.
"""
def __init__(self, col, row, x, y):
self._time = time()
self._col = int(col)
self._row = int(row)
self._x = self._clamped(float(x))
self._y = self._clamped(float(y))
self._angle = None
self._distance = None
def _clamped(self, v):
return max(-1, min(1, v))
@property
def col(self):
"""
The column.
"""
return self._col
@property
def row(self):
"""
The row.
"""
return self._row
@property
def x(self):
"""
The x position of the Blue Dot, 0 being centre, -1 being far
left and 1 being far right.
"""
return self._x
@property
def y(self):
"""
The y position of the Blue Dot, 0 being centre, -1 being at
the bottom and 1 being at the top.
"""
return self._y
@property
def angle(self):
"""
The angle from centre of where the Blue Dot is pressed, held or released.
0 degrees is up, 0..180 degrees clockwise, -180..0 degrees anti-clockwise.
"""
if self._angle is None:
self._angle = degrees(atan2(self.x, self.y))
return self._angle
@property
def distance(self):
"""
The distance from centre of where the Blue Dot is pressed, held or released.
The radius of the Blue Dot is 1.
"""
if self._distance is None:
self._distance = self._clamped(hypot(self.x, self.y))
return self._distance
@property
def middle(self):
"""
Returns ``True`` if the Blue Dot is pressed, held or released in the middle.
"""
return self.distance <= 0.5
@property
def top(self):
"""
Returns ``True`` if the Blue Dot is pressed, held or released at the top.
"""
return self.distance > 0.5 and (-45 < self.angle <= 45)
@property
def right(self):
"""
Returns ``True`` if the Blue Dot is pressed, held or released on the right.
"""
return self.distance > 0.5 and (45 < self.angle <= 135)
@property
def bottom(self):
"""
Returns ``True`` if the Blue Dot is pressed, held or released at the bottom.
"""
return self.distance > 0.5 and (self.angle > 135 or self.angle <= -135)
@property
def left(self):
"""
Returns ``True`` if the Blue Dot is pressed, held or released on the left.
"""
return self.distance > 0.5 and (-135 < self.angle <= -45)
@property
def time(self):
"""
The time the blue dot was at this position.
.. note::
This is the time the message was received from the Blue Dot app,
not the time it was sent.
"""
return self._time
def __str__(self):
return "BlueDotPosition - col={}, row={}, x={}, y={}".format(
self.col, self.row, self.x, self.y
)
class BlueDotInteraction:
"""
Represents an interaction with the Blue Dot, from when it was pressed to
when it was released.
A :class:`BlueDotInteraction` can be active or inactive, i.e. it is active
because the Blue Dot has not been released, or inactive because the Blue
Dot was released and the interaction finished.
:param BlueDotPosition pressed_position:
The BlueDotPosition when the Blue Dot was pressed.
"""
def __init__(self, pressed_position):
self._active = True
self._positions = []
self._positions.append(pressed_position)
@property
def active(self):
"""
Returns ``True`` if the interaction is still active, i.e. the Blue Dot
hasnt been released.
"""
return self._active
@property
def positions(self):
"""
A sequence of :class:`BlueDotPosition` instances for all the positions
which make up this interaction.
The first position is where the Blue Dot was pressed, the last is where
the Blue Dot was released, all position in between are where the position
Blue Dot changed (i.e. moved) when it was held down.
"""
return self._positions
@property
def pressed_position(self):
"""
Returns the position when the Blue Dot was pressed i.e. where the
interaction started.
"""
return self._positions[0]
@property
def released_position(self):
"""
Returns the position when the Blue Dot was released i.e. where the
interaction ended.
If the interaction is still active it returns ``None``.
"""
return self._positions[-1] if not self.active else None
@property
def current_position(self):
"""
Returns the current position for the interaction.
If the interaction is inactive, it will return the position when the
Blue Dot was released.
"""
return self._positions[-1]
@property
def previous_position(self):
"""
Returns the previous position for the interaction.
If the interaction contains only 1 position, None will be returned.
"""
return self._positions[-2] if len(self._positions) > 1 else None
@property
def duration(self):
"""
Returns the duration in seconds of the interaction, i.e. the amount time
between when the Blue Dot was pressed and now or when it was released.
"""
if self.active:
return time() - self.pressed_position.time
else:
return self.released_position.time - self.pressed_position.time
@property
def distance(self):
"""
Returns the total distance of the Blue Dot interaction
"""
dist = 0
for i in range(1, len(self._positions)):
p1 = self._positions[i-1]
p2 = self._positions[i]
dist += hypot(p2.x - p1.x, p2.y - p1.y)
return dist
def moved(self, moved_position):
"""
Adds an additional position to the interaction, called when the position
the Blue Dot is pressed moves.
"""
if self._active:
self._positions.append(moved_position)
def released(self, released_position):
"""
Called when the Blue Dot is released and completes a Blue Dot interaction
:param BlueDotPosition released_position:
The BlueDotPosition when the Blue Dot was released.
"""
self._active = False
self._positions.append(released_position)
class BlueDotSwipe:
"""
Represents a Blue Dot swipe interaction.
A :class:`BlueDotSwipe` can be valid or invalid based on whether the Blue Dot
interaction was a swipe or not.
:param BlueDotInteraction interaction:
The BlueDotInteraction object to be used to determine whether the interaction
was a swipe.
"""
def __init__(self, interaction):
self._interaction = interaction
self._col = interaction.current_position.col
self._col = interaction.current_position.col
self._speed_threshold = 2
self._angle = None
self._distance = None
self._valid = self._is_valid_swipe()
def _is_valid_swipe(self):
#the validity of a swipe is based on the speed of the interaction,
# so a short fast swipe is valid as well as a long slow swipe
#self._speed = self.distance / self.interaction.duration
self._speed = self.distance / self.interaction.duration
if not self.interaction.active and self._speed > self._speed_threshold:
return True
else:
return False
@property
def col(self):
"""
The column.
"""
return self.interaction.current_position.col
@property
def row(self):
"""
The row.
"""
return self.interaction.current_position.row
@property
def interaction(self):
"""
The :class:`BlueDotInteraction` object relating to this swipe.
"""
return self._interaction
@property
def valid(self):
"""
Returns ``True`` if the Blue Dot interaction is a swipe.
"""
return self._valid
@property
def distance(self):
"""
Returns the distance of the swipe (i.e. the distance between the pressed
and released positions)
"""
# should this be the total length of the swipe. All the points? It might be slow to calculate
if self._distance == None:
self._distance = hypot(
self.interaction.released_position.x - self.interaction.pressed_position.x,
self.interaction.released_position.y - self.interaction.pressed_position.y)
return self._distance
@property
def angle(self):
"""
Returns the angle of the swipe (i.e. the angle between the pressed
and released positions)
"""
if self._angle == None:
self._angle = degrees(atan2(
self.interaction.released_position.x - self.interaction.pressed_position.x,
self.interaction.released_position.y - self.interaction.pressed_position.y))
return self._angle
@property
def speed(self):
"""
Returns the speed of the swipe in Blue Dot radius / second.
"""
return self._speed
@property
def up(self):
"""
Returns ``True`` if the Blue Dot was swiped up.
"""
return self.valid and (-45 < self.angle <= 45)
@property
def down(self):
"""
Returns ``True`` if the Blue Dot was swiped down.
"""
return self.valid and (self.angle > 135 or self.angle <= -135)
@property
def left(self):
"""
Returns ``True`` if the Blue Dot was swiped left.
"""
return self.valid and (-135 < self.angle <= -45)
@property
def right(self):
"""
Returns ``True`` if the Blue Dot was swiped right.
"""
return self.valid and (45 < self.angle <= 135)
@property
def direction(self):
"""
Returns the direction ("up", "down", "left", "right") of the swipe.
If the swipe is not valid `None` is returned.
"""
if self.up:
return "up"
elif self.down:
return "down"
elif self.right:
return "right"
elif self.left:
return "left"
else:
return None
def __str__(self):
return "BlueDotSwipe - col={}, row={}, direction={}".format(
self.col, self.row, self.direction
)
class BlueDotRotation:
def __init__(self, interaction, no_of_segments):
"""
Represents a Blue Dot rotation.
A :class:`BlueDotRotation` can be valid or invalid based on whether the Blue Dot
interaction was a rotation or not.
:param BlueDotInteraction interaction:
The object to be used to determine whether the interaction
was a rotation.
"""
self._interaction = interaction
self._value = 0
self._clockwise = False
self._anti_clockwise = False
self._previous_segment = 0
self._current_segment = 0
prev_pos = interaction.previous_position
pos = interaction.current_position
# was there a previous position (i.e. the interaction has more than 2 positions)
if prev_pos != None:
# were both positions in the 'outer circle'
if prev_pos.distance > 0.5 and pos.distance > 0.5:
# what segments are the positions in
deg_per_seg = (360 / no_of_segments)
self._previous_segment = int((prev_pos.angle + 180) / deg_per_seg) + 1
self._current_segment = int((pos.angle + 180) / deg_per_seg) + 1
# were the positions in different segments
if self._previous_segment != self._current_segment:
# calculate the rotation
diff = self._previous_segment - self._current_segment
if diff != 0:
if diff == -1:
self._value = 1
elif diff == 1:
self._value = -1
elif diff == (no_of_segments - 1):
self._value = 1
elif diff == (1 - no_of_segments):
self._value = -1
@property
def col(self):
"""
The column.
"""
return self.interaction.current_position.col
@property
def row(self):
"""
The row.
"""
return self.interaction.current_position.row
@property
def valid(self):
"""
Returns ``True`` if the Blue Dot was rotated.
"""
return self._value != 0
@property
def interaction(self):
"""
The :class:`BlueDotInteraction` object relating to this rotation.
"""
return self._interaction
@property
def value(self):
"""
Returns 0 if the Blue Dot wasn't rotated, -1 if rotated anti-clockwise and 1 if rotated clockwise.
"""
return self._value
@property
def anti_clockwise(self):
"""
Returns ``True`` if the Blue Dot was rotated anti-clockwise.
"""
return self._value == -1
@property
def clockwise(self):
"""
Returns ``True`` if the Blue Dot was rotated clockwise.
"""
return self._value == 1
def __str__(self):
return "BlueDotRotation - col={}, row={}, value={}".format(
self.col, self.row, self.value
)
================================================
FILE: bluedot/mock.py
================================================
from .btcomm import BluetoothServer, BluetoothClient, BluetoothAdapter
from .dot import BlueDot
from .threads import WrapThread
from .constants import PROTOCOL_VERSION
CLIENT_NAME = "Mock client"
class MockBluetoothAdapter(BluetoothAdapter):
def __init__(self, device = "mock0", address = "00:00:00:00:00:00"):
self._device = device
self._address = address
self._powered = True
self._discoverable = False
self._pairable = False
self._pairing_thread = None
@property
def powered(self):
return self._powered
@powered.setter
def powered(self, value):
self._powered = value
@property
def discoverable(self):
return self._discoverable
@discoverable.setter
def discoverable(self, value):
self._discoverable = value
@property
def pairable(self):
return self._pairable
@pairable.setter
def pairable(self, value):
self._pairable = value
@property
def paired_devices(self):
return [["01:01:01:01:01:01", "mock_device_1"], ["02:02:02:02:02:02", "mock_device_2"]]
class MockBluetoothServer(BluetoothServer):
"""
:class:`MockBluetoothServer` inherits from
:class:`~.btcomm.BluetoothServer` but overrides ``__init__``, :meth:`start`
, :meth:`stop` and :meth:`send_raw` to create a :class:`MockBluetoothServer` which can
be used for testing and debugging.
"""
def __init__(self,
data_received_callback,
auto_start = True,
device = "mock0",
port = 1,
encoding = "utf-8",
power_up_device = False,
when_client_connects = None,
when_client_disconnects = None):
super(MockBluetoothServer, self).__init__(
data_received_callback,
auto_start,
device,
port,
encoding,
power_up_device,
when_client_connects,
when_client_disconnects)
self._mock_client = None
def start(self):
self._running = True
def stop(self):
self._running = False
def mock_client_connected(self, mock_client = None):
"""
Simulates a client connected to the :class:`~.btcomm.BluetoothServer`.
:param MockBluetoothClient mock_client:
The mock client to interact with, defaults to `None`. If `None`,
client address is set to '99:99:99:99:99:99'
"""
self._mock_client = mock_client
if not self._client_connected:
if self._mock_client is None:
client_address = "99:99:99:99:99:99"
else:
client_address = self._mock_client.adapter.address
self._client_connected = True
self._client_info = (client_address, self.port)
#call the call back
if self.when_client_connects:
WrapThread(target=self.when_client_connects).start()
def mock_client_disconnected(self):
"""
Simulates a client disconnecting from the
:class:`~.btcomm.BluetoothServer`.
"""
if self._client_connected:
self._client_connected = False
self._client_info = None
if self._when_client_disconnects:
WrapThread(target=self.when_client_disconnects).start()
def mock_client_sending_data(self, data):
"""
Simulates a client sending data to the
:class:`~.btcomm.BluetoothServer`.
"""
if self._client_connected:
self._data_received_callback(data)
def _send_data(self, data):
if self._mock_client is not None:
# call the data received callback
if self._encoding:
data = data.decode(self._encoding)
self._mock_client.mock_server_sending_data(data)
def _setup_adapter(self, device):
self._adapter = MockBluetoothAdapter(device)
class MockBluetoothClient(BluetoothClient):
"""
:class:`MockBluetoothClient` inherits from
:class:`~.btcomm.BluetoothClient` but overrides ``__init__``, :meth:`connect`
and :meth:`send_raw` to create a :class:`MockBluetoothServer` which can
be used for testing and debugging.
Note - the `server` parameter should be an instance of :class:`MockBluetoothServer`.
"""
def __init__(self,
server,
data_received_callback,
port = 1,
device = "mock1",
encoding = "utf-8",
power_up_device = False,
auto_connect = True):
super(MockBluetoothClient, self).__init__(
server,
data_received_callback,
port,
device,
encoding,
power_up_device,
auto_connect)
def connect(self):
"""
Connect to a Bluetooth server.
"""
self._server.mock_client_connected(self)
self._connected = True
def disconnect(self):
"""
Disconnect from a Bluetooth server.
"""
self._server.mock_client_disconnected()
self._connected = False
def mock_server_sending_data(self, data):
"""
Simulates a server sending data to the
:class:`~.btcomm.BluetoothClient`.
"""
if self._connected:
self._data_received_callback(data)
def _send_data(self, data):
# send data to the server
# call the data received callback
if self._encoding:
data = data.decode(self._encoding)
self._server.mock_client_sending_data(data)
def _setup_adapter(self, device):
self._adapter = MockBluetoothAdapter(device, address = "11:11:11:11:11:11")
class MockBlueDot(BlueDot):
"""
:class:`MockBlueDot` inherits from :class:`BlueDot` but overrides
:meth:`_create_server`, to create a :class:`~.mock.MockBluetoothServer`
which can be used for testing and debugging.
"""
def _create_server(self):
self._server = MockBluetoothServer(
self._data_received,
when_client_connects = self._client_connected,
when_client_disconnects = self._client_disconnected,
device = self.device,
port = self.port,
power_up_device = self._power_up_device,
auto_start = False)
def mock_client_connected(self):
"""
Simulates a client connecting to the Blue Dot.
:param string client_address:
The mock client mac address, defaults to '11:11:11:11:11:11'
"""
self._server.mock_client_connected()
# send protocol version to server
self._server.mock_client_sending_data("3,{},{}\n".format(PROTOCOL_VERSION, CLIENT_NAME))
def mock_client_disconnected(self):
"""
Simulates a client disconnecting from the Blue Dot.
"""
self._server.mock_client_disconnected()
def mock_blue_dot_pressed(self, col, row, x, y):
"""
Simulates the Blue Dot being pressed.
:param int col:
The column position of the button
:param int row:
The row position of the button
:param int x:
The x position where the button was pressed
:param int y:
The y position where the button was pressed
"""
self._server.mock_client_sending_data("1,{},{},{},{}\n".format(col, row, x, y))
def mock_blue_dot_released(self, col, row, x, y):
"""
Simulates the Blue Dot being released.
:param int col:
The column position of the button
:param int row:
The row position of the button
:param int x:
The x position where the button was released
:param int y:
The y position where the button was released
"""
self._server.mock_client_sending_data("0,{},{},{},{}\n".format(col, row, x, y))
def mock_blue_dot_moved(self, col, row, x, y):
"""
Simulates the Blue Dot being moved.
:param int col:
The column position of the button
:param int row:
The row position of the button
:param int x:
The x position where the button was moved too
:param int y:
The y position where the button was moved too
"""
self._server.mock_client_sending_data("2,{},{},{},{}\n".format(col, row, x, y))
def launch_mock_app(self):
"""
Launches a mock Blue Dot app.
The mock app reacts to mouse clicks and movement and calls the mock blue
dot methods to simulates presses.
This is useful for testing, allowing you to interact with Blue Dot without
having to script mock functions.
The mock app uses pygame which will need to be installed.
"""
self._mock_app_thread = WrapThread(target=self._launch_mock_app)
self._mock_app_thread.start()
def _launch_mock_app(self):
# imported here, so pygame is only a pre-requisite for the mock app
from .app import BlueDotClient, ButtonScreen
class MockBlueDotClient(BlueDotClient):
def _run(self):
button_screen = MockButtonScreen(self._screen, self._font, self._device, self._server, self._port, self._width, self._height)
button_screen.run()
class MockButtonScreen(ButtonScreen):
def _connect(self):
self.bt_client = MockBluetoothClient(self.server, self._data_received, device = self.device, auto_connect = True)
MockBlueDotClient("mock2", self._server, self._port, None, None, None)
================================================
FILE: bluedot/threads.py
================================================
import atexit
from threading import Thread, Event
_THREADS = set()
def _shutdown():
while _THREADS:
for t in _THREADS.copy():
t.stop()
atexit.register(_shutdown)
class WrapThread(Thread):
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
super(WrapThread, self).__init__(group, target, name, args, kwargs)
self.stopping = Event()
self.daemon = True
def start(self):
self.stopping.clear()
_THREADS.add(self)
super(WrapThread, self).start()
def stop(self):
self.stopping.set()
self.join()
def join(self):
super(WrapThread, self).join()
_THREADS.discard(self)
================================================
FILE: bluedot/utils.py
================================================
from __future__ import unicode_literals
import dbus
import time
import sys
SERVICE_NAME = "org.bluez"
ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1"
DEVICE_INTERFACE = SERVICE_NAME + ".Device1"
PROFILE_MANAGER = SERVICE_NAME + ".ProfileManager1"
def get_managed_objects():
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object(SERVICE_NAME, "/"), "org.freedesktop.DBus.ObjectManager")
return manager.GetManagedObjects()
def find_adapter(pattern=None):
return find_adapter_in_objects(get_managed_objects(), pattern)
def find_adapter_in_objects(objects, pattern=None):
bus = dbus.SystemBus()
for path, ifaces in objects.items():
adapter = ifaces.get(ADAPTER_INTERFACE)
if adapter is None:
continue
if not pattern or pattern == adapter["Address"] or path.endswith(pattern):
obj = bus.get_object(SERVICE_NAME, path)
return dbus.Interface(obj, ADAPTER_INTERFACE)
raise Exception("Bluetooth adapter {} not found".format(pattern))
def get_adapter_property(device_name, prop):
bus = dbus.SystemBus()
adapter_path = find_adapter(device_name).object_path
adapter = dbus.Interface(bus.get_object(SERVICE_NAME, adapter_path),"org.freedesktop.DBus.Properties")
return adapter.Get(ADAPTER_INTERFACE, prop)
def get_mac(device_name):
return get_adapter_property(device_name, "Address")
def get_adapter_powered_status(device_name):
powered = get_adapter_property(device_name, "Powered")
return bool(powered)
def get_adapter_discoverable_status(device_name):
discoverable = get_adapter_property(device_name, "Discoverable")
return bool(discoverable)
def get_adapter_pairable_status(device_name):
pairable = get_adapter_property(device_name, "Pairable")
return bool(pairable)
def get_paired_devices(device_name):
paired_devices = []
bus = dbus.SystemBus()
adapter_path = find_adapter(device_name).object_path
om = dbus.Interface(bus.get_object(SERVICE_NAME, "/"), "org.freedesktop.DBus.ObjectManager")
objects = om.GetManagedObjects()
for path, interfaces in objects.items():
if DEVICE_INTERFACE not in interfaces:
continue
properties = interfaces[DEVICE_INTERFACE]
if properties["Adapter"] != adapter_path:
continue
paired_devices.append((str(properties["Address"]), str(properties["Alias"])))
return paired_devices
def device_discoverable(device_name, discoverable):
bus = dbus.SystemBus()
adapter_path = find_adapter(device_name).object_path
adapter = dbus.Interface(bus.get_object(SERVICE_NAME, adapter_path),"org.freedesktop.DBus.Properties")
if discoverable:
value = dbus.Boolean(1)
else:
value = dbus.Boolean(0)
adapter.Set(ADAPTER_INTERFACE, "Discoverable", value)
def device_pairable(device_name, pairable):
bus = dbus.SystemBus()
adapter_path = find_adapter(device_name).object_path
adapter = dbus.Interface(bus.get_object(SERVICE_NAME, adapter_path),"org.freedesktop.DBus.Properties")
if pairable:
value = dbus.Boolean(1)
else:
value = dbus.Boolean(0)
adapter.Set(ADAPTER_INTERFACE, "Pairable", value)
def device_powered(device_name, powered):
bus = dbus.SystemBus()
adapter_path = find_adapter(device_name).object_path
adapter = dbus.Interface(bus.get_object(SERVICE_NAME, adapter_path),"org.freedesktop.DBus.Properties")
if powered:
value = dbus.Boolean(1)
else:
value = dbus.Boolean(0)
adapter.Set(ADAPTER_INTERFACE, "Powered", value)
def register_spp(port):
service_record = """
<?xml version="1.0" encoding="UTF-8" ?>
<record>
<attribute id="0x0001">
<sequence>
<uuid value="0x1101"/>
</sequence>
</attribute>
<attribute id="0x0004">
<sequence>
<sequence>
<uuid value="0x0100"/>
</sequence>
<sequence>
<uuid value="0x0003"/>
<uint8 value="{}" name="channel"/>
</sequence>
</sequence>
</attribute>
<attribute id="0x0100">
<text value="Serial Port" name="name"/>
</attribute>
</record>
""".format(port)
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object(SERVICE_NAME, "/org/bluez"), PROFILE_MANAGER)
path = "/bluez"
uuid = "00001101-0000-1000-8000-00805f9b34fb"
opts = {
# "AutoConnect" : True,
"ServiceRecord" : service_record
}
try:
manager.RegisterProfile(path, uuid, opts)
except dbus.exceptions.DBusException as e:
#the spp profile has already been registered, ignore
if str(e) != "org.bluez.Error.AlreadyExists: Already Exists":
raise(e)
================================================
FILE: clients/android/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: clients/android/README.rst
================================================
Blue Dot Android App
====================
The `Blue Dot app`_ is available to download from the Google Play store.
Please leave a rating and review if you find Blue Dot useful :)
|bluedotapp| |bluedotappdevices|
Start
-----
1. Download the `Blue Dot app`_ from the Google Play store.
2. If you havent already done so, pair your raspberry pi as described in the `getting started`_ guide
3. Run the Blue Dot app
|bluedotappicon|
4. Select your Raspberry Pi from the paired devices list
|bluedotappdevices|
5. Press the Dot
|bluedotapp|
.. _Blue Dot app: http://play.google.com/store/apps/details?id=com.stuffaboutcode.bluedot
.. _getting started: http://bluedot.readthedocs.io/en/latest/gettingstarted.html
.. |bluedotapp| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotandroid_small.png
:height: 247 px
:width: 144 px
:scale: 100 %
:alt: blue dot app
.. |bluedotappdevices| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotandroiddevices_small.png
:height: 246 px
:width: 144 px
:scale: 100 %
:alt: blue dot app devices list
.. |bluedotappicon| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotandroidicon.png
:height: 143 px
:width: 127 px
:scale: 100 %
:alt: blue dot icon
================================================
FILE: clients/android/app/.gitignore
================================================
/build
================================================
FILE: clients/android/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.stuffaboutcode.bluedot"
minSdkVersion 14
targetSdkVersion 33
versionCode 10
versionName "2.2.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.preference:preference:1.2.0'
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.12'
}
configurations {
all {
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
}
}
================================================
FILE: clients/android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Mart\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: clients/android/app/release/output-metadata.json
================================================
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.stuffaboutcode.bluedot",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 10,
"versionName": "2.2.1",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}
================================================
FILE: clients/android/app/src/androidTest/java/com/stuffaboutcode/bluedot/ExampleInstrumentedTest.java
================================================
package com.stuffaboutcode.bluedot;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.stuffaboutcode.bluedot", appContext.getPackageName());
}
}
================================================
FILE: clients/android/app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.stuffaboutcode.bluedot">
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />/>
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher_bluebutton"
android:label="Blue Dot"
android:roundIcon="@mipmap/ic_launcher_bluebutton"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:exported="true"
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".Devices">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.stuffaboutcode.bluedot.Devices" />
</activity>
<activity
android:exported="true"
android:name=".Devices"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Button"
android:configChanges="orientation|screenSize" />
</application>
</manifest>
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/BluetoothChatService.java
================================================
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.stuffaboutcode.bluedot;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.UUID;
import java.lang.reflect.Method;
import com.stuffaboutcode.logger.Log;
/**
* This class does all the work for setting up and managing Bluetooth
* connections with other devices. It has a thread that listens for
* incoming connections, a thread for connecting with a device, and a
* thread for performing data transmissions when connected.
*/
public class BluetoothChatService {
// Debugging
private static final String TAG = "BluetoothChatService";
// Name for the SDP record when creating server socket
private static final String NAME_SECURE = "BluetoothChatSecure";
private static final String NAME_INSECURE = "BluetoothChatInsecure";
// Unique UUID for this application
private static final UUID MY_UUID_SECURE =
UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
private static final UUID MY_UUID_INSECURE =
UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
// Member fields
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private AcceptThread mInsecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
private int mNewState;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
/**
* Constructor. Prepares a new BluetoothChat session.
*
* @param context The UI Activity Context
* @param handler A Handler to send messages back to the UI Activity
*/
public BluetoothChatService(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mNewState = mState;
mHandler = handler;
}
/**
* Update UI title according to the current state of the chat connection
*/
private synchronized void updateUserInterfaceTitle() {
mState = getState();
Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState);
mNewState = mState;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget();
}
/**
* Return the current connection state.
*/
public synchronized int getState() {
return mState;
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume()
*/
public synchronized void start() {
Log.d(TAG, "start");
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to listen on a BluetoothServerSocket
if (mSecureAcceptThread == null) {
mSecureAcceptThread = new AcceptThread(true);
mSecureAcceptThread.start();
}
if (mInsecureAcceptThread == null) {
mInsecureAcceptThread = new AcceptThread(false);
mInsecureAcceptThread.start();
}
// Update UI title
updateUserInterfaceTitle();
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
*
* @param device The BluetoothDevice to connect
* @param secure Socket Security type - Secure (true) , Insecure (false)
*/
public synchronized void connect(BluetoothDevice device, int port, boolean secure) {
Log.d(TAG, "connect to: " + device);
// Cancel any thread attempting to make a connection
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to connect with the given device
mConnectThread = new ConnectThread(device, port, secure);
mConnectThread.start();
// Update UI title
updateUserInterfaceTitle();
}
/**
* Start the ConnectedThread to begin managing a Bluetooth connection
*
* @param socket The BluetoothSocket on which the connection was made
* @param device The BluetoothDevice that has been connected
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "connected, Socket Type:" + socketType);
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Cancel the accept thread because we only want to connect to one device
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
// Update UI title
updateUserInterfaceTitle();
}
/**
* Stop all threads
*/
public synchronized void stop() {
Log.d(TAG, "stop");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
}
/**
* Write to the ConnectedThread in an unsynchronized manner
*
* @param out The bytes to write
* @see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private void connectionFailed() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(Constants.TOAST, "Unable to connect");
msg.setData(bundle);
mHandler.sendMessage(msg);
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
private void connectionLost() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(Constants.TOAST, "Connection lost");
msg.setData(bundle);
mHandler.sendMessage(msg);
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
// Start the service over to restart listening mode
//BluetoothChatService.this.start();
}
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket;
private String mSocketType;
public AcceptThread(boolean secure) {
BluetoothServerSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Create a new listening server socket
try {
if (secure) {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
MY_UUID_SECURE);
} else {
tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
NAME_INSECURE, MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
}
mmServerSocket = tmp;
mState = STATE_LISTEN;
}
public void run() {
Log.d(TAG, "Socket Type: " + mSocketType +
"BEGIN mAcceptThread" + this);
setName("AcceptThread" + mSocketType);
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
break;
}
// If a connection was accepted
if (socket != null) {
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice(),
mSocketType);
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
}
public void cancel() {
Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
}
}
}
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, int port, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
mSocketType += (Integer.toString(port));
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
// if the port is 0 use the service record to connect, otherwise connect direct
try {
if (secure) {
if (port == 0) {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
}
else {
Method createRfcommSocket =
device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
tmp = (BluetoothSocket) createRfcommSocket.invoke(device, port);
}
} else {
if (port == 0) {
tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);
}
else {
Method createInsecureRfcommSocket =
device.getClass().getMethod("createInsecureRfcommSocket", new Class[]{int.class});
tmp = (BluetoothSocket) createInsecureRfcommSocket.invoke(device, port);
}
}
} catch (IOException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
mState = STATE_CONNECTING;
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
if (mmSocket != null) {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
}
else {
// This shouldn't happen, but was reported as an ANR so sometimes it must!
connectionFailed();
return;
}
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
}
public void cancel() {
try {
if (mmSocket != null) {
mmSocket.close();
}
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}
}
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mState = STATE_CONNECTED;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
//byte[] buffer = new byte[1024];
//int bytes;
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
byte[] buffer = new byte[1024];
int bytes;
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Disconnected", e);
connectionLost();
break;
}
}
}
/**
* Write to the connected OutStream.
*
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
gitextract_s_m_z04y/
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── PRIVACYPOLICY.MD
├── README.rst
├── bluedot/
│ ├── __init__.py
│ ├── app.py
│ ├── btcomm.py
│ ├── colors.py
│ ├── constants.py
│ ├── dot.py
│ ├── exceptions.py
│ ├── interactions.py
│ ├── mock.py
│ ├── threads.py
│ └── utils.py
├── clients/
│ ├── android/
│ │ ├── .gitignore
│ │ ├── README.rst
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ ├── release/
│ │ │ │ ├── app-release.aab
│ │ │ │ ├── app-release.apk
│ │ │ │ └── output-metadata.json
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── com/
│ │ │ │ └── stuffaboutcode/
│ │ │ │ └── bluedot/
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── com/
│ │ │ │ │ └── stuffaboutcode/
│ │ │ │ │ ├── bluedot/
│ │ │ │ │ │ ├── BluetoothChatService.java
│ │ │ │ │ │ ├── Button.java
│ │ │ │ │ │ ├── Constants.java
│ │ │ │ │ │ ├── Devices.java
│ │ │ │ │ │ ├── DynamicMatrix.java
│ │ │ │ │ │ └── SettingsActivity.java
│ │ │ │ │ └── logger/
│ │ │ │ │ ├── Log.java
│ │ │ │ │ ├── LogFragment.java
│ │ │ │ │ ├── LogNode.java
│ │ │ │ │ ├── LogView.java
│ │ │ │ │ ├── LogWrapper.java
│ │ │ │ │ └── MessageOnlyLogFilter.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── round_button.xml
│ │ │ │ ├── layout/
│ │ │ │ │ ├── activity_button.xml
│ │ │ │ │ ├── activity_devices.xml
│ │ │ │ │ └── settings_activity.xml
│ │ │ │ ├── menu/
│ │ │ │ │ └── settings_menu.xml
│ │ │ │ ├── values/
│ │ │ │ │ ├── arrays.xml
│ │ │ │ │ ├── attrs.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ └── xml/
│ │ │ │ └── root_preferences.xml
│ │ │ └── test/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── stuffaboutcode/
│ │ │ └── bluedot/
│ │ │ └── ExampleUnitTest.java
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ └── python/
│ └── README.rst
├── docs/
│ ├── Makefile
│ ├── bluedotandroidapp.rst
│ ├── bluedotpythonapp.rst
│ ├── btcommapi.rst
│ ├── build.rst
│ ├── changelog.rst
│ ├── conf.py
│ ├── dotapi.rst
│ ├── examples/
│ │ ├── bt_enumerate.py
│ │ ├── bt_pair_button.py
│ │ ├── bt_pairing.py
│ │ ├── camera.py
│ │ ├── dpad.py
│ │ ├── dpad_layout.py
│ │ ├── goodbye_world.py
│ │ ├── hello_event.py
│ │ ├── hello_world.py
│ │ ├── joypad.py
│ │ ├── led1.py
│ │ ├── led2.py
│ │ ├── led3.py
│ │ ├── looks_border.py
│ │ ├── looks_color.py
│ │ ├── looks_square.py
│ │ ├── looks_visible.py
│ │ ├── many_buttons.py
│ │ ├── many_buttons_random_colors.py
│ │ ├── mock_app.py
│ │ ├── mock_script.py
│ │ ├── multiple_dots.py
│ │ ├── robot1.py
│ │ ├── robot2.py
│ │ ├── robot3.py
│ │ ├── rotation.py
│ │ ├── shout_hello.py
│ │ ├── slider_centre.py
│ │ ├── slider_dimmer.py
│ │ ├── slider_left_right.py
│ │ ├── swipe1.py
│ │ ├── swipe2.py
│ │ ├── swipe_direction.py
│ │ ├── swipe_speed_angle.py
│ │ ├── two_buttons.py
│ │ ├── two_buttons_gap.py
│ │ └── two_buttons_two_events.py
│ ├── gettingstarted.rst
│ ├── index.rst
│ ├── mockapi.rst
│ ├── pairpiandroid.rst
│ ├── pairpipi.rst
│ ├── protocol.rst
│ └── recipes.rst
├── examples/
│ ├── adapter_details.py
│ ├── click_wheel.py
│ ├── client_connects.py
│ ├── client_debug.py
│ ├── color_changer.py
│ ├── connect_multiple_apps.py
│ ├── dot_changer.py
│ ├── dot_debug.py
│ ├── dot_single_button_debug.py
│ ├── double_press.py
│ ├── dpad.py
│ ├── dpad_layout.py
│ ├── hello_world.py
│ ├── joypad.py
│ ├── many_buttons.py
│ ├── matrix_of_dots.py
│ ├── mock_app_debug.py
│ ├── mock_btcomm_debug.py
│ ├── mock_debug.py
│ ├── remote_camera.py
│ ├── server_debug.py
│ ├── simple_robot.py
│ ├── simple_variable_robot.py
│ ├── slider_centre.py
│ ├── slider_horizontal.py
│ ├── slider_led_dimmer.py
│ ├── source_robot.py
│ ├── swipe_debug.py
│ ├── threaded_callbacks.py
│ ├── two_buttons.py
│ └── wait_for_events.py
├── setup.py
└── tests/
├── test_blue_dot.py
├── test_bluetooth_adapter.py
└── test_bluetooth_comms.py
SYMBOL INDEX (649 symbols across 80 files)
FILE: bluedot/app.py
class BlueDotClient (line 20) | class BlueDotClient:
method __init__ (line 21) | def __init__(self, device, server, port, fullscreen, width, height):
method _run (line 60) | def _run(self):
class BlueDotScreen (line 71) | class BlueDotScreen:
method __init__ (line 72) | def __init__(self, screen, font, width, height):
method draw_screen (line 84) | def draw_screen(self):
method draw_close_button (line 90) | def draw_close_button(self):
method draw_error (line 102) | def draw_error(self, e):
method draw_status_message (line 107) | def draw_status_message(self, message, colour = BLUE.rgb):
method draw_text (line 113) | def draw_text(self, text, colour, start_y, antiaalias=False, backgroun...
class DevicesScreen (line 159) | class DevicesScreen(BlueDotScreen):
method __init__ (line 160) | def __init__(self, screen, font, device, port, width, height):
method draw_screen (line 167) | def draw_screen(self):
method run (line 183) | def run(self):
class ButtonScreen (line 220) | class ButtonScreen(BlueDotScreen):
method __init__ (line 221) | def __init__(self, screen, font, device, server, port, width, height):
method draw_screen (line 238) | def draw_screen(self):
method _draw_dot (line 256) | def _draw_dot(self):
method _process (line 284) | def _process(self, op, pos):
method _send_protocol_version (line 301) | def _send_protocol_version(self):
method _send_message (line 305) | def _send_message(self, message):
method _data_received (line 312) | def _data_received(self, data):
method _process_commands (line 324) | def _process_commands(self, commands):
method run (line 359) | def run(self):
method _connect (line 412) | def _connect(self):
function main (line 420) | def main():
FILE: bluedot/btcomm.py
class BluetoothAdapter (line 24) | class BluetoothAdapter:
method __init__ (line 39) | def __init__(self, device = "hci0"):
method device (line 45) | def device(self):
method address (line 52) | def address(self):
method powered (line 61) | def powered(self):
method powered (line 73) | def powered(self, value):
method discoverable (line 77) | def discoverable(self):
method discoverable (line 84) | def discoverable(self, value):
method pairable (line 88) | def pairable(self):
method pairable (line 95) | def pairable(self, value):
method paired_devices (line 99) | def paired_devices(self):
method allow_pairing (line 112) | def allow_pairing(self, timeout = 60):
method _expire_pairing (line 134) | def _expire_pairing(self, timeout):
class BluetoothServer (line 141) | class BluetoothServer:
method __init__ (line 202) | def __init__(self,
method device (line 233) | def device(self):
method adapter (line 240) | def adapter(self):
method port (line 248) | def port(self):
method encoding (line 255) | def encoding(self):
method running (line 262) | def running(self):
method server_address (line 269) | def server_address(self):
method client_address (line 278) | def client_address(self):
method client_connected (line 291) | def client_connected(self):
method data_received_callback (line 298) | def data_received_callback(self):
method data_received_callback (line 308) | def data_received_callback(self, value):
method when_client_connects (line 312) | def when_client_connects(self):
method when_client_connects (line 319) | def when_client_connects(self, value):
method when_client_disconnects (line 323) | def when_client_disconnects(self):
method when_client_disconnects (line 330) | def when_client_disconnects(self, value):
method start (line 333) | def start(self):
method stop (line 367) | def stop(self):
method send (line 376) | def send(self, data):
method _send_data (line 392) | def _send_data(self, data):
method disconnect_client (line 401) | def disconnect_client(self):
method _setup_adapter (line 417) | def _setup_adapter(self, device):
method _wait_for_connection (line 420) | def _wait_for_connection(self):
method _read (line 448) | def _read(self):
method _handle_bt_error (line 471) | def _handle_bt_error(self, bt_error):
class BluetoothClient (line 490) | class BluetoothClient():
method __init__ (line 544) | def __init__(self,
method device (line 570) | def device(self):
method server (line 577) | def server(self):
method port (line 587) | def port(self):
method adapter (line 594) | def adapter(self):
method encoding (line 602) | def encoding(self):
method client_address (line 609) | def client_address(self):
method connected (line 616) | def connected(self):
method data_received_callback (line 623) | def data_received_callback(self):
method data_received_callback (line 633) | def data_received_callback(self, value):
method connect (line 636) | def connect(self):
method disconnect (line 667) | def disconnect(self):
method send (line 685) | def send(self, data):
method _send_data (line 700) | def _send_data(self, data):
method _read (line 709) | def _read(self):
method _setup_adapter (line 727) | def _setup_adapter(self, device):
method _handle_bt_error (line 730) | def _handle_bt_error(self, bt_error):
FILE: bluedot/colors.py
class Color (line 3) | class Color:
method __init__ (line 22) | def __init__(self, red = 255, green = 255, blue = 255, alpha = 255):
method red (line 29) | def red(self):
method green (line 36) | def green(self):
method blue (line 43) | def blue(self):
method alpha (line 50) | def alpha(self):
method rgb (line 57) | def rgb(self):
method rgba (line 64) | def rgba(self):
method str_rgb (line 71) | def str_rgb(self):
method str_rgba (line 79) | def str_rgba(self):
method str_argb (line 87) | def str_argb(self):
method get_adjusted_color (line 94) | def get_adjusted_color(self, factor):
method __eq__ (line 103) | def __eq__(self, other):
method __str__ (line 107) | def __str__(self):
function parse_color (line 1250) | def parse_color(value):
FILE: bluedot/dot.py
class Dot (line 17) | class Dot:
method __init__ (line 21) | def __init__(self, color, square, border, visible):
method is_pressed (line 52) | def is_pressed(self):
method value (line 59) | def value(self):
method values (line 66) | def values(self):
method position (line 74) | def position(self):
method when_pressed (line 90) | def when_pressed(self):
method when_pressed (line 123) | def when_pressed(self, value):
method set_when_pressed (line 126) | def set_when_pressed(self, callback, background=False):
method when_double_pressed (line 141) | def when_double_pressed(self):
method when_double_pressed (line 159) | def when_double_pressed(self, value):
method set_when_double_pressed (line 162) | def set_when_double_pressed(self, callback, background=False):
method double_press_time (line 177) | def double_press_time(self):
method double_press_time (line 184) | def double_press_time(self, value):
method when_released (line 188) | def when_released(self):
method when_released (line 202) | def when_released(self, value):
method set_when_released (line 205) | def set_when_released(self, callback, background=False):
method when_moved (line 220) | def when_moved(self):
method when_moved (line 234) | def when_moved(self, value):
method set_when_moved (line 237) | def set_when_moved(self, callback, background=False):
method when_swiped (line 252) | def when_swiped(self):
method when_swiped (line 266) | def when_swiped(self, value):
method set_when_swiped (line 269) | def set_when_swiped(self, callback, background=False):
method rotation_segments (line 284) | def rotation_segments(self):
method rotation_segments (line 292) | def rotation_segments(self, value):
method when_rotated (line 296) | def when_rotated(self):
method when_rotated (line 311) | def when_rotated(self, value):
method set_when_rotated (line 314) | def set_when_rotated(self, callback, background=False):
method color (line 330) | def color(self):
method color (line 346) | def color(self, value):
method square (line 350) | def square(self):
method square (line 357) | def square(self, value):
method border (line 361) | def border(self):
method border (line 368) | def border(self, value):
method visible (line 372) | def visible(self):
method visible (line 384) | def visible(self, value):
method wait_for_press (line 387) | def wait_for_press(self, timeout = None):
method wait_for_double_press (line 398) | def wait_for_double_press(self, timeout = None):
method wait_for_release (line 409) | def wait_for_release(self, timeout = None):
method wait_for_move (line 420) | def wait_for_move(self, timeout = None):
method wait_for_swipe (line 431) | def wait_for_swipe(self, timeout = None):
method press (line 442) | def press(self, position):
method release (line 456) | def release(self, position):
method move (line 470) | def move(self, position):
method double_press (line 482) | def double_press(self, position):
method swipe (line 494) | def swipe(self, swipe):
method rotate (line 506) | def rotate(self, rotation):
method _process_callback (line 516) | def _process_callback(self, callback, arg, background):
class BlueDotButton (line 538) | class BlueDotButton(Dot):
method __init__ (line 586) | def __init__(self, bd, col, row, color, square, border, visible):
method color (line 597) | def color(self):
method color (line 601) | def color(self, value):
method square (line 606) | def square(self):
method square (line 610) | def square(self, value):
method border (line 615) | def border(self):
method border (line 619) | def border(self, value):
method visible (line 624) | def visible(self):
method visible (line 628) | def visible(self, value):
method modified (line 633) | def modified(self):
method interaction (line 646) | def interaction(self):
method press (line 660) | def press(self, position):
method release (line 672) | def release(self, position):
method move (line 683) | def move(self, position):
method is_double_press (line 694) | def is_double_press(self, position):
method get_swipe (line 717) | def get_swipe(self):
method get_rotation (line 727) | def get_rotation(self):
method _build_config_msg (line 740) | def _build_config_msg(self):
method _send_config (line 750) | def _send_config(self):
class BlueDot (line 754) | class BlueDot(Dot):
method __init__ (line 812) | def __init__(self,
method buttons (line 847) | def buttons(self):
method cols (line 854) | def cols(self):
method cols (line 861) | def cols(self, value):
method rows (line 865) | def rows(self):
method rows (line 872) | def rows(self, value):
method device (line 876) | def device(self):
method port (line 883) | def port(self):
method server (line 890) | def server(self):
method adapter (line 898) | def adapter(self):
method paired_devices (line 905) | def paired_devices(self):
method print_messages (line 919) | def print_messages(self):
method print_messages (line 927) | def print_messages(self, value):
method running (line 931) | def running(self):
method is_connected (line 938) | def is_connected(self):
method is_pressed (line 945) | def is_pressed(self):
method interaction (line 961) | def interaction(self):
method rotation_segments (line 982) | def rotation_segments(self):
method rotation_segments (line 995) | def rotation_segments(self, value):
method double_press_time (line 1001) | def double_press_time(self):
method double_press_time (line 1013) | def double_press_time(self, value):
method color (line 1019) | def color(self):
method color (line 1040) | def color(self, value):
method square (line 1046) | def square(self):
method square (line 1058) | def square(self, value):
method border (line 1064) | def border(self):
method border (line 1076) | def border(self, value):
method visible (line 1082) | def visible(self):
method visible (line 1097) | def visible(self, value):
method when_client_connects (line 1103) | def when_client_connects(self):
method when_client_connects (line 1114) | def when_client_connects(self, value):
method set_when_client_connects (line 1117) | def set_when_client_connects(self, callback, background=False):
method when_client_disconnects (line 1132) | def when_client_disconnects(self):
method when_client_disconnects (line 1142) | def when_client_disconnects(self, value):
method set_when_client_disconnects (line 1145) | def set_when_client_disconnects(self, callback, background=False):
method wait_for_connection (line 1159) | def wait_for_connection(self, timeout = None):
method start (line 1170) | def start(self):
method _create_server (line 1179) | def _create_server(self):
method stop (line 1189) | def stop(self):
method allow_pairing (line 1195) | def allow_pairing(self, timeout = 60):
method resize (line 1206) | def resize(self, cols, rows):
method _get_button (line 1239) | def _get_button(self, key):
method _client_connected (line 1245) | def _client_connected(self):
method _client_disconnected (line 1257) | def _client_disconnected(self):
method _data_received (line 1264) | def _data_received(self, data):
method _process_commands (line 1276) | def _process_commands(self, commands):
method _parse_interaction_msg (line 1318) | def _parse_interaction_msg(self, operation, params):
method _process_press (line 1331) | def _process_press(self, button, position):
method _process_move (line 1341) | def _process_move(self, button, position):
method _process_release (line 1352) | def _process_release(self, button, position):
method _check_protocol_version (line 1364) | def _check_protocol_version(self, protocol_version, client_name):
method _send_bluedot_config (line 1383) | def _send_bluedot_config(self):
method _print_message (line 1405) | def _print_message(self, message):
method __getitem__ (line 1409) | def __getitem__(self, key):
FILE: bluedot/exceptions.py
class ButtonDoesNotExist (line 1) | class ButtonDoesNotExist(Exception):
FILE: bluedot/interactions.py
class BlueDotPosition (line 4) | class BlueDotPosition:
method __init__ (line 16) | def __init__(self, col, row, x, y):
method _clamped (line 25) | def _clamped(self, v):
method col (line 29) | def col(self):
method row (line 36) | def row(self):
method x (line 43) | def x(self):
method y (line 51) | def y(self):
method angle (line 59) | def angle(self):
method distance (line 69) | def distance(self):
method middle (line 79) | def middle(self):
method top (line 86) | def top(self):
method right (line 93) | def right(self):
method bottom (line 100) | def bottom(self):
method left (line 107) | def left(self):
method time (line 114) | def time(self):
method __str__ (line 125) | def __str__(self):
class BlueDotInteraction (line 131) | class BlueDotInteraction:
method __init__ (line 143) | def __init__(self, pressed_position):
method active (line 149) | def active(self):
method positions (line 157) | def positions(self):
method pressed_position (line 169) | def pressed_position(self):
method released_position (line 177) | def released_position(self):
method current_position (line 187) | def current_position(self):
method previous_position (line 197) | def previous_position(self):
method duration (line 206) | def duration(self):
method distance (line 217) | def distance(self):
method moved (line 229) | def moved(self, moved_position):
method released (line 237) | def released(self, released_position):
class BlueDotSwipe (line 248) | class BlueDotSwipe:
method __init__ (line 259) | def __init__(self, interaction):
method _is_valid_swipe (line 268) | def _is_valid_swipe(self):
method col (line 279) | def col(self):
method row (line 286) | def row(self):
method interaction (line 293) | def interaction(self):
method valid (line 300) | def valid(self):
method distance (line 307) | def distance(self):
method angle (line 321) | def angle(self):
method speed (line 334) | def speed(self):
method up (line 341) | def up(self):
method down (line 348) | def down(self):
method left (line 355) | def left(self):
method right (line 362) | def right(self):
method direction (line 369) | def direction(self):
method __str__ (line 385) | def __str__(self):
class BlueDotRotation (line 391) | class BlueDotRotation:
method __init__ (line 392) | def __init__(self, interaction, no_of_segments):
method col (line 439) | def col(self):
method row (line 446) | def row(self):
method valid (line 453) | def valid(self):
method interaction (line 460) | def interaction(self):
method value (line 467) | def value(self):
method anti_clockwise (line 474) | def anti_clockwise(self):
method clockwise (line 481) | def clockwise(self):
method __str__ (line 487) | def __str__(self):
FILE: bluedot/mock.py
class MockBluetoothAdapter (line 8) | class MockBluetoothAdapter(BluetoothAdapter):
method __init__ (line 9) | def __init__(self, device = "mock0", address = "00:00:00:00:00:00"):
method powered (line 18) | def powered(self):
method powered (line 22) | def powered(self, value):
method discoverable (line 26) | def discoverable(self):
method discoverable (line 30) | def discoverable(self, value):
method pairable (line 34) | def pairable(self):
method pairable (line 38) | def pairable(self, value):
method paired_devices (line 42) | def paired_devices(self):
class MockBluetoothServer (line 46) | class MockBluetoothServer(BluetoothServer):
method __init__ (line 53) | def __init__(self,
method start (line 75) | def start(self):
method stop (line 78) | def stop(self):
method mock_client_connected (line 81) | def mock_client_connected(self, mock_client = None):
method mock_client_disconnected (line 102) | def mock_client_disconnected(self):
method mock_client_sending_data (line 113) | def mock_client_sending_data(self, data):
method _send_data (line 121) | def _send_data(self, data):
method _setup_adapter (line 128) | def _setup_adapter(self, device):
class MockBluetoothClient (line 132) | class MockBluetoothClient(BluetoothClient):
method __init__ (line 141) | def __init__(self,
method connect (line 159) | def connect(self):
method disconnect (line 166) | def disconnect(self):
method mock_server_sending_data (line 173) | def mock_server_sending_data(self, data):
method _send_data (line 181) | def _send_data(self, data):
method _setup_adapter (line 188) | def _setup_adapter(self, device):
class MockBlueDot (line 191) | class MockBlueDot(BlueDot):
method _create_server (line 197) | def _create_server(self):
method mock_client_connected (line 207) | def mock_client_connected(self):
method mock_client_disconnected (line 218) | def mock_client_disconnected(self):
method mock_blue_dot_pressed (line 224) | def mock_blue_dot_pressed(self, col, row, x, y):
method mock_blue_dot_released (line 242) | def mock_blue_dot_released(self, col, row, x, y):
method mock_blue_dot_moved (line 261) | def mock_blue_dot_moved(self, col, row, x, y):
method launch_mock_app (line 280) | def launch_mock_app(self):
method _launch_mock_app (line 295) | def _launch_mock_app(self):
FILE: bluedot/threads.py
function _shutdown (line 6) | def _shutdown():
class WrapThread (line 14) | class WrapThread(Thread):
method __init__ (line 15) | def __init__(self, group=None, target=None, name=None, args=(), kwargs...
method start (line 20) | def start(self):
method stop (line 25) | def stop(self):
method join (line 29) | def join(self):
FILE: bluedot/utils.py
function get_managed_objects (line 12) | def get_managed_objects():
function find_adapter (line 17) | def find_adapter(pattern=None):
function find_adapter_in_objects (line 20) | def find_adapter_in_objects(objects, pattern=None):
function get_adapter_property (line 31) | def get_adapter_property(device_name, prop):
function get_mac (line 37) | def get_mac(device_name):
function get_adapter_powered_status (line 40) | def get_adapter_powered_status(device_name):
function get_adapter_discoverable_status (line 44) | def get_adapter_discoverable_status(device_name):
function get_adapter_pairable_status (line 48) | def get_adapter_pairable_status(device_name):
function get_paired_devices (line 52) | def get_paired_devices(device_name):
function device_discoverable (line 71) | def device_discoverable(device_name, discoverable):
function device_pairable (line 81) | def device_pairable(device_name, pairable):
function device_powered (line 91) | def device_powered(device_name, powered):
function register_spp (line 101) | def register_spp(port):
FILE: clients/android/app/src/androidTest/java/com/stuffaboutcode/bluedot/ExampleInstrumentedTest.java
class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
method useAppContext (line 19) | @Test
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/BluetoothChatService.java
class BluetoothChatService (line 43) | public class BluetoothChatService {
method BluetoothChatService (line 79) | public BluetoothChatService(Context context, Handler handler) {
method updateUserInterfaceTitle (line 89) | private synchronized void updateUserInterfaceTitle() {
method getState (line 101) | public synchronized int getState() {
method start (line 109) | public synchronized void start() {
method connect (line 143) | public synchronized void connect(BluetoothDevice device, int port, boo...
method connected (line 173) | public synchronized void connected(BluetoothSocket socket, BluetoothDe...
method stop (line 216) | public synchronized void stop() {
method write (line 249) | public void write(byte[] out) {
method connectionFailed (line 264) | private void connectionFailed() {
method connectionLost (line 283) | private void connectionLost() {
class AcceptThread (line 304) | private class AcceptThread extends Thread {
method AcceptThread (line 309) | public AcceptThread(boolean secure) {
method run (line 329) | public void run() {
method cancel (line 374) | public void cancel() {
class ConnectThread (line 390) | private class ConnectThread extends Thread {
method ConnectThread (line 395) | public ConnectThread(BluetoothDevice device, int port, boolean secur...
method run (line 432) | public void run() {
method cancel (line 472) | public void cancel() {
class ConnectedThread (line 487) | private class ConnectedThread extends Thread {
method ConnectedThread (line 492) | public ConnectedThread(BluetoothSocket socket, String socketType) {
method run (line 511) | public void run() {
method write (line 540) | public void write(byte[] buffer) {
method cancel (line 552) | public void cancel() {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Button.java
class Button (line 22) | public class Button extends AppCompatActivity {
method onCreate (line 40) | @Override
method calcX (line 119) | private double calcX(DynamicMatrix.MatrixCell cell, float actual_x) {
method calcY (line 126) | private double calcY(DynamicMatrix.MatrixCell cell, float actual_y) {
method calcX (line 133) | private double calcX(View roundButton, MotionEvent event) {
method calcY (line 139) | private double calcY(View roundButton, MotionEvent event) {
method buildMessage (line 145) | private String buildMessage(String operation, int col, int row, double...
method send (line 149) | public void send(String message) {
method disconnect (line 167) | private void disconnect() {
method msg (line 175) | private void msg(String message) {
method parseData (line 180) | private void parseData(String data) {
method processMessage (line 210) | private void processMessage(String message) {
method processSetMatrixMessage (line 235) | private boolean processSetMatrixMessage(String parameters[]) {
method processSetCellMessage (line 270) | private boolean processSetCellMessage(String parameters[]) {
method convertColor (line 308) | private String convertColor(String color) {
method handleMessage (line 324) | @Override
method onBackPressed (line 381) | @Override
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Constants.java
type Constants (line 22) | public interface Constants {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Devices.java
class Devices (line 30) | public class Devices
method checkPermissions (line 48) | private void checkPermissions(){
method onCreate (line 60) | @Override
method onResume (line 92) | @Override
method onCreateOptionsMenu (line 99) | @Override
method onOptionsItemSelected (line 106) | @Override
method onSharedPreferenceChanged (line 122) | @Override
method setConnectMsg (line 130) | private void setConnectMsg() {
method setConnectMsg (line 135) | private void setConnectMsg(SharedPreferences sharedPreferences) {
method pairedDevicesList (line 148) | private void pairedDevicesList() {
method onItemClick (line 172) | public void onItemClick (AdapterView<?> av, View v, int arg2, long arg3)
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/DynamicMatrix.java
class DynamicMatrix (line 16) | class DynamicMatrix extends View {
type DynamicMatrixListener (line 30) | public interface DynamicMatrixListener {
method onPress (line 31) | public void onPress(MatrixCell cell, int pointerId, float actual_x, ...
method onMove (line 32) | public void onMove(MatrixCell cell, int pointerId, float actual_x, f...
method onRelease (line 33) | public void onRelease(MatrixCell cell, int pointerId, float actual_x...
method DynamicMatrix (line 38) | public DynamicMatrix(Context context, AttributeSet attrs) {
method setOnUseListener (line 59) | public void setOnUseListener(DynamicMatrixListener listener) {
method init (line 64) | private void init() {
method onSizeChanged (line 78) | @Override
method setupMatrix (line 93) | private void setupMatrix() {
method sizeMatrix (line 119) | private void sizeMatrix() {
method sizeCell (line 162) | private RectF sizeCell(int c, int r) {
method onDraw (line 171) | protected void onDraw(Canvas canvas) {
method onTouchEvent (line 221) | @Override
method findCellFromXY (line 308) | private MatrixCell findCellFromXY(float x, float y) {
method performClick (line 326) | @Override
method update (line 333) | public void update() {
method getCells (line 339) | public ArrayList<ArrayList<MatrixCell>> getCells() {
method getCell (line 343) | public MatrixCell getCell(int col, int row) {
method setSize (line 347) | public void setSize(int cols, int rows) {
method setColor (line 357) | public void setColor(int color) {
method setVisible (line 365) | public void setVisible(boolean value) {
method setBorder (line 373) | public void setBorder(boolean value) {
method setSquare (line 381) | public void setSquare(boolean value) {
method getCols (line 389) | public int getCols() {
method setCols (line 393) | public void setCols(int value) {
method getRows (line 397) | public int getRows() {
method setRows (line 401) | public void setRows(int value) {
class MatrixPointer (line 408) | private class MatrixPointer {
method MatrixPointer (line 414) | private MatrixPointer(int pointerId, float x, float y, MatrixCell pr...
method move (line 420) | private void move(float x, float y) {
method getPressedCell (line 424) | private MatrixCell getPressedCell() {
method getX (line 427) | private float getX() {
method getY (line 430) | private float getY() {
class MatrixCell (line 436) | public class MatrixCell {
method MatrixCell (line 442) | private MatrixCell(int col, int row, RectF bounds, boolean visible, ...
method getBounds (line 454) | public RectF getBounds() {
method setBounds (line 457) | private void setBounds(RectF value) {
method getInnerBounds (line 460) | public RectF getInnerBounds() {
method getColor (line 468) | public int getColor() {
method getMovedColor (line 471) | public int getMovedColor() {
method setColor (line 474) | public void setColor(int value) {
method getBorder (line 477) | public boolean getBorder() {
method setBorder (line 480) | public void setBorder(boolean value) {
method getVisible (line 483) | public boolean getVisible() {
method setVisible (line 486) | public void setVisible(boolean value) {
method getSquare (line 489) | public boolean getSquare() {
method setSquare (line 492) | public void setSquare(boolean value) {
method getCol (line 495) | public int getCol() {
method getRow (line 498) | public int getRow() {
method getWidth (line 501) | public float getWidth() {
method getHeight (line 504) | public float getHeight() {
method getPressed (line 507) | public boolean getPressed() {
method press (line 512) | private void press() {
method release (line 520) | private void release() {
method moved (line 528) | private void moved() {
method updateColors (line 534) | private void updateColors(int color) {
method manipulateColor (line 547) | private int manipulateColor(int color, float factor) {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/SettingsActivity.java
class SettingsActivity (line 13) | public class SettingsActivity extends AppCompatActivity {
method onCreate (line 15) | @Override
class SettingsFragment (line 33) | public static class SettingsFragment
method onCreatePreferences (line 37) | @Override
method onResume (line 44) | @Override
method onPause (line 52) | @Override
method onSharedPreferenceChanged (line 60) | @Override
method setupPreferences (line 65) | private void setupPreferences() {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/Log.java
class Log (line 26) | public class Log {
method getLogNode (line 43) | public static LogNode getLogNode() {
method setLogNode (line 50) | public static void setLogNode(LogNode node) {
method println (line 64) | public static void println(int priority, String tag, String msg, Throw...
method println (line 78) | public static void println(int priority, String tag, String msg) {
method v (line 90) | public static void v(String tag, String msg, Throwable tr) {
method v (line 100) | public static void v(String tag, String msg) {
method d (line 113) | public static void d(String tag, String msg, Throwable tr) {
method d (line 123) | public static void d(String tag, String msg) {
method i (line 135) | public static void i(String tag, String msg, Throwable tr) {
method i (line 145) | public static void i(String tag, String msg) {
method w (line 157) | public static void w(String tag, String msg, Throwable tr) {
method w (line 167) | public static void w(String tag, String msg) {
method w (line 178) | public static void w(String tag, Throwable tr) {
method e (line 190) | public static void e(String tag, String msg, Throwable tr) {
method e (line 200) | public static void e(String tag, String msg) {
method wtf (line 212) | public static void wtf(String tag, String msg, Throwable tr) {
method wtf (line 222) | public static void wtf(String tag, String msg) {
method wtf (line 233) | public static void wtf(String tag, Throwable tr) {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogFragment.java
class LogFragment (line 49) | public class LogFragment extends Fragment {
method LogFragment (line 54) | public LogFragment() {}
method inflateViews (line 56) | public View inflateViews() {
method onCreateView (line 85) | @Override
method getLogView (line 106) | public LogView getLogView() {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogNode.java
type LogNode (line 27) | public interface LogNode {
method println (line 37) | public void println(int priority, String tag, String msg, Throwable tr);
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogView.java
class LogView (line 25) | public class LogView extends TextView implements LogNode {
method LogView (line 27) | public LogView(Context context) {
method LogView (line 31) | public LogView(Context context, AttributeSet attrs) {
method LogView (line 35) | public LogView(Context context, AttributeSet attrs, int defStyle) {
method println (line 47) | @Override
method getNext (line 108) | public LogNode getNext() {
method setNext (line 112) | public void setNext(LogNode node) {
method appendIfNotNull (line 125) | private StringBuilder appendIfNotNull(StringBuilder source, String add...
method appendToLog (line 140) | public void appendToLog(String s) {
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogWrapper.java
class LogWrapper (line 24) | public class LogWrapper implements LogNode {
method getNext (line 32) | public LogNode getNext() {
method setNext (line 39) | public void setNext(LogNode node) {
method println (line 51) | @Override
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/MessageOnlyLogFilter.java
class MessageOnlyLogFilter (line 23) | public class MessageOnlyLogFilter implements LogNode {
method MessageOnlyLogFilter (line 32) | public MessageOnlyLogFilter(LogNode next) {
method MessageOnlyLogFilter (line 36) | public MessageOnlyLogFilter() {
method println (line 39) | @Override
method getNext (line 49) | public LogNode getNext() {
method setNext (line 56) | public void setNext(LogNode node) {
FILE: clients/android/app/src/test/java/com/stuffaboutcode/bluedot/ExampleUnitTest.java
class ExampleUnitTest (line 12) | public class ExampleUnitTest {
method addition_isCorrect (line 13) | @Test
FILE: docs/conf.py
class Mock (line 28) | class Mock:
method __init__ (line 30) | def __init__(self, *args, **kw): pass
method __call__ (line 31) | def __call__(self, *args, **kw): return Mock()
method __mul__ (line 32) | def __mul__(self, other): return Mock()
method __and__ (line 33) | def __and__(self, other): return Mock()
method __bool__ (line 34) | def __bool__(self): return False
method __nonzero__ (line 35) | def __nonzero__(self): return False
method __getattr__ (line 37) | def __getattr__(cls, name):
FILE: docs/examples/camera.py
function take_picture (line 8) | def take_picture():
FILE: docs/examples/dpad.py
function dpad (line 4) | def dpad(pos):
FILE: docs/examples/dpad_layout.py
function up (line 4) | def up():
function down (line 7) | def down():
function left (line 10) | def left():
function right (line 13) | def right():
FILE: docs/examples/goodbye_world.py
function say_hello (line 4) | def say_hello():
function say_goodbye (line 7) | def say_goodbye():
FILE: docs/examples/hello_event.py
function say_hello (line 4) | def say_hello():
FILE: docs/examples/joypad.py
function up (line 4) | def up():
function down (line 7) | def down():
function left (line 10) | def left():
function right (line 13) | def right():
function button_A (line 16) | def button_A():
function button_B (line 19) | def button_B():
FILE: docs/examples/many_buttons.py
function pressed (line 4) | def pressed(pos):
FILE: docs/examples/many_buttons_random_colors.py
function pressed (line 5) | def pressed(pos):
FILE: docs/examples/mock_app.py
function say_hello (line 4) | def say_hello():
FILE: docs/examples/mock_script.py
function say_hello (line 3) | def say_hello():
FILE: docs/examples/multiple_dots.py
function bd1_pressed (line 4) | def bd1_pressed():
function bd2_pressed (line 7) | def bd2_pressed():
FILE: docs/examples/robot1.py
function move (line 8) | def move(pos):
function stop (line 18) | def stop():
FILE: docs/examples/robot2.py
function move (line 8) | def move(pos):
function stop (line 18) | def stop():
FILE: docs/examples/robot3.py
function pos_to_values (line 5) | def pos_to_values(x, y):
function clamped (line 10) | def clamped(v):
function drive (line 13) | def drive():
FILE: docs/examples/rotation.py
function rotated (line 6) | def rotated(rotation):
FILE: docs/examples/shout_hello.py
function shout_hello (line 4) | def shout_hello():
FILE: docs/examples/slider_centre.py
function show_percentage (line 4) | def show_percentage(pos):
FILE: docs/examples/slider_dimmer.py
function set_brightness (line 5) | def set_brightness(pos):
FILE: docs/examples/slider_left_right.py
function show_percentage (line 4) | def show_percentage(pos):
FILE: docs/examples/swipe2.py
function swiped (line 4) | def swiped():
FILE: docs/examples/swipe_direction.py
function swiped (line 4) | def swiped(swipe):
FILE: docs/examples/swipe_speed_angle.py
function swiped (line 4) | def swiped(swipe):
FILE: docs/examples/two_buttons.py
function pressed (line 4) | def pressed(pos):
FILE: docs/examples/two_buttons_gap.py
function pressed (line 4) | def pressed(pos):
FILE: docs/examples/two_buttons_two_events.py
function pressed_1 (line 4) | def pressed_1(pos):
function pressed_2 (line 7) | def pressed_2(pos):
FILE: examples/click_wheel.py
function rotated (line 6) | def rotated(rotation):
FILE: examples/client_connects.py
function client_connected (line 4) | def client_connected():
function client_disconnected (line 7) | def client_disconnected():
FILE: examples/client_debug.py
function data_received (line 6) | def data_received(data):
FILE: examples/color_changer.py
function on (line 6) | def on(pos):
function off (line 11) | def off():
FILE: examples/connect_multiple_apps.py
function bd1_pressed (line 4) | def bd1_pressed():
function bd2_pressed (line 7) | def bd2_pressed():
FILE: examples/dot_changer.py
function change_dot (line 6) | def change_dot(pos):
FILE: examples/dot_debug.py
function pressed (line 8) | def pressed(pos):
function pressed_two (line 11) | def pressed_two(pos):
function released (line 14) | def released():
function moved (line 17) | def moved(pos):
function swiped (line 20) | def swiped(swipe):
function double_presed (line 23) | def double_presed(pos):
function client_connected (line 26) | def client_connected():
function client_disconnected (line 29) | def client_disconnected():
function rotated (line 32) | def rotated(rotation):
FILE: examples/dot_single_button_debug.py
function pressed (line 7) | def pressed(pos):
function released (line 10) | def released():
function moved (line 13) | def moved(pos):
function swiped (line 16) | def swiped(swipe):
function double_presed (line 19) | def double_presed(pos):
function client_connected (line 22) | def client_connected():
function client_disconnected (line 25) | def client_disconnected():
FILE: examples/double_press.py
function double_pressed (line 5) | def double_pressed():
FILE: examples/dpad.py
function dpad (line 4) | def dpad(pos):
FILE: examples/dpad_layout.py
function up (line 4) | def up():
function down (line 7) | def down():
function left (line 10) | def left():
function right (line 13) | def right():
FILE: examples/joypad.py
function up (line 4) | def up():
function down (line 7) | def down():
function left (line 10) | def left():
function right (line 13) | def right():
function button_A (line 16) | def button_A():
function button_B (line 19) | def button_B():
FILE: examples/many_buttons.py
function pressed (line 4) | def pressed(pos):
FILE: examples/matrix_of_dots.py
function pressed (line 8) | def pressed(pos):
function moved (line 11) | def moved(pos):
function released (line 14) | def released(pos):
function double_press (line 17) | def double_press(pos):
function swipe (line 20) | def swipe(swipe):
function rotation (line 23) | def rotation(rotation):
function increase_matrix (line 26) | def increase_matrix():
function change_color (line 30) | def change_color():
FILE: examples/mock_app_debug.py
function pressed (line 6) | def pressed(pos):
function released (line 9) | def released():
function moved (line 13) | def moved(pos):
FILE: examples/mock_btcomm_debug.py
function s_client_connected (line 3) | def s_client_connected():
function s_data_received (line 6) | def s_data_received(data):
function c_data_received (line 15) | def c_data_received(data):
FILE: examples/mock_debug.py
function pressed (line 6) | def pressed(pos):
function released (line 9) | def released():
function moved (line 13) | def moved(pos):
FILE: examples/remote_camera.py
function take_picture (line 8) | def take_picture():
FILE: examples/server_debug.py
function data_received (line 5) | def data_received(data):
function client_connected (line 9) | def client_connected():
function client_disconnected (line 12) | def client_disconnected():
FILE: examples/simple_robot.py
function move (line 8) | def move(pos):
function stop (line 18) | def stop():
FILE: examples/simple_variable_robot.py
function move (line 8) | def move(pos):
function stop (line 18) | def stop():
FILE: examples/slider_centre.py
function show_percentage (line 4) | def show_percentage(pos):
FILE: examples/slider_horizontal.py
function show_percentage (line 4) | def show_percentage(pos):
FILE: examples/slider_led_dimmer.py
function set_brightness (line 5) | def set_brightness(pos):
FILE: examples/source_robot.py
function pos_to_values (line 5) | def pos_to_values(x, y):
function clamped (line 10) | def clamped(v):
function drive (line 13) | def drive():
FILE: examples/swipe_debug.py
function released (line 10) | def released():
function valid_swipe (line 15) | def valid_swipe(swipe):
FILE: examples/threaded_callbacks.py
function pressed (line 6) | def pressed():
function released (line 11) | def released():
FILE: examples/two_buttons.py
function pressed (line 4) | def pressed(pos):
FILE: tests/test_blue_dot.py
function test_default_values (line 7) | def test_default_values():
function test_modify_values (line 29) | def test_modify_values():
function test_start_stop (line 51) | def test_start_stop():
function test_connect_disconnect (line 59) | def test_connect_disconnect():
function test_when_connect_disconnect (line 68) | def test_when_connect_disconnect():
function test_when_connect_disconnect_background (line 85) | def test_when_connect_disconnect_background():
function test_resize (line 105) | def test_resize():
function test_pressed_moved_released (line 113) | def test_pressed_moved_released():
function test_double_press (line 165) | def test_double_press():
function test_when_pressed_moved_released (line 229) | def test_when_pressed_moved_released():
function test_when_pressed_moved_released_background (line 274) | def test_when_pressed_moved_released_background():
function test_position (line 332) | def test_position():
function test_interaction (line 385) | def test_interaction():
function test_swipe (line 434) | def test_swipe():
function test_callback_in_class (line 516) | def test_callback_in_class():
function test_rotation (line 547) | def test_rotation():
function test_button_does_not_exist (line 601) | def test_button_does_not_exist():
function test_allow_pairing (line 607) | def test_allow_pairing():
function test_dot_appearance (line 616) | def test_dot_appearance():
function test_dot_colors (line 666) | def test_dot_colors():
function delay_function (line 697) | def delay_function(func, time):
function _delayed_function (line 701) | def _delayed_function(func, time):
FILE: tests/test_bluetooth_adapter.py
function test_bluetooth_adapter (line 10) | def test_bluetooth_adapter():
FILE: tests/test_bluetooth_comms.py
function test_server_default_values (line 27) | def test_server_default_values():
function test_server_alt_values (line 43) | def test_server_alt_values():
function test_server_start_stop (line 71) | def test_server_start_stop():
function test_client_default_values (line 81) | def test_client_default_values():
function test_client_alt_values (line 99) | def test_client_alt_values():
function test_client_connect_disconnect (line 121) | def test_client_connect_disconnect():
function test_send_receive (line 148) | def test_send_receive():
function test_volume_data (line 172) | def test_volume_data():
Condensed preview — 148 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (414K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 71,
"preview": "# These are supported funding model platforms\n\ngithub: [martinohanlon]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 721,
"preview": "---\nname: Bug report\nabout: Having trouble with BlueDot?\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nFill in the details or"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 288,
"preview": "---\nname: Feature request\nabout: Got an idea for BlueDot?\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the featur"
},
{
"path": ".gitignore",
"chars": 324,
"preview": "*.py[cdo]\npythonhosted/\n.pytest_cache/\n\n# Editor detritus\n*.vim\n*.swp\ntags\n.vscode\n.idea\n\n# Packaging detritus\n*.egg\n*.e"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "MIT License\n\nCopyright (c) 2017 Martin O'Hanlon\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "PRIVACYPOLICY.MD",
"chars": 762,
"preview": "# BlueDot Privacy Policy\n\nThe BlueDot Python library and Android app do not store your personal data. That is it!\n\nThere"
},
{
"path": "README.rst",
"chars": 4721,
"preview": "Blue Dot\n========\n\n|pypibadge| |docsbadge|\n\n.. raw:: html\n\n <iframe allowtransparency=\"true\" style=\"background-color:"
},
{
"path": "bluedot/__init__.py",
"chars": 190,
"preview": "from .dot import BlueDot, BlueDotButton\nfrom .interactions import BlueDotInteraction, BlueDotPosition, BlueDotRotation, "
},
{
"path": "bluedot/app.py",
"chars": 15700,
"preview": "import os\nos.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'\n\nfrom argparse import ArgumentParser\nimport pygame\nimport sys\nf"
},
{
"path": "bluedot/btcomm.py",
"chars": 23725,
"preview": "from __future__ import unicode_literals\n\nimport socket\nimport sys\nimport errno\n\nfrom .utils import (\n register_spp,\n "
},
{
"path": "bluedot/colors.py",
"chars": 43112,
"preview": "# color codes obtained from https://www.webucator.com/blog/2015/03/python-color-constants-module/\r\n\r\nclass Color:\r\n \""
},
{
"path": "bluedot/constants.py",
"chars": 48,
"preview": "PROTOCOL_VERSION = 2\r\nCHECK_PROTOCOL_TIMEOUT = 2"
},
{
"path": "bluedot/dot.py",
"chars": 49183,
"preview": "from __future__ import division\r\n\r\nimport sys\r\nimport warnings\r\nfrom time import sleep, time\r\nfrom threading import Even"
},
{
"path": "bluedot/exceptions.py",
"chars": 46,
"preview": "class ButtonDoesNotExist(Exception):\r\n pass"
},
{
"path": "bluedot/interactions.py",
"chars": 14878,
"preview": "from time import time\r\nfrom math import atan2, degrees, hypot\r\n\r\nclass BlueDotPosition:\r\n \"\"\"\r\n Represents a posit"
},
{
"path": "bluedot/mock.py",
"chars": 9723,
"preview": "from .btcomm import BluetoothServer, BluetoothClient, BluetoothAdapter\nfrom .dot import BlueDot\nfrom .threads import Wra"
},
{
"path": "bluedot/threads.py",
"chars": 711,
"preview": "import atexit\nfrom threading import Thread, Event\n\n_THREADS = set()\n\ndef _shutdown():\n while _THREADS:\n for t "
},
{
"path": "bluedot/utils.py",
"chars": 4787,
"preview": "from __future__ import unicode_literals\n\nimport dbus\nimport time\nimport sys\n\nSERVICE_NAME = \"org.bluez\"\nADAPTER_INTERFAC"
},
{
"path": "clients/android/.gitignore",
"chars": 127,
"preview": "*.iml\r\n.gradle\r\n/local.properties\r\n/.idea/workspace.xml\r\n/.idea/libraries\r\n.DS_Store\r\n/build\r\n/captures\r\n.externalNative"
},
{
"path": "clients/android/README.rst",
"chars": 1349,
"preview": "Blue Dot Android App\n====================\n\nThe `Blue Dot app`_ is available to download from the Google Play store.\n\nPle"
},
{
"path": "clients/android/app/.gitignore",
"chars": 8,
"preview": "/build\r\n"
},
{
"path": "clients/android/app/build.gradle",
"chars": 1140,
"preview": "apply plugin: 'com.android.application'\r\n\r\nandroid {\r\n compileSdkVersion 33\r\n defaultConfig {\r\n application"
},
{
"path": "clients/android/app/proguard-rules.pro",
"chars": 966,
"preview": "# Add project specific ProGuard rules here.\r\n# By default, the flags in this file are appended to flags specified\r\n# in "
},
{
"path": "clients/android/app/release/output-metadata.json",
"chars": 383,
"preview": "{\n \"version\": 3,\n \"artifactType\": {\n \"type\": \"APK\",\n \"kind\": \"Directory\"\n },\n \"applicationId\": \"com.stuffabout"
},
{
"path": "clients/android/app/src/androidTest/java/com/stuffaboutcode/bluedot/ExampleInstrumentedTest.java",
"chars": 792,
"preview": "package com.stuffaboutcode.bluedot;\r\n\r\nimport android.content.Context;\r\nimport androidx.test.platform.app.Instrumentatio"
},
{
"path": "clients/android/app/src/main/AndroidManifest.xml",
"chars": 1807,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n packag"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/BluetoothChatService.java",
"chars": 20015,
"preview": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Button.java",
"chars": 13885,
"preview": "package com.stuffaboutcode.bluedot;\r\n\r\nimport android.graphics.Color;\r\nimport android.os.Handler;\r\nimport android.os.Mes"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Constants.java",
"chars": 1495,
"preview": "/*\r\n * Copyright (C) 2014 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/Devices.java",
"chars": 6462,
"preview": "package com.stuffaboutcode.bluedot;\r\n\r\nimport androidx.appcompat.app.AppCompatActivity;\r\n\r\nimport android.content.pm.Pac"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/DynamicMatrix.java",
"chars": 17977,
"preview": "package com.stuffaboutcode.bluedot;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport andro"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/bluedot/SettingsActivity.java",
"chars": 2675,
"preview": "package com.stuffaboutcode.bluedot;\n\nimport android.os.Bundle;\n\nimport androidx.appcompat.app.ActionBar;\nimport androidx"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/Log.java",
"chars": 8662,
"preview": "/*\r\n * Copyright (C) 2013 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/LogFragment.java",
"chars": 3986,
"preview": "/*\r\n* Copyright 2013 The Android Open Source Project\r\n*\r\n* Licensed under the Apache License, Version 2.0 (the \"License\""
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/LogNode.java",
"chars": 1840,
"preview": "/*\r\n * Copyright (C) 2012 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/LogView.java",
"chars": 5256,
"preview": "/*\r\n * Copyright (C) 2013 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/LogWrapper.java",
"chars": 2836,
"preview": "/*\r\n * Copyright (C) 2012 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/java/com/stuffaboutcode/logger/MessageOnlyLogFilter.java",
"chars": 1751,
"preview": "/*\r\n * Copyright (C) 2013 The Android Open Source Project\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"L"
},
{
"path": "clients/android/app/src/main/res/drawable/round_button.xml",
"chars": 395,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n<item and"
},
{
"path": "clients/android/app/src/main/res/layout/activity_button.xml",
"chars": 2132,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas"
},
{
"path": "clients/android/app/src/main/res/layout/activity_devices.xml",
"chars": 1947,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas"
},
{
"path": "clients/android/app/src/main/res/layout/settings_activity.xml",
"chars": 315,
"preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n and"
},
{
"path": "clients/android/app/src/main/res/menu/settings_menu.xml",
"chars": 439,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n xmlns:android=\"http"
},
{
"path": "clients/android/app/src/main/res/values/arrays.xml",
"chars": 787,
"preview": "<resources>\n <string-array name=\"ports\">\n <item>1</item>\n <item>2</item>\n <item>3</item>\n "
},
{
"path": "clients/android/app/src/main/res/values/attrs.xml",
"chars": 225,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"DynamicMatrix\">\n <attr name=\"rows"
},
{
"path": "clients/android/app/src/main/res/values/colors.xml",
"chars": 430,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n <color name=\"colorPrimary\">#3F51B5</color>\r\n <color name=\"co"
},
{
"path": "clients/android/app/src/main/res/values/dimens.xml",
"chars": 70,
"preview": "<resources>\r\n <dimen name=\"fab_margin\">16dp</dimen>\r\n</resources>\r\n"
},
{
"path": "clients/android/app/src/main/res/values/strings.xml",
"chars": 876,
"preview": "<resources>\r\n <string name=\"app_name\">BlueButton</string>\r\n <string name=\"title_activity_button\">Button</string>\r\n"
},
{
"path": "clients/android/app/src/main/res/values/styles.xml",
"chars": 728,
"preview": "<resources>\r\n\r\n <!-- Base application theme. -->\r\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkAction"
},
{
"path": "clients/android/app/src/main/res/xml/root_preferences.xml",
"chars": 1389,
"preview": "<!--\n ~ Copyright 2018 The app Open Source Project\n ~\n ~ Licensed under the Apache License, Version 2.0 (the \"License"
},
{
"path": "clients/android/app/src/test/java/com/stuffaboutcode/bluedot/ExampleUnitTest.java",
"chars": 420,
"preview": "package com.stuffaboutcode.bluedot;\r\n\r\nimport org.junit.Test;\r\n\r\nimport static org.junit.Assert.*;\r\n\r\n/**\r\n * Example lo"
},
{
"path": "clients/android/build.gradle",
"chars": 559,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\r\n\r\nbuildscript {\r\n "
},
{
"path": "clients/android/gradle/wrapper/gradle-wrapper.properties",
"chars": 236,
"preview": "#Fri Dec 23 09:57:24 GMT 2022\r\ndistributionBase=GRADLE_USER_HOME\r\ndistributionUrl=https\\://services.gradle.org/distribut"
},
{
"path": "clients/android/gradle.properties",
"chars": 802,
"preview": "# Project-wide Gradle settings.\r\n\r\n# IDE (e.g. Android Studio) users:\r\n# Gradle settings configured through the IDE *wil"
},
{
"path": "clients/android/gradlew",
"chars": 4971,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "clients/android/gradlew.bat",
"chars": 2404,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "clients/android/settings.gradle",
"chars": 16,
"preview": "include ':app'\r\n"
},
{
"path": "clients/python/README.rst",
"chars": 1844,
"preview": "Blue Dot Python App\n===================\n\nBlue Dot Python app allows you to use another Raspberry Pi (or linux based comp"
},
{
"path": "docs/Makefile",
"chars": 604,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHI"
},
{
"path": "docs/bluedotandroidapp.rst",
"chars": 951,
"preview": "Blue Dot Android App\n====================\n\nThe `Blue Dot app`_ is available to download from the Google Play store.\n\nPle"
},
{
"path": "docs/bluedotpythonapp.rst",
"chars": 1455,
"preview": "Blue Dot Python App\n===================\n\nBlue Dot Python app allows you to use another Raspberry Pi (or linux based comp"
},
{
"path": "docs/btcommapi.rst",
"chars": 544,
"preview": "Bluetooth Comm API\n==================\n\nBlue Dot also contains a useful :mod:`btcomm` API for sending and\nreceiving data "
},
{
"path": "docs/build.rst",
"chars": 904,
"preview": "Build\r\n=====\r\n\r\nThese are instructions for how to develop, build and deploy Blue Dot.\r\n\r\nDevelop\r\n-------\r\n\r\nInstall / u"
},
{
"path": "docs/changelog.rst",
"chars": 5437,
"preview": "Change log\n==========\n\n.. currentmodule:: bluedot\n\nBluedot Python library\n----------------------\n\n2.0.0 - 2020-11-01\n~~~"
},
{
"path": "docs/conf.py",
"chars": 6442,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# bluedot documentation build configuration file, created by\n# sphinx-q"
},
{
"path": "docs/dotapi.rst",
"chars": 407,
"preview": "Blue Dot API\n============\n\n.. module:: bluedot\n\nBlueDot\n-------\n\n.. autoclass:: BlueDot\n\nBlueDotButton\n-------------\n\n.."
},
{
"path": "docs/examples/bt_enumerate.py",
"chars": 139,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\n\ndevices = bd.paired_devices\nfor d in devices:\n device_address = d[0]\n "
},
{
"path": "docs/examples/bt_pair_button.py",
"chars": 166,
"preview": "from bluedot import BlueDot\nfrom gpiozero import Button\nfrom signal import pause\n\nbd = BlueDot()\nbutton = Button(27)\n\nbu"
},
{
"path": "docs/examples/bt_pairing.py",
"chars": 97,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\nbd = BlueDot()\nbd.allow_pairing()\n\npause()\n"
},
{
"path": "docs/examples/camera.py",
"chars": 205,
"preview": "from bluedot import BlueDot\nfrom picamera import PiCamera\nfrom signal import pause\n\nbd = BlueDot()\ncam = PiCamera()\n\ndef"
},
{
"path": "docs/examples/dpad.py",
"chars": 323,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef dpad(pos):\n if pos.top:\n print(\"up\")\n elif pos.bo"
},
{
"path": "docs/examples/dpad_layout.py",
"chars": 511,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef up():\r\n print(\"up\")\r\n\r\ndef down():\r\n print(\"down\")\r\n\r"
},
{
"path": "docs/examples/goodbye_world.py",
"chars": 221,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef say_hello():\n print(\"Hello World\")\n\ndef say_goodbye():\n "
},
{
"path": "docs/examples/hello_event.py",
"chars": 149,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef say_hello():\n print(\"Hello World\")\n\nbd = BlueDot()\nbd.when_"
},
{
"path": "docs/examples/hello_world.py",
"chars": 84,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.wait_for_press()\nprint(\"Hello World\")\n"
},
{
"path": "docs/examples/joypad.py",
"chars": 851,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef up():\r\n print(\"up\")\r\n\r\ndef down():\r\n print(\"down\")\r\n\r"
},
{
"path": "docs/examples/led1.py",
"chars": 156,
"preview": "import os\nfrom bluedot import BlueDot\nfrom gpiozero import LED\n\nbd = BlueDot()\nled = LED(27)\n\nbd.wait_for_press()\nled.on"
},
{
"path": "docs/examples/led2.py",
"chars": 170,
"preview": "from bluedot import BlueDot\nfrom gpiozero import LED\nfrom signal import pause\n\nbd = BlueDot()\nled = LED(27)\n\nbd.when_pre"
},
{
"path": "docs/examples/led3.py",
"chars": 141,
"preview": "from bluedot import BlueDot\nfrom gpiozero import LED\nfrom signal import pause\n\nbd = BlueDot()\nled = LED(27)\n\nled.source "
},
{
"path": "docs/examples/looks_border.py",
"chars": 59,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.border = True"
},
{
"path": "docs/examples/looks_color.py",
"chars": 59,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.color = \"red\""
},
{
"path": "docs/examples/looks_square.py",
"chars": 59,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.square = True"
},
{
"path": "docs/examples/looks_visible.py",
"chars": 61,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.visible = False"
},
{
"path": "docs/examples/many_buttons.py",
"chars": 204,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print(\"button {}.{} pressed\".format(pos."
},
{
"path": "docs/examples/many_buttons_random_colors.py",
"chars": 318,
"preview": "from bluedot import BlueDot, COLORS\r\nfrom random import choice\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print"
},
{
"path": "docs/examples/mock_app.py",
"chars": 178,
"preview": "from bluedot import MockBlueDot\nfrom signal import pause\n\ndef say_hello():\n print(\"Hello World\")\n\nbd = MockBlueDot()\n"
},
{
"path": "docs/examples/mock_script.py",
"chars": 181,
"preview": "from bluedot import MockBlueDot\n\ndef say_hello():\n print(\"Hello World\")\n\nbd = MockBlueDot()\nbd.when_pressed = say_hel"
},
{
"path": "docs/examples/multiple_dots.py",
"chars": 276,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef bd1_pressed():\n print(\"BlueDot 1 pressed\")\n\ndef bd2_pressed"
},
{
"path": "docs/examples/robot1.py",
"chars": 433,
"preview": "from bluedot import BlueDot\nfrom gpiozero import Robot\nfrom signal import pause\n\nbd = BlueDot()\nrobot = Robot(left=(17, "
},
{
"path": "docs/examples/robot2.py",
"chars": 493,
"preview": "from bluedot import BlueDot\nfrom gpiozero import Robot\nfrom signal import pause\n\nbd = BlueDot()\nrobot = Robot(left=(lfpi"
},
{
"path": "docs/examples/robot3.py",
"chars": 550,
"preview": "from gpiozero import Robot\nfrom bluedot import BlueDot\nfrom signal import pause\n\ndef pos_to_values(x, y):\n left = y i"
},
{
"path": "docs/examples/rotation.py",
"chars": 322,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ncount = 0\n\ndef rotated(rotation):\n global count\n count += ro"
},
{
"path": "docs/examples/shout_hello.py",
"chars": 154,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef shout_hello():\n print(\"HELLO\")\n\nbd = BlueDot()\nbd.when_doub"
},
{
"path": "docs/examples/slider_centre.py",
"chars": 219,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef show_percentage(pos):\n percentage = round(pos.distance * 10"
},
{
"path": "docs/examples/slider_dimmer.py",
"chars": 240,
"preview": "from bluedot import BlueDot\nfrom gpiozero import PWMLED\nfrom signal import pause\n\ndef set_brightness(pos):\n brightnes"
},
{
"path": "docs/examples/slider_left_right.py",
"chars": 252,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef show_percentage(pos):\n horizontal = ((pos.x + 1) / 2)\n p"
},
{
"path": "docs/examples/swipe1.py",
"chars": 88,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\nbd.wait_for_swipe()\nprint(\"Blue Dot swiped\")\n"
},
{
"path": "docs/examples/swipe2.py",
"chars": 146,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef swiped():\n print(\"Blue Dot swiped\")\n\nbd = BlueDot()\nbd.when"
},
{
"path": "docs/examples/swipe_direction.py",
"chars": 290,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef swiped(swipe):\n if swipe.up:\n print(\"up\")\n elif s"
},
{
"path": "docs/examples/swipe_speed_angle.py",
"chars": 274,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef swiped(swipe):\n print(\"Swiped\")\n print(\"speed={}\".format"
},
{
"path": "docs/examples/two_buttons.py",
"chars": 196,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print(\"button {}.{} pressed\".format(pos."
},
{
"path": "docs/examples/two_buttons_gap.py",
"chars": 229,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print(\"button {}.{} pressed\".format(pos."
},
{
"path": "docs/examples/two_buttons_two_events.py",
"chars": 274,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed_1(pos):\r\n print(\"button 1 pressed\")\r\n\r\ndef press"
},
{
"path": "docs/gettingstarted.rst",
"chars": 2200,
"preview": "Getting Started\n===============\n\nIn order to use Blue Dot you will need:\n\n* A Raspberry Pi\n\n - with built-in Bluetooth "
},
{
"path": "docs/index.rst",
"chars": 262,
"preview": ".. include:: ../README.rst\n\nTable of Contents\n=================\n\n.. toctree::\n :maxdepth: 2\n\n gettingstarted\n pair"
},
{
"path": "docs/mockapi.rst",
"chars": 439,
"preview": "Mock API\n========\nBlue Dot also contains a useful :mod:`mock` API for simulating Blue Dot and\nbluetooth comms. This is u"
},
{
"path": "docs/pairpiandroid.rst",
"chars": 1522,
"preview": "Pair a Raspberry Pi and Android phone\n=====================================\n\nUsing the Desktop\n-----------------\n\nOn you"
},
{
"path": "docs/pairpipi.rst",
"chars": 1760,
"preview": "Pair 2 Raspberry Pis\n====================\n\nThe instructions below describe pairing a couple of Raspberry Pis which eithe"
},
{
"path": "docs/protocol.rst",
"chars": 7147,
"preview": "Protocol\n========\n\nBlue Dot uses a client/server model. The :class:`BlueDot` class starts a\nBluetooth server, the Blue D"
},
{
"path": "docs/recipes.rst",
"chars": 11871,
"preview": "Recipes\n=======\n\nThe recipes provide examples of how you can use Blue Dot. Don't be restricted\nby these ideas and be sur"
},
{
"path": "examples/adapter_details.py",
"chars": 379,
"preview": "from bluedot import BlueDot\nbd = BlueDot()\n\nprint(\"Address {}\".format(bd.adapter.address))\nprint(\"powered {}\".format(bd."
},
{
"path": "examples/click_wheel.py",
"chars": 266,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ncount = 0\n\ndef rotated(rotation):\n global count\n count += ro"
},
{
"path": "examples/client_connects.py",
"chars": 344,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef client_connected():\r\n print(\"callback - a blue dot clien"
},
{
"path": "examples/client_debug.py",
"chars": 388,
"preview": "from bluedot.btcomm import BluetoothClient\nfrom datetime import datetime\nfrom time import sleep\nfrom signal import pause"
},
{
"path": "examples/color_changer.py",
"chars": 383,
"preview": "from bluedot import BlueDot\n# color zero can be installed using sudo pip3 install colorzero\nfrom colorzero import Color\n"
},
{
"path": "examples/connect_multiple_apps.py",
"chars": 292,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef bd1_pressed():\r\n print(\"BlueDot 1 pressed\")\r\n\r\ndef bd2_p"
},
{
"path": "examples/dot_changer.py",
"chars": 721,
"preview": "from bluedot import BlueDot\r\nfrom bluedot.colors import RED, GREEN\r\n\r\nfrom signal import pause\r\n\r\ndef change_dot(pos):\r\n"
},
{
"path": "examples/dot_debug.py",
"chars": 1640,
"preview": "from bluedot import BlueDot\nfrom time import sleep, time\n\ndot = BlueDot(auto_start_server = False)\ndot.resize(1,2)\ndot.a"
},
{
"path": "examples/dot_single_button_debug.py",
"chars": 1473,
"preview": "from bluedot import BlueDot\r\nfrom time import sleep, time\r\n\r\ndot = BlueDot(auto_start_server = False)\r\ndot.allow_pairing"
},
{
"path": "examples/double_press.py",
"chars": 177,
"preview": "from signal import pause\r\nfrom bluedot import BlueDot\r\nbd = BlueDot()\r\n\r\ndef double_pressed():\r\n print(\"double presse"
},
{
"path": "examples/dpad.py",
"chars": 301,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef dpad(pos):\n if pos.top:\n print(\"up\")\n elif pos.bo"
},
{
"path": "examples/dpad_layout.py",
"chars": 511,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef up():\r\n print(\"up\")\r\n\r\ndef down():\r\n print(\"down\")\r\n\r"
},
{
"path": "examples/hello_world.py",
"chars": 114,
"preview": "from bluedot import BlueDot\n\ndot = BlueDot()\ndot.wait_for_press()\nprint(\"You pressed the blue dot - Hello world\")\n"
},
{
"path": "examples/joypad.py",
"chars": 851,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef up():\r\n print(\"up\")\r\n\r\ndef down():\r\n print(\"down\")\r\n\r"
},
{
"path": "examples/many_buttons.py",
"chars": 204,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print(\"button {}.{} pressed\".format(pos."
},
{
"path": "examples/matrix_of_dots.py",
"chars": 1353,
"preview": "from bluedot import BlueDot, COLORS\r\nfrom signal import pause\r\nfrom random import choice\r\n\r\nbd = BlueDot()\r\nbd.resize(1,"
},
{
"path": "examples/mock_app_debug.py",
"chars": 740,
"preview": "from bluedot import MockBlueDot\nfrom time import sleep, time\n\nmbd = MockBlueDot(auto_start_server = False)\n\ndef pressed("
},
{
"path": "examples/mock_btcomm_debug.py",
"chars": 490,
"preview": "from bluedot.mock import MockBluetoothClient, MockBluetoothServer\n\ndef s_client_connected():\n print(\"s: c connected\")"
},
{
"path": "examples/mock_debug.py",
"chars": 882,
"preview": "from bluedot import MockBlueDot\nfrom time import sleep, time\n\nmbd = MockBlueDot(auto_start_server = False)\n\ndef pressed("
},
{
"path": "examples/remote_camera.py",
"chars": 206,
"preview": "from bluedot import BlueDot\nfrom picamera import PiCamera\nfrom signal import pause\n\ndot = BlueDot()\ncam = PiCamera()\n\nde"
},
{
"path": "examples/server_debug.py",
"chars": 712,
"preview": "from bluedot.btcomm import BluetoothServer\nfrom time import sleep\nfrom signal import pause\n\ndef data_received(data):\n "
},
{
"path": "examples/simple_robot.py",
"chars": 430,
"preview": "from bluedot import BlueDot\nfrom gpiozero import Robot\nfrom signal import pause\n\nbd = BlueDot()\nrobot = Robot(left=(10, "
},
{
"path": "examples/simple_variable_robot.py",
"chars": 478,
"preview": "from bluedot import BlueDot\nfrom gpiozero import Robot\nfrom signal import pause\n\nbd = BlueDot()\nrobot = Robot(left=(10, "
},
{
"path": "examples/slider_centre.py",
"chars": 219,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef show_percentage(pos):\n percentage = round(pos.distance * 10"
},
{
"path": "examples/slider_horizontal.py",
"chars": 252,
"preview": "from bluedot import BlueDot\nfrom signal import pause\n\ndef show_percentage(pos):\n horizontal = ((pos.x + 1) / 2)\n p"
},
{
"path": "examples/slider_led_dimmer.py",
"chars": 242,
"preview": "from bluedot import BlueDot\nfrom gpiozero import PWMLED\nfrom signal import pause\n\ndef set_brightness(pos):\n brightnes"
},
{
"path": "examples/source_robot.py",
"chars": 578,
"preview": "from gpiozero import Robot\nfrom bluedot import BlueDot\nfrom signal import pause\n\ndef pos_to_values(x, y):\n left = y i"
},
{
"path": "examples/swipe_debug.py",
"chars": 917,
"preview": "from bluedot import BlueDot, BlueDotSwipe\nfrom signal import pause\n\nbd = BlueDot()\n\nprint(\"waiting for swipe\")\nbd.wait_f"
},
{
"path": "examples/threaded_callbacks.py",
"chars": 323,
"preview": "from bluedot import BlueDot\r\nfrom time import sleep\r\nfrom signal import pause\r\nbd = BlueDot()\r\n\r\ndef pressed():\r\n pri"
},
{
"path": "examples/two_buttons.py",
"chars": 196,
"preview": "from bluedot import BlueDot\r\nfrom signal import pause\r\n\r\ndef pressed(pos):\r\n print(\"button {}.{} pressed\".format(pos."
},
{
"path": "examples/wait_for_events.py",
"chars": 345,
"preview": "from bluedot import BlueDot\nfrom turtle import Turtle\n\nbd = BlueDot()\n\nwhile True:\n bd.wait_for_press()\n print(\"pr"
},
{
"path": "setup.py",
"chars": 3569,
"preview": "import sys\nfrom setuptools import setup\n\nif sys.version_info.major == 2:\n raise ValueError('This package does not sup"
},
{
"path": "tests/test_blue_dot.py",
"chars": 21131,
"preview": "import pytest\nfrom bluedot import MockBlueDot, BlueDotSwipe, BlueDotRotation\nfrom bluedot.exceptions import ButtonDoesNo"
},
{
"path": "tests/test_bluetooth_adapter.py",
"chars": 1397,
"preview": "import pytest\r\nfrom bluedot.btcomm import BluetoothAdapter\r\n\r\ntry:\r\n bta = BluetoothAdapter(\"hci0\")\r\nexcept Exception"
},
{
"path": "tests/test_bluetooth_comms.py",
"chars": 5632,
"preview": "import pytest\r\nfrom bluedot.btcomm import BluetoothAdapter, BluetoothServer, BluetoothClient\r\nfrom threading import Even"
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the martinohanlon/BlueDot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 148 files (374.3 KB), approximately 98.0k tokens, and a symbol index with 649 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.