Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/introduction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.1"
"version": "3.13.2"
}
},
"nbformat": 4,
Expand Down
42 changes: 15 additions & 27 deletions src/bluetooth-extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,34 +69,20 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
);
let runningItemsList: Array<IRunningSessions.IRunningItem>;

function createTestFunction(device: BluetoothManager.Device) {
return function test(node: HTMLElement): boolean {
const testString = buildCompleteIdentifier(device.native);
return node.title === testString;
};
}

commands.addCommand(CommandIDs.disconnectDevice, {
execute: async args => {
bluetoothManager.deviceList.filter(item => {
const testWithDevice = createTestFunction(item);
const node = app.contextMenuHitTest(testWithDevice);
const identifier = buildCompleteIdentifier(item.native);
if (identifier === node?.title) {
bluetoothManager.disconnectDevice(item);
}
});
app.commands.addCommand(CommandIDs.disconnectDevice, {
execute: (args) => {
const selectedDevice= bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
if (selectedDevice) {
bluetoothManager.disconnectDevice(selectedDevice);
return selectedDevice;
} else {
throw new Error('No device provided or device is invalid');
}
},
caption: trans.__('Disconnect device'),
label: trans.__('Disconnect Device')
});

/* Adding commands to the context menu of the relevant connected device*/
app.contextMenu.addItem({
command: CommandIDs.disconnectDevice,
selector: 'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-Move-Hub',
rank: 0
});

app.commands.addCommand(CommandIDs.openDeviceRegistryDialog, {
execute: async () => {
Expand All @@ -121,6 +107,7 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
}
});


managers.add({
name: trans.__('Bluetooth Devices'),
supportsMultipleViews: false,
Expand All @@ -130,10 +117,12 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
runningItemsList.push(
new BluetoothDeviceRunningItem(
device,
bluetoothManager as BluetoothManager
bluetoothManager as BluetoothManager,
commands
)
);
});
}
);
return runningItemsList;
},
shutdownAll: () => {
Expand Down Expand Up @@ -163,8 +152,7 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {

export class DropDownRegistry
extends Widget
implements Dialog.IBodyWidget<string>
{
implements Dialog.IBodyWidget<string> {
constructor(registry: BluetoothManager.DeviceRegistry) {
super();
this._selectList = document.createElement('select');
Expand Down
35 changes: 28 additions & 7 deletions src/bluetooth/BluetoothDeviceRunningItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,42 @@ import { IRunningSessions } from '@jupyterlab/running';
import { BluetoothConnectIcon } from './icon';
import { BluetoothManager } from './BluetoothManager';
import { buildCompleteIdentifier } from '../bluetooth-extension';
import { Menu } from '@lumino/widgets';
import { CommandRegistry } from '@lumino/commands';

export const disconnectDevice = 'bluetooth-manager:disconnect-device';

export class BluetoothDeviceRunningItem
implements IRunningSessions.IRunningItem
{
constructor(device: BluetoothManager.Device, manager: BluetoothManager) {
implements IRunningSessions.IRunningItem {
constructor(device: BluetoothManager.Device, bluetoothManager: BluetoothManager, commands: CommandRegistry) {
this._device = device;
this.manager = manager;

this.bluetoothManager = bluetoothManager;
if (this._device.native.name) {
const deviceName = this._device.native.name;
this.className = 'jp-bluetooth-' + deviceName.replace(/\s+/g, '-');
}
this.commands = commands;
}

className?: string | undefined;

open() {
const commands = this.commands;
const deviceID = this._device.native.id;
const menu = new Menu({ commands: commands })
this._device.contextCommands.map((command: string) => {
menu.addItem({ command: command, args: {deviceID}})
})
menu.addClass('jp-bluetooth-device-running-item-menu')
const deviceElement = document.querySelector(`.${this.className}`);
if (deviceElement) {
const rect = deviceElement.getBoundingClientRect();
const x = rect.left;
const y = rect.bottom;
menu.open(x, y);
}
}

icon() {
return BluetoothConnectIcon;
}
Expand All @@ -33,9 +53,10 @@ export class BluetoothDeviceRunningItem
}

shutdown() {
this.manager.disconnectDevice(this._device);
this.bluetoothManager.disconnectDevice(this._device);
}

private _device: BluetoothManager.Device;
public manager: BluetoothManager;
public bluetoothManager: BluetoothManager;
public commands: CommandRegistry;
}
2 changes: 2 additions & 0 deletions src/bluetooth/BluetoothManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,15 @@ export namespace BluetoothManager {
public connected: Signal<this, boolean>;
public disconnected: Signal<this, boolean>;
public isDisposed: boolean;
public contextCommands:Array<string>;

constructor(native: BluetoothDevice) {
this.connected = new Signal<this, boolean>(this);
this.disconnected = new Signal<this, boolean>(this);
this.isConnected = false;
this.isDisposed = false;
this.native = native;
this.contextCommands = ['bluetooth-manager:disconnect-device', 'bluetooth-manager:add-lego-movehub-control-panel']
}

async connectAndGetAllServices(): Promise<
Expand Down
47 changes: 9 additions & 38 deletions src/movehub-extension/components/ConnectionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CommandRegistry } from '@lumino/commands';
import { BluetoothManager } from '../../bluetooth/BluetoothManager';
export const connectMoveHub = 'bluetooth-manager:connect-movehub';
export const disconnectMoveHub = 'bluetooth-manager:disconnect-movehub';
import { movehubRegistryItem } from '..';


export default function ConnectionStatus({ device }: IMoveHubPanelProps) {
const [deviceState, setDeviceState] = useState<DeviceInfo>(defaultDeviceInfo);
Expand Down Expand Up @@ -46,54 +46,25 @@ export default function ConnectionStatus({ device }: IMoveHubPanelProps) {
export class ConnectionStatusWidget extends ReactWidget {
public device: MoveHub;
public menu: Menu;
public commands: CommandRegistry;

constructor(device: MoveHub, bluetoothManager: BluetoothManager) {
constructor(device: MoveHub, bluetoothManager: BluetoothManager, commands: CommandRegistry) {
super();
this.device = device;
this.commands = new CommandRegistry();
this.commands.addCommand(disconnectMoveHub, {
execute: args => {
bluetoothManager.disconnectDevice(device);
return device;
},
caption: 'Disconnect MoveHub',
label: 'Disconnect MoveHub',
isEnabled: () => {
if (device.deviceInfo.connected) {
return true;
} else {
return false;
}
}
});
this.commands.addCommand(connectMoveHub, {
execute: args => {
const newDevice = bluetoothManager.connectDevice(movehubRegistryItem);
return newDevice;
},
caption: 'Connect MoveHub',
label: 'Connect MoveHub',
isEnabled: () => {
if (device.deviceInfo.connected) {
return false;
} else {
return true;
}
}
});
this.menu = new Menu({ commands: this.commands });
const deviceID = this.device.native.id;

this.menu = new Menu({ commands: commands });
this.menu.addItem({
command: disconnectMoveHub
command: disconnectMoveHub,
args: {deviceID}
});
this.menu.addItem({
command: connectMoveHub
command: connectMoveHub,
args: {deviceID}
});
this.menu.addClass('jp-connection-status-menu');
}

openMenu(event: React.MouseEvent<HTMLDivElement>) {
console.log('You have clicked');
if (this.menu && typeof this.menu.isVisible !== 'undefined') {
this.menu.open(event.clientX, event.clientY);
} else {
Expand Down
107 changes: 64 additions & 43 deletions src/movehub-extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export * from './version';
export * from './widget';
export const addLEGOMoveHubControlPanel =
'bluetooth-manager:add-lego-movehub-control-panel';
export const connectMoveHub = 'bluetooth-manager:connect-movehub';
export const disconnectMoveHub = 'bluetooth-manager:disconnect-movehub';
export const moveHubServiceUUID = '00001623-1212-efde-1623-785feabcd123';
export const moveHubCharacteristicUUID = '00001624-1212-efde-1623-785feabcd123';
export const movehubRegistryItem: IDeviceRegistryItem = {
Expand Down Expand Up @@ -81,39 +83,32 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin<void> = {
const device = result[
result.length - 1
] as MoveHub; /* the last added MoveHub device*/
/*let isThemeLight: boolean = themeManager.theme;
themeManager.themeChanged.connect((sender: any, args: IChangedArgs<string, string | null, string>) => {
const theme = args.newValue;
console.log("Is the theme light?:", themeManager.isLight(theme));
isThemeLight = themeManager.isLight(theme);
console.log('Theme is:', theme);
});*/
const content = new MoveHubPanelWidget(device, themeManager);
content.addClass('jp-movehub-panel-content');
const toolbar = new Toolbar();
toolbar.addClass('jp-movehub-panel-toolbar');
const main = new MainAreaWidget({ content, toolbar });
main.addClass('jp-movehub-panel-main');
main.toolbar.addItem(
'connection-status',
new ConnectionStatusWidget(device, bluetoothManager)
);
main.toolbar.addItem(
'select-lego-model',
new LegoBuildSelectorWidget(device)
);
toolbar.addItem('spacer', Toolbar.createSpacerItem());
main.toolbar.addItem('battery-gauge', new BatteryWidget(device, themeManager));
main.toolbar.addItem(
'device-identifier',
new DeviceIdentifierWidget(device)
);
main.id = 'lego-movehub-control-panel';
main.title.label = 'LEGO® Move Hub';
main.title.closable = true;
main.title.icon = LegoBrickIcon;
app.shell.add(main, 'main');

const content = new MoveHubPanelWidget(device, themeManager);
content.addClass('jp-movehub-panel-content');
const toolbar = new Toolbar();
toolbar.addClass('jp-movehub-panel-toolbar');
const main = new MainAreaWidget({ content, toolbar });
main.addClass('jp-movehub-panel-main');
main.toolbar.addItem(
'connection-status',
new ConnectionStatusWidget(device, bluetoothManager, app.commands)
);
main.toolbar.addItem(
'select-lego-model',
new LegoBuildSelectorWidget(device)
);
toolbar.addItem('spacer', Toolbar.createSpacerItem());
main.toolbar.addItem('battery-gauge', new BatteryWidget(device, themeManager));
main.toolbar.addItem(
'device-identifier',
new DeviceIdentifierWidget(device)
);
main.id = 'lego-movehub-control-panel';
main.title.label = 'LEGO® Move Hub';
main.title.closable = true;
main.title.icon = LegoBrickIcon;
app.shell.add(main, 'main');


} else {
throw new Error('The device is not a Move Hub.');
Expand All @@ -123,17 +118,43 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin<void> = {
label: trans.__('Open a LEGO® Move Hub Control Panel')
});

app.contextMenu.addItem({
command: addLEGOMoveHubControlPanel,
selector:
'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-LEGO-Move-Hub',
rank: 1
app.commands.addCommand(disconnectMoveHub, {
execute: args => {
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
if (selectedDevice && selectedDevice instanceof MoveHub) {
bluetoothManager.disconnectDevice(selectedDevice);
return selectedDevice;
} else {
throw new Error('No device provided or device is invalid');
}
},
caption: 'Disconnect MoveHub',
label: 'Disconnect MoveHub',
isEnabled: (args) => {
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
if (selectedDevice && selectedDevice instanceof MoveHub && selectedDevice.deviceInfo.connected) {
return true;
} else {
return false;
}
}
});

app.contextMenu.addItem({
command: addLEGOMoveHubControlPanel,
selector: 'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-Move-Hub',
rank: 1

app.commands.addCommand(connectMoveHub, {
execute: args => {
const newDevice = bluetoothManager.connectDevice(movehubRegistryItem);
return newDevice;
},
caption: 'Connect MoveHub',
label: 'Connect MoveHub',
isEnabled: (args) => {
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
if (selectedDevice && selectedDevice instanceof MoveHub && selectedDevice.deviceInfo.connected) {
return false;
} else {
return true;
}
}
});
}
};
Expand Down
1 change: 1 addition & 0 deletions src/movehub-extension/moveHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class MoveHub extends BluetoothManager.Device {
driveSpeed: DEFAULT_CONFIG.DRIVE_SPEED,
turnSpeed: DEFAULT_CONFIG.TURN_SPEED
};
this.contextCommands = ['bluetooth-manager:disconnect-device','bluetooth-manager:add-lego-movehub-control-panel']
}
logDebug(message?: any, ...optionalParams: any[]): void {
if (message) {
Expand Down