diff --git a/README.md b/README.md
index bb9183b20..f54889b18 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-Make without limit! Device Simulator Express, a Microsoft Garage project, allows you to code microcontrollers without the hardware on hand! You can program your Adafruit Circuit Playground Express (CPX) or your BBC micro:bit! Test and debug your code on the device simulator and see the same
+Make without limit! Device Simulator Express, a Microsoft Garage project, allows you to code microcontrollers without the hardware on hand! You can program your Adafruit Circuit Playground Express (CPX), your BBC micro:bit or the Adafruit CLUE! Test and debug your code on the device simulator and see the same
result when you plug in your actual microcontroller. Curious about the output of the device, the serial
monitor allows you to observe the device output.
@@ -18,7 +18,7 @@ monitor allows you to observe the device output.
[ ](#bbc-microbit-simulator)
-- [**Adafruit CLUE**](#adafruit-clue-simulator) (hidden behind preview flag)
+- [**Adafruit CLUE**](#adafruit-clue-simulator)
[ ](#adafruit-clue-simulator)
@@ -120,8 +120,6 @@ In Device Simulator Express, you can use keyboard to interact with the device:
## Adafruit CLUE Simulator
-NOTE: This simulator is hidden under the preview mode flag. See below on how to enable the preview mode flag.
-
### Features
- IntelliSense and syntax highlighting for CircuitPython code for the following drivers and libraries:
@@ -164,22 +162,6 @@ NOTE: This simulator is hidden under the preview mode flag. See below on how to
- Refresh the simulator: Shift + R
- Run the simulator: Shift + F
-## How to enable preview flag
-
-Currently, we have our Adafruit CLUE simulator hidden behind a preview flag and we want you to try it out!
-
-### I. Open settings
-
-For Windows and Linux, you can use Ctrl + , or use `File -> Preferences -> Settings` in the top menu to navigate to settings. For Mac, you can use Cmd + , or use `Code -> Preferences -> Settings`.
-
-
-
-### II. Search for our preview flag and enable it!
-
-In the top search bar, search for `DeviceSimulatorExpress.previewMode`. Then, check the checkbox for the setting that pops up when you serach.
-
-
-
## How to use
To use Device Simulator Express, install the extension from the marketplace and reload VS Code.
@@ -225,8 +207,12 @@ Before deploying the Python code to your CPX device, you need to format your dev
- Download the lastest versions of the cpx libraries (link: https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-libraries).
- _For the micro:bit_:
+
- Download the firmware with the .hex file (link: https://microbit.org/get-started/user-guide/firmware/).
+- _For the CLUE_:
+ - Download the latest versions of the cpx libraries and follow the instructions here (link:https://learn.adafruit.com/adafruit-clue/circuitpython).
+
1. Plug in your device (make sure it’s formatted properly already).
2. Run the command `"Device Simulator Express: Deploy to Device"`.
@@ -305,6 +291,7 @@ A `ThirdPartyNotices.txt` file is provided in the extension's source code listin
- If you try to deploy to the CPX while it's plugged in but you still get an error saying it cannot find the board, make sure your device is formatted correctly and that its name matches `CIRCUITPY`.
- If you can't get the Simulator communication working while debugging, try to open your `Settings` and check the port used under `"Device Simulator Express: Debugger Server Port"`. You can either change it (usually ports above 5000 should work) or try to free it, then start debugging again.
- When you are using the serial monitor, if you get some unusual error messages, unplug the device and reload the VS Code windows.
+- If you're using Ubuntu and having some problems with setting up the environment, try reviewing [this article's](https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-ubuntu-16-04) "Step 1" section on how to set up Python 3 on Ubuntu 16.04. Then, ensure that you've run `sudo apt-get install -y python3-venv` to allow for virtual environment creation.
## License
diff --git a/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz b/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz
deleted file mode 100644
index 85b10a468..000000000
Binary files a/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz and /dev/null differ
diff --git a/locales/en/package.i18n.json b/locales/en/package.i18n.json
index 86e67f9b4..4f34f2536 100644
--- a/locales/en/package.i18n.json
+++ b/locales/en/package.i18n.json
@@ -13,6 +13,5 @@
"deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration",
"deviceSimulatorExpressExtension.configuration.properties.configEnvOnChange": "When you change the Python interpreter, the Device Simulator Express will automatically configure itself for the required dependencies.",
"deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger.",
- "deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask if we can download dependencies. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files.",
- "deviceSimulatorExpressExtension.configuration.properties.previewMode": "Enable this to test out and play with the new Adafruit CLUE simulator!"
+ "deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask if we can download dependencies. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files."
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index e2b169c9b..777c7b6d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30807,9 +30807,9 @@
}
},
"typescript": {
- "version": "3.4.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
- "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
+ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true
},
"typescript-react-intl": {
diff --git a/package.json b/package.json
index cc1b237bd..5a8555f52 100644
--- a/package.json
+++ b/package.json
@@ -144,12 +144,6 @@
"default": 5577,
"description": "%deviceSimulatorExpressExtension.configuration.properties.debuggerPort%",
"scope": "resource"
- },
- "deviceSimulatorExpress.previewMode": {
- "type": "boolean",
- "default": false,
- "description": "%deviceSimulatorExpressExtension.configuration.properties.previewMode%",
- "scope": "resource"
}
}
},
@@ -286,7 +280,7 @@
"tslint-microsoft-contrib": "^6.1.0",
"tslint-react": "^3.6.0",
"tslint-react-hooks": "^2.0.0",
- "typescript": "^3.3.1",
+ "typescript": "^3.8.3",
"typescript-react-intl": "^0.4.0",
"version-from-git": "^1.1.1",
"vsce": "^1.47.0",
diff --git a/package.nls.json b/package.nls.json
index ea3ad1667..e662c005a 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -13,6 +13,5 @@
"deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration",
"deviceSimulatorExpressExtension.configuration.properties.configEnvOnChange": "When you change the Python interpreter, the Device Simulator Express will automatically configure itself for the required dependencies.",
"deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger.",
- "deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask for dependency downloads. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files.",
- "deviceSimulatorExpressExtension.configuration.properties.previewMode": "Enable this to test out and play with the new Adafruit CLUE simulator!"
+ "deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask for dependency downloads. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files."
}
\ No newline at end of file
diff --git a/src/adafruit_circuitplayground/constants.py b/src/adafruit_circuitplayground/constants.py
index a96083795..2c47a919d 100644
--- a/src/adafruit_circuitplayground/constants.py
+++ b/src/adafruit_circuitplayground/constants.py
@@ -1,6 +1,25 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
+
+class EXPRESS_STATE:
+ BUTTON_A = "button_a"
+ BUTTON_B = "button_b"
+ ACCELERATION = "acceleration"
+ BRIGHTNESS = "brightness"
+ PIXELS = "pixels"
+ RED_LED = "red_led"
+ SWITCH = "switch"
+ TEMPERATURE = "temperature"
+ LIGHT = "light"
+ MOTION_X = "motion_x"
+ MOTION_Y = "motion_y"
+ MOTION_Z = "motion_z"
+ TOUCH = "touch"
+ SHAKE = "shake"
+ DETECT_TAPS = "detect_taps"
+
+
ASSIGN_PIXEL_TYPE_ERROR = (
"The pixel color value type should be tuple, list or hexadecimal."
)
@@ -32,18 +51,28 @@
TIME_DELAY = 0.03
-EVENTS_BUTTON_PRESS = ["button_a", "button_b", "switch"]
-EVENTS_SENSOR_CHANGED = ["temperature", "light", "motion_x", "motion_y", "motion_z"]
+EVENTS_BUTTON_PRESS = [
+ EXPRESS_STATE.BUTTON_A,
+ EXPRESS_STATE.BUTTON_B,
+ EXPRESS_STATE.SWITCH,
+]
+EVENTS_SENSOR_CHANGED = [
+ EXPRESS_STATE.TEMPERATURE,
+ EXPRESS_STATE.LIGHT,
+ EXPRESS_STATE.MOTION_X,
+ EXPRESS_STATE.MOTION_Y,
+ EXPRESS_STATE.MOTION_Z,
+]
ALL_EXPECTED_INPUT_EVENTS = [
- "button_a",
- "button_b",
- "switch",
- "temperature",
- "light",
- "shake",
- "motion_x",
- "motion_y",
- "motion_z",
- "touch",
+ EXPRESS_STATE.BUTTON_A,
+ EXPRESS_STATE.BUTTON_B,
+ EXPRESS_STATE.SWITCH,
+ EXPRESS_STATE.TEMPERATURE,
+ EXPRESS_STATE.LIGHT,
+ EXPRESS_STATE.SHAKE,
+ EXPRESS_STATE.MOTION_X,
+ EXPRESS_STATE.MOTION_Y,
+ EXPRESS_STATE.MOTION_Z,
+ EXPRESS_STATE.TOUCH,
]
diff --git a/src/adafruit_circuitplayground/express.py b/src/adafruit_circuitplayground/express.py
index 40b82adff..7f2857508 100644
--- a/src/adafruit_circuitplayground/express.py
+++ b/src/adafruit_circuitplayground/express.py
@@ -14,67 +14,69 @@
from collections import namedtuple
import common
-Acceleration = namedtuple("acceleration", ["x", "y", "z"])
+Acceleration = namedtuple(CONSTANTS.EXPRESS_STATE.ACCELERATION, ["x", "y", "z"])
class Express:
def __init__(self):
# State in the Python process
- self.__state = {
- "brightness": 1.0,
- "button_a": False,
- "button_b": False,
- "pixels": [
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- (0, 0, 0),
- ],
- "red_led": False,
- "switch": False,
- "temperature": 0,
- "light": 0,
- "motion_x": 0,
- "motion_y": 0,
- "motion_z": 0,
- "touch": [False] * 7,
- "shake": False,
- }
+ self.__state = {}
+ self.__state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS] = 1.0
+ self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_A] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_B] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.PIXELS] = [
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ (0, 0, 0),
+ ]
+ self.__state[CONSTANTS.EXPRESS_STATE.RED_LED] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.SWITCH] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.TEMPERATURE] = 0
+ self.__state[CONSTANTS.EXPRESS_STATE.LIGHT] = 0
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_X] = 0
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_Y] = 0
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_Z] = 0
+ self.__state[CONSTANTS.EXPRESS_STATE.TOUCH] = [False] * 7
+ self.__state[CONSTANTS.EXPRESS_STATE.SHAKE] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.DETECT_TAPS] = 0
self.pixels = Pixel(self.__state)
@property
def acceleration(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_ACCELERATION)
return Acceleration(
- self.__state["motion_x"], self.__state["motion_y"], self.__state["motion_z"]
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_X],
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_Y],
+ self.__state[CONSTANTS.EXPRESS_STATE.MOTION_Z],
)
@property
def button_a(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_BUTTON_A)
- return self.__state["button_a"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_A]
@property
def button_b(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_BUTTON_B)
- return self.__state["button_b"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_B]
@property
def detect_taps(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_DETECT_TAPS)
- return self.__state["detect_taps"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.DETECT_TAPS]
@detect_taps.setter
def detect_taps(self, value):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_DETECT_TAPS)
value_int = int(value)
- self.__state["detect_taps"] = (
+ self.__state[CONSTANTS.EXPRESS_STATE.DETECT_TAPS] = (
value_int if (value_int == 1 or value_int == 2) else 1
)
@@ -88,28 +90,28 @@ def tapped(self):
@property
def red_led(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_RED_LED)
- return self.__state["red_led"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.RED_LED]
@red_led.setter
def red_led(self, value):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_RED_LED)
- self.__state["red_led"] = bool(value)
+ self.__state[CONSTANTS.EXPRESS_STATE.RED_LED] = bool(value)
self.__show()
@property
def switch(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_SWITCH)
- return self.__state["switch"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.SWITCH]
@property
def temperature(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_TEMPERATURE)
- return self.__state["temperature"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.TEMPERATURE]
@property
def light(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_LIGHT)
- return self.__state["light"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.LIGHT]
def __show(self):
if utils.debug_mode:
@@ -121,7 +123,7 @@ def __show(self):
def __touch(self, i):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_TOUCH)
- return self.__state["touch"][i - 1]
+ return self.__state[CONSTANTS.EXPRESS_STATE.TOUCH][i - 1]
@property
def touch_A1(self):
@@ -160,7 +162,7 @@ def adjust_touch_threshold(self, adjustment):
def shake(self, shake_threshold=30):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_SHAKE)
- return self.__state["shake"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.SHAKE]
def play_file(self, file_name):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_PLAY_FILE)
diff --git a/src/adafruit_circuitplayground/pixel.py b/src/adafruit_circuitplayground/pixel.py
index 758084f59..22fcfc8ae 100644
--- a/src/adafruit_circuitplayground/pixel.py
+++ b/src/adafruit_circuitplayground/pixel.py
@@ -36,7 +36,7 @@ def __getitem__(self, index):
if not self.__valid_index(index):
raise IndexError(CONSTANTS.INDEX_ERROR)
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_PIXELS)
- return self.__state["pixels"][index]
+ return self.__state[CONSTANTS.EXPRESS_STATE.PIXELS][index]
def __setitem__(self, index, val):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_PIXELS)
@@ -46,11 +46,13 @@ def __setitem__(self, index, val):
else:
if not self.__valid_index(index):
raise IndexError(CONSTANTS.INDEX_ERROR)
- self.__state["pixels"][index] = self.__extract_pixel_value(val, is_slice)
+ self.__state[CONSTANTS.EXPRESS_STATE.PIXELS][
+ index
+ ] = self.__extract_pixel_value(val, is_slice)
self.__show_if_auto_write()
def __iter__(self):
- yield from self.__state["pixels"]
+ yield from self.__state[CONSTANTS.EXPRESS_STATE.PIXELS]
def __enter__(self):
return self
@@ -59,18 +61,20 @@ def __repr__(self):
return "[" + ", ".join([str(x) for x in self]) + "]"
def __len__(self):
- return len(self.__state["pixels"])
+ return len(self.__state[CONSTANTS.EXPRESS_STATE.PIXELS])
def __valid_index(self, index):
return (
type(index) is int
- and index >= -len(self.__state["pixels"])
- and index < len(self.__state["pixels"])
+ and index >= -len(self.__state[CONSTANTS.EXPRESS_STATE.PIXELS])
+ and index < len(self.__state[CONSTANTS.EXPRESS_STATE.PIXELS])
)
def fill(self, val):
- for index in range(len(self.__state["pixels"])):
- self.__state["pixels"][index] = self.__extract_pixel_value(val)
+ for index in range(len(self.__state[CONSTANTS.EXPRESS_STATE.PIXELS])):
+ self.__state[CONSTANTS.EXPRESS_STATE.PIXELS][
+ index
+ ] = self.__extract_pixel_value(val)
self.__show_if_auto_write()
def __extract_pixel_value(self, val, is_slice=False):
@@ -113,14 +117,14 @@ def __valid_rgb_value(self, pixValue):
@property
def brightness(self):
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_BRIGHTNESS)
- return self.__state["brightness"]
+ return self.__state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS]
@brightness.setter
def brightness(self, brightness):
if not self.__valid_brightness(brightness):
raise ValueError(CONSTANTS.BRIGHTNESS_RANGE_ERROR)
telemetry_py.send_telemetry(TelemetryEvent.CPX_API_BRIGHTNESS)
- self.__state["brightness"] = brightness
+ self.__state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS] = brightness
self.__show_if_auto_write()
def __valid_brightness(self, brightness):
diff --git a/src/adafruit_circuitplayground/test/test_express.py b/src/adafruit_circuitplayground/test/test_express.py
index b83ba3a23..0732d9641 100644
--- a/src/adafruit_circuitplayground/test/test_express.py
+++ b/src/adafruit_circuitplayground/test/test_express.py
@@ -4,19 +4,19 @@
import playsound
from ..express import Express
from ..pixel import Pixel
+from .. import constants as CONSTANTS
class TestExpress(object):
def setup_method(self):
self.cpx = Express()
- self.__state = {
- "brightness": 1.0,
- "button_a": False,
- "button_b": False,
- "pixels": [(255, 0, 0)] * 10,
- "red_led": False,
- "switch": False,
- }
+ self.__state = {}
+ self.__state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS] = 1.0
+ self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_A] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.BUTTON_B] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.PIXELS] = [(255, 0, 0)] * 10
+ self.__state[CONSTANTS.EXPRESS_STATE.RED_LED] = False
+ self.__state[CONSTANTS.EXPRESS_STATE.SWITCH] = False
self.pixels = Pixel(self.__state)
self.__speaker_enabled = False
@@ -24,24 +24,24 @@ def test_acceleration(self):
mock_motion_x = 10
mock_motion_y = -10
mock_motion_z = -20
- self.cpx._Express__state["motion_x"] = mock_motion_x
- self.cpx._Express__state["motion_y"] = mock_motion_y
- self.cpx._Express__state["motion_z"] = mock_motion_z
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.MOTION_X] = mock_motion_x
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.MOTION_Y] = mock_motion_y
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.MOTION_Z] = mock_motion_z
accel = self.cpx.acceleration
assert accel[0] == 10
assert accel[1] == -10
assert accel[2] == -20
def test_button_a(self):
- self.cpx._Express__state["button_a"] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.BUTTON_A] = True
assert self.cpx.button_a
def test_button_b(self):
- self.cpx._Express__state["button_b"] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.BUTTON_B] = True
assert self.cpx.button_b
def test_taps(self):
- self.cpx._Express__state["detect_taps"] = 2
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.DETECT_TAPS] = 2
assert 2 == self.cpx.detect_taps
@pytest.mark.parametrize("taps, expected", [(1, 1), (2, 2), (3, 1)])
@@ -50,7 +50,7 @@ def test_taps_setter(self, taps, expected):
assert expected == self.cpx.detect_taps
def test_red_led(self):
- self.cpx._Express__state["red_led"] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.RED_LED] = True
assert self.cpx.red_led
def test_red_led_int(self):
@@ -62,47 +62,47 @@ def test_red_led_string(self):
assert self.cpx.red_led
def test_switch(self):
- self.cpx._Express__state["switch"] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.SWITCH] = True
assert self.cpx.switch
def test_temperature(self):
- self.cpx._Express__state["temperature"] = 31
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TEMPERATURE] = 31
assert 31 == self.cpx.temperature
def test_light(self):
- self.cpx._Express__state["light"] = 255
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.LIGHT] = 255
assert 255 == self.cpx.light
def test_shake(self):
- self.cpx._Express__state["shake"] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.SHAKE] = True
assert self.cpx.shake()
def test_touch_A1(self):
- self.cpx._Express__state["touch"][0] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][0] = True
assert self.cpx.touch_A1
def test_touch_A2(self):
- self.cpx._Express__state["touch"][1] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][1] = True
assert self.cpx.touch_A2
def test_touch_A3(self):
- self.cpx._Express__state["touch"][2] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][2] = True
assert self.cpx.touch_A3
def test_touch_A4(self):
- self.cpx._Express__state["touch"][3] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][3] = True
assert self.cpx.touch_A4
def test_touch_A5(self):
- self.cpx._Express__state["touch"][4] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][4] = True
assert self.cpx.touch_A5
def test_touch_A6(self):
- self.cpx._Express__state["touch"][5] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][5] = True
assert self.cpx.touch_A6
def test_touch_A7(self):
- self.cpx._Express__state["touch"][6] = True
+ self.cpx._Express__state[CONSTANTS.EXPRESS_STATE.TOUCH][6] = True
assert self.cpx.touch_A7
def test_play_file_mp4_wrong_type(self):
diff --git a/src/adafruit_circuitplayground/test/test_pixel.py b/src/adafruit_circuitplayground/test/test_pixel.py
index 521958552..e1179939b 100644
--- a/src/adafruit_circuitplayground/test/test_pixel.py
+++ b/src/adafruit_circuitplayground/test/test_pixel.py
@@ -1,20 +1,19 @@
import pytest
from ..pixel import Pixel
+from .. import constants as CONSTANTS
class TestPixel(object):
def setup_method(self):
- self.pixel = Pixel(
- {
- "brightness": 1.0,
- "button_a": False,
- "button_b": False,
- "pixels": [(255, 0, 0), (0, 255, 0), (0, 0, 255)],
- "red_led": False,
- "switch": False,
- }
- )
+ state = {}
+ state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS] = 1.0
+ state[CONSTANTS.EXPRESS_STATE.BUTTON_A] = False
+ state[CONSTANTS.EXPRESS_STATE.BUTTON_B] = False
+ state[CONSTANTS.EXPRESS_STATE.PIXELS] = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
+ state[CONSTANTS.EXPRESS_STATE.RED_LED] = False
+ state[CONSTANTS.EXPRESS_STATE.SWITCH] = False
+ self.pixel = Pixel(state)
def test_get_item_out_of_bounds(self):
with pytest.raises(IndexError):
@@ -52,7 +51,10 @@ def test_valid_index(self, index, expected):
def test_fill(self):
self.pixel.fill((123, 123, 123))
- assert all(p == (123, 123, 123) for p in self.pixel._Pixel__state["pixels"])
+ assert all(
+ p == (123, 123, 123)
+ for p in self.pixel._Pixel__state[CONSTANTS.EXPRESS_STATE.PIXELS]
+ )
@pytest.mark.parametrize(
"val, expected",
@@ -101,7 +103,7 @@ def test_valid_rgb_value(self, pixValue, expected):
assert expected == self.pixel._Pixel__valid_rgb_value(pixValue)
def test_get_brightness(self):
- self.pixel._Pixel__state["brightness"] = 0.4
+ self.pixel._Pixel__state[CONSTANTS.EXPRESS_STATE.BRIGHTNESS] = 0.4
assert 0.4 == pytest.approx(self.pixel.brightness)
@pytest.mark.parametrize("brightness", [-0.1, 1.1])
diff --git a/src/base_circuitpython/__init__.py b/src/base_circuitpython/__init__.py
index 5b5bbaf34..398a3936b 100644
--- a/src/base_circuitpython/__init__.py
+++ b/src/base_circuitpython/__init__.py
@@ -3,4 +3,6 @@
import sys
abs_path = pathlib.Path(__file__).parent.absolute()
+clue_path = os.path.join(abs_path, "../clue")
sys.path.insert(0, os.path.join(abs_path))
+sys.path.insert(0, os.path.join(clue_path))
diff --git a/src/base_circuitpython/displayio/constants.py b/src/base_circuitpython/displayio/constants.py
index e683684b9..bca672472 100644
--- a/src/base_circuitpython/displayio/constants.py
+++ b/src/base_circuitpython/displayio/constants.py
@@ -4,6 +4,7 @@
INCORR_SUBCLASS = "Layer must be a Group or TileGrid subclass."
LAYER_ALREADY_IN_GROUP = "Layer already in a group."
GROUP_FULL = "Group Full"
+SCALE_TOO_SMALL = "scale must be >= 1"
SCREEN_HEIGHT_WIDTH = 240
diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py
index 4704c67cf..c5dccf90a 100644
--- a/src/base_circuitpython/displayio/group.py
+++ b/src/base_circuitpython/displayio/group.py
@@ -8,6 +8,7 @@
import common
import board
+import sys
# Group implementation loosely based on the
# displayio.Group class in Adafruit CircuitPython
@@ -33,12 +34,22 @@ class Group:
"""
def __init__(
- self, max_size, scale=1, x=0, y=0, check_active_group_ref=True, auto_write=True
+ self,
+ max_size=sys.maxsize,
+ scale=1,
+ x=0,
+ y=0,
+ check_active_group_ref=True,
+ auto_write=True,
):
self.__check_active_group_ref = check_active_group_ref
self.__auto_write = auto_write
self.__contents = []
self.__max_size = max_size
+
+ if scale < 1:
+ raise ValueError(CONSTANTS.SCALE_TOO_SMALL)
+
self.__scale = scale
"""
.. attribute:: scale
@@ -89,6 +100,9 @@ def scale(self):
@scale.setter
def scale(self, val):
+ if val < 1:
+ raise ValueError(CONSTANTS.SCALE_TOO_SMALL)
+
if self.__scale != val:
self.__scale = val
self.__elem_changed()
diff --git a/src/base_circuitpython/displayio/test/test_group.py b/src/base_circuitpython/displayio/test/test_group.py
index 7eee71159..4d943ffc8 100644
--- a/src/base_circuitpython/displayio/test/test_group.py
+++ b/src/base_circuitpython/displayio/test/test_group.py
@@ -53,6 +53,11 @@ def test_incorr_subclass(self, group_item):
with pytest.raises(ValueError, match=CONSTANTS.INCORR_SUBCLASS):
g1.append(group_item)
+ @pytest.mark.parametrize("scale", [(0), (-2)])
+ def test_invalid_scale(self, scale):
+ with pytest.raises(ValueError, match=CONSTANTS.SCALE_TOO_SMALL):
+ g1 = Group(scale=scale)
+
def test_layer_already_in_group(self):
g1 = Group(max_size=4)
diff --git a/src/clue/adafruit_clue.py b/src/clue/adafruit_clue.py
index 874e50e46..059573021 100644
--- a/src/clue/adafruit_clue.py
+++ b/src/clue/adafruit_clue.py
@@ -56,9 +56,7 @@
https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel
"""
-from common.telemetry_events import TelemetryEvent
-from common.telemetry import telemetry_py
-from common import utils
+import common
from base_circuitpython import base_cp_constants as CONSTANTS
import neopixel
import time
@@ -115,8 +113,8 @@ def __init__(
self._font = terminalio.FONT
if font:
self._font = font
- self.text_group = displayio.Group(max_size=20, scale=text_scale)
self.text_scale = text_scale
+ self.text_group = displayio.Group(max_size=20, scale=self.text_scale)
if title:
# Fail gracefully if title is longer than 60 characters.
if len(title) > 60:
@@ -130,12 +128,12 @@ def __init__(
scale=title_scale,
)
title.x = 0
- title.y = 8
- self._y = title.y + 18 * text_scale
+ title.y = 8 * self.text_scale
+ self._y = title.y + 18 * self.text_scale
self.text_group.append(title)
else:
- self._y = 3
+ self._y = 3 * self.text_scale
self._lines = []
for num in range(1):
@@ -205,7 +203,7 @@ def __init__(self):
self.__state[CONSTANTS.CLUE_STATE.PROXIMITY] = 0
self.__state[CONSTANTS.CLUE_STATE.GESTURE] = ""
self.__state[CONSTANTS.CLUE_STATE.HUMIDITY] = 0
- self.__state[CONSTANTS.CLUE_STATE.PRESSURE] = 0
+ self.__state[CONSTANTS.CLUE_STATE.PRESSURE] = 1013
self.__state[CONSTANTS.CLUE_STATE.PIXEL] = neopixel.NeoPixel(
pin=CONSTANTS.CLUE_PIN, n=1, pixel_order=neopixel.RGB
)
@@ -247,7 +245,9 @@ def button_a(self):
if clue.button_a:
print("Button A pressed")
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_BUTTON_A)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_BUTTON_A
+ )
return self.__state[CONSTANTS.CLUE_STATE.BUTTON_A]
@property
@@ -261,7 +261,9 @@ def button_b(self):
if clue.button_b:
print("Button B pressed")
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_BUTTON_B)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_BUTTON_B
+ )
return self.__state[CONSTANTS.CLUE_STATE.BUTTON_B]
@property
@@ -273,7 +275,9 @@ def were_pressed(self):
while True:
print(clue.were_pressed)
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_WERE_PRESSED)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_WERE_PRESSED
+ )
ret = self.__state[CONSTANTS.CLUE_STATE.PRESSED_BUTTONS].copy()
self.__state[CONSTANTS.CLUE_STATE.PRESSED_BUTTONS].clear()
return ret
@@ -288,7 +292,9 @@ def acceleration(self):
while True:
print("Accel: {:.2f} {:.2f} {:.2f}".format(*clue.acceleration))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_ACCELERATION)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_ACCELERATION
+ )
return (
self.__state[CONSTANTS.CLUE_STATE.MOTION_X],
self.__state[CONSTANTS.CLUE_STATE.MOTION_Y],
@@ -307,7 +313,9 @@ def shake(self, shake_threshold=30, avg_count=10, total_delay=0.1):
:param total_delay: The total time in seconds it takes to obtain avg_count
readings from acceleration. (Default 0.1)
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SHAKE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SHAKE
+ )
is_shaken = self.__state[CONSTANTS.CLUE_STATE.GESTURE] == CONSTANTS.SHAKE
return is_shaken
@@ -322,7 +330,9 @@ def color(self):
while True:
print("Color: R: {} G: {} B: {} C: {}".format(*clue.color))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_COLOR)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_COLOR
+ )
return (
self.__state[CONSTANTS.CLUE_STATE.LIGHT_R],
self.__state[CONSTANTS.CLUE_STATE.LIGHT_G],
@@ -339,7 +349,9 @@ def temperature(self):
from adafruit_clue import clue
print("Temperature: {:.1f}C".format(clue.temperature))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_TEMPERATURE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_TEMPERATURE
+ )
return self.__state[CONSTANTS.CLUE_STATE.TEMPERATURE]
@property
@@ -352,7 +364,9 @@ def magnetic(self):
while True:
print("Magnetic: {:.3f} {:.3f} {:.3f}".format(*clue.magnetic))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_MAGNETIC)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_MAGNETIC
+ )
return (
self.__state[CONSTANTS.CLUE_STATE.MAGNET_X],
self.__state[CONSTANTS.CLUE_STATE.MAGNET_Y],
@@ -370,7 +384,9 @@ def proximity(self):
while True:
print("Proximity: {}".format(clue.proximity))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_PROXIMITY)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_PROXIMITY
+ )
return self.__state[CONSTANTS.CLUE_STATE.PROXIMITY]
@property
@@ -379,7 +395,9 @@ def gyro(self):
This example prints the values. Try moving the board to see how the printed values change.
print("Gyro: {:.2f} {:.2f} {:.2f}".format(*clue.gyro))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_GYRO)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_GYRO
+ )
return (
self.__state[CONSTANTS.CLUE_STATE.GYRO_X],
self.__state[CONSTANTS.CLUE_STATE.GYRO_Y],
@@ -398,7 +416,9 @@ def gesture(self):
while True:
print("Gesture: {}".format(clue.gesture))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_GESTURE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_GESTURE
+ )
gesture_mapping = {"": 0, "up": 1, "down": 2, "left": 3, "right": 4}
return gesture_mapping.get(self.__state[CONSTANTS.CLUE_STATE.GESTURE], 0)
@@ -412,7 +432,9 @@ def humidity(self):
while True:
print("Humidity: {:.1f}%".format(clue.humidity))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_HUMIDITY)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_HUMIDITY
+ )
return self.__state[CONSTANTS.CLUE_STATE.HUMIDITY]
@property
@@ -424,7 +446,9 @@ def pressure(self):
from adafruit_clue import clue
print("Pressure: {:.3f}hPa".format(clue.pressure))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_PRESSURE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_PRESSURE
+ )
return self.__state[CONSTANTS.CLUE_STATE.PRESSURE]
@property
@@ -450,7 +474,9 @@ def altitude(self):
POWER_CONSTANT,
)
)
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_ALTITUDE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_ALTITUDE
+ )
return altitude
@property
@@ -464,12 +490,16 @@ def sea_level_pressure(self):
clue.sea_level_pressure = 1015
print("Pressure: {:.3f}hPa".format(clue.pressure))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SEA_LEVEL_PRESSURE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SEA_LEVEL_PRESSURE
+ )
return self.__state[CONSTANTS.CLUE_STATE.SEA_LEVEL_PRESSURE]
@sea_level_pressure.setter
def sea_level_pressure(self, value):
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SEA_LEVEL_PRESSURE)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SEA_LEVEL_PRESSURE
+ )
self.__state[CONSTANTS.CLUE_STATE.SEA_LEVEL_PRESSURE] = value
@property
@@ -482,7 +512,9 @@ def pixel(self):
while True:
clue.pixel.fill((255, 0, 255))
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_PIXEL)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_PIXEL
+ )
return self.__state[CONSTANTS.CLUE_STATE.PIXEL]
@property
@@ -500,8 +532,10 @@ def touch_0(self):
if clue.touch_0:
print("Touched pad 0")
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_TOUCH)
- utils.print_for_unimplemented_functions(Clue.touch_0.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_TOUCH
+ )
+ common.utils.print_for_unimplemented_functions("touch_0")
@property
def touch_1(self):
@@ -518,8 +552,10 @@ def touch_1(self):
if clue.touch_1:
print("Touched pad 1")
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_TOUCH)
- utils.print_for_unimplemented_functions(Clue.touch_1.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_TOUCH
+ )
+ common.utils.print_for_unimplemented_functions("touch_1")
@property
def touch_2(self):
@@ -536,8 +572,10 @@ def touch_2(self):
if clue.touch_2:
print("Touched pad 2")
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_TOUCH)
- utils.print_for_unimplemented_functions(Clue.touch_2.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_TOUCH
+ )
+ common.utils.print_for_unimplemented_functions("touch_2")
@property
def white_leds(self):
@@ -550,12 +588,16 @@ def white_leds(self):
from adafruit_clue import clue
clue.white_leds = True
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_WHITE_LEDS)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_WHITE_LEDS
+ )
return self.__state[CONSTANTS.CLUE_STATE.WHITE_LEDS]
@white_leds.setter
def white_leds(self, value):
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_WHITE_LEDS)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_WHITE_LEDS
+ )
self.__set_leds(CONSTANTS.CLUE_STATE.WHITE_LEDS, value)
@property
@@ -569,12 +611,16 @@ def red_led(self):
from adafruit_clue import clue
clue.red_led = True
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_RED_LED)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_RED_LED
+ )
return self.__state[CONSTANTS.CLUE_STATE.RED_LED]
@red_led.setter
def red_led(self, value):
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_RED_LED)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_RED_LED
+ )
self.__set_leds(CONSTANTS.CLUE_STATE.RED_LED, value)
def play_tone(self, frequency, duration):
@@ -591,8 +637,10 @@ def play_tone(self, frequency, duration):
from adafruit_clue import clue
clue.play_tone(880, 1)
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SOUND)
- utils.print_for_unimplemented_functions(Clue.play_tone.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SOUND
+ )
+ common.utils.print_for_unimplemented_functions(Clue.play_tone.__name__)
def start_tone(self, frequency):
""" Not Implemented!
@@ -614,8 +662,10 @@ def start_tone(self, frequency):
else:
clue.stop_tone()
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SOUND)
- utils.print_for_unimplemented_functions(Clue.start_tone.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SOUND
+ )
+ common.utils.print_for_unimplemented_functions(Clue.start_tone.__name__)
def stop_tone(self):
""" Not Implemented!
@@ -635,8 +685,10 @@ def stop_tone(self):
else:
clue.stop_tone()
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SOUND)
- utils.print_for_unimplemented_functions(Clue.stop_tone.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SOUND
+ )
+ common.utils.print_for_unimplemented_functions(Clue.stop_tone.__name__)
@property
def sound_level(self):
@@ -651,8 +703,10 @@ def sound_level(self):
while True:
print(clue.sound_level)
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SOUND)
- utils.print_for_unimplemented_functions(Clue.sound_level.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SOUND
+ )
+ common.utils.print_for_unimplemented_functions("sound_level")
def loud_sound(self, sound_threshold=200):
"""Not Implemented!
@@ -684,8 +738,10 @@ def loud_sound(self, sound_threshold=200):
else:
clue.pixel.fill(0)
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_SOUND)
- utils.print_for_unimplemented_functions(Clue.loud_sound.__name__)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_SOUND
+ )
+ common.utils.print_for_unimplemented_functions(Clue.loud_sound.__name__)
@staticmethod
def simple_text_display(
@@ -745,7 +801,9 @@ def simple_text_display(
clue_data[2].text = "Magnetic: {:.3f} {:.3f} {:.3f}".format(*clue.magnetic)
clue_data.show()
"""
- telemetry_py.send_telemetry(TelemetryEvent.CLUE_API_TEXT_DISPLAY)
+ common.telemetry.telemetry_py.send_telemetry(
+ common.telemetry_events.TelemetryEvent.CLUE_API_TEXT_DISPLAY
+ )
return _ClueSimpleTextDisplay(
title=title,
title_color=title_color,
@@ -775,7 +833,12 @@ def __set_leds(self, led, value):
value = bool(value)
self.__state[led] = value
sendable_json = {led: value}
- utils.send_to_simulator(sendable_json, CONSTANTS.CLUE)
+ if common.utils.debug_mode:
+ common.debugger_communication_client.debug_send_to_simulator(
+ sendable_json, CONSTANTS.CLUE
+ )
+ else:
+ common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE)
clue = Clue() # pylint: disable=invalid-name
diff --git a/src/clue/adafruit_display_text/label.py b/src/clue/adafruit_display_text/label.py
new file mode 100644
index 000000000..f7bab0b7f
--- /dev/null
+++ b/src/clue/adafruit_display_text/label.py
@@ -0,0 +1,280 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2019 Scott Shawcroft for Adafruit Industries LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+"""
+`adafruit_display_text.label`
+====================================================
+
+Displays text labels using CircuitPython's displayio.
+
+* Author(s): Scott Shawcroft
+
+Implementation Notes
+--------------------
+
+**Hardware:**
+
+**Software and Dependencies:**
+
+* Adafruit CircuitPython firmware for the supported boards:
+ https://github.com/adafruit/circuitpython/releases
+
+"""
+
+import displayio
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Text.git"
+
+
+class Label(displayio.Group):
+ """A label displaying a string of text. The origin point set by ``x`` and ``y``
+ properties will be the left edge of the bounding box, and in the center of a M
+ glyph (if its one line), or the (number of lines * linespacing + M)/2. That is,
+ it will try to have it be center-left as close as possible.
+
+ :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``.
+ Must include a capital M for measuring character size.
+ :param str text: Text to display
+ :param int max_glyphs: The largest quantity of glyphs we will display
+ :param int color: Color of all text in RGB hex
+ :param double line_spacing: Line spacing of text to display"""
+
+ def __init__(
+ self,
+ font,
+ *,
+ x=0,
+ y=0,
+ text=None,
+ max_glyphs=None,
+ color=0xFFFFFF,
+ background_color=None,
+ line_spacing=1.25,
+ **kwargs
+ ):
+ if not max_glyphs and not text:
+ raise RuntimeError("Please provide a max size, or initial text")
+ if not max_glyphs:
+ max_glyphs = len(text)
+ super().__init__(max_size=max_glyphs, auto_write=False, **kwargs)
+ self.width = max_glyphs
+ self.font = font
+ self._text = None
+ self._anchor_point = (0, 0)
+ self.x = x
+ self.y = y
+
+ self.palette = displayio.Palette(2)
+ if background_color is not None:
+ self.palette[0] = background_color
+ self.palette.make_opaque(0)
+ self._transparent_background = False
+ else:
+ self.palette[0] = 0
+ self.palette.make_transparent(0)
+ self._transparent_background = True
+ self.palette[1] = color
+
+ bounds = self.font.get_bounding_box()
+ self.height = bounds[1]
+ self._line_spacing = line_spacing
+ self._boundingbox = None
+
+ if text is not None:
+ self._update_text(str(text))
+
+ def _update_text(self, new_text): # pylint: disable=too-many-locals
+ x = 0
+ y = 0
+ i = 0
+ old_c = 0
+ y_offset = int(
+ (
+ self.font.get_glyph(ord("M")).height
+ - new_text.count("\n") * self.height * self.line_spacing
+ )
+ / 2
+ )
+ # print("y offset from baseline", y_offset)
+ left = right = top = bottom = 0
+ for character in new_text:
+ if character == "\n":
+ y += int(self.height * self._line_spacing)
+ x = 0
+ continue
+ glyph = self.font.get_glyph(ord(character))
+ if not glyph:
+ continue
+ right = max(right, x + glyph.width)
+ if y == 0: # first line, find the Ascender height
+ top = min(top, -glyph.height + y_offset)
+ bottom = max(bottom, y - glyph.dy + y_offset)
+ position_y = y - glyph.height - glyph.dy + y_offset
+ position_x = x + glyph.dx
+ if (
+ not self._text
+ or old_c >= len(self._text)
+ or character != self._text[old_c]
+ ):
+ try:
+ face = displayio.TileGrid(
+ glyph.bitmap,
+ pixel_shader=self.palette,
+ default_tile=glyph.tile_index,
+ tile_width=glyph.width,
+ tile_height=glyph.height,
+ position=(position_x, position_y),
+ )
+ except TypeError:
+ face = displayio.TileGrid(
+ glyph.bitmap,
+ pixel_shader=self.palette,
+ default_tile=glyph.tile_index,
+ tile_width=glyph.width,
+ tile_height=glyph.height,
+ x=position_x,
+ y=position_y,
+ )
+ if i < len(self):
+ self[i] = face
+ else:
+ self.append(face)
+ elif self._text and character == self._text[old_c]:
+ try:
+ self[i].position = (position_x, position_y)
+ except AttributeError:
+ self[i].x = position_x
+ self[i].y = position_y
+
+ x += glyph.shift_x
+
+ # TODO skip this for control sequences or non-printables.
+ i += 1
+ old_c += 1
+ # skip all non-prinables in the old string
+ while (
+ self._text
+ and old_c < len(self._text)
+ and (
+ self._text[old_c] == "\n"
+ or not self.font.get_glyph(ord(self._text[old_c]))
+ )
+ ):
+ old_c += 1
+ # Remove the rest
+ while len(self) > i:
+ self.pop()
+ self._text = new_text
+ self._boundingbox = (left, top, left + right, bottom - top)
+
+ @property
+ def bounding_box(self):
+ """An (x, y, w, h) tuple that completely covers all glyphs. The
+ first two numbers are offset from the x, y origin of this group"""
+ return tuple(self._boundingbox)
+
+ @property
+ def line_spacing(self):
+ """The amount of space between lines of text, in multiples of the font's
+ bounding-box height. (E.g. 1.0 is the bounding-box height)"""
+ return self._line_spacing
+
+ @line_spacing.setter
+ def line_spacing(self, spacing):
+ if self._line_spacing != spacing:
+ self._line_spacing = spacing
+ self._Group__trigger_draw()
+
+ @property
+ def color(self):
+ """Color of the text as an RGB hex number."""
+ return self.palette[1]
+
+ @color.setter
+ def color(self, new_color):
+ self.palette[1] = new_color
+ self._Group__trigger_draw()
+
+ @property
+ def background_color(self):
+ """Color of the background as an RGB hex number."""
+ if not self._transparent_background:
+ return self.palette[0]
+ return None
+
+ @background_color.setter
+ def background_color(self, new_color):
+ if new_color is not None:
+ self.palette[0] = new_color
+ self.palette.make_opaque(0)
+ self._transparent_background = False
+ else:
+ self.palette[0] = 0
+ self.palette.make_transparent(0)
+ self._transparent_background = True
+
+ self._Group__trigger_draw()
+
+ @property
+ def text(self):
+ """Text to display."""
+ return self._text
+
+ @text.setter
+ def text(self, new_text):
+ # APR 2, 2O2O ->
+ # Added manual trigger to update text since
+ # on-screen text updating is a lot faster this way.
+
+ # Previously, the group was set to auto_write=True
+ # and each letter in the group was individually
+ # drawn and sent to the front-end display.
+ if new_text != self._text:
+ self._update_text(str(new_text))
+ self._Group__trigger_draw()
+
+ @property
+ def anchor_point(self):
+ """Point that anchored_position moves relative to.
+ Tuple with decimal percentage of width and height.
+ (E.g. (0,0) is top left, (1.0, 0.5): is middle right.)"""
+ return self._anchor_point
+
+ @anchor_point.setter
+ def anchor_point(self, new_anchor_point):
+ if self._anchor_point != new_anchor_point:
+ self._Group__trigger_draw()
+
+ @property
+ def anchored_position(self):
+ """Position relative to the anchor_point. Tuple containing x,y
+ pixel coordinates."""
+ return (
+ self.x - self._boundingbox[2] * self._anchor_point[0],
+ self.y - self._boundingbox[3] * self._anchor_point[1],
+ )
+
+ @anchored_position.setter
+ def anchored_position(self, new_position):
+ self.x = int(new_position[0] - (self._boundingbox[2] * self._anchor_point[0]))
+ self.y = int(new_position[1] - (self._boundingbox[3] * self._anchor_point[1]))
+ self._Group__trigger_draw()
diff --git a/src/common/utils.py b/src/common/utils.py
index 1af1c0eb5..d6d241b2f 100644
--- a/src/common/utils.py
+++ b/src/common/utils.py
@@ -24,8 +24,11 @@ def update_state_with_device_name(state, device_name):
return updated_state
-def create_message(state):
- message = {"type": "state", "data": json.dumps(state)}
+def create_message(msg, send_type="state"):
+ if isinstance(msg, dict):
+ msg = json.dumps(msg)
+
+ message = {"type": send_type, "data": msg}
return message
@@ -41,6 +44,13 @@ def send_to_simulator(state, device_name):
time.sleep(CONSTANTS.TIME_DELAY)
+def send_print_to_simulator(raw_msg):
+ data_str = str(raw_msg)
+ message = create_message(data_str, "print")
+ print(json.dumps(message) + "\0", file=sys.__stdout__, flush=True)
+ time.sleep(CONSTANTS.TIME_DELAY)
+
+
def remove_leading_slashes(string):
string = string.lstrip("\\/")
return string
@@ -52,14 +62,6 @@ def escape_if_OSX(file_name):
return file_name
-def print_for_unimplemented_functions(function_name, one_more_call=False):
- # Frame 0 is this function call
- # Frame 1 is the call that calls this function, which is a microbit function
- # Frame 2 is the call that calls the microbit function, which is in the user's file
- # If one_more_call is True, then there is another frame between what was originally supposed to be frame 1 and 2.
- frame_no = 2 if not one_more_call else 3
- line_number = sys._getframe(frame_no).f_lineno
- user_file_name = sys._getframe(frame_no).f_code.co_filename
- print(
- f"'{function_name}' on line {line_number} in {user_file_name} is not implemented in the simulator but it will work on the actual device!"
- )
+def print_for_unimplemented_functions(function_name):
+ msg = f"'{function_name}' is not implemented in the simulator but it will work on the actual device!\n"
+ send_print_to_simulator(msg)
diff --git a/src/constants.ts b/src/constants.ts
index fb77451e7..3654347d9 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -22,7 +22,6 @@ export const CONFIG = {
PYTHON_PATH: "python.pythonPath",
SHOW_DEPENDENCY_INSTALL: "deviceSimulatorExpress.showDependencyInstall",
SHOW_NEW_FILE_POPUP: "deviceSimulatorExpress.showNewFilePopup",
- ENABLE_PREVIEW_MODE: "deviceSimulatorExpress.previewMode",
};
export const CONSTANTS = {
@@ -112,10 +111,6 @@ export const CONSTANTS = {
"error.noProgramFoundDebug",
"Cannot find a program to debug."
),
- NO_PIP: localize(
- "error.noPip",
- "We found that you don't have Pip installed on your computer, please install it and try again."
- ),
NO_PYTHON_PATH: localize(
"error.noPythonPath",
"We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again."
@@ -533,7 +528,7 @@ export const STATUS_BAR_PRIORITY = {
};
export const VERSIONS = {
- MIN_PY_VERSION: "3.7.0",
+ MIN_PY_VERSION: "Python 3.7.0",
};
export const HELPER_FILES = {
@@ -547,6 +542,7 @@ export const HELPER_FILES = {
export const GLOBAL_ENV_VARS = {
PYTHON: "python",
+ PYTHON3: "python3",
};
export const LANGUAGE_VARS = {
PYTHON: { ID: "python", FILE_ENDS: ".py" },
diff --git a/src/extension.ts b/src/extension.ts
index cae41100f..b733129f0 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -132,6 +132,10 @@ export async function activate(context: vscode.ExtensionContext) {
const openWebview = () => {
if (currentPanel && currentPanel.webview) {
messagingService.setWebview(currentPanel.webview);
+ currentPanel.webview.html = webviewService.getWebviewContent(
+ WEBVIEW_TYPES.SIMULATOR,
+ true
+ );
currentPanel.reveal(vscode.ViewColumn.Beside);
} else {
currentPanel = vscode.window.createWebviewPanel(
@@ -275,7 +279,6 @@ export async function activate(context: vscode.ExtensionContext) {
currentPanel,
context
);
- console.log("sent");
}
currentPanel.onDidDispose(
@@ -307,14 +310,8 @@ export async function activate(context: vscode.ExtensionContext) {
const openSimulator: vscode.Disposable = vscode.commands.registerCommand(
"deviceSimulatorExpress.common.openSimulator",
async () => {
- const isPreviewMode = getIsPreviewMode();
-
const chosen_device = await vscode.window.showQuickPick(
- Object.values(CONSTANTS.DEVICE_NAME_FORMAL).filter(
- device =>
- isPreviewMode ||
- device !== CONSTANTS.DEVICE_NAME_FORMAL.CLUE
- )
+ Object.values(CONSTANTS.DEVICE_NAME_FORMAL)
);
if (!chosen_device) {
@@ -417,14 +414,8 @@ export async function activate(context: vscode.ExtensionContext) {
const newFile: vscode.Disposable = vscode.commands.registerCommand(
"deviceSimulatorExpress.common.newFile",
async () => {
- const isPreviewMode = getIsPreviewMode();
-
const chosen_device = await vscode.window.showQuickPick(
- Object.values(CONSTANTS.DEVICE_NAME_FORMAL).filter(
- device =>
- isPreviewMode ||
- device !== CONSTANTS.DEVICE_NAME_FORMAL.CLUE
- )
+ Object.values(CONSTANTS.DEVICE_NAME_FORMAL)
);
if (!chosen_device) {
@@ -595,7 +586,7 @@ export async function activate(context: vscode.ExtensionContext) {
// base_64 strings on UNIX systems.
// added any incomplete data to beginning
- let processedData = pythonProcessDataBuffer
+ const processedData = pythonProcessDataBuffer
.join("")
.concat(dataFromTheProcess);
pythonProcessDataBuffer = [];
@@ -794,14 +785,8 @@ export async function activate(context: vscode.ExtensionContext) {
const deployToDevice: vscode.Disposable = vscode.commands.registerCommand(
"deviceSimulatorExpress.common.deployToDevice",
async () => {
- const isPreviewMode = getIsPreviewMode();
-
const chosen_device = await vscode.window.showQuickPick(
- Object.values(CONSTANTS.DEVICE_NAME_FORMAL).filter(
- device =>
- isPreviewMode ||
- device !== CONSTANTS.DEVICE_NAME_FORMAL.CLUE
- )
+ Object.values(CONSTANTS.DEVICE_NAME_FORMAL)
);
if (!chosen_device) {
@@ -1021,13 +1006,6 @@ export async function activate(context: vscode.ExtensionContext) {
}
);
- const getIsPreviewMode = (): boolean => {
- const isPreviewMode: boolean = vscode.workspace
- .getConfiguration()
- .get(CONFIG.ENABLE_PREVIEW_MODE);
- return isPreviewMode;
- };
-
context.subscriptions.push(
installDependencies,
runSimulator,
diff --git a/src/install_dependencies.py b/src/install_dependencies.py
index afadb9dd5..00fe81d37 100644
--- a/src/install_dependencies.py
+++ b/src/install_dependencies.py
@@ -3,7 +3,7 @@
import pathlib
import os
-os.chdir(pathlib.Path(__file__).parent.parent.absolute())
+os.chdir(str(pathlib.Path(__file__).parent.parent.absolute()))
subprocess.check_call(
[sys.executable, "-m", "pip", "install", "-r", "./out/requirements.txt"]
)
diff --git a/src/latest_release_note.ts b/src/latest_release_note.ts
index ba165ecba..65547e1aa 100644
--- a/src/latest_release_note.ts
+++ b/src/latest_release_note.ts
@@ -1,57 +1,26 @@
// TODO: find a better way of loading html into a string
-export const LATEST_RELEASE_NOTE = `
Device Simulator Express Release Notes ⌨️🐍💞 (April 15, 2020)
+export const LATEST_RELEASE_NOTE = `Device Simulator Express Release Notes 📝(April 20, 2020)
-
- We're unveiling a new addition to our DSX family of microcontroller simulators! Please welcome the Adafruit
- CLUE
- simulator 💕🔍.
- This change is hidden under a preview flag by default. See
- here to learn how to enable
- preview mode!
-
-
- Also, support for BBC micro:bit simulation is now officially released ! 💖✨ Previously, it was hidden
- behind a
- preview flag.
-
-
-
Features:
-
+
+
LIGHT GESTURE PR O XIMI T Y
+
+
Changes:
- Command palette only shows actions (ie: Open Simulator, Deploy to Device) once instead of per device.
-
-
+ The Adafruit CLUE is enabled by default.
+ The "Getting Started" page now includes some guidance on the debugger.
Fixes:
- Fixed issue with sensors on micro:bit debugger.
+ Implemented fix for the switch on the CPX.
+ The CLUE debugger's LEDs can now correctly show on the UI.
+ The dependency installation process is fixed, and users without Git installed can now run setup.
+
- Keep being a coding champ 🤩🏆🙌,
+
+
PS: You can read about the DSX team in the article here !
+ Create something wonderful ✨🙌,
      - The Device Simulator Express Team
`;
diff --git a/src/micropython/microbit/__model/microbit_model.py b/src/micropython/microbit/__model/microbit_model.py
index b9149e721..1f78e35ae 100644
--- a/src/micropython/microbit/__model/microbit_model.py
+++ b/src/micropython/microbit/__model/microbit_model.py
@@ -30,15 +30,11 @@ def __init__(self):
def panic(self, n):
# Due to the shim, there is another call frame.
- utils.print_for_unimplemented_functions(
- MicrobitModel.panic.__name__, one_more_call=True
- )
+ utils.print_for_unimplemented_functions(MicrobitModel.panic.__name__)
def reset(self):
# Due to the shim, there is another call frame.
- utils.print_for_unimplemented_functions(
- MicrobitModel.reset.__name__, one_more_call=True
- )
+ utils.print_for_unimplemented_functions(MicrobitModel.reset.__name__)
def sleep(self, n):
time.sleep(n / 1000)
diff --git a/src/requirements.txt b/src/requirements.txt
index 79749da82..66da4eb19 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -7,7 +7,6 @@ PyObjC; platform_system == "darwin"
uflash==1.3.0
adafruit-circuitpython-fancyled==1.3.3
Pillow==7.0.0
-adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz
adafruit-circuitpython-bitmap_font==1.1.0
adafruit-circuitpython-display-shapes==1.2.0
adafruit-circuitpython-neopixel==5.0.0
\ No newline at end of file
diff --git a/src/service/setupService.ts b/src/service/setupService.ts
index c68f78fbd..04c757a72 100644
--- a/src/service/setupService.ts
+++ b/src/service/setupService.ts
@@ -32,8 +32,11 @@ export class SetupService {
context: vscode.ExtensionContext,
needsResponse: boolean = false
) => {
- const originalpythonExecutablePath = await this.getCurrentPythonExecutablePath();
- let pythonExecutablePath = originalpythonExecutablePath;
+ const originalPythonExecutablePath = await this.getCurrentPythonExecutablePath();
+ if (originalPythonExecutablePath === "") {
+ return;
+ }
+ let pythonExecutablePath = originalPythonExecutablePath;
const pythonExecutableName: string =
os.platform() === "win32"
? HELPER_FILES.PYTHON_EXE
@@ -71,7 +74,7 @@ export class SetupService {
} else {
pythonExecutablePath = await this.promptInstallVenv(
context,
- originalpythonExecutablePath,
+ originalPythonExecutablePath,
pythonExecutableName
);
this.telemetryAI.trackFeatureUsage(
@@ -92,7 +95,7 @@ export class SetupService {
TelemetryEventName.SETUP_HAS_VENV
);
}
- if (pythonExecutablePath === originalpythonExecutablePath) {
+ if (pythonExecutablePath === originalPythonExecutablePath) {
// going with original interpreter, either because
// already in venv or error in creating custom venv
if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) {
@@ -159,40 +162,151 @@ export class SetupService {
return pythonExecutablePath;
};
- public getCurrentPythonExecutablePath = async () => {
- let originalpythonExecutablePath = "";
-
+ public getCurrentPythonExecutablePath = async (
+ isTryingPython3: boolean = false
+ ) => {
+ let originalPythonExecutablePath = "";
+ const systemPythonVar = isTryingPython3
+ ? GLOBAL_ENV_VARS.PYTHON3
+ : GLOBAL_ENV_VARS.PYTHON;
// try to get name from interpreter
try {
- originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH);
+ originalPythonExecutablePath = getConfig(CONFIG.PYTHON_PATH);
} catch (err) {
- originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON;
+ originalPythonExecutablePath = systemPythonVar;
}
if (
- originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON ||
- originalpythonExecutablePath === ""
+ originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON3 ||
+ originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON ||
+ originalPythonExecutablePath === ""
) {
+ // catching any instance where the python path needs to be resolved
+ // from an system variable
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH
);
try {
const { stdout } = await this.executePythonCommand(
- GLOBAL_ENV_VARS.PYTHON,
+ systemPythonVar,
`-c "import sys; print(sys.executable)"`
);
- originalpythonExecutablePath = stdout.trim();
+ originalPythonExecutablePath = stdout.trim();
} catch (err) {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_NO_PYTHON_PATH
);
+ if (isTryingPython3) {
+ // if trying python3 failed, that means that BOTH
+ // python and python3 failed as system variables
+ // so that means that there is no python
+ vscode.window
+ .showErrorMessage(
+ CONSTANTS.ERROR.NO_PYTHON_PATH,
+ DialogResponses.INSTALL_PYTHON
+ )
+ .then((selection: vscode.MessageItem | undefined) => {
+ if (selection === DialogResponses.INSTALL_PYTHON) {
+ const okAction = () => {
+ this.telemetryAI.trackFeatureUsage(
+ TelemetryEventName.SETUP_DOWNLOAD_PYTHON
+ );
+ open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
+ };
+ showPrivacyModal(
+ okAction,
+ CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON
+ );
+ }
+ });
+ // no python installed, cannot get path
+ return "";
+ } else {
+ // "python" didn't resolve to anything, trying "python3"
+ return this.getCurrentPythonExecutablePath(true);
+ }
+ }
+ if (
+ !(await this.validatePythonVersion(
+ originalPythonExecutablePath
+ ))
+ ) {
+ this.telemetryAI.trackFeatureUsage(
+ TelemetryEventName.SETUP_INVALID_PYTHON_VER
+ );
+ if (isTryingPython3) {
+ // if we're trying python3, it means we already tried python and it
+ // all doesn't seem to work, but it got this far, so it means that
+ // their system python3 version is still not above 3.7, but they
+ // don't have a path selected.
+ vscode.window
+ .showInformationMessage(
+ CONSTANTS.ERROR.INVALID_PYTHON_PATH,
+ DialogResponses.INSTALL_PYTHON
+ )
+ .then(
+ (installChoice: vscode.MessageItem | undefined) => {
+ if (
+ installChoice ===
+ DialogResponses.INSTALL_PYTHON
+ ) {
+ const okAction = () => {
+ this.telemetryAI.trackFeatureUsage(
+ TelemetryEventName.SETUP_DOWNLOAD_PYTHON
+ );
+ open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
+ };
+ showPrivacyModal(
+ okAction,
+ CONSTANTS.INFO
+ .THIRD_PARTY_WEBSITE_PYTHON
+ );
+ }
+ }
+ );
+ return "";
+ } else {
+ // otherwise, we ran the "python" system variable
+ // and we can try python3
+ return this.getCurrentPythonExecutablePath(true);
+ }
+ }
+ } else {
+ // should only be applicable if the user defined their own path
+
+ // fix path to be absolute
+ if (!path.isAbsolute(originalPythonExecutablePath)) {
+ originalPythonExecutablePath = path.join(
+ vscode.workspace.rootPath,
+ originalPythonExecutablePath
+ );
+ }
+
+ if (!fs.existsSync(originalPythonExecutablePath)) {
+ await vscode.window.showErrorMessage(
+ CONSTANTS.ERROR.BAD_PYTHON_PATH
+ );
+ this.telemetryAI.trackFeatureUsage(
+ TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH
+ );
+ return "";
+ }
+
+ if (
+ !(await this.validatePythonVersion(
+ originalPythonExecutablePath
+ ))
+ ) {
+ this.telemetryAI.trackFeatureUsage(
+ TelemetryEventName.SETUP_INVALID_PYTHON_VER
+ );
vscode.window
- .showErrorMessage(
- CONSTANTS.ERROR.NO_PYTHON_PATH,
+ .showInformationMessage(
+ CONSTANTS.ERROR.INVALID_PYTHON_PATH,
DialogResponses.INSTALL_PYTHON
)
- .then((selection: vscode.MessageItem | undefined) => {
- if (selection === DialogResponses.INSTALL_PYTHON) {
+ .then((installChoice: vscode.MessageItem | undefined) => {
+ if (installChoice === DialogResponses.INSTALL_PYTHON) {
const okAction = () => {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_DOWNLOAD_PYTHON
@@ -205,49 +319,21 @@ export class SetupService {
);
}
});
- // no python installed, cannot get path
return "";
}
}
- // fix path to be absolute
- if (!path.isAbsolute(originalpythonExecutablePath)) {
- originalpythonExecutablePath = path.join(
- vscode.workspace.rootPath,
- originalpythonExecutablePath
- );
- }
-
- if (!fs.existsSync(originalpythonExecutablePath)) {
- await vscode.window.showErrorMessage(
- CONSTANTS.ERROR.BAD_PYTHON_PATH
- );
- this.telemetryAI.trackFeatureUsage(
- TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH
- );
- return "";
- }
- if (!(await this.validatePythonVersion(originalpythonExecutablePath))) {
- this.telemetryAI.trackFeatureUsage(
- TelemetryEventName.SETUP_INVALID_PYTHON_VER
- );
- return "";
- }
-
- return originalpythonExecutablePath;
+ return originalPythonExecutablePath;
};
public isPipInstalled = async (pythonExecutablePath: string) => {
try {
- const { stdout } = await this.executePythonCommand(
- pythonExecutablePath,
- " -m pip"
- );
+ await this.executePythonCommand(pythonExecutablePath, " -m pip");
return true;
} catch (err) {
vscode.window
.showErrorMessage(
- CONSTANTS.ERROR.NO_PIP,
+ `We found that you may not have Pip installed on your interpreter at ${pythonExecutablePath}, please install it and try again.`,
DialogResponses.INSTALL_PIP
)
.then((selection: vscode.MessageItem | undefined) => {
@@ -294,22 +380,6 @@ export class SetupService {
"--version"
);
if (stdout < VERSIONS.MIN_PY_VERSION) {
- vscode.window
- .showInformationMessage(
- CONSTANTS.ERROR.INVALID_PYTHON_PATH,
- DialogResponses.INSTALL_PYTHON
- )
- .then((installChoice: vscode.MessageItem | undefined) => {
- if (installChoice === DialogResponses.INSTALL_PYTHON) {
- const okAction = () => {
- open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
- };
- showPrivacyModal(
- okAction,
- CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON
- );
- }
- });
return false;
} else {
return true;
@@ -398,7 +468,7 @@ export class SetupService {
);
vscode.window
.showErrorMessage(
- `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`,
+ `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}. If you're on Linux, try running "sudo apt-get install python3-venv".`,
DialogResponses.READ_INSTALL_MD
)
.then((selection: vscode.MessageItem | undefined) => {
diff --git a/src/view/components/clue/ClueImage.tsx b/src/view/components/clue/ClueImage.tsx
index 0ad426c96..85694d4e0 100644
--- a/src/view/components/clue/ClueImage.tsx
+++ b/src/view/components/clue/ClueImage.tsx
@@ -10,7 +10,6 @@ import { ClueSvg, IRefObject } from "./Clue_svg";
interface EventTriggers {
onMouseUp: (event: Event, buttonKey: string) => void;
onMouseDown: (event: Event, buttonKey: string) => void;
- onMouseLeave: (event: Event, buttonKey: string) => void;
onKeyEvent: (event: KeyboardEvent, active: boolean, key: string) => void;
}
interface IProps {
@@ -42,15 +41,13 @@ export class ClueImage extends React.Component {
}
}
componentDidUpdate() {
- if (this.svgRef.current) {
- if (this.context === VIEW_STATE.PAUSE) {
- disableAllButtons(this.svgRef.current.getButtons());
- } else if (this.context === VIEW_STATE.RUNNING) {
- setupAllButtons(
- this.props.eventTriggers,
- this.svgRef.current.getButtons()
- );
- }
+ if (this.context === VIEW_STATE.PAUSE && this.svgRef.current) {
+ disableAllButtons(this.svgRef.current.getButtons());
+ } else if (this.context === VIEW_STATE.RUNNING && this.svgRef.current) {
+ setupAllButtons(
+ this.props.eventTriggers,
+ this.svgRef.current.getButtons()
+ );
}
}
componentWillUnmount() {
@@ -89,24 +86,22 @@ export class ClueImage extends React.Component {
);
}
public updateButtonAttributes(key: BUTTONS_KEYS, isActive: boolean) {
- if (this.svgRef.current) {
- const button = this.svgRef.current.getButtons()[key].current;
- if (button) {
- button.focus();
- if (isActive) {
- button.children[0].setAttribute(
- "class",
- BUTTON_STYLING_CLASSES.KEYPRESSED
- );
- } else {
- button.children[0].setAttribute(
- "class",
- BUTTON_STYLING_CLASSES.DEFAULT
- );
- }
- button.setAttribute("pressed", `${isActive}`);
- button.setAttribute("aria-pressed", `${isActive}`);
+ const button = this.svgRef.current?.getButtons()[key].current;
+ if (button) {
+ button.focus();
+ if (isActive) {
+ button.children[0].setAttribute(
+ "class",
+ BUTTON_STYLING_CLASSES.KEYPRESSED
+ );
+ } else {
+ button.children[0].setAttribute(
+ "class",
+ BUTTON_STYLING_CLASSES.DEFAULT
+ );
}
+ button.setAttribute("pressed", `${isActive}`);
+ button.setAttribute("aria-pressed", `${isActive}`);
}
}
}
@@ -125,9 +120,7 @@ const setupButton = (
buttonElement.onmouseup = e => {
eventTriggers.onMouseUp(e, key);
};
- buttonElement.onmouseleave = e => {
- eventTriggers.onMouseLeave(e, key);
- };
+
buttonElement.onkeydown = e => {
// ensure that the keydown is enter,
// or else it may register shortcuts twice
@@ -155,7 +148,6 @@ const disableAllButtons = (buttonRefs: IRefObject) => {
// to implement
ref.current.onmousedown = null;
ref.current.onmouseup = null;
- ref.current.onmouseleave = null;
ref.current.onkeydown = null;
ref.current.onkeyup = null;
ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED);
diff --git a/src/view/components/clue/ClueSimulator.tsx b/src/view/components/clue/ClueSimulator.tsx
index f0225d2e9..a15f101f8 100644
--- a/src/view/components/clue/ClueSimulator.tsx
+++ b/src/view/components/clue/ClueSimulator.tsx
@@ -7,14 +7,14 @@ import {
VIEW_STATE,
WEBVIEW_MESSAGES,
} from "../../constants";
+import { ViewStateContext } from "../../context";
+import "../../styles/Simulator.css";
import "../../styles/Simulator.css";
import PlayLogo from "../../svgs/play_svg";
import StopLogo from "../../svgs/stop_svg";
import { sendMessage } from "../../utils/MessageUtils";
import ActionBar from "../simulator/ActionBar";
import { BUTTONS_KEYS, ClueImage } from "./ClueImage";
-import "../../styles/Simulator.css";
-import { ViewStateContext } from "../../context";
export const DEFAULT_CLUE_STATE: IClueState = {
buttons: { button_a: false, button_b: false },
@@ -128,7 +128,6 @@ export class ClueSimulator extends React.Component {
eventTriggers={{
onMouseDown: this.onMouseDown,
onMouseUp: this.onMouseUp,
- onMouseLeave: this.onMouseLeave,
onKeyEvent: this.onKeyEvent,
}}
displayMessage={this.state.clue.displayMessage}
@@ -194,18 +193,17 @@ export class ClueSimulator extends React.Component {
},
});
};
+
protected onMouseUp = (event: Event, key: string) => {
event.preventDefault();
this.handleButtonClick(key, false);
};
+
protected onMouseDown = (event: Event, key: string) => {
event.preventDefault();
this.handleButtonClick(key, true);
};
- protected onMouseLeave = (event: Event, key: string) => {
- event.preventDefault();
- console.log(`To implement onMouseLeave ${key}`);
- };
+
protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) {
event.stopPropagation();
if (
@@ -213,57 +211,49 @@ export class ClueSimulator extends React.Component {
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(key, active);
- if (this.imageRef.current) {
- if (key === BUTTONS_KEYS.BTN_A) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_A,
- active
- );
- } else if (key === BUTTONS_KEYS.BTN_B) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_B,
- active
- );
- } else if (key === BUTTONS_KEYS.BTN_AB) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_AB,
- active
- );
- }
+ if (key === BUTTONS_KEYS.BTN_A) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_A,
+ active
+ );
+ } else if (key === BUTTONS_KEYS.BTN_B) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_B,
+ active
+ );
+ } else if (key === BUTTONS_KEYS.BTN_AB) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_AB,
+ active
+ );
}
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_A, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_A,
- active
- );
- }
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_A,
+ active
+ );
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_B, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_B,
- active
- );
- }
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_B,
+ active
+ );
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_AB,
- active
- );
- }
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_AB,
+ active
+ );
} else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) {
this.togglePlayClick();
} else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) {
diff --git a/src/view/components/clue/Clue_svg.tsx b/src/view/components/clue/Clue_svg.tsx
index f5bfe5b92..bcbf4e6d2 100644
--- a/src/view/components/clue/Clue_svg.tsx
+++ b/src/view/components/clue/Clue_svg.tsx
@@ -2,10 +2,10 @@
// Licensed under the MIT license.
import * as React from "react";
+import { CLUE_LEDS_COLORS, CONSTANTS } from "../../constants";
import "../../styles/SimulatorSvg.css";
-import { DEFAULT_CLUE_STATE } from "./ClueSimulator";
-import { CONSTANTS, CLUE_LEDS_COLORS } from "../../constants";
import svg from "../cpx/Svg_utils";
+import { DEFAULT_CLUE_STATE } from "./ClueSimulator";
export interface IRefObject {
[key: string]: React.RefObject;
}
@@ -1124,8 +1124,8 @@ export class ClueSvg extends React.Component {
}
private updateDisplay() {
- if (this.displayRef.current && this.props.displayImage) {
- this.displayRef.current.setAttribute(
+ if (this.props.displayImage) {
+ this.displayRef.current?.setAttribute(
"href",
`data:image/png;base64,${this.props.displayImage}`
);
@@ -1140,26 +1140,23 @@ export class ClueSvg extends React.Component {
(255 - neopixel[1]) * CONSTANTS.LED_TINT_FACTOR},${neopixel[2] +
(255 - neopixel[2]) * CONSTANTS.LED_TINT_FACTOR})`;
- if (this.ledsRefs.neopixel.current) {
- this.ledsRefs.neopixel.current.setAttribute("fill", rgbColor);
- }
- if (this.gradientRefs.neopixel.current) {
- if (neopixel === DEFAULT_CLUE_STATE.leds.neopixel) {
- this.gradientRefs.neopixel.current.setAttribute(
- "stop-opacity",
- "0"
- );
- } else {
- this.gradientRefs.neopixel.current.setAttribute(
- "stop-opacity",
- "1"
- );
- }
- this.gradientRefs.neopixel.current.setAttribute(
- "stop-color",
- rgbColor
+ this.ledsRefs.neopixel.current?.setAttribute("fill", rgbColor);
+
+ if (neopixel === DEFAULT_CLUE_STATE.leds.neopixel) {
+ this.gradientRefs.neopixel.current?.setAttribute(
+ "stop-opacity",
+ "0"
+ );
+ } else {
+ this.gradientRefs.neopixel.current?.setAttribute(
+ "stop-opacity",
+ "1"
);
}
+ this.gradientRefs.neopixel.current?.setAttribute(
+ "stop-color",
+ rgbColor
+ );
}
private updateLeds() {
// update white led
@@ -1167,25 +1164,21 @@ export class ClueSvg extends React.Component {
this.ledsRefs.whiteLeds.map(
(ledRef: React.RefObject) => {
- if (ledRef.current && this.gradientRefs.whiteLed.current) {
- svg.setLed(
- isWhiteLedOn,
- CLUE_LEDS_COLORS.WHITE_LEDS_OFF,
- CLUE_LEDS_COLORS.WHITE_LEDS_ON,
- ledRef.current,
- this.gradientRefs.whiteLed.current
- );
- }
+ svg.setLed(
+ isWhiteLedOn,
+ CLUE_LEDS_COLORS.WHITE_LEDS_OFF,
+ CLUE_LEDS_COLORS.WHITE_LEDS_ON,
+ ledRef.current,
+ this.gradientRefs.whiteLed.current
+ );
}
);
- if (this.ledsRefs.redLed.current && this.gradientRefs.redLed.current) {
- svg.setLed(
- isRedLedOn,
- CLUE_LEDS_COLORS.RED_LED_OFF,
- CLUE_LEDS_COLORS.RED_LED_ON,
- this.ledsRefs.redLed.current,
- this.gradientRefs.redLed.current
- );
- }
+ svg.setLed(
+ isRedLedOn,
+ CLUE_LEDS_COLORS.RED_LED_OFF,
+ CLUE_LEDS_COLORS.RED_LED_ON,
+ this.ledsRefs.redLed.current,
+ this.gradientRefs.redLed.current
+ );
}
}
diff --git a/src/view/components/cpx/CpxSimulator.tsx b/src/view/components/cpx/CpxSimulator.tsx
index 24cf1728b..b0d77f441 100644
--- a/src/view/components/cpx/CpxSimulator.tsx
+++ b/src/view/components/cpx/CpxSimulator.tsx
@@ -368,11 +368,12 @@ class Simulator extends React.Component<{}, IState> {
}
private handleSwitchClick() {
- let cpxState = this.state.cpx;
- const switchIsOn: boolean = !this.state.cpx.switch;
+ const switchIsOn = !this.state.cpx.switch;
updateSwitch(switchIsOn);
- cpxState = { ...cpxState, switch: switchIsOn };
- this.setState({ ...this.state, ...cpxState });
+ this.setState({
+ ...this.state,
+ cpx: { ...this.state.cpx, switch: switchIsOn },
+ });
return { switch: switchIsOn };
}
diff --git a/src/view/components/cpx/Svg_utils.tsx b/src/view/components/cpx/Svg_utils.tsx
index 29dab77de..92836b923 100644
--- a/src/view/components/cpx/Svg_utils.tsx
+++ b/src/view/components/cpx/Svg_utils.tsx
@@ -82,15 +82,15 @@ namespace svg {
ledStatus: boolean,
offColor: string,
onColor: string,
- ledElement: SVGElement,
- gradientStopElement: SVGStopElement
+ ledElement: SVGElement | null,
+ gradientStopElement: SVGStopElement | null
) {
if (ledStatus) {
- ledElement.setAttribute("fill", onColor);
- gradientStopElement.setAttribute("stop-opacity", "1");
+ ledElement?.setAttribute("fill", onColor);
+ gradientStopElement?.setAttribute("stop-opacity", "1");
} else {
- ledElement.setAttribute("fill", offColor);
- gradientStopElement.setAttribute("stop-opacity", "0");
+ ledElement?.setAttribute("fill", offColor);
+ gradientStopElement?.setAttribute("stop-opacity", "0");
}
}
}
diff --git a/src/view/components/microbit/MicrobitImage.tsx b/src/view/components/microbit/MicrobitImage.tsx
index c471ecb56..a216dceef 100644
--- a/src/view/components/microbit/MicrobitImage.tsx
+++ b/src/view/components/microbit/MicrobitImage.tsx
@@ -10,7 +10,6 @@ import { IRefObject, MicrobitSvg } from "./Microbit_svg";
interface EventTriggers {
onMouseUp: (event: Event, buttonKey: string) => void;
onMouseDown: (event: Event, buttonKey: string) => void;
- onMouseLeave: (event: Event, buttonKey: string) => void;
onKeyEvent: (event: KeyboardEvent, active: boolean, key: string) => void;
}
interface IProps {
@@ -80,24 +79,22 @@ export class MicrobitImage extends React.Component {
return ;
}
public updateButtonAttributes(key: BUTTONS_KEYS, isActive: boolean) {
- if (this.svgRef.current) {
- const button = this.svgRef.current.getButtons()[key].current;
- if (button) {
- button.focus();
- if (isActive) {
- button.children[0].setAttribute(
- "class",
- BUTTON_STYLING_CLASSES.KEYPRESSED
- );
- } else {
- button.children[0].setAttribute(
- "class",
- BUTTON_STYLING_CLASSES.DEFAULT
- );
- }
- button.setAttribute("pressed", `${isActive}`);
- button.setAttribute("aria-pressed", `${isActive}`);
+ const button = this.svgRef.current?.getButtons()[key].current;
+ if (button) {
+ button.focus();
+ if (isActive) {
+ button.children[0].setAttribute(
+ "class",
+ BUTTON_STYLING_CLASSES.KEYPRESSED
+ );
+ } else {
+ button.children[0].setAttribute(
+ "class",
+ BUTTON_STYLING_CLASSES.DEFAULT
+ );
}
+ button.setAttribute("pressed", `${isActive}`);
+ button.setAttribute("aria-pressed", `${isActive}`);
}
}
}
@@ -117,9 +114,7 @@ const setupButton = (
buttonElement.onmouseup = e => {
eventTriggers.onMouseUp(e, key);
};
- buttonElement.onmouseleave = e => {
- eventTriggers.onMouseLeave(e, key);
- };
+
buttonElement.onkeydown = e => {
// ensure that the keydown is enter,
// or else it may register shortcuts twice
@@ -147,7 +142,6 @@ const disableAllButtons = (buttonRefs: IRefObject) => {
// to implement
ref.current.onmousedown = null;
ref.current.onmouseup = null;
- ref.current.onmouseleave = null;
ref.current.onkeydown = null;
ref.current.onkeyup = null;
ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED);
diff --git a/src/view/components/microbit/MicrobitSimulator.tsx b/src/view/components/microbit/MicrobitSimulator.tsx
index ff44ac7b2..d3507710c 100644
--- a/src/view/components/microbit/MicrobitSimulator.tsx
+++ b/src/view/components/microbit/MicrobitSimulator.tsx
@@ -124,7 +124,6 @@ export class MicrobitSimulator extends React.Component {
eventTriggers={{
onMouseDown: this.onMouseDown,
onMouseUp: this.onMouseUp,
- onMouseLeave: this.onMouseLeave,
onKeyEvent: this.onKeyEvent,
}}
leds={this.state.microbit.leds}
@@ -186,18 +185,17 @@ export class MicrobitSimulator extends React.Component {
},
});
};
+
protected onMouseUp = (event: Event, key: string) => {
event.preventDefault();
this.handleButtonClick(key, false);
};
+
protected onMouseDown = (event: Event, key: string) => {
event.preventDefault();
this.handleButtonClick(key, true);
};
- protected onMouseLeave = (event: Event, key: string) => {
- event.preventDefault();
- console.log(`To implement onMouseLeave ${key}`);
- };
+
protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) {
event.stopPropagation();
if (
@@ -205,57 +203,52 @@ export class MicrobitSimulator extends React.Component {
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(key, active);
- if (this.imageRef.current) {
- if (key === BUTTONS_KEYS.BTN_A) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_A,
- active
- );
- } else if (key === BUTTONS_KEYS.BTN_B) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_B,
- active
- );
- } else if (key === BUTTONS_KEYS.BTN_AB) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_AB,
- active
- );
- }
+ if (key === BUTTONS_KEYS.BTN_A) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_A,
+ active
+ );
+ } else if (key === BUTTONS_KEYS.BTN_B) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_B,
+ active
+ );
+ } else if (key === BUTTONS_KEYS.BTN_AB) {
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_AB,
+ active
+ );
}
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_A, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_A,
- active
- );
- }
+
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_A,
+ active
+ );
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_B, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_B,
- active
- );
- }
+
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_B,
+ active
+ );
} else if (
[event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) &&
this.context === VIEW_STATE.RUNNING
) {
this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active);
- if (this.imageRef.current) {
- this.imageRef.current.updateButtonAttributes(
- BUTTONS_KEYS.BTN_AB,
- active
- );
- }
+
+ this.imageRef.current?.updateButtonAttributes(
+ BUTTONS_KEYS.BTN_AB,
+ active
+ );
} else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) {
this.togglePlayClick();
} else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) {
diff --git a/src/view/components/microbit/__snapshots__/Microbit.spec.tsx.snap b/src/view/components/microbit/__snapshots__/Microbit.spec.tsx.snap
index 8dc222774..1ab115851 100644
--- a/src/view/components/microbit/__snapshots__/Microbit.spec.tsx.snap
+++ b/src/view/components/microbit/__snapshots__/Microbit.spec.tsx.snap
@@ -2781,9 +2781,9 @@ Array [
onMouseLeave={[Function]}
>
= props => {
return (
-
+
{props.axisProperties.sliderProps.map(
(sliderProperties: ISliderProps, index: number) => {
return (
diff --git a/src/view/components/toolbar/motion/Gesture.tsx b/src/view/components/toolbar/Gesture.tsx
similarity index 91%
rename from src/view/components/toolbar/motion/Gesture.tsx
rename to src/view/components/toolbar/Gesture.tsx
index 934c19345..a89a3bd5c 100644
--- a/src/view/components/toolbar/motion/Gesture.tsx
+++ b/src/view/components/toolbar/Gesture.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
-import { CONSTANTS } from "../../../constants";
-import { Dropdown } from "../../Dropdown";
-import SensorButton from "../SensorButton";
+import { CONSTANTS } from "../../constants";
+import { Dropdown } from "../Dropdown";
+import SensorButton from "./SensorButton";
const GESTURE_BUTTON_MESSAGE = "Send Gesture";
interface IProps {
diff --git a/src/view/components/toolbar/InputSlider.tsx b/src/view/components/toolbar/InputSlider.tsx
index 1e293ebaa..d98e118cc 100644
--- a/src/view/components/toolbar/InputSlider.tsx
+++ b/src/view/components/toolbar/InputSlider.tsx
@@ -22,7 +22,7 @@ class InputSlider extends React.Component
{
render() {
const isInputDisabled = this.context === VIEW_STATE.PAUSE;
return (
-
+
{this.props.axisLabel}
void;
- value: number;
-}
-
-class LightSensorBar extends React.Component
{
- constructor(props: any) {
- super(props);
- }
-
- render() {
- return (
-
-
-
- );
- }
-}
-
-export default LightSensorBar;
diff --git a/src/view/components/toolbar/SensorButton.tsx b/src/view/components/toolbar/SensorButton.tsx
index 601bb80c1..b50759bd4 100644
--- a/src/view/components/toolbar/SensorButton.tsx
+++ b/src/view/components/toolbar/SensorButton.tsx
@@ -13,8 +13,8 @@ class SensorButton extends React.Component {
public setButtonClass = (isActive: boolean) => {
const isInputDisabled = this.context === VIEW_STATE.PAUSE;
- if (isActive && !isInputDisabled && this.buttonRef.current) {
- this.buttonRef.current.setAttribute(
+ if (isActive && !isInputDisabled) {
+ this.buttonRef.current?.setAttribute(
"class",
"sensor-button active-button"
);
diff --git a/src/view/components/toolbar/SensorModalUtils.tsx b/src/view/components/toolbar/SensorModalUtils.tsx
index b49979fa9..c7ea53f85 100644
--- a/src/view/components/toolbar/SensorModalUtils.tsx
+++ b/src/view/components/toolbar/SensorModalUtils.tsx
@@ -3,13 +3,9 @@
import * as React from "react";
import { SENSOR_LIST } from "../../constants";
import { ARROW_RIGHT_SVG } from "../../svgs/arrow_right_svg";
-import { TAG_INPUT_SVG } from "../../svgs/tag_input_svg";
-import { TAG_OUTPUT_SVG } from "../../svgs/tag_output_svg";
import * as CLUE_MODAL from "./clue/ClueModalContent";
-import LightSensorBar from "./LightSensorBar";
+import * as CPX_MODAL from "./cpx/CpxModalContent";
import * as MICROBIT_MODAL from "./microbit/MicrobitModalContent";
-import MotionSensorBar from "./motion/MotionSensorBar";
-import TemperatureSensorBar from "./TemperatureSensorBar";
export const TRY_IT_MAKE_CODE = (
@@ -65,8 +61,8 @@ export const CPX_TOOLBAR_ICON_ID = {
};
export const MICROBIT_TOOLBAR_ICON_ID = {
- TEMPERATURE: "toolbar-temperature-sensor",
- LIGHT: "toolbar-light-sensor",
+ TEMPERATURE: "toolbar-microbit-temperature-sensor",
+ LIGHT: "toolbar-microbit-light-sensor",
ACCELEROMETER: "toolbar-accelerometer-sensor",
LEDS: "toolbar-microbit-led",
PUSH_BUTTON: "toolbar-microbit-a-b-push",
@@ -115,200 +111,24 @@ export const DEFAULT_MODAL_CONTENT: IModalContent = {
id: "none",
};
-export const GPIO_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-gpio.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: TAG_OUTPUT_SVG,
- descriptionText: "toolbar-gpio.description",
- tryItDescription: "toolbar-gpio.tryItDescription",
- components: undefined,
- id: "GPIO",
- };
-};
-
-export const IR_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-ir-sensor.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: TAG_OUTPUT_SVG,
- descriptionText: "toolbar-ir-sensor.description",
- tryItDescription: "toolbar-ir-sensor.tryItDescription",
- components: [TRY_IT_MAKE_CODE, FEATURE_REQUEST_ON_GITHUB],
- id: "IR",
- };
-};
-export const LIGHT_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-light-sensor.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- descriptionText: "toolbar-light-sensor.description",
- tryItDescription: "toolbar-light-sensor.tryItDescription",
- components: [
-
,
- ],
- id: "light_sensor",
- };
-};
-export const MOTION_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- const motionSensorValues = {
- X: sensorValues[SENSOR_LIST.MOTION_X],
- Y: sensorValues[SENSOR_LIST.MOTION_Y],
- Z: sensorValues[SENSOR_LIST.MOTION_Z],
- };
- return {
- descriptionTitle: "toolbar-motion-sensor.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- descriptionText: "toolbar-motion-sensor.description",
- tryItDescription: "toolbar-motion-sensor.tryItDescription",
- components: [
-
,
- TRY_IT_MAKE_CODE,
- FEATURE_REQUEST_ON_GITHUB,
- ],
- id: "motion_sensor",
- };
-};
-export const NEOP_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-neo-pixels.title",
- tagInput: undefined,
- tagOutput: TAG_OUTPUT_SVG,
- descriptionText: "toolbar-neo-pixels.description",
- tryItDescription: "toolbar-neo-pixels.tryItDescription",
- components: undefined,
- id: "neon_pixel",
- };
-};
-export const PUSHB_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-a-b-push.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- descriptionText: "toolbar-a-b-push.description",
- tryItDescription: "toolbar-a-b-push.tryItDescription",
- components: undefined,
- id: "push_btn",
- };
-};
-export const RED_LED_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-red-led.title",
- tagInput: undefined,
- tagOutput: TAG_OUTPUT_SVG,
- descriptionText: "toolbar-red-led.description",
- tryItDescription: "toolbar-red-led.tryItDescription",
- components: undefined,
- id: "red_LED",
- };
-};
-export const SOUND_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-sound-sensor.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- descriptionText: "toolbar-sound-sensor.description",
- tryItDescription: "toolbar-sound-sensor.tryItDescription",
- components: [TRY_IT_MAKE_CODE, FEATURE_REQUEST_ON_GITHUB],
- id: "sound_sensor",
- };
-};
-export const SWITCH_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-slider-switch.title",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- descriptionText: "toolbar-slider-switch.description",
- tryItDescription: "toolbar-slider-switch.tryItDescription",
- components: undefined,
- id: "slider_switch",
- };
-};
-export const SPEAKER_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- descriptionTitle: "toolbar-speaker.title",
- tagInput: undefined,
- tagOutput: TAG_OUTPUT_SVG,
- descriptionText: "toolbar-speaker.description",
- tryItDescription: "toolbar-speaker.tryItDescription",
- components: [FEATURE_REQUEST_ON_GITHUB],
- id: "speaker",
- };
-};
-export const TEMPERATURE_MODAL_CONTENT = (
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
- sensorValues: { [key: string]: number }
-): IModalContent => {
- return {
- components: [
-
,
- ],
- descriptionText: "toolbar-temperature-sensor.description",
- descriptionTitle: "toolbar-temperature-sensor.title",
- id: "temperature",
- tagInput: TAG_INPUT_SVG,
- tagOutput: undefined,
- tryItDescription: "toolbar-temperature-sensor.tryItDescription",
- };
-};
-
export const LABEL_TO_MODAL_CONTENT_CONSTRUCTOR = new Map([
- [CPX_TOOLBAR_ICON_ID.GPIO, GPIO_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.IR, IR_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.LIGHT, LIGHT_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.MOTION, MOTION_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.NEO_PIXEL, NEOP_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.PUSH_BUTTON, PUSHB_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.RED_LED, RED_LED_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.SOUND, SOUND_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.SPEAKER, SPEAKER_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.SWITCH, SWITCH_MODAL_CONTENT],
- [CPX_TOOLBAR_ICON_ID.TEMPERATURE, TEMPERATURE_MODAL_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.GPIO, CPX_MODAL.GPIO_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.IR, CPX_MODAL.IR_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.LIGHT, CPX_MODAL.LIGHT_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.MOTION, CPX_MODAL.MOTION_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.NEO_PIXEL, CPX_MODAL.NEOP_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.PUSH_BUTTON, CPX_MODAL.PUSHB_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.RED_LED, CPX_MODAL.RED_LED_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.SOUND, CPX_MODAL.SOUND_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.SPEAKER, CPX_MODAL.SPEAKER_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.SWITCH, CPX_MODAL.SWITCH_CONTENT],
+ [CPX_TOOLBAR_ICON_ID.TEMPERATURE, CPX_MODAL.TEMPERATURE_CONTENT],
[
MICROBIT_TOOLBAR_ICON_ID.ACCELEROMETER,
MICROBIT_MODAL.ACCELEROMETER_CONTENT,
],
+ [MICROBIT_TOOLBAR_ICON_ID.TEMPERATURE, MICROBIT_MODAL.TEMPERATURE_CONTENT],
+ [MICROBIT_TOOLBAR_ICON_ID.LIGHT, MICROBIT_MODAL.LIGHT_CONTENT],
[MICROBIT_TOOLBAR_ICON_ID.COMPASS, MICROBIT_MODAL.COMPASS_CONTENT],
[MICROBIT_TOOLBAR_ICON_ID.LEDS, MICROBIT_MODAL.LED_CONTENT],
[MICROBIT_TOOLBAR_ICON_ID.PUSH_BUTTON, MICROBIT_MODAL.BUTTON_CONTENT],
diff --git a/src/view/components/toolbar/TemperatureSensorBar.tsx b/src/view/components/toolbar/TemperatureSensorBar.tsx
deleted file mode 100644
index 064c32b6e..000000000
--- a/src/view/components/toolbar/TemperatureSensorBar.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import * as React from "react";
-import { SENSOR_LIST } from "../../constants";
-import "../../styles/TemperatureSensorBar.css";
-import { ISensorProps, ISliderProps, X_SLIDER_INDEX } from "../../viewUtils";
-import InputSlider from "./InputSlider";
-
-const TEMPERATURE_SLIDER_PROPS: ISliderProps = {
- axisLabel: " ",
- maxLabel: "Hot",
- maxValue: 125,
- minLabel: "Cold",
- minValue: -55,
- type: SENSOR_LIST.TEMPERATURE,
-};
-const TEMPERATURE_SENSOR_PROPERTIES: ISensorProps = {
- LABEL: "Temperature sensor",
- sliderProps: [TEMPERATURE_SLIDER_PROPS],
- unitLabel: "°C",
-};
-interface IProps {
- onUpdateSensor: (sensor: SENSOR_LIST, value: number) => void;
- value: number;
-}
-class TemperatureSensorBar extends React.Component
{
- constructor(props: any) {
- super(props);
- }
-
- render() {
- return (
-
-
-
- );
- }
-}
-
-export default TemperatureSensorBar;
diff --git a/src/view/components/toolbar/__snapshots__/Toolbar.spec.tsx.snap b/src/view/components/toolbar/__snapshots__/Toolbar.spec.tsx.snap
index 7da991a70..50676ed29 100644
--- a/src/view/components/toolbar/__snapshots__/Toolbar.spec.tsx.snap
+++ b/src/view/components/toolbar/__snapshots__/Toolbar.spec.tsx.snap
@@ -20,9 +20,9 @@ exports[`Toolbar component should render correctly 1`] = `
onMouseLeave={[Function]}
>
void,
sensorValues: { [key: string]: number }
): IModalContent => {
+ const temperatureSensorValues = {
+ T: sensorValues[SENSOR_LIST.TEMPERATURE],
+ };
return {
components: [
- ,
],
descriptionText: "toolbar-clue-temperature-sensor.description",
@@ -55,9 +57,10 @@ export const ACCELEROMETER_CONTENT = (
};
return {
components: (
-
),
descriptionText: "toolbar-clue-accelerometer-sensor.description",
@@ -123,7 +126,7 @@ export const LIGHT_CONTENT = (
onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
sensorValues: { [key: string]: number }
): IModalContent => {
- const accelerometerSensorValues = {
+ const lightSensorValues = {
R: sensorValues[SENSOR_LIST.LIGHT_R],
G: sensorValues[SENSOR_LIST.LIGHT_G],
B: sensorValues[SENSOR_LIST.LIGHT_B],
@@ -133,7 +136,7 @@ export const LIGHT_CONTENT = (
components: (
),
diff --git a/src/view/components/toolbar/clue/ClueSensorProperties.tsx b/src/view/components/toolbar/clue/ClueSensorProperties.tsx
index 77b1109cb..2e2b9975a 100644
--- a/src/view/components/toolbar/clue/ClueSensorProperties.tsx
+++ b/src/view/components/toolbar/clue/ClueSensorProperties.tsx
@@ -146,3 +146,54 @@ export const CLUE_PRESSURE_PROPERTIES: ISensorProps = {
],
unitLabel: "hPa",
};
+const MOTION_SLIDER_PROPS_X: ISliderProps = {
+ axisLabel: "X",
+ maxLabel: "Right",
+ maxValue: 1023,
+ minLabel: "Left",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_X,
+};
+
+const MOTION_SLIDER_PROPS_Y: ISliderProps = {
+ axisLabel: "Y",
+ maxLabel: "Front",
+ maxValue: 1023,
+ minLabel: "Back",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_Y,
+};
+
+const MOTION_SLIDER_PROPS_Z: ISliderProps = {
+ axisLabel: "Z",
+ maxLabel: "Down",
+ maxValue: 1023,
+ minLabel: "Up",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_Z,
+};
+
+export const MOTION_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Motion sensor",
+ sliderProps: [
+ MOTION_SLIDER_PROPS_X,
+ MOTION_SLIDER_PROPS_Y,
+ MOTION_SLIDER_PROPS_Z,
+ ],
+ unitLabel: "m/s2",
+};
+
+const TEMPERATURE_SLIDER_PROPS: ISliderProps = {
+ axisLabel: "T",
+ maxLabel: "Hot",
+ maxValue: 125,
+ minLabel: "Cold",
+ minValue: -55,
+ type: SENSOR_LIST.TEMPERATURE,
+};
+
+export const TEMPERATURE_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Temperature sensor",
+ sliderProps: [TEMPERATURE_SLIDER_PROPS],
+ unitLabel: "°C",
+};
diff --git a/src/view/components/toolbar/cpx/CpxModalContent.tsx b/src/view/components/toolbar/cpx/CpxModalContent.tsx
new file mode 100644
index 000000000..55166eb2a
--- /dev/null
+++ b/src/view/components/toolbar/cpx/CpxModalContent.tsx
@@ -0,0 +1,198 @@
+import * as React from "react";
+import { SENSOR_LIST } from "../../../constants";
+import { TAG_INPUT_SVG } from "../../../svgs/tag_input_svg";
+import { TAG_OUTPUT_SVG } from "../../../svgs/tag_output_svg";
+import { GenericSliderComponent } from "../GenericSliderComponent";
+import {
+ FEATURE_REQUEST_ON_GITHUB,
+ IModalContent,
+ TRY_IT_MAKE_CODE,
+} from "../SensorModalUtils";
+import * as SENSOR_PROPERTIES from "./CpxSensorProperties";
+
+export const GPIO_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-gpio.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: TAG_OUTPUT_SVG,
+ descriptionText: "toolbar-gpio.description",
+ tryItDescription: "toolbar-gpio.tryItDescription",
+ components: undefined,
+ id: "GPIO",
+ };
+};
+
+export const IR_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-ir-sensor.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: TAG_OUTPUT_SVG,
+ descriptionText: "toolbar-ir-sensor.description",
+ tryItDescription: "toolbar-ir-sensor.tryItDescription",
+ components: [TRY_IT_MAKE_CODE, FEATURE_REQUEST_ON_GITHUB],
+ id: "IR",
+ };
+};
+export const LIGHT_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ const lightSensorValues = {
+ L: sensorValues[SENSOR_LIST.LIGHT],
+ };
+ return {
+ descriptionTitle: "toolbar-light-sensor.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-light-sensor.description",
+ tryItDescription: "toolbar-light-sensor.tryItDescription",
+ components: [
+ ,
+ ],
+ id: "light_sensor",
+ };
+};
+export const MOTION_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ const motionSensorValues = {
+ X: sensorValues[SENSOR_LIST.MOTION_X],
+ Y: sensorValues[SENSOR_LIST.MOTION_Y],
+ Z: sensorValues[SENSOR_LIST.MOTION_Z],
+ };
+ return {
+ descriptionTitle: "toolbar-motion-sensor.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-motion-sensor.description",
+ tryItDescription: "toolbar-motion-sensor.tryItDescription",
+ components: [
+ ,
+ TRY_IT_MAKE_CODE,
+ FEATURE_REQUEST_ON_GITHUB,
+ ],
+ id: "motion_sensor",
+ };
+};
+export const NEOP_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-neo-pixels.title",
+ tagInput: undefined,
+ tagOutput: TAG_OUTPUT_SVG,
+ descriptionText: "toolbar-neo-pixels.description",
+ tryItDescription: "toolbar-neo-pixels.tryItDescription",
+ components: undefined,
+ id: "neon_pixel",
+ };
+};
+export const PUSHB_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-a-b-push.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-a-b-push.description",
+ tryItDescription: "toolbar-a-b-push.tryItDescription",
+ components: undefined,
+ id: "push_btn",
+ };
+};
+export const RED_LED_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-red-led.title",
+ tagInput: undefined,
+ tagOutput: TAG_OUTPUT_SVG,
+ descriptionText: "toolbar-red-led.description",
+ tryItDescription: "toolbar-red-led.tryItDescription",
+ components: undefined,
+ id: "red_LED",
+ };
+};
+export const SOUND_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-sound-sensor.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-sound-sensor.description",
+ tryItDescription: "toolbar-sound-sensor.tryItDescription",
+ components: [TRY_IT_MAKE_CODE, FEATURE_REQUEST_ON_GITHUB],
+ id: "sound_sensor",
+ };
+};
+export const SWITCH_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-slider-switch.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-slider-switch.description",
+ tryItDescription: "toolbar-slider-switch.tryItDescription",
+ components: undefined,
+ id: "slider_switch",
+ };
+};
+export const SPEAKER_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ return {
+ descriptionTitle: "toolbar-speaker.title",
+ tagInput: undefined,
+ tagOutput: TAG_OUTPUT_SVG,
+ descriptionText: "toolbar-speaker.description",
+ tryItDescription: "toolbar-speaker.tryItDescription",
+ components: [FEATURE_REQUEST_ON_GITHUB],
+ id: "speaker",
+ };
+};
+export const TEMPERATURE_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ const temperatureSensorValues = {
+ T: sensorValues[SENSOR_LIST.TEMPERATURE],
+ };
+ return {
+ components: [
+ ,
+ ],
+ descriptionText: "toolbar-temperature-sensor.description",
+ descriptionTitle: "toolbar-temperature-sensor.title",
+ id: "temperature",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ tryItDescription: "toolbar-temperature-sensor.tryItDescription",
+ };
+};
diff --git a/src/view/components/toolbar/cpx/CpxSensorProperties.tsx b/src/view/components/toolbar/cpx/CpxSensorProperties.tsx
new file mode 100644
index 000000000..6a5a9990a
--- /dev/null
+++ b/src/view/components/toolbar/cpx/CpxSensorProperties.tsx
@@ -0,0 +1,66 @@
+import { SENSOR_LIST } from "../../../constants";
+import { ISensorProps, ISliderProps } from "../../../viewUtils";
+
+const LIGHT_SLIDER_PROPS: ISliderProps = {
+ maxValue: 255,
+ minValue: 0,
+ minLabel: "Dark",
+ maxLabel: "Bright",
+ type: "light",
+ axisLabel: "L",
+};
+
+export const LIGHT_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Light sensor",
+ sliderProps: [LIGHT_SLIDER_PROPS],
+ unitLabel: "Lux",
+};
+
+const TEMPERATURE_SLIDER_PROPS: ISliderProps = {
+ axisLabel: "T",
+ maxLabel: "Hot",
+ maxValue: 125,
+ minLabel: "Cold",
+ minValue: -55,
+ type: SENSOR_LIST.TEMPERATURE,
+};
+export const TEMPERATURE_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Temperature sensor",
+ sliderProps: [TEMPERATURE_SLIDER_PROPS],
+ unitLabel: "°C",
+};
+
+const MOTION_SLIDER_PROPS_X: ISliderProps = {
+ axisLabel: "X",
+ maxLabel: "Right",
+ maxValue: 78,
+ minLabel: "Left",
+ minValue: -78,
+ type: SENSOR_LIST.MOTION_X,
+};
+const MOTION_SLIDER_PROPS_Y: ISliderProps = {
+ axisLabel: "Y",
+ maxLabel: "Front",
+ maxValue: 78,
+ minLabel: "Back",
+ minValue: -78,
+ type: SENSOR_LIST.MOTION_Y,
+};
+const MOTION_SLIDER_PROPS_Z: ISliderProps = {
+ axisLabel: "Z",
+ maxLabel: "Down",
+ maxValue: 78,
+ minLabel: "Up",
+ minValue: -78,
+ type: SENSOR_LIST.MOTION_Z,
+};
+
+export const MOTION_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Motion sensor",
+ sliderProps: [
+ MOTION_SLIDER_PROPS_X,
+ MOTION_SLIDER_PROPS_Y,
+ MOTION_SLIDER_PROPS_Z,
+ ],
+ unitLabel: "Lux",
+};
diff --git a/src/view/components/toolbar/microbit/MicrobitModalContent.tsx b/src/view/components/toolbar/microbit/MicrobitModalContent.tsx
index 9eb4fd8a3..6d4ae76ef 100644
--- a/src/view/components/toolbar/microbit/MicrobitModalContent.tsx
+++ b/src/view/components/toolbar/microbit/MicrobitModalContent.tsx
@@ -2,9 +2,10 @@ import * as React from "react";
import { GESTURES_MICROBIT, SENSOR_LIST } from "../../../constants";
import { TAG_INPUT_SVG } from "../../../svgs/tag_input_svg";
import { TAG_OUTPUT_SVG } from "../../../svgs/tag_output_svg";
-import { Accelerometer } from "../motion/Accelerometer";
-import { Gesture } from "../motion/Gesture";
+import { GenericSliderComponent } from "../GenericSliderComponent";
+import { Gesture } from "../Gesture";
import { FEATURE_REQUEST_ON_GITHUB, IModalContent } from "../SensorModalUtils";
+import * as SENSOR_PROPERTIES from "./MicrobitSensorProperties";
export const ACCELEROMETER_CONTENT = (
onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
@@ -18,9 +19,10 @@ export const ACCELEROMETER_CONTENT = (
};
return {
components: (
-
),
descriptionText: "toolbar-accelerometer-sensor.description",
@@ -136,3 +138,50 @@ export const GESTURE_CONTENT = (
id: "gesture_sensor",
};
};
+export const LIGHT_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ const lightSensorValues = {
+ L: sensorValues[SENSOR_LIST.LIGHT],
+ };
+ return {
+ descriptionTitle: "toolbar-light-sensor.title",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ descriptionText: "toolbar-light-sensor.description",
+ tryItDescription: "toolbar-light-sensor.tryItDescription",
+ components: [
+ ,
+ ],
+ id: "light_sensor",
+ };
+};
+
+export const TEMPERATURE_CONTENT = (
+ onUpdateValue: (sensor: SENSOR_LIST, value: number) => void,
+ sensorValues: { [key: string]: number }
+): IModalContent => {
+ const temperatureSensorValues = {
+ T: sensorValues[SENSOR_LIST.TEMPERATURE],
+ };
+ return {
+ components: [
+ ,
+ ],
+ descriptionText: "toolbar-temperature-sensor.description",
+ descriptionTitle: "toolbar-temperature-sensor.title",
+ id: "temperature",
+ tagInput: TAG_INPUT_SVG,
+ tagOutput: undefined,
+ tryItDescription: "toolbar-temperature-sensor.tryItDescription",
+ };
+};
diff --git a/src/view/components/toolbar/motion/Accelerometer.tsx b/src/view/components/toolbar/microbit/MicrobitSensorProperties.tsx
similarity index 51%
rename from src/view/components/toolbar/motion/Accelerometer.tsx
rename to src/view/components/toolbar/microbit/MicrobitSensorProperties.tsx
index d6f4759d0..42470ce44 100644
--- a/src/view/components/toolbar/motion/Accelerometer.tsx
+++ b/src/view/components/toolbar/microbit/MicrobitSensorProperties.tsx
@@ -1,65 +1,69 @@
-import * as React from "react";
-import { SENSOR_LIST } from "../../../constants";
-import { ISensorProps, ISliderProps } from "../../../viewUtils";
-
-import { GenericSliderComponent } from "../GenericSliderComponent";
-
-const MOTION_SLIDER_PROPS_X: ISliderProps = {
- axisLabel: "X",
- maxLabel: "Right",
- maxValue: 1023,
- minLabel: "Left",
- minValue: -1023,
- type: SENSOR_LIST.MOTION_X,
-};
-
-const MOTION_SLIDER_PROPS_Y: ISliderProps = {
- axisLabel: "Y",
- maxLabel: "Front",
- maxValue: 1023,
- minLabel: "Back",
- minValue: -1023,
- type: SENSOR_LIST.MOTION_Y,
-};
-
-const MOTION_SLIDER_PROPS_Z: ISliderProps = {
- axisLabel: "Z",
- maxLabel: "Down",
- maxValue: 1023,
- minLabel: "Up",
- minValue: -1023,
- type: SENSOR_LIST.MOTION_Z,
-};
-
-const MOTION_SENSOR_PROPERTIES: ISensorProps = {
- LABEL: "Motion sensor",
- sliderProps: [
- MOTION_SLIDER_PROPS_X,
- MOTION_SLIDER_PROPS_Y,
- MOTION_SLIDER_PROPS_Z,
- ],
- unitLabel: "Lux",
-};
-
-interface IProps {
- axisValues: {
- X: number;
- Y: number;
- Z: number;
- };
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void;
-}
-
-export class Accelerometer extends React.Component {
- render() {
- return (
-
-
-
- );
- }
-}
+import { SENSOR_LIST } from "../../../constants";
+import { ISensorProps, ISliderProps } from "../../../viewUtils";
+
+const LIGHT_SLIDER_PROPS: ISliderProps = {
+ maxValue: 255,
+ minValue: 0,
+ minLabel: "Dark",
+ maxLabel: "Bright",
+ type: "light",
+ axisLabel: "L",
+};
+
+export const LIGHT_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Light sensor",
+ sliderProps: [LIGHT_SLIDER_PROPS],
+ unitLabel: "Lux",
+};
+
+const MOTION_SLIDER_PROPS_X: ISliderProps = {
+ axisLabel: "X",
+ maxLabel: "Right",
+ maxValue: 1023,
+ minLabel: "Left",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_X,
+};
+
+const MOTION_SLIDER_PROPS_Y: ISliderProps = {
+ axisLabel: "Y",
+ maxLabel: "Front",
+ maxValue: 1023,
+ minLabel: "Back",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_Y,
+};
+
+const MOTION_SLIDER_PROPS_Z: ISliderProps = {
+ axisLabel: "Z",
+ maxLabel: "Down",
+ maxValue: 1023,
+ minLabel: "Up",
+ minValue: -1023,
+ type: SENSOR_LIST.MOTION_Z,
+};
+
+export const MOTION_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Motion sensor",
+ sliderProps: [
+ MOTION_SLIDER_PROPS_X,
+ MOTION_SLIDER_PROPS_Y,
+ MOTION_SLIDER_PROPS_Z,
+ ],
+ unitLabel: "m/s2",
+};
+
+const TEMPERATURE_SLIDER_PROPS: ISliderProps = {
+ axisLabel: "T",
+ maxLabel: "Hot",
+ maxValue: 125,
+ minLabel: "Cold",
+ minValue: -55,
+ type: SENSOR_LIST.TEMPERATURE,
+};
+
+export const TEMPERATURE_SENSOR_PROPERTIES: ISensorProps = {
+ LABEL: "Temperature sensor",
+ sliderProps: [TEMPERATURE_SLIDER_PROPS],
+ unitLabel: "°C",
+};
diff --git a/src/view/components/toolbar/motion/Accelerometer.spec.tsx b/src/view/components/toolbar/motion/Accelerometer.spec.tsx
deleted file mode 100644
index 548e496ff..000000000
--- a/src/view/components/toolbar/motion/Accelerometer.spec.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import { IntlProvider } from "react-intl";
-import * as testRenderer from "react-test-renderer";
-import { Accelerometer } from "./Accelerometer";
-
-describe("Accelerometer component ", () => {
- const mockProps = {
- axisValues: {
- X: 1,
- Y: 0,
- Z: 1,
- },
- onUpdateValue: () => {},
- };
-
- it("should render correctly", () => {
- const component = testRenderer
- .create(
-
-
-
- )
- .toJSON();
- expect(component).toMatchSnapshot();
- });
-
- it("should render without crashing", () => {
- const div = document.createElement("div");
- ReactDOM.render(
-
-
- ,
- div
- );
- ReactDOM.unmountComponentAtNode(div);
- });
-});
diff --git a/src/view/components/toolbar/motion/MotionSensorBar.tsx b/src/view/components/toolbar/motion/MotionSensorBar.tsx
deleted file mode 100644
index 5c54904a1..000000000
--- a/src/view/components/toolbar/motion/MotionSensorBar.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import * as React from "react";
-import { CONSTANTS, SENSOR_LIST, WEBVIEW_MESSAGES } from "../../../constants";
-import "../../../styles/MotionSensorBar.css";
-import { sendMessage } from "../../../utils/MessageUtils";
-import { ISensorProps, ISliderProps } from "../../../viewUtils";
-import svg from "../../cpx/Svg_utils";
-import { GenericSliderComponent } from "../GenericSliderComponent";
-import SensorButton from "../SensorButton";
-
-const MOTION_SLIDER_PROPS_X: ISliderProps = {
- axisLabel: "X",
- maxLabel: "Right",
- maxValue: 78,
- minLabel: "Left",
- minValue: -78,
- type: SENSOR_LIST.MOTION_X,
-};
-const MOTION_SLIDER_PROPS_Y: ISliderProps = {
- axisLabel: "Y",
- maxLabel: "Front",
- maxValue: 78,
- minLabel: "Back",
- minValue: -78,
- type: SENSOR_LIST.MOTION_Y,
-};
-const MOTION_SLIDER_PROPS_Z: ISliderProps = {
- axisLabel: "Z",
- maxLabel: "Down",
- maxValue: 78,
- minLabel: "Up",
- minValue: -78,
- type: SENSOR_LIST.MOTION_Z,
-};
-
-const MOTION_SENSOR_PROPERTIES: ISensorProps = {
- LABEL: "Motion sensor",
- sliderProps: [
- MOTION_SLIDER_PROPS_X,
- MOTION_SLIDER_PROPS_Y,
- MOTION_SLIDER_PROPS_Z,
- ],
- unitLabel: "Lux",
-};
-interface IProps {
- axisValues: {
- X: number;
- Y: number;
- Z: number;
- };
- onUpdateValue: (sensor: SENSOR_LIST, value: number) => void;
-}
-
-class MotionSensorBar extends React.Component {
- constructor(props: any) {
- super(props);
- }
-
- render() {
- return (
-
- );
- }
-
- private onMouseDown = (event: React.PointerEvent) => {
- this.updateShakePress(true, event.currentTarget.id);
- this.handleOnclick(true, "shake");
- };
-
- private onKeyUp = (event: React.KeyboardEvent) =>
- this.onKeyEvent(event, false);
-
- private onKeyDown = (event: React.KeyboardEvent) =>
- this.onKeyEvent(event, true);
-
- private onMouseUp = (event: React.PointerEvent) => {
- this.updateShakePress(false, event.currentTarget.id);
- this.handleOnclick(false, "shake");
- };
-
- private handleOnclick = (active: boolean, type: string) => {
- const messageState = { [type]: active };
- sendMessage(WEBVIEW_MESSAGES.SENSOR_CHANGED, messageState);
- };
-
- private onKeyEvent(
- event: React.KeyboardEvent,
- active: boolean
- ) {
- if (
- [event.keyCode, event.key].includes(CONSTANTS.KEYBOARD_KEYS.ENTER)
- ) {
- this.handleOnclick(active, "shake");
- }
- }
-
- private updateShakePress = (shakeState: boolean, id: string): void => {
- const svgElement = window.document.getElementById("cpx_svg");
- const buttonElement = window.document.getElementById(id);
- const cpxSvg: SVGElement = (svgElement as unknown) as SVGElement;
-
- if (svgElement && cpxSvg && buttonElement) {
- buttonElement.setAttribute("aria-pressed", shakeState.toString());
- shakeState
- ? svg.addClass(cpxSvg, "shake-pressed")
- : svg.removeClass(cpxSvg, "shake-pressed");
- }
- };
-}
-
-export default MotionSensorBar;
diff --git a/src/view/components/toolbar/motion/__snapshots__/Accelerometer.spec.tsx.snap b/src/view/components/toolbar/motion/__snapshots__/Accelerometer.spec.tsx.snap
deleted file mode 100644
index 2e78e8daf..000000000
--- a/src/view/components/toolbar/motion/__snapshots__/Accelerometer.spec.tsx.snap
+++ /dev/null
@@ -1,210 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Accelerometer component should render correctly 1`] = `
-
-`;
diff --git a/src/view/container/device/__snapshots__/Device.spec.tsx.snap b/src/view/container/device/__snapshots__/Device.spec.tsx.snap
index bce966d43..54a45df26 100644
--- a/src/view/container/device/__snapshots__/Device.spec.tsx.snap
+++ b/src/view/container/device/__snapshots__/Device.spec.tsx.snap
@@ -2783,9 +2783,9 @@ exports[`Device component should render correctly 1`] = `
onMouseLeave={[Function]}
>
- Select a Device
+ Learn more about:
-
- Circuit Playground Express
-
-
- micro:bit
-
-
+ Circuit Playground Express
+
+
+ micro:bit
+
+
+ CLUE
+
+
+
- CLUE
-
+
+ Debugger
+
+
-
+
Copy these snippets of code to a .py file and run the simulator
-
+
Tutorial for Circuit Playground Express
-
- 1. Import the micro:bit library to use it (This is required)
+
+ > Import the micro:bit library to use it (This is required)
-
+
from adafruit_circuitplayground import cp
-
- 2. Turn on the little red LED
+
+ > Turn on the little red LED
-
+
while True:
-
+
cp.red_led = True
-
- 3. Turn up red LED when button A is clicked
+
+ > Turn up red LED when button A is clicked
-
+
while True:
-
+
if cp.button_a:
-
+
cp.red_led = True
-
- 4. Light up the first neopixel blue
+
+ > Light up the first neopixel blue
-
+
cp.pixels[0] = (0, 0, 255)
-
+
And much more! These links have more tutorials:
-
+
-
+
+
+ Keyboard Shortcuts
+
+
+
+ Push Button:
+
+ A
+
+ for Button A,
+
+ B
+
+ for Button B,
+
+ C
+
+ for Buttons A & B
+
+
+ Refresh the simulator:
+
+ Shift
+
+ +
+
+ R
+
+
+
+ Run the simulator:
+
+ Shift
+
+ +
+
+ F
+
+
+
Tutorial for micro:bit
-
- 1. Import the micro:bit library to use it (This is required)
+
+ > Import the micro:bit library to use it (This is required)
-
+
from microbit import *
-
- 2. Light up your micro:bit with love by showing a heart
+
+ > Light up your micro:bit with love by showing a heart
-
+
display.show(Image.HEART)
-
- 3. Use your micro:bit to tell the world how you’re feeling
+
+ > Use your micro:bit to tell the world how you’re feeling
-
+
while True:
-
+
if button_a.is_pressed():
-
+
display.show(Image.HAPPY)
-
+
if button_b.is_pressed():
-
+
display.show(Image.SAD)
-
- 4. Read then display the temperature
+
+ > Read then display the temperature
-
+
while True:
-
+
temp = temperature()
-
+
display.show(temp)
-
- 5. Display your name with the scroll functionality
+
+ > Display your name with the scroll functionality
-
+
while True:
-
+
display.show("Your name")
-
-
-
- Tutorial for CLUE
-
- 0. Enable Preview Mode to use the CLUE (This is required)
+ Keyboard Shortcuts
-
- a. Access your settings:
-
-
- Windows or Linux: press
+ Push Button:
- Ctrl
+ A
+
+ for Button A,
+
+ B
+
+ for Button B,
+
+ C
+
+ for Buttons A & B
+
+
+ Refresh the simulator:
+
+ Shift
+
- ,
+ R
- or go to
-
- File -> Preferences -> Settings
-
- Mac: press
+ Run the simulator:
- Cmd
+ Shift
+
- ,
+ F
- or go to
-
- Code -> Preferences -> Settings
-
- .
-
- b. Check the
-
- "Device Simulator Express: Preview Mode"
-
- setting.
-
-
-
- 1. Import the the main CLUE library (This is required)
+
+
+
+ Tutorial for CLUE
+
+
+ > Import the the main CLUE library (This is required)
-
+
from adafruit_clue import clue
-
- 2. Display text on the CLUE and change the text when a button is pressed
+
+ > Display text on the CLUE and change the text when a button is pressed
-
- clue_data = clue.simple_text_display(title="CLUE!", text_scale=2)
+
+ clue_data = clue.simple_text_display(title="CLUE!")
-
+
while True:
-
+
clue_data[1].text = "Hello World!"
-
- clue_data[3].text = "Temperature:
- {}
- ".format(clue.temperature)
-
-
+
if clue.button_a:
-
+
clue_data[5].text = "A is pressed!"
-
+
else:
-
+
clue_data[5].text = "A is not pressed!"
-
+
clue_data.show()
-
- 3. Create a slide show on the CLUE
+
+ > Create a slide show on the CLUE
Make sure there are bitmap (.bmp) pictures of your choice in the same directory as the code file.
@@ -332,86 +448,454 @@ exports[`GettingStartedPage component should render correctly 1`] = `
-
+
import board
-
+
from adafruit_slideshow import SlideShow
-
+
-
- slideshow = SlideShow(board.DISPLAY, auto_advance=True, dwell=3, fade_effect=True)
+
+ slideshow = SlideShow(clue.display, auto_advance=True, dwell=3, fade_effect=True)
-
+
while slideshow.update():
-
+
pass
-
- 4. Light up the neopixel green
+
+ > Light up the neopixel green
-
+
clue.pixel.fill(clue.GREEN)
-
- 5. Draw a blue rectangle on the screen
+
+ > Display sensor data on the CLUE
+
+
+
+ clue_data = clue.simple_text_display(title="CLUE Sensor Data!", title_scale=2)
+
+
+ while True:
+
+
+ clue_data[0].text = "Acceleration:
+ {}
+
+ {}
+
+ {}
+ ".format(*clue.acceleration)
+
+
+ clue_data[1].text = "Gyro:
+ {}
+
+ {}
+
+ {}
+ ".format(*clue.gyro)
+
+
+ clue_data[2].text = "Magnetic:
+ {}
+
+ {}
+
+ {}
+ ".format(*clue.magnetic)
+
+
+ clue_data[3].text = "Pressure:
+ {}
+ hPa".format(clue.pressure)
+
+
+ clue_data[4].text = "Altitude:
+ {}
+ m".format(clue.altitude)
+
+
+ clue_data[5].text = "Temperature:
+ {}
+ C".format(clue.temperature)
+
+
+ clue_data[6].text = "Humidity:
+ {}
+ %".format(clue.humidity)
+
+
+ clue_data[7].text = "Proximity:
+ {}
+ ".format(clue.proximity)
+
+
+ clue_data[8].text = "Gesture:
+ {}
+ ".format(clue.gesture)
+
+
+ clue_data[9].text = "Color: R:
+ {}
+ G:
+ {}
+ B:
+ {}
+ C:
+ {}
+ ".format(*clue.color)
+
+
+ clue_data[10].text = "Button A:
+ {}
+ ".format(clue.button_a)
+
+
+ clue_data[11].text = "Button B:
+ {}
+ ".format(clue.button_b)
+
+
+ clue_data.show()
+
+
+
+ > Draw a blue rectangle on the screen
-
+
import board
-
+
import displayio
-
+
from adafruit_display_shapes.rect import Rect
-
+
-
+
splash = displayio.Group(max_size=20)
-
+
board.DISPLAY.show(splash)
-
+
-
+
rect = Rect(80, 20, 41, 41, fill=0x0000FF)
-
+
splash.append(rect)
-
+
And much more! These links have more tutorials:
-
+
Getting started with CLUE and CircuitPython
-
+
More example code
+
+ Keyboard Shortcuts
+
+
+
+ Push Button:
+
+ A
+
+ for Button A,
+
+ B
+
+ for Button B,
+
+ C
+
+ for Buttons A & B
+
+
+ Refresh the simulator:
+
+ Shift
+
+ +
+
+ R
+
+
+
+ Run the simulator:
+
+ Shift
+
+ +
+
+ F
+
+
+
+ Capacitive Touch Sensor:
+
+ Shift
+
+ +
+
+ 1
+
+ ~
+
+ 7
+
+ for GPIO pins A1 - A7
+
+
+ Slider Switch:
+
+ Shift
+
+ +
+
+ S
+
+
+
+
+
+
+ Tutorial for using the debugger
+
+
+ > Enter debug mode
+
+
+ Press
+
+ F5
+
+ or go to
+
+ Run -> Start Debugging
+
+
+
+
+ > Set a breakpoint or multiple breakpoints
+
+
+ Stopping at a breakpoint pauses the program at that particular place.
+
+
+ Use the debug toolbar or the shortcuts below
+
+
+
+
+
+ F5
+
+ to continue / pause
+
+
+
+ F10
+
+ to step over (execute current statement and all functions that it calls and stop before the next statement)
+
+
+
+ F11
+
+ to step into (stop at first statement of first function called from first line)
+
+
+
+ Shift
+
+ +
+
+ F11
+
+ to step out (run current function to end)
+
+
+
+ Ctrl
+
+ +
+
+ Shift
+
+ +
+
+ F11
+
+ to restart
+
+
+
+ Shift
+
+ +
+
+ F5
+
+ to stop
+
+
+
+
+ > Observe the device's state on the "Variables" tab on the left when stopped at a breakpoint
+
+
+
+ And much more! These links have more tutorials:
+
+
+
+ Learn more about debugging in VS Code
+
+
`;
diff --git a/src/view/pages/gettingStarted.css b/src/view/pages/gettingStarted.css
index e85a91a11..d312598b1 100644
--- a/src/view/pages/gettingStarted.css
+++ b/src/view/pages/gettingStarted.css
@@ -2,9 +2,25 @@
display: none;
}
+.codeText {
+ overflow-x: auto;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+li:not(:last-child) {
+ margin-bottom: 6px;
+}
+
+.normalFontWeight {
+ font-weight: normal;
+}
+
.deviceSelector {
width: 250px;
- border: 1px solid #3d484c;
+ border: 1px solid var(--vscode-dropdown-border);
+ background-color: var(--vscode-dropdown-background);
+ color: var(--vscode-dropdown-foreground);
margin: 0 0 5px;
padding: 8px;
border-radius: 5px;
@@ -12,6 +28,16 @@
padding-right: 30px;
}
+.deviceSelector optgroup {
+ background-color: var(--vscode-dropdown-listBackground);
+ color: var(--vscode-dropdown-foreground);
+}
+
+.deviceSelector option {
+ background-color: var(--vscode-dropdown-listBackground);
+ color: var(--vscode-dropdown-foreground);
+}
+
.codeBox {
display: block;
width: 90%;
@@ -26,5 +52,5 @@
.container {
text-align: left;
- padding: 0 10px 0 10px;
+ padding: 0 10px 20px 10px;
}
diff --git a/src/view/pages/gettingStarted.tsx b/src/view/pages/gettingStarted.tsx
index 1d00df250..add49b661 100644
--- a/src/view/pages/gettingStarted.tsx
+++ b/src/view/pages/gettingStarted.tsx
@@ -5,20 +5,16 @@ export class GettingStartedPage extends React.Component {
private selectRef: React.RefObject = React.createRef();
componentDidMount() {
- if (this.selectRef.current) {
- this.selectRef.current.addEventListener("change", (event: any) => {
- const visibleElement = document.querySelector(
- ".visibleElement"
- );
- const target = document.getElementById(event!.target!.value);
- if (visibleElement !== null) {
- visibleElement.className = "inv";
- }
- if (target !== null) {
- target.className = "visibleElement";
- }
- });
- }
+ this.selectRef.current?.addEventListener("change", (event: any) => {
+ const visibleElement = document.querySelector(".visibleElement");
+ const target = document.getElementById(event!.target!.value);
+ if (visibleElement !== null) {
+ visibleElement.className = "inv";
+ }
+ if (target !== null) {
+ target.className = "visibleElement";
+ }
+ });
}
render() {
@@ -26,190 +22,258 @@ export class GettingStartedPage extends React.Component {
Getting started
-
- Select a Device
+ Learn more about:
- Circuit Playground Express
- micro:bit
- CLUE
+
+
+ Circuit Playground Express
+
+ micro:bit
+ CLUE
+
+
+ Debugger
+
-
+
Copy these snippets of code to a .py file and run the
simulator
-
+
{/* prettier-ignore */}
Tutorial for Circuit Playground Express
-
- 1. Import the micro:bit library to use it (This is
+
+ > Import the micro:bit library to use it (This is
required)
- from adafruit_circuitplayground import cp
+ from adafruit_circuitplayground import cp
- 2. Turn on the little red LED
+ > Turn on the little red LED
- while True:
- cp.red_led = True
+ while True:
+ cp.red_led = True
- 3. Turn up red LED when button A is clicked
+ > Turn up red LED when button A is clicked
- while True:
- if cp.button_a:
- cp.red_led = True
+ while True:
+ if cp.button_a:
+ cp.red_led = True
- 4. Light up the first neopixel blue
+ > Light up the first neopixel blue
- cp.pixels[0] = (0, 0, 255)
+ cp.pixels[0] = (0, 0, 255)
- And much more! These links have more tutorials:
-
+ And much more! These links have more tutorials:
+
Getting started with CPX and CircuitPython
-
+
More example code
+ Keyboard Shortcuts
+
+ Push Button: A for Button A, B for Button B, C for Buttons A & B
+ Refresh the simulator: Shift + R
+ Run the simulator: Shift + F
+
{/* prettier-ignore */}
Tutorial for micro:bit
-
- 1. Import the micro:bit library to use it (This is
+
+ > Import the micro:bit library to use it (This is
required)
- from microbit import *
+ from microbit import *
-
- 2. Light up your micro:bit with love by showing a
+
+ > Light up your micro:bit with love by showing a
heart
- display.show(Image.HEART)
+ display.show(Image.HEART)
-
- 3. Use your micro:bit to tell the world how you’re
+
+ > Use your micro:bit to tell the world how you’re
feeling
- while True:
- if button_a.is_pressed():
- display.show(Image.HAPPY)
- if button_b.is_pressed():
- display.show(Image.SAD)
+ while True:
+ if button_a.is_pressed():
+ display.show(Image.HAPPY)
+ if button_b.is_pressed():
+ display.show(Image.SAD)
- 4. Read then display the temperature
+ > Read then display the temperature
- while True:
- temp = temperature()
- display.show(temp)
+ while True:
+ temp = temperature()
+ display.show(temp)
-
- 5. Display your name with the scroll functionality
+
+ > Display your name with the scroll functionality
- while True:
- display.show("Your name")
+ while True:
+ display.show("Your name")
- And much more! These links have more tutorials:
-
+ And much more! These links have more tutorials:
+
Microbit Tutorials
-
+
Microbit official documentation
+ Keyboard Shortcuts
+
+ Push Button: A for Button A, B for Button B, C for Buttons A & B
+ Refresh the simulator: Shift + R
+ Run the simulator: Shift + F
+
{/* prettier-ignore */}
Tutorial for CLUE
-
0. Enable Preview Mode to use the CLUE (This is required)
-
a. Access your settings:
-
-
- Windows or Linux: press Ctrl + , or go to File -> Preferences -> Settings
- Mac: press Cmd + , or go to Code -> Preferences -> Settings
.
-
-
b. Check the "Device Simulator Express: Preview Mode"
setting.
-
-
- 1. Import the the main CLUE library (This is
+
+ > Import the the main CLUE library (This is
required)
- from adafruit_clue import clue
+ from adafruit_clue import clue
-
- 2. Display text on the CLUE and change the text when
+
+ > Display text on the CLUE and change the text when
a button is pressed
-
- clue_data = clue.simple_text_display(title="CLUE!", text_scale=2)
+
+ clue_data = clue.simple_text_display(title="CLUE!")
- while True:
- clue_data[1].text = "Hello World!"
- clue_data[3].text = "Temperature: {"{}"}".format(clue.temperature)
- if clue.button_a:
- clue_data[5].text = "A is pressed!"
- else:
- clue_data[5].text = "A is not pressed!"
- clue_data.show()
+ while True:
+ clue_data[1].text = "Hello World!"
+ if clue.button_a:
+ clue_data[5].text = "A is pressed!"
+ else:
+ clue_data[5].text = "A is not pressed!"
+ clue_data.show()
- 3. Create a slide show on the CLUE
+ > Create a slide show on the CLUE
Make sure there are bitmap (.bmp) pictures of your choice in the same directory
as the code file.
- import board
- from adafruit_slideshow import SlideShow
-
- slideshow = SlideShow(board.DISPLAY, auto_advance=True, dwell=3, fade_effect=True)
+ import board
+ from adafruit_slideshow import SlideShow
+
+ slideshow = SlideShow(clue.display, auto_advance=True, dwell=3, fade_effect=True)
- while slideshow.update():
- pass
+ while slideshow.update():
+ pass
-
4. Light up the neopixel green
+
> Light up the neopixel green
- clue.pixel.fill(clue.GREEN)
+ clue.pixel.fill(clue.GREEN)
-
5. Draw a blue rectangle on the screen
+
> Display sensor data on the CLUE
- import board
- import displayio
- from adafruit_display_shapes.rect import Rect
-
- splash = displayio.Group(max_size=20)
- board.DISPLAY.show(splash)
-
- rect = Rect(80, 20, 41, 41, fill=0x0000FF)
- splash.append(rect)
+ clue_data = clue.simple_text_display(title="CLUE Sensor Data!", title_scale=2)
+ while True:
+ clue_data[0].text = "Acceleration: {"{}"} {"{}"} {"{}"}".format(*clue.acceleration)
+ clue_data[1].text = "Gyro: {"{}"} {"{}"} {"{}"}".format(*clue.gyro)
+ clue_data[2].text = "Magnetic: {"{}"} {"{}"} {"{}"}".format(*clue.magnetic)
+ clue_data[3].text = "Pressure: {"{}"}hPa".format(clue.pressure)
+ clue_data[4].text = "Altitude: {"{}"}m".format(clue.altitude)
+ clue_data[5].text = "Temperature: {"{}"}C".format(clue.temperature)
+ clue_data[6].text = "Humidity: {"{}"}%".format(clue.humidity)
+ clue_data[7].text = "Proximity: {"{}"}".format(clue.proximity)
+ clue_data[8].text = "Gesture: {"{}"}".format(clue.gesture)
+ clue_data[9].text = "Color: R: {"{}"} G: {"{}"} B: {"{}"} C: {"{}"}".format(*clue.color)
+ clue_data[10].text = "Button A: {"{}"}".format(clue.button_a)
+ clue_data[11].text = "Button B: {"{}"}".format(clue.button_b)
+ clue_data.show()
-
And much more! These links have more tutorials:
-
+ > Draw a blue rectangle on the screen
+
+ import board
+ import displayio
+ from adafruit_display_shapes.rect import Rect
+
+ splash = displayio.Group(max_size=20)
+ board.DISPLAY.show(splash)
+
+ rect = Rect(80, 20, 41, 41, fill=0x0000FF)
+ splash.append(rect)
+
+ And much more! These links have more tutorials:
+
Getting started with CLUE and CircuitPython
-
+
More example code
+ Keyboard Shortcuts
+
+ Push Button: A for Button A, B for Button B, C for Buttons A & B
+ Refresh the simulator: Shift + R
+ Run the simulator: Shift + F
+ Capacitive Touch Sensor: Shift + 1 ~ 7 for GPIO pins A1 - A7
+ Slider Switch: Shift + S
+
+
+ {/* prettier-ignore */}
+
+
Tutorial for using the debugger
+
+ > Enter debug mode
+
+
Press F5 or go to Run -> Start Debugging
+
+
+ > Set a breakpoint or multiple breakpoints
+
+
Stopping at a breakpoint pauses the program at that particular place.
+
Use the debug toolbar or the shortcuts below
+
+
+ F5 to continue / pause
+ F10 to step over (execute current statement and all functions that it calls and stop before the next statement)
+ F11 to step into (stop at first statement of first function called from first line)
+ Shift + F11 to step out (run current function to end)
+ Ctrl + Shift + F11 to restart
+ Shift + F5 to stop
+
+
+
+ > Observe the device's state on the "Variables" tab on the left when stopped at a breakpoint
+
+
+
And much more! These links have more tutorials:
+
+
+ Learn more about debugging in VS Code
+
+
diff --git a/src/view/pages/gettingStartedPictures/debugger/debugger_vars.png b/src/view/pages/gettingStartedPictures/debugger/debugger_vars.png
new file mode 100644
index 000000000..267f461ef
Binary files /dev/null and b/src/view/pages/gettingStartedPictures/debugger/debugger_vars.png differ
diff --git a/src/view/pages/gettingStartedPictures/debugger/debugging.gif b/src/view/pages/gettingStartedPictures/debugger/debugging.gif
new file mode 100644
index 000000000..5d42a436a
Binary files /dev/null and b/src/view/pages/gettingStartedPictures/debugger/debugging.gif differ
diff --git a/src/view/pages/gettingStartedPictures/debugger/start_debugging.jpg b/src/view/pages/gettingStartedPictures/debugger/start_debugging.jpg
new file mode 100644
index 000000000..3d39fbf66
Binary files /dev/null and b/src/view/pages/gettingStartedPictures/debugger/start_debugging.jpg differ
diff --git a/src/view/pages/gettingStartedPictures/debugger/toolbar.png b/src/view/pages/gettingStartedPictures/debugger/toolbar.png
new file mode 100644
index 000000000..754e68e03
Binary files /dev/null and b/src/view/pages/gettingStartedPictures/debugger/toolbar.png differ
diff --git a/src/view/styles/ToolBar.css b/src/view/styles/ToolBar.css
index ef35688a9..faca0569c 100644
--- a/src/view/styles/ToolBar.css
+++ b/src/view/styles/ToolBar.css
@@ -167,10 +167,16 @@
-webkit-appearance: none;
text-decoration: none;
}
+
.gesture-container {
display: "flex";
justify-content: "space-between";
flex-direction: "column";
align-items: "center";
height: 750;
+ padding-top: 10px;
+}
+
+.generic-slider-component {
+ padding-top: 10px;
}