row : mCells) {
for (MatrixCell cell : row ) {
cell.setSquare(value);
}
}
}
public int getCols() {
return mCols;
}
public void setCols(int value) {
setSize(value, getRows());
}
public int getRows() {
return mRows;
}
public void setRows(int value) {
setSize(getCols(), value);
}
// internal classes
// keeps track of a single pointer on the matrix
private class MatrixPointer {
private int mPointedId;
private float mX, mY;
private MatrixCell mPressedCell;
private MatrixPointer(int pointerId, float x, float y, MatrixCell pressedCell) {
mPointedId = pointerId;
mX = x;
mY = y;
mPressedCell = pressedCell;
}
private void move(float x, float y) {
mX = x;
mY = y;
}
private MatrixCell getPressedCell() {
return mPressedCell;
}
private float getX() {
return mX;
}
private float getY() {
return mY;
}
}
// represents a cell on the matrix, used to keep track of the state
public class MatrixCell {
private int mRow, mCol, mCurrentColor, mReleasedColor, mPressedColor, mMovedColor;
private RectF mBounds;
private boolean mBorder, mPressed, mVisible, mSquare;
private MatrixCell(int col, int row, RectF bounds, boolean visible, boolean border, int color, boolean square) {
mCol = col;
mRow = row;
mBounds = bounds;
mBorder = border;
mPressed = false;
mVisible = visible;
mSquare = square;
updateColors(color);
}
/*public int getRow() { return mRow; }
public int getCol() { return mCol; }*/
public RectF getBounds() {
return mBounds;
}
private void setBounds(RectF value) {
mBounds = value;
}
public RectF getInnerBounds() {
float border = (mCellSize * Constants.BORDER_THICKNESS) / 2;
return new RectF(
mBounds.left + border,
mBounds.top + border,
mBounds.right - border,
mBounds.bottom - border);
}
public int getColor() {
return mCurrentColor;
}
public int getMovedColor() {
return mMovedColor;
}
public void setColor(int value) {
updateColors(value);
}
public boolean getBorder() {
return mBorder;
}
public void setBorder(boolean value) {
mBorder = value;
}
public boolean getVisible() {
return mVisible;
}
public void setVisible(boolean value) {
mVisible = value;
}
public boolean getSquare() {
return mSquare;
}
public void setSquare(boolean value) {
mSquare = value;
}
public int getCol() {
return mCol;
}
public int getRow() {
return mRow;
}
public float getWidth() {
return mBounds.right - mBounds.left;
}
public float getHeight() {
return mBounds.bottom - mBounds.top;
}
public boolean getPressed() {
return mPressed;
}
// called when the cell is pressed
private void press() {
mCurrentColor = mPressedColor;
mPressed = true;
invalidate();
requestLayout();
}
// called when the cell is released
private void release() {
mCurrentColor = mReleasedColor;
mPressed = false;
invalidate();
requestLayout();
}
// called when the cell is moved
private void moved() {
invalidate();
requestLayout();
}
// manages the colors in the cell
private void updateColors(int color) {
mReleasedColor = color;
mPressedColor = manipulateColor(color, 0.85f);
mMovedColor = manipulateColor(color, 0.7f);
if (mPressed) {
mCurrentColor = mPressedColor;
} else {
mCurrentColor = mReleasedColor;
}
}
// manipulates a color
private int manipulateColor(int color, float factor) {
int a = Color.alpha(color);
int r = Math.round(Color.red(color) * factor);
int g = Math.round(Color.green(color) * factor);
int b = Math.round(Color.blue(color) * factor);
return Color.argb(a,
Math.min(r,255),
Math.min(g,255),
Math.min(b,255));
}
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/bluedot/SettingsActivity.java
================================================
package com.stuffaboutcode.bluedot;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.Preference;
import androidx.preference.ListPreference;
import androidx.preference.SwitchPreferenceCompat;
import android.content.SharedPreferences;
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings, new SettingsFragment())
.commit();
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
public static class SettingsFragment
extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener{
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
setupPreferences();
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) {
setupPreferences();
}
private void setupPreferences() {
Preference default_port = findPreference("default_port");
SwitchPreferenceCompat default_port_switch = (SwitchPreferenceCompat)default_port;
Preference port = findPreference("port");
ListPreference port_list = (ListPreference) port;
if (default_port_switch != null) {
if (port_list != null) {
// disable the port if the default_port is enabled
if (default_port_switch.isChecked()) port.setVisible(false);
else port.setVisible(true);
// set the port summary
port.setSummary(port_list.getEntry());
}
}
}
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/Log.java
================================================
/*
* Copyright (C) 2013 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.logger;
/**
* Helper class for a list (or tree) of LoggerNodes.
*
* When this is set as the head of the list,
* an instance of it can function as a drop-in replacement for {@link android.util.Log}.
* Most of the methods in this class server only to map a method call in Log to its equivalent
* in LogNode.
*/
public class Log {
// Grabbing the native values from Android's native logging facilities,
// to make for easy migration and interop.
public static final int NONE = -1;
public static final int VERBOSE = android.util.Log.VERBOSE;
public static final int DEBUG = android.util.Log.DEBUG;
public static final int INFO = android.util.Log.INFO;
public static final int WARN = android.util.Log.WARN;
public static final int ERROR = android.util.Log.ERROR;
public static final int ASSERT = android.util.Log.ASSERT;
// Stores the beginning of the LogNode topology.
private static LogNode mLogNode;
/**
* Returns the next LogNode in the linked list.
*/
public static LogNode getLogNode() {
return mLogNode;
}
/**
* Sets the LogNode data will be sent to.
*/
public static void setLogNode(LogNode node) {
mLogNode = node;
}
/**
* Instructs the LogNode to print the log data provided. Other LogNodes can
* be chained to the end of the LogNode as desired.
*
* @param priority Log level of the data being logged. Verbose, Error, etc.
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void println(int priority, String tag, String msg, Throwable tr) {
if (mLogNode != null) {
mLogNode.println(priority, tag, msg, tr);
}
}
/**
* Instructs the LogNode to print the log data provided. Other LogNodes can
* be chained to the end of the LogNode as desired.
*
* @param priority Log level of the data being logged. Verbose, Error, etc.
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged. The actual message to be logged.
*/
public static void println(int priority, String tag, String msg) {
println(priority, tag, msg, null);
}
/**
* Prints a message at VERBOSE priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void v(String tag, String msg, Throwable tr) {
println(VERBOSE, tag, msg, tr);
}
/**
* Prints a message at VERBOSE priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void v(String tag, String msg) {
v(tag, msg, null);
}
/**
* Prints a message at DEBUG priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void d(String tag, String msg, Throwable tr) {
println(DEBUG, tag, msg, tr);
}
/**
* Prints a message at DEBUG priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void d(String tag, String msg) {
d(tag, msg, null);
}
/**
* Prints a message at INFO priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void i(String tag, String msg, Throwable tr) {
println(INFO, tag, msg, tr);
}
/**
* Prints a message at INFO priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void i(String tag, String msg) {
i(tag, msg, null);
}
/**
* Prints a message at WARN priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void w(String tag, String msg, Throwable tr) {
println(WARN, tag, msg, tr);
}
/**
* Prints a message at WARN priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void w(String tag, String msg) {
w(tag, msg, null);
}
/**
* Prints a message at WARN priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void w(String tag, Throwable tr) {
w(tag, null, tr);
}
/**
* Prints a message at ERROR priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void e(String tag, String msg, Throwable tr) {
println(ERROR, tag, msg, tr);
}
/**
* Prints a message at ERROR priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void e(String tag, String msg) {
e(tag, msg, null);
}
/**
* Prints a message at ASSERT priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void wtf(String tag, String msg, Throwable tr) {
println(ASSERT, tag, msg, tr);
}
/**
* Prints a message at ASSERT priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged.
*/
public static void wtf(String tag, String msg) {
wtf(tag, msg, null);
}
/**
* Prints a message at ASSERT priority.
*
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public static void wtf(String tag, Throwable tr) {
wtf(tag, null, tr);
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogFragment.java
================================================
/*
* Copyright 2013 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.
*/
/*
* Copyright 2013 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.logger;
import android.graphics.Typeface;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
/**
* Simple fraggment which contains a LogView and uses is to output log data it receives
* through the LogNode interface.
*/
public class LogFragment extends Fragment {
private LogView mLogView;
private ScrollView mScrollView;
public LogFragment() {}
public View inflateViews() {
mScrollView = new ScrollView(getActivity());
ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mScrollView.setLayoutParams(scrollParams);
mLogView = new LogView(getActivity());
ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams);
logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
mLogView.setLayoutParams(logParams);
mLogView.setClickable(true);
mLogView.setFocusable(true);
mLogView.setTypeface(Typeface.MONOSPACE);
// Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
int paddingDips = 16;
double scale = getResources().getDisplayMetrics().density;
int paddingPixels = (int) ((paddingDips * (scale)) + .5);
mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels);
mLogView.setCompoundDrawablePadding(paddingPixels);
mLogView.setGravity(Gravity.BOTTOM);
mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium);
mScrollView.addView(mLogView);
return mScrollView;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View result = inflateViews();
mLogView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
return result;
}
public LogView getLogView() {
return mLogView;
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogNode.java
================================================
/*
* Copyright (C) 2012 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.logger;
/**
* Basic interface for a logging system that can output to one or more targets.
* Note that in addition to classes that will output these logs in some format,
* one can also implement this interface over a filter and insert that in the chain,
* such that no targets further down see certain data, or see manipulated forms of the data.
* You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
* it received to HTML and sent it along to the next node in the chain, without printing it
* anywhere.
*/
public interface LogNode {
/**
* Instructs first LogNode in the list to print the log data provided.
* @param priority Log level of the data being logged. Verbose, Error, etc.
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged. The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
public void println(int priority, String tag, String msg, Throwable tr);
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogView.java
================================================
/*
* Copyright (C) 2013 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.logger;
import android.app.Activity;
import android.content.Context;
import android.util.*;
import android.widget.TextView;
/** Simple TextView which is used to output log data received through the LogNode interface.
*/
public class LogView extends TextView implements LogNode {
public LogView(Context context) {
super(context);
}
public LogView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LogView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Formats the log data and prints it out to the LogView.
* @param priority Log level of the data being logged. Verbose, Error, etc.
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged. The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
@Override
public void println(int priority, String tag, String msg, Throwable tr) {
String priorityStr = null;
// For the purposes of this View, we want to print the priority as readable text.
switch(priority) {
case android.util.Log.VERBOSE:
priorityStr = "VERBOSE";
break;
case android.util.Log.DEBUG:
priorityStr = "DEBUG";
break;
case android.util.Log.INFO:
priorityStr = "INFO";
break;
case android.util.Log.WARN:
priorityStr = "WARN";
break;
case android.util.Log.ERROR:
priorityStr = "ERROR";
break;
case android.util.Log.ASSERT:
priorityStr = "ASSERT";
break;
default:
break;
}
// Handily, the Log class has a facility for converting a stack trace into a usable string.
String exceptionStr = null;
if (tr != null) {
exceptionStr = android.util.Log.getStackTraceString(tr);
}
// Take the priority, tag, message, and exception, and concatenate as necessary
// into one usable line of text.
final StringBuilder outputBuilder = new StringBuilder();
String delimiter = "\t";
appendIfNotNull(outputBuilder, priorityStr, delimiter);
appendIfNotNull(outputBuilder, tag, delimiter);
appendIfNotNull(outputBuilder, msg, delimiter);
appendIfNotNull(outputBuilder, exceptionStr, delimiter);
// In case this was originally called from an AsyncTask or some other off-UI thread,
// make sure the update occurs within the UI thread.
((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() {
@Override
public void run() {
// Display the text we just generated within the LogView.
appendToLog(outputBuilder.toString());
}
})));
if (mNext != null) {
mNext.println(priority, tag, msg, tr);
}
}
public LogNode getNext() {
return mNext;
}
public void setNext(LogNode node) {
mNext = node;
}
/** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
* the logger takes so many arguments that might be null, this method helps cut out some of the
* agonizing tedium of writing the same 3 lines over and over.
* @param source StringBuilder containing the text to append to.
* @param addStr The String to append
* @param delimiter The String to separate the source and appended strings. A tab or comma,
* for instance.
* @return The fully concatenated String as a StringBuilder
*/
private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
if (addStr != null) {
if (addStr.length() == 0) {
delimiter = "";
}
return source.append(addStr).append(delimiter);
}
return source;
}
// The next LogNode in the chain.
LogNode mNext;
/** Outputs the string as a new line of log data in the LogView. */
public void appendToLog(String s) {
append("\n" + s);
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/LogWrapper.java
================================================
/*
* Copyright (C) 2012 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.logger;
import android.util.Log;
/**
* Helper class which wraps Android's native Log utility in the Logger interface. This way
* normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
*/
public class LogWrapper implements LogNode {
// For piping: The next node to receive Log data after this one has done its work.
private LogNode mNext;
/**
* Returns the next LogNode in the linked list.
*/
public LogNode getNext() {
return mNext;
}
/**
* Sets the LogNode data will be sent to..
*/
public void setNext(LogNode node) {
mNext = node;
}
/**
* Prints data out to the console using Android's native log mechanism.
* @param priority Log level of the data being logged. Verbose, Error, etc.
* @param tag Tag for for the log data. Can be used to organize log statements.
* @param msg The actual message to be logged. The actual message to be logged.
* @param tr If an exception was thrown, this can be sent along for the logging facilities
* to extract and print useful information.
*/
@Override
public void println(int priority, String tag, String msg, Throwable tr) {
// There actually are log methods that don't take a msg parameter. For now,
// if that's the case, just convert null to the empty string and move on.
String useMsg = msg;
if (useMsg == null) {
useMsg = "";
}
// If an exeption was provided, convert that exception to a usable string and attach
// it to the end of the msg method.
if (tr != null) {
msg += "\n" + Log.getStackTraceString(tr);
}
// This is functionally identical to Log.x(tag, useMsg);
// For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
Log.println(priority, tag, useMsg);
// If this isn't the last node in the chain, move things along.
if (mNext != null) {
mNext.println(priority, tag, msg, tr);
}
}
}
================================================
FILE: clients/android/app/src/main/java/com/stuffaboutcode/logger/MessageOnlyLogFilter.java
================================================
/*
* Copyright (C) 2013 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.logger;
/**
* Simple {@link LogNode} filter, removes everything except the message.
* Useful for situations like on-screen log output where you don't want a lot of metadata displayed,
* just easy-to-read message updates as they're happening.
*/
public class MessageOnlyLogFilter implements LogNode {
LogNode mNext;
/**
* Takes the "next" LogNode as a parameter, to simplify chaining.
*
* @param next The next LogNode in the pipeline.
*/
public MessageOnlyLogFilter(LogNode next) {
mNext = next;
}
public MessageOnlyLogFilter() {
}
@Override
public void println(int priority, String tag, String msg, Throwable tr) {
if (mNext != null) {
getNext().println(Log.NONE, null, msg, null);
}
}
/**
* Returns the next LogNode in the chain.
*/
public LogNode getNext() {
return mNext;
}
/**
* Sets the LogNode data will be sent to..
*/
public void setNext(LogNode node) {
mNext = node;
}
}
================================================
FILE: clients/android/app/src/main/res/drawable/round_button.xml
================================================
-
-
================================================
FILE: clients/android/app/src/main/res/layout/activity_button.xml
================================================
================================================
FILE: clients/android/app/src/main/res/layout/activity_devices.xml
================================================
================================================
FILE: clients/android/app/src/main/res/layout/settings_activity.xml
================================================
================================================
FILE: clients/android/app/src/main/res/menu/settings_menu.xml
================================================
================================================
FILE: clients/android/app/src/main/res/values/arrays.xml
================================================
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
================================================
FILE: clients/android/app/src/main/res/values/attrs.xml
================================================
================================================
FILE: clients/android/app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
#FFFFFF
#FF0000
#0000FF
#6d6d6e
#0000FF
================================================
FILE: clients/android/app/src/main/res/values/dimens.xml
================================================
16dp
================================================
FILE: clients/android/app/src/main/res/values/strings.xml
================================================
BlueButton
Button
Settings
Messages
Sync
Your signature
Default reply action
Sync email periodically
Download incoming attachments
Automatically download attachments for incoming emails
Only download attachments when manually requested
================================================
FILE: clients/android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: clients/android/app/src/main/res/xml/root_preferences.xml
================================================
================================================
FILE: clients/android/app/src/test/java/com/stuffaboutcode/bluedot/ExampleUnitTest.java
================================================
package com.stuffaboutcode.bluedot;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: clients/android/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: clients/android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Dec 23 09:57:24 GMT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
================================================
FILE: clients/android/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: clients/android/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: clients/android/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: clients/android/settings.gradle
================================================
include ':app'
================================================
FILE: clients/python/README.rst
================================================
Blue Dot Python App
===================
Blue Dot Python app allows you to use another Raspberry Pi (or linux based computer) as the Blue Dot remote.
|bluedotpython| |bluedotpythondevices|
Start
-----
The app is included in the bluedot Python library:
1. If you havent already done so, pair your raspberry pi and install the Python library as described in the `getting started`_ guide
2. Run the Blue Dot app::
bluedotapp
4. Select your Raspberry Pi from the paired devices list
|bluedotpythondevices|
5. Press the Dot
|bluedotpython|
Options
-------
To get help with the Blue Dot app options::
bluedotapp --help
If you have more than 1 bluetooth device you can use ``--device`` to use a particular device::
bluedotapp --device hci1
You can specify the server to connect to at startup by using the ``--server`` option::
bluedotapp --server myraspberrypi
If you are using a different port you can specify it using the ``--port`` option::
bluedotapp --port 1
The screen size of the Blue Dot app can be changed using the ``width`` and ``height`` options and specifying the number of pixels::
bluedotapp --width 500 --height 500
The app can also be used full screen, if no ``width`` or ``height`` is given the screen will be sized to the current resolution of the screen::
bluedotapp --fullscreen
.. _getting started: http://bluedot.readthedocs.io/en/latest/gettingstarted.html
.. |bluedotpython| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotpython.png
:height: 274 px
:width: 324 px
:scale: 100 %
:alt: blue dot python app
.. |bluedotpythondevices| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotpythondevices.png
:height: 273 px
:width: 326 px
:scale: 100 %
:alt: blue dot devices
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = bluedot
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/bluedotandroidapp.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
:doc:`gettingstarted` 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
.. |bluedotapp| image:: images/bluedotandroid_small.png
:alt: Screenshot of Blue Dot app
.. |bluedotappdevices| image:: images/bluedotandroiddevices_small.png
:alt: Screenshot of Blue Dot devices list screen
.. |bluedotappicon| image:: images/bluedotandroidicon.png
:alt: Blue Dot icon
================================================
FILE: docs/bluedotpythonapp.rst
================================================
Blue Dot Python App
===================
Blue Dot Python app allows you to use another Raspberry Pi (or linux based computer) as the Blue Dot remote.
|bluedotpython| |bluedotpythondevices|
Start
-----
The app is included in the bluedot Python library:
1. If you havent already done so, pair your raspberry pi and install the Python
library as described in the :doc:`gettingstarted` guide
2. Run the Blue Dot app::
bluedotapp
3. Select your Raspberry Pi from the paired devices list
|bluedotpythondevices|
4. Press the Dot
|bluedotpython|
Options
-------
To get help with the Blue Dot app options::
bluedotapp --help
If you have more than 1 bluetooth device you can use ``--device`` to use a particular device::
bluedotapp --device hci1
You can specify the server to connect to at startup by using the ``--server`` option::
bluedotapp --server myraspberrypi
The screen size of the Blue Dot app can be changed using the ``width`` and ``height`` options and specifying the number of pixels::
bluedotapp --width 500 --height 500
The app can also be used full screen, if no ``width`` or ``height`` is given the screen will be sized to the current resolution of the screen::
bluedotapp --fullscreen
.. |bluedotpython| image:: images/bluedotpython.png
:alt: Screenshot of Blue Dot python app
.. |bluedotpythondevices| image:: images/bluedotpythondevices.png
:alt: Screenshot of Blue Dot devices list
================================================
FILE: docs/btcommapi.rst
================================================
Bluetooth Comm API
==================
Blue Dot also contains a useful :mod:`btcomm` API for sending and
receiving data over Bluetooth.
For normal use of Blue Dot, this API doesn't need to be used, but its included
in the documentation for info and for those who might need a simple Bluetooth
communication library.
.. module:: bluedot.btcomm
BluetoothServer
---------------
.. autoclass:: BluetoothServer
BluetoothClient
---------------
.. autoclass:: BluetoothClient
BluetoothAdapter
----------------
.. autoclass:: BluetoothAdapter
================================================
FILE: docs/build.rst
================================================
Build
=====
These are instructions for how to develop, build and deploy Blue Dot.
Develop
-------
Install / upgrade tools::
sudo python3 -m pip install --upgrade pip setuptools wheel twine virtualenv
Create a virtual environment (recommended)::
virtualenv --system-site-packages bluedot-dev
cd bluedot-dev
source bin/activate
Clone repo and install for dev::
git clone https://github.com/martinohanlon/BlueDot
cd BlueDot
git checkout dev
python3 setup.py develop
Test
----
Install `pytest`_::
pip3 install -U pytest
Run tests::
cd BlueDot/tests
pytest -v
Deploy
------
Build for deployment::
python3 setup.py sdist
python3 setup.py bdist_wheel
Deploy to `PyPI`_::
twine upload dist/* --skip-existing
.. _pytest: https://doc.pytest.org/
.. _PyPI: https://pypi.python.org/pypi
================================================
FILE: docs/changelog.rst
================================================
Change log
==========
.. currentmodule:: bluedot
Bluedot Python library
----------------------
2.0.0 - 2020-11-01
~~~~~~~~~~~~~~~~~~
* implementation of multiple buttons in a matrix
* refactor of significant portions of the code base
* improvement to btcomm to manage large messages
* update to MockBlueDot
* deprecated BlueDot.interaction
* added warnings when invalid data is received
* support for protocol version 2
* removed support for Python 2, 3.3 & 3.4
1.3.2 - 2019-04-22
~~~~~~~~~~~~~~~~~~
* change to how callbacks are called
* added `set_when_pressed`, `set_when_released`, etc to allow callbacks to be called in their own threads.
1.3.1 - 2019-01-01
~~~~~~~~~~~~~~~~~~
* minor bug fix to launch_mock_app
1.3.0 - 2018-12-30
~~~~~~~~~~~~~~~~~~
* added ability to change the color, border, shape and visibility of the dot (:attr:`~BlueDot.color`, :attr:`~BlueDot.border`, :attr:`~BlueDot.square`, :attr:`~BlueDot.visible`)
* added protocol version checking
* minor threading changes in btcomm
* updates to the Blue Dot Python app
* rewrite of the mock app
* support for protocol version 1
1.2.3 - 2018-02-22
~~~~~~~~~~~~~~~~~~
* fix to `wait_for_press` and `wait_for_release`
* when_client_connects and when_client_disconnects callbacks are now threaded
* The python blue dot app can now be started with the command `bluedotapp`
* new tests for `wait_for_(events)`
1.2.2 - 2017-12-30
~~~~~~~~~~~~~~~~~~
* bluetooth comms tests and minor bug fix in :class:`~.btcomm.BluetoothClient`
1.2.1 - 2017-12-18
~~~~~~~~~~~~~~~~~~
* massive code and docs tidy up by `Dave Jones`_
1.2.0 - 2017-12-10
~~~~~~~~~~~~~~~~~~
* added when_rotated
* threaded swipe callbacks
* exposed new :class:`BlueDot` properties (:attr:`~BlueDot.adapter`, :attr:`~BlueDot.running`, :attr:`~BlueDot.paired_devices`)
* fixed active bug in interaction
* automated tests
1.1.0 - 2017-11-05
~~~~~~~~~~~~~~~~~~
* threaded callbacks
* python app rounded x,y performance improvements
1.0.4 - 2017-09-10
~~~~~~~~~~~~~~~~~~
* serial port profile port fix
* launching multiple blue dots fix
1.0.3 - 2017-07-28
~~~~~~~~~~~~~~~~~~
* python 2 bug fix
1.0.2 - 2017-07-23
~~~~~~~~~~~~~~~~~~
* bug fix
1.0.1 - 2017-06-19
~~~~~~~~~~~~~~~~~~
* bug fixes
1.0.0 - 2017-06-04
~~~~~~~~~~~~~~~~~~
* production release!
* added double click
* doc updates
* minor changes
0.4.0 - 2017-05-05
~~~~~~~~~~~~~~~~~~
* added swipes and interactions
* doc updates
* bug fix in :attr:`BlueDot.when_moved`
0.3.0 - 2017-05-01
~~~~~~~~~~~~~~~~~~
* Python Blue Dot app
* minor bug fix in :class:`~.btcomm.BluetoothClient`
0.2.1 - 2017-04-23
~~~~~~~~~~~~~~~~~~
* bug fix in :class:`MockBlueDot`
* doc fixes
0.2.0 - 2017-04-23
~~~~~~~~~~~~~~~~~~
* added :attr:`~BlueDot.when_client_connects`, :attr:`~BlueDot.when_client_disconnects`
* added :meth:`~BlueDot.allow_pairing` functions
* refactored Bluetooth comms
* added :class:`~.btcomm.BluetoothAdapter`
0.1.2 - 2017-04-14
~~~~~~~~~~~~~~~~~~
* mock blue dot improvements
* doc fixes
0.1.1 - 2017-04-08
~~~~~~~~~~~~~~~~~~
* clamped distance in :class:`BlueDotPosition`
0.1.0 - 2017-04-07
~~~~~~~~~~~~~~~~~~
* Check Bluetooth adapter is powered
* Handle client connection timeouts
* Docs & image updates
0.0.6 - 2017-04-05
~~~~~~~~~~~~~~~~~~
* Added :class:`MockBlueDot` for testing and debugging
* more docs
0.0.4 - 2017-03-31
~~~~~~~~~~~~~~~~~~
Updates after alpha feedback
* Python 2 compatibility
* ``.dot_position`` to ``.position``
* ``.values`` added
* clamped ``x``, ``y`` to 1
* loads of doc updates
0.0.2 - 2017-03-29
~~~~~~~~~~~~~~~~~~
Alpha - initial testing
Android app
-----------
10 (2.2.1) - 2022-01-03
~~~~~~~~~~~~~~~~~~~~~~~~
* Android 12+ fixes
9 (2.2) - 2022-12-23
~~~~~~~~~~~~~~~~~~~~~~~~
* Android SDK and API version uplift (due to google play store minimum requirements change)
8 (2.1) - 2020-12-28
~~~~~~~~~~~~~~~~~~~~~~~~
* removed "auto port discovery" after the introduction of pulseaudio to Raspberry Pi OS broke it
* introduced the "default port" setting as an alternative
7 (2.0) - 2020-11-01
~~~~~~~~~~~~~~~~~~~~~~~~
* implementation of multiple buttons in a matrix
* support for protocol version 2
6 (1.3.1) - 2019-12-30
~~~~~~~~~~~~~~~~~~~~~~~~
* Minor bug fix
5 (1.3) - 2019-12-29
~~~~~~~~~~~~~~~~~~~~~~~~
* Added settings menu so a specific bluetooth port can be selected
* Using specific bluetooth ports, multiple apps can now connect to a single BT devices
* Minor bugs fixes
4 (1.2) - 2018-12-30
~~~~~~~~~~~~~~~~~~~~~~~~
* Rewrite of the Button view
* Rewrite of the Bluetooth comms layer
* Support for colours, square and border
* Landscape (and portrait) views
* added protocol version checking
* support for protocol version 1
3 (1.1.1) - 2018-09-21
~~~~~~~~~~~~~~~~~~~~~~~~
* Android SDK version uplift (due to google play store minimum requirements change)
2 (1.1) - 2017-11-05
~~~~~~~~~~~~~~~~~~~~~~~~
* better responsive layout
* fixed issues with small screen devices
* rounded x,y values increasing performance
* new help icon
* link to https://bluedot.readthedocs.io not http
1 (0.0.2) - 2017-04-05
~~~~~~~~~~~~~~~~~~~~~~~~
* icon transparency
* connection monitor
* added info icon to https://bluedot.readthedocs.io
0 (0.0.1) - 2017-03-29
~~~~~~~~~~~~~~~~~~~~~~~~
* alpha - initial testing
.. _Dave Jones: https://github.com/waveform80
================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# bluedot documentation build configuration file, created by
# sphinx-quickstart on Mon Mar 27 21:11:18 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from datetime import datetime
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
import setup as _setup
# Mock out certain modules while building documentation
class Mock:
__all__ = []
def __init__(self, *args, **kw): pass
def __call__(self, *args, **kw): return Mock()
def __mul__(self, other): return Mock()
def __and__(self, other): return Mock()
def __bool__(self): return False
def __nonzero__(self): return False
@classmethod
def __getattr__(cls, name):
if name in ('__file__', '__path__'):
return '/dev/null'
else:
return Mock()
sys.modules['dbus'] = Mock()
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = _setup.__project__
author = _setup.__author__
copyright = "%d %s" % (datetime.now().year, author)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = _setup.__version__
# The full version, including alpha/beta/rc tags.
release = _setup.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Autodoc configuration ------------------------------------------------
autodoc_member_order = 'groupwise'
autodoc_default_flags = ['members']
# -- Intersphinx configuration --------------------------------------------
intersphinx_mapping = {
'python': ('https://docs.python.org/3.5', None),
'gpiozero': ('https://gpiozero.readthedocs.io/en/latest', None),
'picamera': ('https://picamera.readthedocs.io/en/latest', None),
}
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
if on_rtd:
html_theme = 'sphinx_rtd_theme'
#html_theme_options = {}
html_sidebars = {
'**': [
'globaltoc.html',
'relations.html',
'searchbox.html',
],
}
else:
html_theme = 'default'
#html_theme_options = {}
#html_sidebars = {}
html_title = '%s %s Documentation' % (project, version)
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
suppress_warnings = ['image.nonlocal_uri']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
'papersize': 'a4paper',
'pointsize': '10pt',
'preamble': r'\def\thempfootnote{\arabic{mpfootnote}}', # workaround sphinx issue #2530
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(
'index', # source start file
'%s.tex' % _setup.__project__, # target filename
'%s Documentation' % project, # title
_setup.__author__, # author
'manual', # documentclass
True, # documents ref'd from toctree only
),
]
latex_show_pagerefs = True
latex_show_urls = 'footnote'
# -- Options for epub output ----------------------------------------------
epub_basename = project
#epub_theme = 'epub'
#epub_title = html_title
epub_author = author
epub_identifier = 'https://bluedot.readthedocs.io/'
#epub_tocdepth = 3
epub_show_urls = 'no'
#epub_use_index = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, project, '%s Documentation' % project, [author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = []
================================================
FILE: docs/dotapi.rst
================================================
Blue Dot API
============
.. module:: bluedot
BlueDot
-------
.. autoclass:: BlueDot
BlueDotButton
-------------
.. autoclass:: BlueDotButton
BlueDotPosition
---------------
.. autoclass:: BlueDotPosition
BlueDotInteraction
------------------
.. autoclass:: BlueDotInteraction
BlueDotSwipe
------------
.. autoclass:: BlueDotSwipe
BlueDotRotation
---------------
.. autoclass:: BlueDotRotation
================================================
FILE: docs/examples/bt_enumerate.py
================================================
from bluedot import BlueDot
bd = BlueDot()
devices = bd.paired_devices
for d in devices:
device_address = d[0]
device_name = d[1]
================================================
FILE: docs/examples/bt_pair_button.py
================================================
from bluedot import BlueDot
from gpiozero import Button
from signal import pause
bd = BlueDot()
button = Button(27)
button.when_pressed = bd.allow_pairing
pause()
================================================
FILE: docs/examples/bt_pairing.py
================================================
from bluedot import BlueDot
from signal import pause
bd = BlueDot()
bd.allow_pairing()
pause()
================================================
FILE: docs/examples/camera.py
================================================
from bluedot import BlueDot
from picamera import PiCamera
from signal import pause
bd = BlueDot()
cam = PiCamera()
def take_picture():
cam.capture("pic.jpg")
bd.when_pressed = take_picture
pause()
================================================
FILE: docs/examples/dpad.py
================================================
from bluedot import BlueDot
from signal import pause
def dpad(pos):
if pos.top:
print("up")
elif pos.bottom:
print("down")
elif pos.left:
print("left")
elif pos.right:
print("right")
elif pos.middle:
print("fire")
bd = BlueDot()
bd.when_pressed = dpad
pause()
================================================
FILE: docs/examples/dpad_layout.py
================================================
from bluedot import BlueDot
from signal import pause
def up():
print("up")
def down():
print("down")
def left():
print("left")
def right():
print("right")
bd = BlueDot(cols=3, rows=3)
bd.color = "gray"
bd.square = True
bd[0,0].visible = False
bd[2,0].visible = False
bd[0,2].visible = False
bd[2,2].visible = False
bd[1,1].visible = False
bd[1,0].when_pressed = up
bd[1,2].when_pressed = down
bd[0,1].when_pressed = left
bd[2,1].when_pressed = right
pause()
================================================
FILE: docs/examples/goodbye_world.py
================================================
from bluedot import BlueDot
from signal import pause
def say_hello():
print("Hello World")
def say_goodbye():
print("goodbye")
bd = BlueDot()
bd.when_pressed = say_hello
bd.when_released = say_goodbye
pause()
================================================
FILE: docs/examples/hello_event.py
================================================
from bluedot import BlueDot
from signal import pause
def say_hello():
print("Hello World")
bd = BlueDot()
bd.when_pressed = say_hello
pause()
================================================
FILE: docs/examples/hello_world.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.wait_for_press()
print("Hello World")
================================================
FILE: docs/examples/joypad.py
================================================
from bluedot import BlueDot
from signal import pause
def up():
print("up")
def down():
print("down")
def left():
print("left")
def right():
print("right")
def button_A():
print("A")
def button_B():
print("B")
bd = BlueDot(cols=5, rows=3)
# dpad buttons
bd.color = "gray"
bd.square = True
bd[0,0].visible = False
bd[2,0].visible = False
bd[0,2].visible = False
bd[2,2].visible = False
bd[1,1].visible = False
bd[1,0].when_pressed = up
bd[1,2].when_pressed = down
bd[0,1].when_pressed = left
bd[2,1].when_pressed = right
# buttons
bd[3,0].visible = False
bd[4,0].visible = False
bd[3,2].visible = False
bd[4,2].visible = False
bd[3,1].color = "blue"
bd[3,1].square = False
bd[3,1].when_pressed = button_A
bd[4,1].color = "red"
bd[4,1].when_pressed = button_B
pause()
================================================
FILE: docs/examples/led1.py
================================================
import os
from bluedot import BlueDot
from gpiozero import LED
bd = BlueDot()
led = LED(27)
bd.wait_for_press()
led.on()
bd.wait_for_release()
led.off()
================================================
FILE: docs/examples/led2.py
================================================
from bluedot import BlueDot
from gpiozero import LED
from signal import pause
bd = BlueDot()
led = LED(27)
bd.when_pressed = led.on
bd.when_released = led.off
pause()
================================================
FILE: docs/examples/led3.py
================================================
from bluedot import BlueDot
from gpiozero import LED
from signal import pause
bd = BlueDot()
led = LED(27)
led.source = bd.values
pause()
================================================
FILE: docs/examples/looks_border.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.border = True
================================================
FILE: docs/examples/looks_color.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.color = "red"
================================================
FILE: docs/examples/looks_square.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.square = True
================================================
FILE: docs/examples/looks_visible.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.visible = False
================================================
FILE: docs/examples/many_buttons.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=2, rows=5)
bd.when_pressed = pressed
pause()
================================================
FILE: docs/examples/many_buttons_random_colors.py
================================================
from bluedot import BlueDot, COLORS
from random import choice
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=2, rows=5)
bd.when_pressed = pressed
for button in bd.buttons:
button.color = choice(list(COLORS.values()))
pause()
================================================
FILE: docs/examples/mock_app.py
================================================
from bluedot import MockBlueDot
from signal import pause
def say_hello():
print("Hello World")
bd = MockBlueDot()
bd.when_pressed = say_hello
bd.launch_mock_app()
pause()
================================================
FILE: docs/examples/mock_script.py
================================================
from bluedot import MockBlueDot
def say_hello():
print("Hello World")
bd = MockBlueDot()
bd.when_pressed = say_hello
bd.mock_client_connected()
bd.mock_blue_dot_pressed(0,0)
================================================
FILE: docs/examples/multiple_dots.py
================================================
from bluedot import BlueDot
from signal import pause
def bd1_pressed():
print("BlueDot 1 pressed")
def bd2_pressed():
print("BlueDot 2 pressed")
bd1 = BlueDot(port = 1)
bd2 = BlueDot(port = 2)
bd1.when_pressed = bd1_pressed
bd2.when_pressed = bd2_pressed
pause()
================================================
FILE: docs/examples/robot1.py
================================================
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause
bd = BlueDot()
robot = Robot(left=(17, 18), right=(22, 23))
def move(pos):
if pos.top:
robot.forward()
elif pos.bottom:
robot.backward()
elif pos.left:
robot.left()
elif pos.right:
robot.right()
def stop():
robot.stop()
bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop
pause()
================================================
FILE: docs/examples/robot2.py
================================================
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause
bd = BlueDot()
robot = Robot(left=(lfpin, lbpin), right=(rfpin, rbpin))
def move(pos):
if pos.top:
robot.forward(pos.distance)
elif pos.bottom:
robot.backward(pos.distance)
elif pos.left:
robot.left(pos.distance)
elif pos.right:
robot.right(pos.distance)
def stop():
robot.stop()
bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop
pause()
================================================
FILE: docs/examples/robot3.py
================================================
from gpiozero import Robot
from bluedot import BlueDot
from signal import pause
def pos_to_values(x, y):
left = y if x > 0 else y + x
right = y if x < 0 else y - x
return (clamped(left), clamped(right))
def clamped(v):
return max(-1, min(1, v))
def drive():
while True:
if bd.is_pressed:
x, y = bd.position.x, bd.position.y
yield pos_to_values(x, y)
else:
yield (0, 0)
robot = Robot(left=(lfpin, lbpin), right=(rfpin, rbpin))
bd = BlueDot()
robot.source = drive()
pause()
================================================
FILE: docs/examples/rotation.py
================================================
from bluedot import BlueDot
from signal import pause
count = 0
def rotated(rotation):
global count
count += rotation.value
print("{} {} {}".format(count,
rotation.clockwise,
rotation.anti_clockwise))
bd = BlueDot()
bd.when_rotated = rotated
pause()
================================================
FILE: docs/examples/shout_hello.py
================================================
from bluedot import BlueDot
from signal import pause
def shout_hello():
print("HELLO")
bd = BlueDot()
bd.when_double_pressed = shout_hello
pause()
================================================
FILE: docs/examples/slider_centre.py
================================================
from bluedot import BlueDot
from signal import pause
def show_percentage(pos):
percentage = round(pos.distance * 100, 2)
print("{}%".format(percentage))
bd = BlueDot()
bd.when_moved = show_percentage
pause()
================================================
FILE: docs/examples/slider_dimmer.py
================================================
from bluedot import BlueDot
from gpiozero import PWMLED
from signal import pause
def set_brightness(pos):
brightness = (pos.y + 1) / 2
led.value = brightness
led = PWMLED(27)
bd = BlueDot()
bd.when_moved = set_brightness
pause()
================================================
FILE: docs/examples/slider_left_right.py
================================================
from bluedot import BlueDot
from signal import pause
def show_percentage(pos):
horizontal = ((pos.x + 1) / 2)
percentage = round(horizontal * 100, 2)
print("{}%".format(percentage))
bd = BlueDot()
bd.when_moved = show_percentage
pause()
================================================
FILE: docs/examples/swipe1.py
================================================
from bluedot import BlueDot
bd = BlueDot()
bd.wait_for_swipe()
print("Blue Dot swiped")
================================================
FILE: docs/examples/swipe2.py
================================================
from bluedot import BlueDot
from signal import pause
def swiped():
print("Blue Dot swiped")
bd = BlueDot()
bd.when_swiped = swiped
pause()
================================================
FILE: docs/examples/swipe_direction.py
================================================
from bluedot import BlueDot
from signal import pause
def swiped(swipe):
if swipe.up:
print("up")
elif swipe.down:
print("down")
elif swipe.left:
print("left")
elif swipe.right:
print("right")
bd = BlueDot()
bd.when_swiped = swiped
pause()
================================================
FILE: docs/examples/swipe_speed_angle.py
================================================
from bluedot import BlueDot
from signal import pause
def swiped(swipe):
print("Swiped")
print("speed={}".format(swipe.speed))
print("angle={}".format(swipe.angle))
print("distance={}".format(swipe.distance))
bd = BlueDot()
bd.when_swiped = swiped
pause()
================================================
FILE: docs/examples/two_buttons.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=2)
bd.when_pressed = pressed
pause()
================================================
FILE: docs/examples/two_buttons_gap.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=3, rows=1)
bd[1,0].visible = False
bd.when_pressed = pressed
pause()
================================================
FILE: docs/examples/two_buttons_two_events.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed_1(pos):
print("button 1 pressed")
def pressed_2(pos):
print("button 2 pressed")
bd = BlueDot(cols=2, rows=1)
bd[0,0].when_pressed = pressed_1
bd[1,0].when_pressed = pressed_2
pause()
================================================
FILE: docs/gettingstarted.rst
================================================
Getting Started
===============
In order to use Blue Dot you will need:
* A Raspberry Pi
- with built-in Bluetooth (such as the Raspberry Pi 3, 4 or Zero W)
- or a USB Bluetooth dongle
* An Android phone or 2nd Raspberry Pi for the remote
* An Internet connection (for the install)
Installation
------------
These instructions assume your Raspberry Pi is running the latest version of
`Raspbian`_.
Android App
~~~~~~~~~~~
If you're using an Android phone, the `Blue Dot app`_ can be installed from the
Google Play Store.
Python Library
~~~~~~~~~~~~~~
Open a terminal (click :menuselection:`Menu --> Accessories --> Terminal`),
then enter::
sudo pip3 install bluedot
To upgrade to the latest version::
sudo pip3 install bluedot --upgrade
Pairing
-------
In order to use Blue Dot you will need to pair the Raspberry Pi to the remote
:doc:`Android phone ` or :doc:`2nd Raspberry Pi `.
Code
----
1. Start up Python 3 (e.g. :menuselection:`Menu --> Programming --> Thonny Python
IDE`)
2. Create a new program
3. Enter the following code::
from bluedot import BlueDot
bd = BlueDot()
bd.wait_for_press()
print("You pressed the blue dot!")
4. Save your program as :file:`mydot.py`
5. Run the program::
Server started ##:##:##:##:##:##
Waiting for connection
.. warning::
Do not save your program as :file:`bluedot.py` as Python will try and
import your program rather than the bluedot module and you will get the
error ``ImportError: cannot import name BlueDot``.
Connecting
----------
Start-up the `Blue Dot app`_ on your Android phone or run the
:doc:`bluedotpythonapp` on your 2nd Raspberry Pi:
1. Select your Raspberry Pi from the list
.. note::
Your python program will need to be running and ``Waiting for connection``
before the BlueDot app will be able to connect to your Raspberry Pi.
2. Press the Blue Dot
Where next
----------
Check out the :doc:`recipes` and the :doc:`dotapi` documentation for more ideas
on using Blue Dot.
.. _Blue Dot app: http://play.google.com/store/apps/details?id=com.stuffaboutcode.bluedot
.. _Raspbian: https://www.raspberrypi.org/downloads/raspbian/
================================================
FILE: docs/index.rst
================================================
.. include:: ../README.rst
Table of Contents
=================
.. toctree::
:maxdepth: 2
gettingstarted
pairpiandroid
pairpipi
recipes
bluedotandroidapp
bluedotpythonapp
dotapi
btcommapi
mockapi
protocol
build
changelog
================================================
FILE: docs/mockapi.rst
================================================
Mock API
========
Blue Dot also contains a useful :mod:`mock` API for simulating Blue Dot and
bluetooth comms. This is useful for testing and allows for prototyping
without having to use a Blue Dot client.
.. module:: bluedot.mock
MockBlueDot
-----------
.. autoclass:: MockBlueDot
MockBluetoothServer
-------------------
.. autoclass:: MockBluetoothServer
MockBluetoothClient
-------------------
.. autoclass:: MockBluetoothClient
================================================
FILE: docs/pairpiandroid.rst
================================================
Pair a Raspberry Pi and Android phone
=====================================
Using the Desktop
-----------------
On your Android phone:
1. Open Settings
2. Select Bluetooth and make your phone "discoverable"
On your Raspberry Pi:
1. Click :menuselection:`Bluetooth --> Turn On Bluetooth` (if it's off)
2. Click :menuselection:`Bluetooth --> Make Discoverable`
3. Click :menuselection:`Bluetooth --> Add Device`
4. Your phone will appear in the list, select it and click :guilabel:`Pair`
On your Android phone and Raspberry Pi.
1. Confirm the pairing code matches
2. Click OK
.. note::
You may receive errors relating to services not being able available or being unable to connect: these can be ignored, your phone and Raspberry Pi are now paired.
Using the Command Line
----------------------
On your Android phone:
1. Open Settings
2. Select Bluetooth and make your phone "discoverable"
On your Raspberry Pi:
1. Type :command:`bluetoothctl` and press Enter to open Bluetooth control
2. At the ``[bluetooth]#`` prompt enter the following commands::
discoverable on
pairable on
agent on
default-agent
scan on
3. Wait for a message to appear showing the Android phone has been found::
[NEW] Device 12:23:34:45:56:67 devicename
4. Type pair with the mac address of your Android phone::
pair 12:23:34:45:56:67
On your Android phone and Raspberry Pi.
1. Confirm the passcode.
2. Type :command:`quit` and press Enter to return to the command line
================================================
FILE: docs/pairpipi.rst
================================================
Pair 2 Raspberry Pis
====================
The instructions below describe pairing a couple of Raspberry Pis which either
have built-in Bluetooth (the Pi 3B or the Pi Zero W) or a USB Bluetooth dongle.
Using the Desktop
-----------------
On the first Raspberry Pi:
1. Click :menuselection:`Bluetooth --> Turn On Bluetooth` (if it's off)
2. Click :menuselection:`Bluetooth --> Make Discoverable`
On the second Raspberry Pi:
1. Click :menuselection:`Bluetooth --> Turn On Bluetooth` (if it's off)
2. Click :menuselection:`Bluetooth --> Make Discoverable`
3. Click :menuselection:`Bluetooth --> Add Device`
4. The first Pi will appear in the list: select it and click the :guilabel:`Pair` button
On the first Raspberry Pi again:
1. Accept the pairing request
.. note::
You may receive errors relating to services not being able available or being unable to connect: these can be ignored.
Using the Command Line
----------------------
On the first Raspberry Pi:
1. Enter :command:`bluetoothctl` to open Bluetooth control
2. At the ``[bluetooth]#`` prompt enter the following commands::
discoverable on
pairable on
agent on
default-agent
On the second Raspberry Pi:
1. Enter :command:`bluetoothctl` to open Bluetooth control
2. At the ``[bluetooth]#`` prompt enter the following commands::
discoverable on
pairable on
agent on
default-agent
scan on
3. Wait for a message to appear showing the first Pi has been found::
[NEW] Device 12:23:34:45:56:67 devicename
4. Type pair with the mac address of the first Pi::
pair 12:23:34:45:56:67
On both Raspberry Pi's:
1. Confirm the passcode.
2. Type :command:`quit` and press Enter to return to the command line
================================================
FILE: docs/protocol.rst
================================================
Protocol
========
Blue Dot uses a client/server model. The :class:`BlueDot` class starts a
Bluetooth server, the Blue Dot application connects as a client.
The detail below can be used to create new applications (clients); if you do
please send a pull request :)
Bluetooth
---------
Communication over Bluetooth is made using a RFCOMM serial port profile using
UUID "00001101-0000-1000-8000-00805f9b34fb".
Specification
-------------
The transmission of data from client to server or server to client is a
simple stream no acknowledgements or data is sent in response to commands.
All messages between conform to the same format::
[operation],[params],[*]\n
Messages are sent as utf-8 encoded strings.
*\\n* represents the new-line character.
The following operations are used to communicate between client and server.
+-------------------+-------------------------------------------------------------+-----------------+
| Operations | Message format | Direction |
+===================+=============================================================+=================+
| Button released | ``0,[col],[row],[x],[y]\n`` | Client > Server |
+-------------------+-------------------------------------------------------------+-----------------+
| Button pressed | ``1,[col],[row],[x],[y]\n`` | Client > Server |
+-------------------+-------------------------------------------------------------+-----------------+
| Button moved | ``2,[col],[row],[x],[y]\n`` | Client > Server |
+-------------------+-------------------------------------------------------------+-----------------+
| Protocol check | ``3,[protocol version],[client name]\n`` | Client > Server |
+-------------------+-------------------------------------------------------------+-----------------+
| Set config | ``4,[color],[square],[border],[visible],[cols],[rows]\n`` | Server > Client |
+-------------------+-------------------------------------------------------------+-----------------+
| Set button config | ``5,[color],[square],[border],[visible],[col],[row]\n`` | Server > Client |
+-------------------+-------------------------------------------------------------+-----------------+
Messages are constructed using the following parameters.
+-------------------+-------------------------------------------------------------------------------------------------------------+
| Parameter | Description |
+===================+=============================================================================================================+
| cols | The number of columns in the matrix of buttons |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| rows | The number of rows in the matrix of buttons |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| col | The column position of the button (0 is top) |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| row | The row position of the button (0 is left) |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| x | Horizontal position between -1 and +1, with 0 being the centre and +1 being the right radius of the button. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| y | Vertical position between -1 and +1, with 0 being the centre and +1 being the top radius of the button. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| protocol version | The version of protocol the client supports. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| client name | The name of the client e.g. "Android Blue Dot App" |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| color | A hex value in the format ``#rrggbbaa`` representing red, green, blue, alpha values. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| square | 0 or 1, 1 if the dot should be a square. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| border | 0 or 1, 1 if the dot should have a border. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
| visible | 0 or 1, 1 if the dot should be visible. |
+-------------------+-------------------------------------------------------------------------------------------------------------+
Messages are sent when:
1. A client connects
2. When the setup (or appearance) of a button changes
3. A button is released, pressed or moved
.. image:: images/protocol_state.png
:alt: Diagram showing the protocol states
Example
-------
When the Android client connects using protocol version 2::
3,2,Android Blue Dot app\n
The setup of the Blue Dot is sent to the client::
4,#0000ffff,0,0,1,1,2\n
If any buttons are different to the default, the configuration is sent::
5,#00ff0000,0,0,1,0,1\n
If the "first" button at position [0,0] is pressed at the top, the following message will be sent::
1,0,0,0.0,1.0\n
While the button is pressed (held down), the user moves their finger to the
far right causing the following message to be sent::
2,0,0,1.0,0.0\n
The button is then released, resulting in the following message::
0,0,0,1.0,0.0\n
The color of the button is changed to "red" the server sends to the client::
5,#ff0000ff,0,0,1,0,0\n
Versions
--------
* 0 - initial version
* 1 - introduction of operation 3, 4
* 2 - Blue Dot version 2, introduction of col, row for multiple buttons and operation 5
================================================
FILE: docs/recipes.rst
================================================
Recipes
=======
The recipes provide examples of how you can use Blue Dot. Don't be restricted
by these ideas and be sure to have a look at the :doc:`dotapi` as there is more
to be discovered.
Button
------
The simplest way to use the Blue Dot is as a wireless button.
Hello World
~~~~~~~~~~~
.. currentmodule:: bluedot
Let's say "Hello World" by creating the :class:`BlueDot` object then waiting
for the Blue Dot app to connect and the button be pressed:
.. literalinclude:: examples/hello_world.py
Alternatively you can also use :attr:`~BlueDot.when_pressed` to call a
function:
.. literalinclude:: examples/hello_event.py
:attr:`~BlueDot.wait_for_release` and :attr:`~BlueDot.when_released` also allow
you to interact when the button is released:
.. literalinclude:: examples/goodbye_world.py
Double presses can also be used with :attr:`~BlueDot.wait_for_double_press` and
:attr:`~BlueDot.when_double_pressed`:
.. literalinclude:: examples/shout_hello.py
Flash an LED
~~~~~~~~~~~~
Using Blue Dot in combination with :mod:`gpiozero` you can interact with
electronic components, such as LEDs, connected to your Raspberry Pi.
When a button is pressed, the LED connected to GPIO 27 will turn on; when
released it will turn off:
.. literalinclude:: examples/led1.py
You could also use :attr:`~BlueDot.when_pressed` and
:attr:`~BlueDot.when_released`:
.. literalinclude:: examples/led2.py
Alternatively use :attr:`~gpiozero.SourceMixin.source` and
:attr:`~BlueDot.values`:
.. literalinclude:: examples/led3.py
Remote Camera
~~~~~~~~~~~~~
Using a Raspberry Pi camera module, :class:`picamera.PiCamera` and
:class:`BlueDot`, you can really easily create a remote camera:
.. literalinclude:: examples/camera.py
Joystick
--------
The Blue Dot can also be used as a joystick when the middle, top, bottom, left
or right areas of the dot are touched.
D-pad
~~~~~
Using the position the Blue Dot was pressed you can work out whether it was
pressed to go up, down, left, right like the `D-pad`_ on a joystick:
.. literalinclude:: examples/dpad.py
At the moment the `D-pad`_ only registers when it is pressed. To get it work
when the position is moved you should add the following line above
:code:`pause()`::
bd.when_moved = dpad
Robot
~~~~~
These recipes assume your robot is constructed with a pair of H-bridges. The
forward and backward pins for the H-bridge of the left wheel are 17 and 18
respectively, and the forward and backward pins for H-bridge of the right wheel
are 22 and 23 respectively.
Using the Blue Dot and :class:`gpiozero.Robot`, you can create a `bluetooth
controlled robot`_ which moves when the dot is pressed and stops when it is
released:
.. literalinclude:: examples/robot1.py
Variable Speed Robot
~~~~~~~~~~~~~~~~~~~~
You can change the robot to use variable speeds, so the further towards the
edge you press the Blue Dot, the faster the robot will go.
The :attr:`~BlueDotPosition.distance` attribute returns how far from the centre
the Blue Dot was pressed, which can be passed to the robot's functions to
change its speed:
.. literalinclude:: examples/robot2.py
Alternatively you can use a generator and yield (x, y) values to the
:attr:`gpiozero.Robot.source` property (courtesy of `Ben Nuttall`_):
.. literalinclude:: examples/robot3.py
Appearance
----------
The button doesn't have to be blue or a dot, you can change how it looks, or make it completely invisible.
.. image:: images/bluedot_color_changing_smaller.gif
:alt: Animation of blue dot app cycling through colors and changing to a square
Colo(u)r
~~~~~~~~
To change the color of the button use the :attr:`~BlueDot.color`: property:
.. literalinclude:: examples/looks_color.py
A dictionary of available colors can be obtained from ``bluedot.COLORS``.
The color can also be set using a hex value of `#rrggbb` or `#rrggbbaa` value::
bd.color = "#00ff00"
Or a tuple of 3 or 4 integers between `0` and `255` either (red, gree, blue) or (red, green, blue, alpha)::
bd.color = (0, 255, 0)
Square
~~~~~~
The button can also be made square using the :attr:`~BlueDot.square`: property:
.. literalinclude:: examples/looks_square.py
Border
~~~~~~
A border can also been added to the button by setting the :attr:`~BlueDot.border`: property to `True`:
.. literalinclude:: examples/looks_border.py
(In)visible
~~~~~~~~~~~
The button can be hidden and shown using the :attr:`~BlueDot.visible`: property:
.. literalinclude:: examples/looks_visible.py
Layout
-------
You can have as many buttons as you want.
The Buttons need to be in a grid of columns and rows.
.. image:: images/layout_many_buttons_small.png
:alt: Android blue dot app showing 10 buttons in a 2x5 grid
By hiding specific buttons and being creative with the button's appearance you can create very sophisticated layouts for your controllers using Blue Dot.
.. image:: images/layout_joypad_small.png
:alt: Android blue dot app showing buttons layed out like a classic joypad
The Blue Dot android app supports multi touch allowing you to use multiple buttons simultaneously
.. note::
Currently only the Android client app supports multi buttons.
Two Buttons
~~~~~~~~~~~
Create 2 buttons side by side, by setting the number of `cols` to `2`:
.. image:: images/layout_2_buttons_small.png
:alt: Android blue dot app showing 2 buttons side by side
.. literalinclude:: examples/two_buttons.py
The buttons could be made verticle by setting the `rows` attribute::
bd = BlueDot(rows=2)
Each button can be set to call its own function by using the grid position:
.. literalinclude:: examples/two_buttons_two_events.py
To create a gap in between the buttons you could create a row of 3 buttons and hide the middle button:
.. image:: images/layout_2_buttons_gap_small.png
:alt: Android blue dot app showing 2 buttons side by side with a gap in the middle
.. literalinclude:: examples/two_buttons_gap.py
Many Buttons
~~~~~~~~~~~~
Create a grid of buttons by setting the `cols` and `rows` e.g. 10 buttons in a 2x5 grid:
.. image:: images/layout_many_buttons_small.png
:alt: Android blue dot app showing 10 buttons in a 2x5 grid
.. literalinclude:: examples/many_buttons.py
You could assign all the buttons random colors:
.. literalinclude:: examples/many_buttons_random_colors.py
D-pad
~~~~~
Create a traditional d-pad layout by using a 3x3 grid and hide the buttons at the corners and in the middle:
.. image:: images/layout_dpad_small.png
:alt: Android blue dot app showing 4 buttons arranged in a cross
.. literalinclude:: examples/dpad_layout.py
Add 2 buttons on the right to create a joypad:
.. image:: images/layout_joypad_small.png
:alt: Android blue dot app showing buttons layed out like a classic joypad
.. literalinclude:: examples/dpad_layout.py
Slider
------
By holding down a button and moving the position you can use it as an
analogue slider.
Centre Out
~~~~~~~~~~
Using the :attr:`BlueDotPosition.distance` property which is returned when the
position is moved you can create a slider which goes from the centre out in any
direction:
.. literalinclude:: examples/slider_centre.py
Left to Right
~~~~~~~~~~~~~
The :attr:`BlueDotPosition.x` property returns a value from -1 (far left) to 1
(far right). Using this value you can create a slider which goes horizontally
through the middle:
.. literalinclude:: examples/slider_left_right.py
To make a vertical slider you could change the code above to use
:attr:`BlueDotPosition.y` instead.
Dimmer Switch
~~~~~~~~~~~~~
Using the :class:`gpiozero.PWMLED` class and :class:`BlueDot` as a vertical
slider you can create a wireless dimmer switch:
.. literalinclude:: examples/slider_dimmer.py
Swiping
-------
You can interact with the Blue Dot by swiping across it, like you would to move
between pages in a mobile app.
Single
~~~~~~
Detecting a single swipe is easy using :attr:`~BlueDot.wait_for_swipe`:
.. literalinclude:: examples/swipe1.py
Alternatively you can also use :attr:`~BlueDot.when_swiped` to call a
function:
.. literalinclude:: examples/swipe2.py
Direction
~~~~~~~~~
You can tell what direction the Blue Dot is swiped by using the
:class:`BlueDotSwipe` object passed to the function assigned to
:attr:`~BlueDot.when_swiped`:
.. literalinclude:: examples/swipe_direction.py
Speed, Angle, and Distance
~~~~~~~~~~~~~~~~~~~~~~~~~~
:class:`BlueDotSwipe` returns more than just the direction. It also includes
the speed of the swipe (in Blue Dot radius per second), the angle, and the
distance between the start and end positions of the swipe:
.. literalinclude:: examples/swipe_speed_angle.py
Rotating
--------
You can use Blue Dot like a rotary encoder or "iPod classic click wheel" -
rotating around the outer edge of the Blue Dot will cause it to "tick". The
Blue Dot is split into a number of virtual segments (the default is 8), when
the position moves from one segment to another, it ticks.
Counter
~~~~~~~
Using the :attr:`~BlueDot.when_rotated` callback you can create a counter which
increments / decrements when the Blue Dot is rotated either clockwise or
anti-clockwise. A :class:`BlueDotRotation` object is passed to the callback.
Its :attr:`~BlueDotRotation.value` property will be -1 if rotated
anti-clockwise and 1 if rotated clockwise:
.. literalinclude:: examples/rotation.py
The rotation speed can be modified using the :attr:`BlueDot.rotation_segments`
property which changes the number of segments the Blue Dot is split into::
bd.rotation_segments = 16
Multiple Blue Dot Apps
----------------------
You can connect multiple Blue Dot clients (apps) to a single server (python
program) by using different Bluetooth ports for each app.
Create multiple `BlueDot` servers using specific ports:
.. literalinclude:: examples/multiple_dots.py
Change the BlueDot app to use the specific port by:
1. Opening settings from the menu
2. Turning *Use default port* off
3. Selecting the specific *Bluetooth port*
.. image:: images/bluedotandroid_settings.png
:alt: Android blue dot app showing the settings option on the menu
.. image:: images/bluedotandroid_settings_defaultport.png
:alt: Android blue dot app showing the settings page and use default port turned on
.. image:: images/bluedotandroid_settings_port.png
:alt: Android blue dot app showing the settings page, use default port turned off and bluetooth port 1 selected
Bluetooth
---------
You can interact with the Bluetooth adapter using :class:`BlueDot`.
Pairing
~~~~~~~
You can put your Raspberry Pi into pairing mode which will allow pairing from
other devices for 60 seconds:
.. literalinclude:: examples/bt_pairing.py
Or connect up a physical button up to start the pairing (the button is assumed
to be wired to GPIO 27):
.. literalinclude:: examples/bt_pair_button.py
Paired Devices
~~~~~~~~~~~~~~
You can iterate over the devices that your Raspberry Pi is paired too:
.. literalinclude:: examples/bt_enumerate.py
Testing
-------
Blue Dot includes a :class:`MockBlueDot` class to allow you to test and debug
your program without having to use Bluetooth or a Blue Dot client.
:class:`MockBlueDot` inherits from :class:`BlueDot` and is used in the same
way, but you have the option of launching a mock app which you can click with a
mouse or writing scripts to simulate the Blue Dot being used.
.. image:: images/mockbluedot.png
:alt: Screenshot of the mock Blue Dot app
Mock App
~~~~~~~~
Launch the mock Blue Dot app to test by clicking the on-screen dot with the
mouse:
.. literalinclude:: examples/mock_app.py
Scripted Tests
~~~~~~~~~~~~~~
Tests can also be scripted using :class:`MockBlueDot`:
.. literalinclude:: examples/mock_script.py
.. _Ben Nuttall: https://github.com/bennuttall
.. _bluetooth controlled robot: https://youtu.be/eW9oEPySF58
.. _D-pad: https://en.wikipedia.org/wiki/D-pad
================================================
FILE: examples/adapter_details.py
================================================
from bluedot import BlueDot
bd = BlueDot()
print("Address {}".format(bd.adapter.address))
print("powered {}".format(bd.adapter.powered))
print("discoverable {}".format(bd.adapter.discoverable))
print("pairable {}".format(bd.adapter.pairable))
print("paired devices:")
paired_devices = bd.paired_devices
for d in paired_devices:
print(" mac {} name {}".format(d[0], d[1]))
================================================
FILE: examples/click_wheel.py
================================================
from bluedot import BlueDot
from signal import pause
count = 0
def rotated(rotation):
global count
count += rotation.value
print("{} {} {}".format(count, rotation.clockwise, rotation.anti_clockwise))
bd = BlueDot()
bd.when_rotated = rotated
pause()
================================================
FILE: examples/client_connects.py
================================================
from bluedot import BlueDot
from signal import pause
def client_connected():
print("callback - a blue dot client connected")
def client_disconnected():
print("callback - the blue dot client disconnected")
bd = BlueDot()
bd.when_client_connects = client_connected
bd.when_client_disconnects = client_disconnected
pause()
================================================
FILE: examples/client_debug.py
================================================
from bluedot.btcomm import BluetoothClient
from datetime import datetime
from time import sleep
from signal import pause
def data_received(data):
print("recv - {}".format(data))
print("Connecting")
c = BluetoothClient("devpi", data_received)
print("Sending")
try:
while True:
c.send("hi {} \n".format(str(datetime.now())))
sleep(1)
finally:
c.disconnect()
================================================
FILE: examples/color_changer.py
================================================
from bluedot import BlueDot
# color zero can be installed using sudo pip3 install colorzero
from colorzero import Color
from signal import pause
def on(pos):
hue = (pos.angle + 180) / 360
c = Color(h=hue, s=1, v=pos.distance)
bd.color = c.rgb_bytes
def off():
bd.color = "blue"
bd = BlueDot()
bd.when_pressed = on
bd.when_moved = on
bd.when_released = off
pause()
================================================
FILE: examples/connect_multiple_apps.py
================================================
from bluedot import BlueDot
from signal import pause
def bd1_pressed():
print("BlueDot 1 pressed")
def bd2_pressed():
print("BlueDot 2 pressed")
bd1 = BlueDot(port = 1)
bd2 = BlueDot(port = 2)
bd1.when_pressed = bd1_pressed
bd2.when_pressed = bd2_pressed
pause()
================================================
FILE: examples/dot_changer.py
================================================
from bluedot import BlueDot
from bluedot.colors import RED, GREEN
from signal import pause
def change_dot(pos):
if pos.top:
if bd.color == "red":
bd.color = GREEN
else:
bd.color = "#ff0000"
elif pos.bottom:
if bd.border:
bd.border = False
else:
bd.border = True
elif pos.left:
if bd.visible:
bd.visible = False
else:
bd.visible = True
elif pos.right:
if bd.square:
bd.square = False
else:
bd.square = True
bd = BlueDot()
bd.color="pink"
bd.border = False
bd.square = True
bd.when_pressed = change_dot
pause()
================================================
FILE: examples/dot_debug.py
================================================
from bluedot import BlueDot
from time import sleep, time
dot = BlueDot(auto_start_server = False)
dot.resize(1,2)
dot.allow_pairing()
def pressed(pos):
print("Pressed: x={} y={} angle={} distance={} middle={} top={} bottom={} left={} right={} time={}".format(pos.x, pos.y, pos.angle, pos.distance, pos.middle, pos.top, pos.bottom, pos.left, pos.right, time()))
def pressed_two(pos):
print("Second dot:" + str(pos))
def released():
print("Released: x={} y={}".format(dot.position.x, dot.position.y))
def moved(pos):
print("Moved: x={} y={}".format(pos.x, pos.y))
def swiped(swipe):
print("Swiped: up={} down={} left={} right={} speed={}".format(swipe.up, swipe.down, swipe.left, swipe.right, swipe.speed))
def double_presed(pos):
print("Double pressed: x={} y={}".format(pos.x, pos.y))
def client_connected():
print("connected callback")
def client_disconnected():
print("disconnected callback")
def rotated(rotation):
print("rotated: direction={}".format(rotation.value))
dot.when_client_connects = client_connected
dot.when_client_disconnects = client_disconnected
dot.when_pressed = pressed
dot.when_released = released
dot.when_moved = moved
dot.when_swiped = swiped
dot.when_double_pressed = double_presed
dot.when_rotated = rotated
dot[0,1].when_pressed = pressed_two
dot.start()
dot.wait_for_press()
print("wait for press")
dot.wait_for_move()
print("wait for move")
dot.wait_for_release()
print("wait for release")
dot.wait_for_double_press()
print("wait for double press")
dot.wait_for_swipe()
print("wait for swipe")
try:
while True:
sleep(0.1)
finally:
dot.stop()
================================================
FILE: examples/dot_single_button_debug.py
================================================
from bluedot import BlueDot
from time import sleep, time
dot = BlueDot(auto_start_server = False)
dot.allow_pairing()
def pressed(pos):
print("Pressed: x={} y={} angle={} distance={} middle={} top={} bottom={} left={} right={} time={}".format(pos.x, pos.y, pos.angle, pos.distance, pos.middle, pos.top, pos.bottom, pos.left, pos.right, time()))
def released():
print("Released: x={} y={}".format(dot.position.x, dot.position.y))
def moved(pos):
print("Moved: x={} y={}".format(pos.x, pos.y))
def swiped(swipe):
print("Swiped: up={} down={} left={} right={} speed={}".format(swipe.up, swipe.down, swipe.left, swipe.right, swipe.speed))
def double_presed(pos):
print("Double pressed: x={} y={}".format(pos.x, pos.y))
def client_connected():
print("connected callback")
def client_disconnected():
print("disconnected callback")
dot.when_client_connects = client_connected
dot.when_client_disconnects = client_disconnected
dot.when_pressed = pressed
dot.when_released = released
dot.when_moved = moved
dot.when_swiped = swiped
dot.when_double_pressed = double_presed
dot.start()
dot.wait_for_press()
print("wait for press")
dot.wait_for_move()
print("wait for move")
dot.wait_for_release()
print("wait for release")
dot.wait_for_double_press()
print("wait for double press")
dot.wait_for_swipe()
print("wait for swipe")
try:
while True:
sleep(0.1)
finally:
dot.stop()
================================================
FILE: examples/double_press.py
================================================
from signal import pause
from bluedot import BlueDot
bd = BlueDot()
def double_pressed():
print("double pressed")
bd.when_double_pressed = double_pressed
pause()
================================================
FILE: examples/dpad.py
================================================
from bluedot import BlueDot
from signal import pause
def dpad(pos):
if pos.top:
print("up")
elif pos.bottom:
print("down")
elif pos.left:
print("left")
elif pos.right:
print("right")
bd = BlueDot()
bd.when_pressed = dpad
bd.when_moved = dpad
pause()
================================================
FILE: examples/dpad_layout.py
================================================
from bluedot import BlueDot
from signal import pause
def up():
print("up")
def down():
print("down")
def left():
print("left")
def right():
print("right")
bd = BlueDot(cols=3, rows=3)
bd.color = "gray"
bd.square = True
bd[0,0].visible = False
bd[2,0].visible = False
bd[0,2].visible = False
bd[2,2].visible = False
bd[1,1].visible = False
bd[1,0].when_pressed = up
bd[1,2].when_pressed = down
bd[0,1].when_pressed = left
bd[2,1].when_pressed = right
pause()
================================================
FILE: examples/hello_world.py
================================================
from bluedot import BlueDot
dot = BlueDot()
dot.wait_for_press()
print("You pressed the blue dot - Hello world")
================================================
FILE: examples/joypad.py
================================================
from bluedot import BlueDot
from signal import pause
def up():
print("up")
def down():
print("down")
def left():
print("left")
def right():
print("right")
def button_A():
print("A")
def button_B():
print("B")
bd = BlueDot(cols=5, rows=3)
# dpad buttons
bd.color = "gray"
bd.square = True
bd[0,0].visible = False
bd[2,0].visible = False
bd[0,2].visible = False
bd[2,2].visible = False
bd[1,1].visible = False
bd[1,0].when_pressed = up
bd[1,2].when_pressed = down
bd[0,1].when_pressed = left
bd[2,1].when_pressed = right
# buttons
bd[3,0].visible = False
bd[4,0].visible = False
bd[3,2].visible = False
bd[4,2].visible = False
bd[3,1].color = "blue"
bd[3,1].square = False
bd[3,1].when_pressed = button_A
bd[4,1].color = "red"
bd[4,1].when_pressed = button_B
pause()
================================================
FILE: examples/many_buttons.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=2, rows=5)
bd.when_pressed = pressed
pause()
================================================
FILE: examples/matrix_of_dots.py
================================================
from bluedot import BlueDot, COLORS
from signal import pause
from random import choice
bd = BlueDot()
bd.resize(1,2)
def pressed(pos):
print("Pressed : {}".format(pos))
def moved(pos):
print("Moved : {}".format(pos))
def released(pos):
print("Released : {}".format(pos))
def double_press(pos):
print("Double press : {}".format(pos))
def swipe(swipe):
print("Swipe : {}".format(swipe))
def rotation(rotation):
print("Rotation : {}".format(rotation))
def increase_matrix():
bd.resize(bd._cols + 1, bd._rows + 1)
# bd._send_cell_config(2, 2, "#ff0000ff", False, False, True)
def change_color():
# increase_matrix()
# bd[bd.cols - 1, bd.rows - 1].color = "green"
for c in range(bd.cols):
for r in range(bd.rows):
bd[c,r].color = choice(list(COLORS.keys()))
#print(bd.cells)
change_color()
# bd.when_pressed = increase_matrix
bd[0,0].when_pressed = pressed
bd[0,0].when_moved = moved
bd[0,0].when_released = released
bd[0,0].when_double_pressed = double_press
bd[0,0].when_swiped = swipe
bd[0,0].when_rotated = rotation
bd.when_pressed = pressed
bd.when_moved = moved
bd.when_released = released
bd.when_double_pressed = double_press
bd.when_swiped = swipe
bd.when_rotated = rotation
pause()
================================================
FILE: examples/mock_app_debug.py
================================================
from bluedot import MockBlueDot
from time import sleep, time
mbd = MockBlueDot(auto_start_server = False)
def pressed(pos):
print("Pressed: x={} y={} angle={} distance={} middle={} top={} bottom={} left={} right={} time={}".format(pos.x, pos.y, pos.angle, pos.distance, pos.middle, pos.top, pos.bottom, pos.left, pos.right, time()))
def released():
print("Released: x={} y={}".format(mbd.position.x, mbd.position.y))
print()
def moved(pos):
print("Moved: x={} y={}".format(pos.x, pos.y))
mbd.when_pressed = pressed
mbd.when_released = released
mbd.when_moved = moved
mbd.start()
#launch a mock app
mbd.launch_mock_app()
try:
while True:
sleep(1)
finally:
mbd.mock_client_disconnected()
mbd.stop()
================================================
FILE: examples/mock_btcomm_debug.py
================================================
from bluedot.mock import MockBluetoothClient, MockBluetoothServer
def s_client_connected():
print("s: c connected")
def s_data_received(data):
print("s: recv - {}".format(data))
print("s: creating")
s = MockBluetoothServer(
s_data_received,
when_client_connects=s_client_connected
)
def c_data_received(data):
print("c: recv - {}".format(data))
print("c: creating")
c = MockBluetoothClient(
s,
c_data_received
)
c.send("hi")
s.send("bye")
================================================
FILE: examples/mock_debug.py
================================================
from bluedot import MockBlueDot
from time import sleep, time
mbd = MockBlueDot(auto_start_server = False)
def pressed(pos):
print("Pressed: x={} y={} angle={} distance={} middle={} top={} bottom={} left={} right={} time={}".format(pos.x, pos.y, pos.angle, pos.distance, pos.middle, pos.top, pos.bottom, pos.left, pos.right, time()))
def released():
print("Released: x={} y={}".format(mbd.position.x, mbd.position.y))
print()
def moved(pos):
print("Moved: x={} y={}".format(pos.x, pos.y))
mbd.when_pressed = pressed
mbd.when_released = released
mbd.when_moved = moved
mbd.start()
mbd.mock_client_connected()
mbd.wait_for_connection()
try:
while True:
mbd.mock_blue_dot_pressed(1.0,1.0)
mbd.mock_blue_dot_moved(0.5,1.0)
mbd.mock_blue_dot_released(0.5,1.0)
sleep(0.1)
finally:
mbd.mock_client_disconnected()
mbd.stop()
================================================
FILE: examples/remote_camera.py
================================================
from bluedot import BlueDot
from picamera import PiCamera
from signal import pause
dot = BlueDot()
cam = PiCamera()
def take_picture():
cam.capture("pic.jpg")
dot.when_pressed = take_picture
pause()
================================================
FILE: examples/server_debug.py
================================================
from bluedot.btcomm import BluetoothServer
from time import sleep
from signal import pause
def data_received(data):
print("recv - {}".format(data))
server.send(data)
def client_connected():
print("client connected")
def client_disconnected():
print("client disconnected")
print("init")
server = BluetoothServer(
data_received,
auto_start = False,
when_client_connects = client_connected,
when_client_disconnects = client_disconnected)
print("starting")
server.start()
print(server.server_address)
print("waiting for connection")
try:
pause()
except KeyboardInterrupt as e:
print("cancelled by user")
finally:
print("stopping")
server.stop()
print("stopped")
================================================
FILE: examples/simple_robot.py
================================================
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause
bd = BlueDot()
robot = Robot(left=(10, 9), right=(8, 7))
def move(pos):
if pos.top:
robot.forward()
elif pos.bottom:
robot.backward()
elif pos.left:
robot.left()
elif pos.right:
robot.right()
def stop():
robot.stop()
bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop
pause()
================================================
FILE: examples/simple_variable_robot.py
================================================
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause
bd = BlueDot()
robot = Robot(left=(10, 9), right=(8, 7))
def move(pos):
if pos.top:
robot.forward(pos.distance)
elif pos.bottom:
robot.backward(pos.distance)
elif pos.left:
robot.left(pos.distance)
elif pos.right:
robot.right(pos.distance)
def stop():
robot.stop()
bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop
pause()
================================================
FILE: examples/slider_centre.py
================================================
from bluedot import BlueDot
from signal import pause
def show_percentage(pos):
percentage = round(pos.distance * 100, 2)
print("{}%".format(percentage))
bd = BlueDot()
bd.when_moved = show_percentage
pause()
================================================
FILE: examples/slider_horizontal.py
================================================
from bluedot import BlueDot
from signal import pause
def show_percentage(pos):
horizontal = ((pos.x + 1) / 2)
percentage = round(horizontal * 100, 2)
print("{}%".format(percentage))
bd = BlueDot()
bd.when_moved = show_percentage
pause()
================================================
FILE: examples/slider_led_dimmer.py
================================================
from bluedot import BlueDot
from gpiozero import PWMLED
from signal import pause
def set_brightness(pos):
brightness = ((pos.y + 1) / 2)
led.value = brightness
bd = BlueDot()
bd.when_moved = set_brightness
led = PWMLED(17)
pause()
================================================
FILE: examples/source_robot.py
================================================
from gpiozero import Robot
from bluedot import BlueDot
from signal import pause
def pos_to_values(x, y):
left = y if x > 0 else y + x
right = y if x < 0 else y - x
return (clamped(left), clamped(right))
def clamped(v):
return max(-1, min(1, v))
def drive():
while True:
if bd.is_pressed:
x, y = bd.position.x, bd.position.y
yield pos_to_values(x, y)
else:
yield (0, 0)
if __name__ == '__main__':
robot = Robot(left=(10, 9), right=(8, 7))
bd = BlueDot()
robot.source = drive()
pause()
================================================
FILE: examples/swipe_debug.py
================================================
from bluedot import BlueDot, BlueDotSwipe
from signal import pause
bd = BlueDot()
print("waiting for swipe")
bd.wait_for_swipe()
print("swiped")
def released():
swipe = BlueDotSwipe(bd.interaction)
if not swipe.valid:
print("Invalid swipe - speed {}".format(swipe.speed))
def valid_swipe(swipe):
#swipe = BlueDotSwipe(bd.interaction)
#print("valid {} duration {} distance {} angle {}".format(swipe.valid, swipe.interaction.duration, swipe.distance, swipe.angle))
#print("up {} down {} left {} right {}".format(swipe.up, swipe.down, swipe.left, swipe.right))
if swipe.up:
print("UP {}".format(swipe.speed))
elif swipe.down:
print("DOWN {}".format(swipe.speed))
elif swipe.left:
print("LEFT {}".format(swipe.speed))
elif swipe.right:
print("RIGHT {}".format(swipe.speed))
bd.when_released = released
bd.when_swiped = valid_swipe
pause()
================================================
FILE: examples/threaded_callbacks.py
================================================
from bluedot import BlueDot
from time import sleep
from signal import pause
bd = BlueDot()
def pressed():
print("pressed - waiting")
sleep(3)
print("pressed - complete")
def released():
print("released")
bd.set_when_pressed(pressed, background=True)
bd.when_released = released
pause()
================================================
FILE: examples/two_buttons.py
================================================
from bluedot import BlueDot
from signal import pause
def pressed(pos):
print("button {}.{} pressed".format(pos.col, pos.row))
bd = BlueDot(cols=2)
bd.when_pressed = pressed
pause()
================================================
FILE: examples/wait_for_events.py
================================================
from bluedot import BlueDot
from turtle import Turtle
bd = BlueDot()
while True:
bd.wait_for_press()
print("pressed")
bd.wait_for_move()
print("moved")
bd.wait_for_release()
print("released")
bd.wait_for_double_press()
print("double press")
bd.wait_for_swipe()
print("swipe")
================================================
FILE: setup.py
================================================
import sys
from setuptools import setup
if sys.version_info.major == 2:
raise ValueError('This package does not support Python 2')
elif sys.version_info.major == 3:
if sys.version_info.minor < 5:
raise ValueError('This package requires Python 3.5 or newer')
else:
raise ValueError('Python version not identified')
__project__ = 'bluedot'
__desc__ = 'A zero boiler plate bluetooth remote'
__version__ = '2.0.0'
__author__ = "Martin O'Hanlon"
__author_email__ = 'martin@ohanlonweb.com'
__license__ = 'MIT'
__url__ = 'https://github.com/martinohanlon/BlueDot'
__python_requires__ = '>=3.5'
# __requires__ = ['dbus-python',]
__keywords__ = [
"raspberry",
"pi",
"raspberry pi",
"bluetooth",
"remote",
"android",
]
__classifiers__ = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Education",
"Intended Audience :: Developers",
"Topic :: Education",
"Topic :: Communications",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
]
__long_description__ = """# Blue Dot
[Blue Dot](http://bluedot.readthedocs.io/en/latest/) allows you to control your Raspberry Pi projects wirelessly - it's a Bluetooth remote and zero boiler plate (super simple to use :) Python library.
## Getting started
1. Install
```
sudo pip3 install bluedot
```
2. Get the [Android Blue Dot app](http://play.google.com/store/apps/details?id=com.stuffaboutcode.bluedot) or use the [Python Blue Dot app](http://bluedot.readthedocs.io/en/latest/bluedotpythonapp.html)
3. Pair your Raspberry Pi
4. Write some code
```python
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](http://bluedot.readthedocs.io/en/latest/gettingstarted.html) 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.
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](http://bluedot.readthedocs.io/en/latest/) describes how to use Blue Dot and the Python library including recipes and ideas.
"""
if __name__ == '__main__':
setup(name='bluedot',
version = __version__,
description = __desc__,
long_description=__long_description__,
long_description_content_type='text/markdown',
url = __url__,
author = __author__,
author_email = __author_email__,
license= __license__,
keywords=__keywords__,
classifiers=__classifiers__,
packages = [__project__],
# install_requires = __requires__,
entry_points={
'console_scripts': [
'bluedotapp = bluedot.app:main'
]},
)
================================================
FILE: tests/test_blue_dot.py
================================================
import pytest
from bluedot import MockBlueDot, BlueDotSwipe, BlueDotRotation
from bluedot.exceptions import ButtonDoesNotExist
from time import sleep
from threading import Event, Thread
def test_default_values():
mbd = MockBlueDot()
assert mbd.device == "hci0"
assert mbd.port == 1
assert mbd.running
assert mbd.cols == 1
assert mbd.rows == 1
assert mbd.print_messages
assert mbd.double_press_time == 0.3
assert mbd.rotation_segments == 8
assert mbd.when_client_connects == None
assert mbd.when_client_disconnects == None
assert mbd.when_pressed == None
assert mbd.when_double_pressed == None
assert mbd.when_moved == None
assert mbd.when_released == None
assert mbd.when_swiped == None
assert len(mbd.buttons) == 1
def test_modify_values():
mbd = MockBlueDot(device = "hci1", port = 2, auto_start_server = False, print_messages = False, cols = 3, rows = 2)
assert mbd.device == "hci1"
assert mbd.port == 2
assert not mbd.running
assert mbd.cols == 3
assert mbd.rows == 2
assert not mbd.print_messages
mbd.print_messages = True
assert mbd.print_messages
assert mbd.double_press_time == 0.3
mbd.double_press_time = 0.4
assert mbd.double_press_time == 0.4
assert mbd.rotation_segments == 8
mbd.rotation_segments = 16
assert mbd.rotation_segments == 16
assert len(mbd.buttons) == 6
def test_start_stop():
mbd = MockBlueDot(auto_start_server = False)
assert not mbd.running
mbd.start()
assert mbd.running
mbd.stop()
assert not mbd.running
def test_connect_disconnect():
mbd = MockBlueDot()
assert not mbd.is_connected
mbd.mock_client_connected()
assert mbd.wait_for_connection(1)
assert mbd.is_connected
mbd.mock_client_disconnected()
assert not mbd.is_connected
def test_when_connect_disconnect():
mbd = MockBlueDot()
event_connect = Event()
mbd.when_client_connects = lambda: event_connect.set()
event_disconnect = Event()
mbd.when_client_disconnects = lambda: event_disconnect.set()
assert not event_connect.is_set()
mbd.mock_client_connected()
assert event_connect.wait(1)
assert not event_disconnect.is_set()
mbd.mock_client_disconnected()
assert event_disconnect.wait(1)
def test_when_connect_disconnect_background():
mbd = MockBlueDot()
event_connect = Event()
mbd.set_when_client_connects(lambda: delay_function(event_connect.set, 0.2), background=True)
event_disconnect = Event()
mbd.set_when_client_disconnects(lambda: delay_function(event_disconnect.set, 0.2), background=True)
mbd.when_client_disconnects = lambda: event_disconnect.set()
assert not event_connect.is_set()
mbd.mock_client_connected()
assert not event_connect.is_set()
assert event_connect.wait(1)
assert not event_disconnect.is_set()
mbd.mock_client_disconnected()
assert not event_disconnect.is_set()
assert event_disconnect.wait(1)
def test_resize():
mbd = MockBlueDot()
mbd.resize(4,3)
assert mbd.cols == 4
assert mbd.rows == 3
assert len(mbd.buttons) == 12
def test_pressed_moved_released():
mbd = MockBlueDot()
mbd.mock_client_connected()
def pressed_moved_released(dot, col, row):
#initial value
assert not mbd.is_pressed
assert dot.value == 0
#pressed
mbd.mock_blue_dot_pressed(col,row,0,0)
assert dot.is_pressed
assert dot.value == 1
#released
mbd.mock_blue_dot_released(col,row,0,0)
assert not dot.is_pressed
assert dot.value == 0
#wait_for_press
delay_function(lambda: mbd.mock_blue_dot_pressed(col,row,0,0), 0.5)
assert dot.wait_for_press(1)
assert not dot.wait_for_release(0)
#wait_for_release
delay_function(lambda: mbd.mock_blue_dot_released(col,row,0,0), 0.5)
assert dot.wait_for_release(1)
assert not dot.wait_for_press(0)
def not_pressed(dot, col, row):
assert not dot.is_pressed
assert not dot.value == 1
mbd.mock_blue_dot_pressed(col,row,0,0)
assert not dot.is_pressed
assert not dot.value == 1
# single button
pressed_moved_released(mbd, 0, 0)
pressed_moved_released(mbd[0,0], 0, 0)
# resize to 2 buttons
mbd.resize(2, 1)
# test second button and main
pressed_moved_released(mbd, 1, 0)
pressed_moved_released(mbd[1,0], 1, 0)
# test second button isn't pressed by first
not_pressed(mbd[1,0], 0, 0)
def test_double_press():
mbd = MockBlueDot()
mbd.mock_client_connected()
def simulate_double_press(col, row):
#sleep longer than the double press time, to clear any past double presses!
sleep(mbd.double_press_time + 0.1)
mbd.mock_blue_dot_pressed(col,row,0,0)
mbd.mock_blue_dot_released(col,row,0,0)
mbd.mock_blue_dot_pressed(col,row,0,0)
mbd.mock_blue_dot_released(col,row,0,0)
def simulate_failed_double_press(col, row):
sleep(mbd.double_press_time + 0.1)
mbd.mock_blue_dot_pressed(col,row,0,0)
mbd.mock_blue_dot_released(col,row,0,0)
sleep(mbd.double_press_time + 0.1)
mbd.mock_blue_dot_pressed(col,row,0,0)
mbd.mock_blue_dot_released(col,row,0,0)
def double_press(dot, col, row):
# when_double_pressed
event_double_pressed = Event()
dot.when_double_pressed = lambda: event_double_pressed.set()
simulate_failed_double_press(col, row)
assert not event_double_pressed.is_set()
simulate_double_press(col, row)
assert event_double_pressed.is_set()
# wait for double press
# double press the blue dot
delay_function(lambda: simulate_double_press(col, row), 0.2)
# wait for double press
assert dot.wait_for_double_press(1)
# dont double press the blue dot
delay_function(lambda: simulate_failed_double_press(col, row), 0.2)
assert not dot.wait_for_double_press(1)
def not_double_press(dot, col, row):
# when_double_pressed
event_double_pressed = Event()
dot.when_double_pressed = lambda: event_double_pressed.set()
simulate_double_press(col, row)
assert not event_double_pressed.is_set()
# single button
double_press(mbd, 0, 0)
double_press(mbd[0,0], 0, 0)
mbd.resize(2, 1)
# two buttons
double_press(mbd, 1, 0)
double_press(mbd[1,0], 1, 0)
# first button doesnt double press second button
not_double_press(mbd[1,0], 0, 0)
def test_when_pressed_moved_released():
mbd = MockBlueDot()
mbd.mock_client_connected()
def when_pressed_moved_released(dot, col, row):
#when_pressed
event_pressed = Event()
dot.when_pressed = lambda: event_pressed.set()
#when_double_pressed
event_double_pressed = Event()
dot.when_double_pressed = lambda: event_double_pressed.set()
#when_moved
event_moved = Event()
dot.when_moved = lambda: event_moved.set()
#when_released
event_released = Event()
dot.when_released = lambda: event_released.set()
assert not event_pressed.is_set()
mbd.mock_blue_dot_pressed(col,row,0,0)
assert event_pressed.is_set()
assert not event_moved.is_set()
mbd.mock_blue_dot_moved(col,row,1,1)
assert event_moved.is_set()
assert not event_released.is_set()
mbd.mock_blue_dot_released(col,row,0,0)
assert event_released.is_set()
assert not event_double_pressed.is_set()
mbd.mock_blue_dot_pressed(col,row,0,0)
assert event_double_pressed.is_set()
when_pressed_moved_released(mbd, 0, 0)
when_pressed_moved_released(mbd[0,0], 0, 0)
mbd.resize(2,1)
when_pressed_moved_released(mbd, 1, 0)
when_pressed_moved_released(mbd[1,0], 1, 0)
def test_when_pressed_moved_released_background():
mbd = MockBlueDot()
mbd.mock_client_connected()
def when_pressed_moved_released_background(dot, col, row):
#when_pressed
event_pressed = Event()
dot.set_when_pressed(lambda: delay_function(event_pressed.set, 0.2), background=True)
#when_double_pressed
event_double_pressed = Event()
dot.set_when_double_pressed(lambda: delay_function(event_double_pressed.set, 0.2), background=True)
#when_moved
event_moved = Event()
dot.set_when_moved(lambda: delay_function(event_moved.set, 0.2), background=True)
#when_released
event_released = Event()
dot.set_when_released(lambda: delay_function(event_released.set, 0.2), background=True)
# test that the events dont block
assert not event_pressed.is_set()
mbd.mock_blue_dot_pressed(col,row,0,0)
assert not event_pressed.is_set()
assert event_pressed.wait(1)
assert not event_moved.is_set()
mbd.mock_blue_dot_moved(col,row,1,1)
assert not event_moved.is_set()
assert event_moved.wait(1)
assert not event_released.is_set()
mbd.mock_blue_dot_released(col,row,0,0)
assert not event_released.is_set()
assert event_released.wait(1)
# set pressed, moved, released to None so they dont wait
mbd.set_when_pressed(None)
mbd.set_when_moved(None)
mbd.set_when_released(None)
mbd.mock_blue_dot_pressed(col,row,0,0)
mbd.mock_blue_dot_moved(col,row,1,1)
mbd.mock_blue_dot_released(col,row,0,0)
assert not event_double_pressed.is_set()
mbd.mock_blue_dot_pressed(col,row,0,0)
assert not event_double_pressed.is_set()
assert event_double_pressed.wait(1)
when_pressed_moved_released_background(mbd, 0, 0)
when_pressed_moved_released_background(mbd[0,0], 0, 0)
mbd.resize(2,1)
when_pressed_moved_released_background(mbd, 1, 0)
when_pressed_moved_released_background(mbd[1,0], 1, 0)
def test_position():
mbd = MockBlueDot()
mbd.mock_client_connected()
def position(dot, col, row):
mbd.mock_blue_dot_pressed(col,row,0,0)
assert not mbd.position.top
assert mbd.position.middle
assert not mbd.position.bottom
assert not mbd.position.left
assert not mbd.position.right
mbd.mock_blue_dot_moved(col,row,1,0)
assert not mbd.position.top
assert not mbd.position.middle
assert not mbd.position.bottom
assert not mbd.position.left
assert mbd.position.right
mbd.mock_blue_dot_moved(col,row,-1,0)
assert not mbd.position.top
assert not mbd.position.middle
assert not mbd.position.bottom
assert mbd.position.left
assert not mbd.position.right
mbd.mock_blue_dot_moved(col,row,0,1)
assert mbd.position.top
assert not mbd.position.middle
assert not mbd.position.bottom
assert not mbd.position.left
assert not mbd.position.right
mbd.mock_blue_dot_moved(col,row,0,-1)
assert not mbd.position.top
assert not mbd.position.middle
assert mbd.position.bottom
assert not mbd.position.left
assert not mbd.position.right
mbd.mock_blue_dot_moved(col,row,0.1234, -0.4567)
assert mbd.position.x == 0.1234
assert mbd.position.y == -0.4567
mbd.mock_blue_dot_moved(col,row,1, 0)
assert mbd.position.distance == 1
assert mbd.position.angle == 90
position(mbd, 0, 0)
position(mbd[0,0], 0, 0)
mbd.resize(2,1)
position(mbd[1,0], 1, 0)
def test_interaction():
mbd = MockBlueDot()
mbd.mock_client_connected()
def interaction(dot, col, row):
assert dot.interaction == None
mbd.mock_blue_dot_pressed(col,row,-1,0)
assert dot.interaction.active
assert len(dot.interaction.positions) == 1
assert dot.interaction.distance == 0
assert dot.interaction.pressed_position.x == -1
assert dot.interaction.pressed_position.y == 0
assert dot.interaction.current_position.x == -1
assert dot.interaction.current_position.y == 0
assert dot.interaction.previous_position == None
assert dot.interaction.released_position == None
mbd.mock_blue_dot_moved(col,row,0,0)
assert dot.interaction.active
assert len(dot.interaction.positions) == 2
assert dot.interaction.distance == 1
assert dot.interaction.pressed_position.x == -1
assert dot.interaction.pressed_position.y == 0
assert dot.interaction.current_position.x == 0
assert dot.interaction.current_position.y == 0
assert dot.interaction.previous_position.x == -1
assert dot.interaction.previous_position.y == 0
assert dot.interaction.released_position == None
mbd.mock_blue_dot_released(col,row,1,0)
assert not dot.interaction.active
assert len(dot.interaction.positions) == 3
assert dot.interaction.distance == 2
assert dot.interaction.pressed_position.x == -1
assert dot.interaction.pressed_position.y == 0
assert dot.interaction.current_position.x == 1
assert dot.interaction.current_position.y == 0
assert dot.interaction.previous_position.x == 0
assert dot.interaction.previous_position.y == 0
assert dot.interaction.released_position.x == 1
assert dot.interaction.released_position.y == 0
interaction(mbd[0,0], 0, 0)
mbd.resize(2,1)
interaction(mbd[1,0], 1, 0)
def test_swipe():
mbd = MockBlueDot()
mbd.mock_client_connected()
def swipe(dot, col, row):
def simulate_swipe(
col, row,
pressed_x, pressed_y,
moved_x, moved_y,
released_x, released_y):
mbd.mock_blue_dot_pressed(col,row,pressed_x, pressed_y)
mbd.mock_blue_dot_moved(col,row,moved_x, moved_y)
mbd.mock_blue_dot_released(col,row,released_x, released_y)
#wait_for_swipe
delay_function(lambda: simulate_swipe(col,row,-1,0,0,0,1,0), 0.5)
assert dot.wait_for_swipe(1)
#when_swiped
event_swiped = Event()
dot.when_swiped = lambda: event_swiped.set()
assert not event_swiped.is_set()
#simulate swipe left to right
simulate_swipe(col,row,-1,0,0,0,1,0)
#check event
assert event_swiped.is_set()
#get the swipe
swipe = BlueDotSwipe(mbd[col, row].interaction)
assert swipe.right
assert not swipe.left
assert not swipe.up
assert not swipe.down
#right to left
event_swiped.clear()
simulate_swipe(col,row,1,0,0,0,-1,0)
assert event_swiped.is_set()
swipe = BlueDotSwipe(mbd[col, row].interaction)
assert not swipe.right
assert swipe.left
assert not swipe.up
assert not swipe.down
#bottom to top
event_swiped.clear()
simulate_swipe(col,row,0,-1,0,0,0,1)
assert event_swiped.is_set()
swipe = BlueDotSwipe(mbd[col, row].interaction)
assert not swipe.right
assert not swipe.left
assert swipe.up
assert not swipe.down
#top to bottom
event_swiped.clear()
simulate_swipe(col,row,0,1,0,0,0,-1)
assert event_swiped.is_set()
swipe = BlueDotSwipe(mbd[col, row].interaction)
assert not swipe.right
assert not swipe.left
assert not swipe.up
assert swipe.down
# background
event_swiped.clear()
dot.set_when_swiped(lambda: delay_function(event_swiped.set, 0.2), background=True)
simulate_swipe(col,row,0,1,0,0,0,-1)
assert not event_swiped.is_set()
assert event_swiped.wait(1)
swipe(mbd, 0, 0)
swipe(mbd[0,0], 0, 0)
mbd.resize(2,1)
swipe(mbd, 1, 0)
swipe(mbd[1,0], 1, 0)
def test_callback_in_class():
class CallbackClass():
def __init__(self):
self.event = Event()
def no_pos(self):
self.event.set()
self.pos = None
def with_pos(self, pos):
self.event.set()
self.pos = pos
cc = CallbackClass()
mbd = MockBlueDot()
mbd.mock_client_connected()
mbd.when_pressed = cc.no_pos
mbd.mock_blue_dot_pressed(0,0,0,0)
assert cc.event.is_set()
assert cc.pos is None
mbd.mock_blue_dot_released(0,0,0,0)
cc.event.clear()
mbd.when_pressed = cc.with_pos
mbd.mock_blue_dot_pressed(0,0,0,0)
assert cc.event.is_set()
assert cc.pos.middle
def test_rotation():
mbd = MockBlueDot()
mbd.mock_client_connected()
def rotation(dot, col, row):
event_rotated = Event()
dot.when_rotated = lambda: event_rotated.set()
assert not event_rotated.is_set()
#press the blue dot, no rotation
mbd.mock_blue_dot_pressed(col,row,-0.1,1)
assert not event_rotated.is_set()
r = BlueDotRotation(mbd[col,row].interaction, mbd[0,0].rotation_segments)
assert not r.valid
assert r.value == 0
assert not r.clockwise
assert not r.anti_clockwise
#rotate clockwise
event_rotated.clear()
mbd.mock_blue_dot_moved(col,row,0.1,1)
assert event_rotated.is_set()
r = BlueDotRotation(mbd[col,row].interaction, mbd[col,row].rotation_segments)
assert r.value == 1
assert r.valid
assert r.clockwise
assert not r.anti_clockwise
#rotate anti-clockwise
event_rotated.clear()
mbd.mock_blue_dot_moved(col,row,-0.1,1)
assert event_rotated.is_set()
r = BlueDotRotation(mbd[col,row].interaction, mbd[col,row].rotation_segments)
assert r.value == -1
assert r.valid
assert not r.clockwise
assert r.anti_clockwise
# background
# rotate clockwise again
event_rotated.clear()
dot.set_when_rotated(lambda: delay_function(event_rotated.set, 0.2), background=True)
mbd.mock_blue_dot_moved(col,row,0.1,1)
assert not event_rotated.is_set()
assert event_rotated.wait(1)
rotation(mbd, 0, 0)
rotation(mbd[0,0], 0, 0)
mbd.resize(2,1)
rotation(mbd, 1, 0)
rotation(mbd[1,0], 1, 0)
def test_button_does_not_exist():
mbd = MockBlueDot()
with pytest.raises(ButtonDoesNotExist):
button = mbd[0,1]
def test_allow_pairing():
mbd = MockBlueDot()
assert not mbd.adapter.discoverable
assert not mbd.adapter.pairable
mbd.allow_pairing()
assert mbd.adapter.discoverable
assert mbd.adapter.pairable
def test_dot_appearance():
mbd = MockBlueDot()
assert mbd.color == "blue"
assert mbd.border == False
assert mbd.square == False
assert mbd.visible == True
mbd.resize(2,1)
for button in mbd.buttons:
assert button.color == "blue"
assert button.border == False
assert button.square == False
assert button.visible == True
mbd[1,0].color = "red"
mbd[1,0].border = True
mbd[1,0].square = True
mbd[1,0].visible = False
assert mbd.color == "blue"
assert mbd.border == False
assert mbd.square == False
assert mbd.visible == True
assert mbd[0,0].color == "blue"
assert mbd[0,0].border == False
assert mbd[0,0].square == False
assert mbd[0,0].visible == True
assert mbd[1,0].color == "red"
assert mbd[1,0].border == True
assert mbd[1,0].square == True
assert mbd[1,0].visible == False
mbd.color = "red"
mbd.border = True
mbd.square = True
mbd.visible = False
assert mbd.color == "red"
assert mbd.border == True
assert mbd.square == True
assert mbd.visible == False
assert mbd[0,0].color == "red"
assert mbd[0,0].border == True
assert mbd[0,0].square == True
assert mbd[0,0].visible == False
def test_dot_colors():
from bluedot.colors import BLUE, RED, GREEN, YELLOW
mbd = MockBlueDot()
assert mbd.color == "blue"
assert mbd.color == (0,0,255)
assert mbd.color == BLUE
assert mbd.color == "#0000ff"
assert mbd.color == "#0000ffff"
mbd.color = RED
assert mbd.color == (255,0,0)
assert mbd.color == "red"
assert mbd.color == "#ff0000"
assert mbd.color == "#ff0000ff"
mbd.color = "green"
assert mbd.color == GREEN
assert mbd.color == (0,128,0)
assert mbd.color == "#008000"
assert mbd.color == "#008000ff"
mbd.color = "#ffff00"
assert mbd.color == YELLOW
assert mbd.color == "yellow"
assert mbd.color == (255,255,0)
assert mbd.color == "#ffff00ff"
mbd.color = "#ffffff11"
assert mbd.color == "#ffffff11"
def delay_function(func, time):
delayed_thread = Thread(target = _delayed_function, args = (func, time))
delayed_thread.start()
def _delayed_function(func, time):
sleep(time)
func()
================================================
FILE: tests/test_bluetooth_adapter.py
================================================
import pytest
from bluedot.btcomm import BluetoothAdapter
try:
bta = BluetoothAdapter("hci0")
except Exception as e:
if str(e) == "Bluetooth adapter hci0 not found":
pytest.skip("Bluetooth adapter hci0 not found - skipping test", allow_module_level = True)
def test_bluetooth_adapter():
bta = BluetoothAdapter()
assert bta.device == "hci0"
assert len(bta.address) == 17
# get the initial state of the adapter
powered = bta.powered
discoverable = bta.discoverable
pairable = bta.pairable
# flip the values back and forward
bta.powered = not powered
assert bta.powered == (not powered)
bta.powered = powered
assert bta.powered == powered
# power up the adapter so discoverable and pairable can be tested
bta.powered = True
assert bta.powered == True
bta.discoverable = not discoverable
assert bta.discoverable == (not discoverable)
bta.discoverable = discoverable
assert bta.discoverable == discoverable
bta.pairable = not pairable
assert bta.pairable == (not pairable)
bta.pairable = pairable
assert bta.pairable == pairable
bta.allow_pairing()
assert bta.pairable == True
assert bta.discoverable == True
# reset the adapter back
bta.powered = powered
bta.discoverable = discoverable
bta.pairable = pairable
================================================
FILE: tests/test_bluetooth_comms.py
================================================
import pytest
from bluedot.btcomm import BluetoothAdapter, BluetoothServer, BluetoothClient
from threading import Event
# have we got 2 adapters
try:
bta0 = BluetoothAdapter("hci0")
bta1 = BluetoothAdapter("hci1")
except Exception as e:
pytest.skip(str(e) + " - skipping test", allow_module_level = True)
# is adapter 0 paired with adapter 1 and vice versa
paired = False
for device in bta0.paired_devices:
if device[0] == bta1.address:
paired = True
if not paired:
pytest.skip("hci0 is not paired with hci1", allow_module_level = True)
paired = False
for device in bta1.paired_devices:
if device[0] == bta0.address:
paired = True
if not paired:
pytest.skip("hci1 is not paired with hci0", allow_module_level = True)
def test_server_default_values():
def data_received(data):
pass
bts = BluetoothServer(data_received)
assert bts.data_received_callback == data_received
assert bts.device == "hci0"
assert bts.server_address == bta0.address
assert bts.running
assert bts.port == 1
assert bts.encoding == "utf-8"
assert bts.when_client_connects == None
assert bts.when_client_disconnects == None
bts.stop()
def test_server_alt_values():
def data_received(data):
pass
def when_client_connects():
pass
def when_client_disconnects():
pass
bts = BluetoothServer(
data_received,
device = "hci1",
auto_start = False,
port = 2,
encoding = None,
when_client_connects = when_client_connects,
when_client_disconnects = when_client_disconnects)
assert bts.data_received_callback == data_received
assert bts.device == "hci1"
assert bts.server_address == bta1.address
assert not bts.running
assert bts.port == 2
assert bts.encoding == None
assert bts.when_client_connects == when_client_connects
assert bts.when_client_disconnects == when_client_disconnects
def test_server_start_stop():
bts = BluetoothServer(None, auto_start = False)
bts.start()
assert bts.running
bts.stop()
assert not bts.running
def test_client_default_values():
def data_received(data):
pass
bts = BluetoothServer(data_received, device = "hci1")
btc = BluetoothClient(bta1.address, data_received)
assert btc.data_received_callback == data_received
assert btc.device == "hci0"
assert btc.server == bta1.address
assert btc.client_address == bta0.address
assert btc.connected
assert btc.port == 1
assert btc.encoding == "utf-8"
btc.disconnect()
bts.stop()
def test_client_alt_values():
def data_received(data):
pass
bts = BluetoothServer(None, port = 2, encoding = None)
btc = BluetoothClient(bta0.address, data_received, device = "hci1", auto_connect = False, port = 2, encoding = None)
assert btc.data_received_callback == data_received
assert btc.device == "hci1"
assert btc.client_address == bta1.address
assert not btc.connected
assert btc.port == 2
assert btc.encoding == None
btc.connect()
assert btc.connected
btc.disconnect()
assert not btc.connected
bts.stop()
def test_client_connect_disconnect():
client_connected = Event()
client_disconnected = Event()
def when_client_connects():
client_connected.set()
def when_client_disconnects():
client_disconnected.set()
bts = BluetoothServer(None)
btc = BluetoothClient(bta0.address, None, device = "hci1", auto_connect = False)
bts.when_client_connects = when_client_connects
bts.when_client_disconnects = when_client_disconnects
btc.connect()
assert btc.connected
assert bts.client_address == btc.client_address
assert client_connected.wait(1)
btc.disconnect()
assert not btc.connected
assert client_disconnected.wait(1)
bts.stop()
def test_send_receive():
data_received_at_server = Event()
data_received_at_client = Event()
def data_received_server(data):
assert data == "hiserver"
data_received_at_server.set()
def data_received_client(data):
assert data == "hiclient"
data_received_at_client.set()
bts = BluetoothServer(data_received_server, device = "hci0")
btc = BluetoothClient(bta0.address, data_received_client, device = "hci1")
btc.send("hiserver")
assert data_received_at_server.wait(1)
bts.send("hiclient")
assert data_received_at_server.wait(1)
btc.disconnect()
bts.stop()
def test_volume_data():
from random import choice, randint
import string
no_of_transmissions = randint(100, 200)
test_data = ''
received = Event()
def data_received_server(data):
assert data == test_data
bts.send(test_data)
def data_received_client(data):
assert data == test_data
received.set()
bts = BluetoothServer(data_received_server, device = "hci0")
btc = BluetoothClient(bta0.address, data_received_client, device = "hci1")
for test_no in range(no_of_transmissions):
test_data = ''.join(choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(randint(30,100)))
print(test_data)
btc.send(test_data)
assert received.wait(1)
received.clear()
btc.disconnect()
bts.stop()