Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 3 additions & 2 deletions src/RokuDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export class RokuDeploy {
for (const char of chars) {
await this.sendKeyEvent({
...options,
key: `lit_${char}`,
key: `lit_${encodeURIComponent(char)}`,
action: 'keypress'
});
}
Expand Down Expand Up @@ -991,7 +991,8 @@ export interface GetDeviceInfoOptions {
enhance?: boolean;
}

type RokuKey = 'back' | 'backspace' | 'channeldown' | 'channelup' | 'down' | 'enter' | 'findremote' | 'fwd' | 'home' | 'info' | 'inputav1' | 'inputhdmi1' | 'inputhdmi2' | 'inputhdmi3' | 'inputhdmi4' | 'inputtuner' | 'instantreplay' | 'left' | 'play' | 'poweroff' | 'rev' | 'right' | 'search' | 'select' | 'up' | 'volumedown' | 'volumemute' | 'volumeup';
export type RokuKey = 'back' | 'backspace' | 'channeldown' | 'channelup' | 'down' | 'enter' | 'findremote' | 'fwd' | 'home' | 'info' | 'inputav1' | 'inputhdmi1' | 'inputhdmi2' | 'inputhdmi3' | 'inputhdmi4' | 'inputtuner' | 'instantreplay' | 'left' | 'play' | 'poweroff' | 'rev' | 'right' | 'search' | 'select' | 'up' | 'volumedown' | 'volumemute' | 'volumeup';

export interface SendKeyEventOptions {
action?: 'keydown' | 'keypress' | 'keyup';
host: string;
Expand Down
9 changes: 9 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ZipCommand } from './commands/ZipCommand';
import { KeyPressCommand } from './commands/KeyPressCommand';
import { KeyUpCommand } from './commands/KeyUpCommand';
import { KeyDownCommand } from './commands/KeyDownCommand';
import { Interactive } from './commands/Interactive';

void yargs

Expand Down Expand Up @@ -148,6 +149,14 @@ void yargs
return new SendTextCommand().run(args);
})

.command('interactive', 'Provides a way to send a series of ECP key events similar to how Roku Remote Tool works but from the command line', (builder) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chris and I played around with some alternative names. How about:

remote-control

So usage would be:

npx roku-deploy remote-control

We could add an alias like RC or R or something if that feels verbose?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated. Added rc shorter version as well

return builder
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false });
}, (args: any) => {
return new Interactive().run(args);
})

.command(['stage', 'prepublishToStaging'], 'Copies all of the referenced files to the staging folder', (builder) => {
return builder
.option('stagingDir', { type: 'string', description: 'The selected staging folder', demandOption: false })
Expand Down
104 changes: 104 additions & 0 deletions src/commands/Interactive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as readline from 'readline';
import type { RokuKey } from '../index';
import { rokuDeploy, util } from '../index';

export class Interactive {
run(args) {
let options = {
...util.getOptionsFromJson(args),
...args
};

if (!options.host) {
console.error('Host is required and neither supplied via --host or in your rokudeploy.json file');
return;
}

readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);

process.stdin.on('keypress', (str, key) => {
const keyName = key.name as unknown;
let rokuDeployKeyName: RokuKey | undefined;
switch (keyName) {
case 'home':
rokuDeployKeyName = keyName;
break;
case 'escape':
rokuDeployKeyName = 'back';
break;
case 'delete':
if (key.ctrl || key.meta || key.shift) {
rokuDeployKeyName = 'backspace';
}
rokuDeployKeyName = 'back';
break;
case 'backspace':
if (key.ctrl || key.meta || key.shift) {
rokuDeployKeyName = 'backspace';
} else {
rokuDeployKeyName = 'instantreplay';
}
break;
case 'end':
rokuDeployKeyName = 'play';
break;
case 'return':
rokuDeployKeyName = 'select';
break;
case 'up':
rokuDeployKeyName = 'up';
if (key.shift) {
rokuDeployKeyName = 'volumeup';
}
break;
case 'down':
rokuDeployKeyName = 'down';
if (key.shift) {
rokuDeployKeyName = 'volumedown';
}
break;
case 'left':
rokuDeployKeyName = 'left';
if (key.shift) {
rokuDeployKeyName = 'rev';
}
break;
case 'right':
rokuDeployKeyName = 'right';
if (key.shift) {
rokuDeployKeyName = 'fwd';
}
break;
default:
if (key.sequence === '*') {
rokuDeployKeyName = 'info';
} else {
if (key.ctrl && key.name === 'c') {
process.exit(); // We provide a way to exit the program
}

let text = key.name;
if (text === undefined) {
text = key.sequence;
}

if (text === 'space') {
text = ' ';
}

void rokuDeploy.sendText({
text: text, ...options
});
}
break;
}

if (rokuDeployKeyName) {
void rokuDeploy.keyPress({ key: rokuDeployKeyName, ...options });
}
});

console.log('Now receiving keyboard input. Press Ctrl+C to exit.\nescape=back, end=play, return=select, shift+left=rev, shift+right=fwd, shift+up=volumeup, shift+down=volumedown, *=options');
}
}