Compare commits
No commits in common. "23245dd54924401f042cc3c7e69d9042d046ee16" and "6cefa7392c407af36683d9ab88acb0815143459d" have entirely different histories.
23245dd549
...
6cefa7392c
@ -97,22 +97,6 @@ func (db *RemoteDatabase) CreateRemoteCommands(remoteId string, commandIds []str
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *RemoteDatabase) GetRemote(remoteId string) (Remote, error) {
|
|
||||||
var remote Remote
|
|
||||||
queryString := "SELECT id, title FROM Remotes WHERE id = ?"
|
|
||||||
row := db.Connection.QueryRow(queryString, remoteId)
|
|
||||||
error := row.Scan(&remote.Id, &remote.Title)
|
|
||||||
if error != nil {
|
|
||||||
return Remote{}, fmt.Errorf("error scanning remote: %s", error)
|
|
||||||
}
|
|
||||||
commands, error := db.GetCommandsByRemoteId(remoteId)
|
|
||||||
if error != nil {
|
|
||||||
return Remote{}, error
|
|
||||||
}
|
|
||||||
remote.Commands = commands
|
|
||||||
return remote, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *RemoteDatabase) GetRemotes() ([]Remote, error) {
|
func (db *RemoteDatabase) GetRemotes() ([]Remote, error) {
|
||||||
rows, error := db.Connection.Query("SELECT id, title FROM Remotes")
|
rows, error := db.Connection.Query("SELECT id, title FROM Remotes")
|
||||||
if error != nil {
|
if error != nil {
|
||||||
|
|||||||
@ -17,14 +17,6 @@ func (rm *RemoteManager) CreateRemote(remote d.Remote) (string, error) {
|
|||||||
return rm.remoteDatabase.CreateRemote(remote)
|
return rm.remoteDatabase.CreateRemote(remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rm *RemoteManager) GetRemote(remoteID string) (d.Remote, error) {
|
|
||||||
remote, err := rm.remoteDatabase.GetRemote(remoteID)
|
|
||||||
if err != nil {
|
|
||||||
return d.Remote{}, err
|
|
||||||
}
|
|
||||||
return remote, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rm *RemoteManager) GetRemotes() ([]d.Remote, error) {
|
func (rm *RemoteManager) GetRemotes() ([]d.Remote, error) {
|
||||||
remotes, err := rm.remoteDatabase.GetRemotes()
|
remotes, err := rm.remoteDatabase.GetRemotes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import (
|
|||||||
m "playback-device-server/management"
|
m "playback-device-server/management"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RemoteApiHandler struct {
|
type RemoteApiHandler struct {
|
||||||
@ -17,7 +16,6 @@ func (r *RemoteApiHandler) Initialize(authenticator *Authenticator) {
|
|||||||
r.router.Use(authenticator.Authenticate("/api/remotes", []string{}))
|
r.router.Use(authenticator.Authenticate("/api/remotes", []string{}))
|
||||||
remotesApi := r.router.Group("/api/remotes")
|
remotesApi := r.router.Group("/api/remotes")
|
||||||
remotesApi.GET("", r.handleGetRemotes)
|
remotesApi.GET("", r.handleGetRemotes)
|
||||||
remotesApi.GET("/:id", r.handleGetRemote)
|
|
||||||
remotesApi.POST("", r.handleCreateRemote)
|
remotesApi.POST("", r.handleCreateRemote)
|
||||||
remotesApi.DELETE("/:id", r.handleDelteRemote)
|
remotesApi.DELETE("/:id", r.handleDelteRemote)
|
||||||
|
|
||||||
@ -44,17 +42,6 @@ func (r *RemoteApiHandler) handleCreateRemote(context echo.Context) error {
|
|||||||
return context.JSON(200, remote)
|
return context.JSON(200, remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RemoteApiHandler) handleGetRemote(context echo.Context) error {
|
|
||||||
id := context.Param("id")
|
|
||||||
remote, err := r.remoteManager.GetRemote(id)
|
|
||||||
if err != nil {
|
|
||||||
SendError(500, context, err.Error())
|
|
||||||
log.Error().Err(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return context.JSON(200, remote)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RemoteApiHandler) handleGetRemotes(context echo.Context) error {
|
func (r *RemoteApiHandler) handleGetRemotes(context echo.Context) error {
|
||||||
remotes, err := r.remoteManager.GetRemotes()
|
remotes, err := r.remoteManager.GetRemotes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -36,6 +36,10 @@ function ListManager(props) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
createEffect(() =>
|
||||||
|
console.log(availableItemsFuse().search(availableItemsSearchString()))
|
||||||
|
);
|
||||||
|
|
||||||
const selectableAvailableItems = createMemo(() =>
|
const selectableAvailableItems = createMemo(() =>
|
||||||
(availableItemsSearchString()
|
(availableItemsSearchString()
|
||||||
? availableItemsFuse()
|
? availableItemsFuse()
|
||||||
|
|||||||
@ -1,180 +0,0 @@
|
|||||||
import { createEffect, createMemo, mergeProps } from "solid-js";
|
|
||||||
import Remote from "../data/remote";
|
|
||||||
import Command from "../data/command";
|
|
||||||
|
|
||||||
function RemoteControl(props) {
|
|
||||||
props = mergeProps(
|
|
||||||
{
|
|
||||||
remote: new Remote(),
|
|
||||||
onCommand: () => {},
|
|
||||||
},
|
|
||||||
props
|
|
||||||
);
|
|
||||||
|
|
||||||
const BUTTON_SIZE_REGULAR = "regular";
|
|
||||||
const BUTTON_SIZE_SMALL = "small";
|
|
||||||
|
|
||||||
const BUTTON_HEIGHT = 2;
|
|
||||||
const BUTTON_WIDTH = BUTTON_HEIGHT * 1.4;
|
|
||||||
const SMALL_BUTTON_HEIGHT = 1.5;
|
|
||||||
const SMALL_BUTTON_WIDTH = SMALL_BUTTON_HEIGHT * 1.3334;
|
|
||||||
const TYPES = Command.TYPES;
|
|
||||||
|
|
||||||
const commandMap = createMemo(() =>
|
|
||||||
props.remote
|
|
||||||
.getCommands()
|
|
||||||
.reduce(
|
|
||||||
(map, command) => ({ ...map, [command.getCommandType()]: command }),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let layout = [
|
|
||||||
[TYPES.POWER, null, TYPES.INPUT],
|
|
||||||
[TYPES.ONE, TYPES.TWO, TYPES.THREE],
|
|
||||||
[TYPES.FOUR, TYPES.FIVE, TYPES.SIX],
|
|
||||||
[TYPES.SEVEN, TYPES.EIGHT, TYPES.NINE],
|
|
||||||
[TYPES.VOLUME_UP, TYPES.ZERO, TYPES.CHANNEL_UP],
|
|
||||||
[TYPES.VOLUME_DOWN, TYPES.MUTE, TYPES.CHANNEL_DOWN],
|
|
||||||
[TYPES.MENU, TYPES.HOME, TYPES.SETTINGS],
|
|
||||||
[TYPES.OPTIONS, TYPES.UP, TYPES.INFO],
|
|
||||||
[TYPES.LEFT, TYPES.ENTER, TYPES.RIGHT],
|
|
||||||
[TYPES.RETURN, TYPES.DOWN, TYPES.EXIT],
|
|
||||||
[TYPES.RED, TYPES.GREEN, TYPES.YELLOW, TYPES.BLUE],
|
|
||||||
[TYPES.PLAY, TYPES.PAUSE, TYPES.STOP],
|
|
||||||
[TYPES.REWIND, null, TYPES.FORWARD],
|
|
||||||
];
|
|
||||||
|
|
||||||
function toButtonProps(type) {
|
|
||||||
let mapping = {
|
|
||||||
[TYPES.POWER]: () => ({ icon: "bi-power", text: "" }),
|
|
||||||
[TYPES.INPUT]: () => ({ icon: "bi-box-arrow-in-right", text: "" }),
|
|
||||||
[TYPES.VOLUME_UP]: () => ({ icon: "bi-volume-up", text: "" }),
|
|
||||||
[TYPES.VOLUME_DOWN]: () => ({ icon: "bi-volume-down", text: "" }),
|
|
||||||
[TYPES.MUTE]: () => ({ icon: "bi-volume-mute", text: "" }),
|
|
||||||
[TYPES.CHANNEL_UP]: () => ({ icon: "bi-chevron-up", text: "" }),
|
|
||||||
[TYPES.CHANNEL_DOWN]: () => ({ icon: "bi-chevron-down", text: "" }),
|
|
||||||
[TYPES.HOME]: () => ({ icon: "bi-house-door", text: "" }),
|
|
||||||
[TYPES.SETTINGS]: () => ({ icon: "bi-gear", text: "" }),
|
|
||||||
[TYPES.INFO]: () => ({ icon: "bi-info-circle", text: "" }),
|
|
||||||
[TYPES.UP]: () => ({ icon: "bi-arrow-up", text: "" }),
|
|
||||||
[TYPES.DOWN]: () => ({ icon: "bi-arrow-down", text: "" }),
|
|
||||||
[TYPES.LEFT]: () => ({ icon: "bi-arrow-left", text: "" }),
|
|
||||||
[TYPES.RIGHT]: () => ({ icon: "bi-arrow-right", text: "" }),
|
|
||||||
[TYPES.ENTER]: () => ({ icon: "", text: "OK" }),
|
|
||||||
[TYPES.EXIT]: () => ({ icon: "", text: "EXIT" }),
|
|
||||||
[TYPES.OPTIONS]: () => ({ icon: "bi-list-task", text: "" }),
|
|
||||||
[TYPES.RETURN]: () => ({ icon: "bi-arrow-return-left", text: "" }),
|
|
||||||
[TYPES.ONE]: () => ({ icon: "", text: "1" }),
|
|
||||||
[TYPES.TWO]: () => ({ icon: "", text: "2" }),
|
|
||||||
[TYPES.THREE]: () => ({ icon: "", text: "3" }),
|
|
||||||
[TYPES.FOUR]: () => ({ icon: "", text: "4" }),
|
|
||||||
[TYPES.FIVE]: () => ({ icon: "", text: "5" }),
|
|
||||||
[TYPES.SIX]: () => ({ icon: "", text: "6" }),
|
|
||||||
[TYPES.SEVEN]: () => ({ icon: "", text: "7" }),
|
|
||||||
[TYPES.EIGHT]: () => ({ icon: "", text: "8" }),
|
|
||||||
[TYPES.NINE]: () => ({ icon: "", text: "9" }),
|
|
||||||
[TYPES.ZERO]: () => ({ icon: "", text: "0" }),
|
|
||||||
[TYPES.MENU]: () => ({ icon: "", text: "MENU" }),
|
|
||||||
[TYPES.PLAY]: () => ({ icon: "bi-play", text: "" }),
|
|
||||||
[TYPES.PAUSE]: () => ({ icon: "bi-pause", text: "" }),
|
|
||||||
[TYPES.STOP]: () => ({ icon: "bi-stop", text: "" }),
|
|
||||||
[TYPES.REWIND]: () => ({ icon: "bi-rewind", text: "" }),
|
|
||||||
[TYPES.FORWARD]: () => ({ icon: "bi-fast-forward", text: "" }),
|
|
||||||
[TYPES.RED]: () => ({
|
|
||||||
class: "bg-danger",
|
|
||||||
buttonSize: BUTTON_SIZE_SMALL,
|
|
||||||
}),
|
|
||||||
[TYPES.GREEN]: () => ({
|
|
||||||
class: "bg-success",
|
|
||||||
buttonSize: BUTTON_SIZE_SMALL,
|
|
||||||
}),
|
|
||||||
[TYPES.YELLOW]: () => ({
|
|
||||||
class: "bg-warning",
|
|
||||||
buttonSize: BUTTON_SIZE_SMALL,
|
|
||||||
}),
|
|
||||||
[TYPES.BLUE]: () => ({
|
|
||||||
class: "bg-primary",
|
|
||||||
buttonSize: BUTTON_SIZE_SMALL,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!(type in mapping)) return {};
|
|
||||||
let props = mapping[type]();
|
|
||||||
props.type = type;
|
|
||||||
props.text = props.text || "";
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
function PlaceholderButton() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={`width: ${BUTTON_WIDTH}em; height: ${BUTTON_HEIGHT}em; margin: 0.2em;`}
|
|
||||||
></div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RemoteButton(props) {
|
|
||||||
props = mergeProps(
|
|
||||||
{
|
|
||||||
onClick: () => {},
|
|
||||||
disabled: false,
|
|
||||||
class: "",
|
|
||||||
buttonSize: BUTTON_SIZE_REGULAR,
|
|
||||||
icon: "",
|
|
||||||
text: "",
|
|
||||||
},
|
|
||||||
props
|
|
||||||
);
|
|
||||||
|
|
||||||
let buttonWidth = BUTTON_WIDTH;
|
|
||||||
let buttonHeight = BUTTON_HEIGHT;
|
|
||||||
if (props.buttonSize === BUTTON_SIZE_SMALL) {
|
|
||||||
buttonWidth = SMALL_BUTTON_WIDTH;
|
|
||||||
buttonHeight = SMALL_BUTTON_HEIGHT;
|
|
||||||
}
|
|
||||||
let buttonFontSize = buttonHeight * 0.4;
|
|
||||||
let iconSize = buttonHeight * 0.5;
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
class={
|
|
||||||
"btn btn-dark d-flex justify-content-center align-items-center " +
|
|
||||||
props.class
|
|
||||||
}
|
|
||||||
style={`width: ${buttonWidth}em; height: ${buttonHeight}em; margin: 0.2em;`}
|
|
||||||
onClick={props.onClick}
|
|
||||||
disabled={props.disabled}
|
|
||||||
>
|
|
||||||
<i style={`font-size: ${iconSize}em;`} class={"bi " + props.icon} />
|
|
||||||
<span style={`font-size: ${buttonFontSize}em;`}>{props.text}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="d-flex flex-column justify-content-center align-items-center p-1 bg-secondary rounded">
|
|
||||||
{layout.map((row) => (
|
|
||||||
<div class="d-flex flex-row justify-content-center align-items-center">
|
|
||||||
{row
|
|
||||||
.map(toButtonProps)
|
|
||||||
.map(({ type, class: className, buttonSize, icon, text }) =>
|
|
||||||
!type ? (
|
|
||||||
<PlaceholderButton />
|
|
||||||
) : (
|
|
||||||
<RemoteButton
|
|
||||||
class={className}
|
|
||||||
buttonSize={buttonSize}
|
|
||||||
onClick={() => props.onCommand(commandMap()[type])}
|
|
||||||
disabled={!commandMap()[type]}
|
|
||||||
icon={icon}
|
|
||||||
text={text}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RemoteControl;
|
|
||||||
@ -61,7 +61,7 @@ function Command({
|
|||||||
|
|
||||||
function getTitle() {
|
function getTitle() {
|
||||||
if (_title) return _title;
|
if (_title) return _title;
|
||||||
return `${Command.getTypeString(_commandType)} (${Command.getProtocolString(_protocol)})`;
|
return `${Command.CommandTypes[commandType]} (${Command.Protocols[protocol]})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTitle(title) {
|
function setTitle(title) {
|
||||||
@ -84,148 +84,73 @@ function Command({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Command.PROTOCOLS = {
|
Command.Protocols = {
|
||||||
SAMSUNG: "samsung",
|
samsung: "Samsung",
|
||||||
NEC: "nec",
|
nec: "NEC",
|
||||||
ONKYO: "onkyo",
|
onkyo: "Onkyo",
|
||||||
APPLE: "apple",
|
apple: "Apple",
|
||||||
DENON: "denon",
|
denon: "Denon",
|
||||||
SHARP: "sharp",
|
sharp: "Sharp",
|
||||||
PANASONIC: "panasonic",
|
panasonic: "Panasonic",
|
||||||
KASEIKYO: "kaseikyo",
|
kaseikyo: "Kaseikyo",
|
||||||
JVC: "jvc",
|
jvc: "JVC",
|
||||||
LG: "lg",
|
lg: "LG",
|
||||||
SONY: "sony",
|
sony: "Sony",
|
||||||
RC5: "rc5",
|
rc5: "RC5",
|
||||||
RC6: "rc6",
|
rc6: "RC6",
|
||||||
UNIVERSAL_PULSE_DISTANCE: "universal_pulse_distance",
|
universal_pulse_distance: "Universal Pulse Distance",
|
||||||
UNIVERSAL_PULSE_WIDTH: "universal_pulse_width",
|
universal_pulse_width: "Universal Pulse Width",
|
||||||
UNIVERSAL_PULSE_DISTANCE_WIDTH: "universal_pulse_distance_width",
|
universal_pulse_distance_width: "Universal Pulse Distance Width",
|
||||||
HASH: "hash",
|
hash: "Hash",
|
||||||
PRONTO: "pronto",
|
pronto: "Pronto",
|
||||||
BOSE_WAVE: "bose_wave",
|
bose_wave: "BoseWave",
|
||||||
BANG_OLUFSEN: "bang_olufsen",
|
bang_olufsen: "Bang & Olufsen",
|
||||||
LEGO: "lego",
|
lego: "Lego",
|
||||||
FAST: "fast",
|
fast: "FAST",
|
||||||
WHYNTER: "whynter",
|
whynter: "Whynter",
|
||||||
MAGIQUEST: "magiquest",
|
magiquest: "MagiQuest",
|
||||||
}
|
};
|
||||||
|
|
||||||
Command.getProtocolString = (protocol) => {
|
Command.CommandTypes = {
|
||||||
let mapping = {
|
power: "Power",
|
||||||
[Command.PROTOCOLS.SAMSUNG]: "Samsung",
|
input: "Input",
|
||||||
[Command.PROTOCOLS.NEC]: "NEC",
|
one: "1",
|
||||||
[Command.PROTOCOLS.ONKYO]: "Onkyo",
|
two: "2",
|
||||||
[Command.PROTOCOLS.APPLE]: "Apple",
|
three: "3",
|
||||||
[Command.PROTOCOLS.DENON]: "Denon",
|
four: "4",
|
||||||
[Command.PROTOCOLS.SHARP]: "Sharp",
|
five: "5",
|
||||||
[Command.PROTOCOLS.PANASONIC]: "Panasonic",
|
six: "6",
|
||||||
[Command.PROTOCOLS.KASEIKYO]: "Kaseikyo",
|
seven: "7",
|
||||||
[Command.PROTOCOLS.JVC]: "JVC",
|
eight: "8",
|
||||||
[Command.PROTOCOLS.LG]: "LG",
|
nine: "9",
|
||||||
[Command.PROTOCOLS.SONY]: "Sony",
|
zero: "0",
|
||||||
[Command.PROTOCOLS.RC5]: "RC5",
|
volume_up: "Volume Up",
|
||||||
[Command.PROTOCOLS.RC6]: "RC6",
|
volume_down: "Volume Down",
|
||||||
[Command.PROTOCOLS.UNIVERSAL_PULSE_DISTANCE]: "Universal Pulse Distance",
|
mute: "Mute",
|
||||||
[Command.PROTOCOLS.UNIVERSAL_PULSE_WIDTH]: "Universal Pulse Width",
|
channel_up: "Channel Up",
|
||||||
[Command.PROTOCOLS.UNIVERSAL_PULSE_DISTANCE_WIDTH]: "Universal Pulse Distance Width",
|
channel_down: "Channel Down",
|
||||||
[Command.PROTOCOLS.HASH]: "Hash",
|
menu: "Menu",
|
||||||
[Command.PROTOCOLS.PRONTO]: "Pronto",
|
home: "Home",
|
||||||
[Command.PROTOCOLS.BOSE_WAVE]: "BoseWave",
|
settings: "Settings",
|
||||||
[Command.PROTOCOLS.BANG_OLUFSEN]: "Bang & Olufsen",
|
options: "Options",
|
||||||
[Command.PROTOCOLS.LEGO]: "Lego",
|
up: "Up",
|
||||||
[Command.PROTOCOLS.FAST]: "FAST",
|
down: "Down",
|
||||||
[Command.PROTOCOLS.WHYNTER]: "Whynter",
|
left: "Left",
|
||||||
[Command.PROTOCOLS.MAGIQUEST]: "MagiQuest",
|
right: "Right",
|
||||||
}
|
enter: "Enter",
|
||||||
return mapping[protocol]
|
info: "Info",
|
||||||
}
|
return: "Return",
|
||||||
|
exit: "Exit",
|
||||||
Command.TYPES = {
|
red: "Red",
|
||||||
POWER: "power",
|
green: "Green",
|
||||||
INPUT: "input",
|
yellow: "Yellow",
|
||||||
ONE: "one",
|
blue: "Blue",
|
||||||
TWO: "two",
|
rewind: "Rewind",
|
||||||
THREE: "three",
|
play: "Play",
|
||||||
FOUR: "four",
|
pause: "Pause",
|
||||||
FIVE: "five",
|
stop: "Stop",
|
||||||
SIX: "six",
|
forward: "Forward",
|
||||||
SEVEN: "seven",
|
other: "Other",
|
||||||
EIGHT: "eight",
|
};
|
||||||
NINE: "nine",
|
|
||||||
ZERO: "zero",
|
|
||||||
VOLUME_UP: "volume_up",
|
|
||||||
VOLUME_DOWN: "volume_down",
|
|
||||||
MUTE: "mute",
|
|
||||||
CHANNEL_UP: "channel_up",
|
|
||||||
CHANNEL_DOWN: "channel_down",
|
|
||||||
MENU: "menu",
|
|
||||||
HOME: "home",
|
|
||||||
SETTINGS: "settings",
|
|
||||||
OPTIONS: "options",
|
|
||||||
UP: "up",
|
|
||||||
DOWN: "down",
|
|
||||||
LEFT: "left",
|
|
||||||
RIGHT: "right",
|
|
||||||
ENTER: "enter",
|
|
||||||
INFO: "info",
|
|
||||||
RETURN: "return",
|
|
||||||
EXIT: "exit",
|
|
||||||
RED: "red",
|
|
||||||
GREEN: "green",
|
|
||||||
YELLOW: "yellow",
|
|
||||||
BLUE: "blue",
|
|
||||||
REWIND: "rewind",
|
|
||||||
PLAY: "play",
|
|
||||||
PAUSE: "pause",
|
|
||||||
STOP: "stop",
|
|
||||||
FORWARD: "forward",
|
|
||||||
OTHER: "other",
|
|
||||||
}
|
|
||||||
|
|
||||||
Command.getTypeString = (type) => {
|
|
||||||
let mapping = {
|
|
||||||
[Command.TYPES.POWER]: "Power",
|
|
||||||
[Command.TYPES.INPUT]: "Input",
|
|
||||||
[Command.TYPES.ONE]: "1",
|
|
||||||
[Command.TYPES.TWO]: "2",
|
|
||||||
[Command.TYPES.THREE]: "3",
|
|
||||||
[Command.TYPES.FOUR]: "4",
|
|
||||||
[Command.TYPES.FIVE]: "5",
|
|
||||||
[Command.TYPES.SIX]: "6",
|
|
||||||
[Command.TYPES.SEVEN]: "7",
|
|
||||||
[Command.TYPES.EIGHT]: "8",
|
|
||||||
[Command.TYPES.NINE]: "9",
|
|
||||||
[Command.TYPES.ZERO]: "0",
|
|
||||||
[Command.TYPES.VOLUME_UP]: "Volume Up",
|
|
||||||
[Command.TYPES.VOLUME_DOWN]: "Volume Down",
|
|
||||||
[Command.TYPES.MUTE]: "Mute",
|
|
||||||
[Command.TYPES.CHANNEL_UP]: "Channel Up",
|
|
||||||
[Command.TYPES.CHANNEL_DOWN]: "Channel Down",
|
|
||||||
[Command.TYPES.MENU]: "Menu",
|
|
||||||
[Command.TYPES.HOME]: "Home",
|
|
||||||
[Command.TYPES.SETTINGS]: "Settings",
|
|
||||||
[Command.TYPES.OPTIONS]: "Options",
|
|
||||||
[Command.TYPES.UP]: "Up",
|
|
||||||
[Command.TYPES.DOWN]: "Down",
|
|
||||||
[Command.TYPES.LEFT]: "Left",
|
|
||||||
[Command.TYPES.RIGHT]: "Right",
|
|
||||||
[Command.TYPES.ENTER]: "Enter",
|
|
||||||
[Command.TYPES.INFO]: "Info",
|
|
||||||
[Command.TYPES.RETURN]: "Return",
|
|
||||||
[Command.TYPES.EXIT]: "Exit",
|
|
||||||
[Command.TYPES.RED]: "Red",
|
|
||||||
[Command.TYPES.GREEN]: "Green",
|
|
||||||
[Command.TYPES.YELLOW]: "Yellow",
|
|
||||||
[Command.TYPES.BLUE]: "Blue",
|
|
||||||
[Command.TYPES.REWIND]: "Rewind",
|
|
||||||
[Command.TYPES.PLAY]: "Play",
|
|
||||||
[Command.TYPES.PAUSE]: "Pause",
|
|
||||||
[Command.TYPES.STOP]: "Stop",
|
|
||||||
[Command.TYPES.FORWARD]: "Forward",
|
|
||||||
[Command.TYPES.OTHER]: "Other",
|
|
||||||
}
|
|
||||||
return mapping[type]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Command;
|
export default Command;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
function Remote({ id, title, commands = [] } = {}) {
|
function Remote({ id, title, commands } = {}) {
|
||||||
let _id = id;
|
let _id = id;
|
||||||
let _title = title;
|
let _title = title;
|
||||||
let _commands = commands;
|
let _commands = commands;
|
||||||
|
|||||||
@ -68,11 +68,7 @@ const Serializer = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deserializeRemote(object) {
|
function deserializeRemote(object) {
|
||||||
return new Remote({
|
return new Remote(object);
|
||||||
id: object.id,
|
|
||||||
title: object.title,
|
|
||||||
commands: deserializeCommands(object.commands),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deserializeRemotes(objects) {
|
function deserializeRemotes(objects) {
|
||||||
|
|||||||
@ -92,10 +92,8 @@ function CreateCommandModal(props) {
|
|||||||
<option value="" selected>
|
<option value="" selected>
|
||||||
Please select
|
Please select
|
||||||
</option>
|
</option>
|
||||||
{Object.values(Command.PROTOCOLS).map((protocol) => (
|
{Object.keys(Command.Protocols).map((protocol) => (
|
||||||
<option value={protocol}>
|
<option value={protocol}>{Command.Protocols[protocol]}</option>
|
||||||
{Command.getProtocolString(protocol)}
|
|
||||||
</option>
|
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -141,9 +139,9 @@ function CreateCommandModal(props) {
|
|||||||
<option value="" selected>
|
<option value="" selected>
|
||||||
Please select
|
Please select
|
||||||
</option>
|
</option>
|
||||||
{Object.values(Command.TYPES).map((commandType) => (
|
{Object.keys(Command.CommandTypes).map((commandType) => (
|
||||||
<option value={commandType}>
|
<option value={commandType}>
|
||||||
{Command.getTypeString(commandType)}
|
{Command.CommandTypes[commandType]}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@ -35,14 +35,8 @@ FieldTitles[COMMAND_TYPE_FIELD] = "Type";
|
|||||||
FieldTitles[TITLE_FIELD] = "Title";
|
FieldTitles[TITLE_FIELD] = "Title";
|
||||||
|
|
||||||
const CommandFieldsRequiringValueMapping = ["protocol", "commandType"];
|
const CommandFieldsRequiringValueMapping = ["protocol", "commandType"];
|
||||||
const commandTypeFuse = new Fuse(
|
const commandTypeFuse = new Fuse(Object.values(Command.CommandTypes));
|
||||||
Object.values(Command.TYPES).map((type) => Command.getTypeString(type))
|
const protocolsFuse = new Fuse(Object.values(Command.Protocols));
|
||||||
);
|
|
||||||
const protocolsFuse = new Fuse(
|
|
||||||
Object.values(Command.PROTOCOLS).map((protocol) =>
|
|
||||||
Command.getProtocolString(protocol)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
function ImportCommandsModal(props) {
|
function ImportCommandsModal(props) {
|
||||||
const [csvString, setCsvString] = createSignal("");
|
const [csvString, setCsvString] = createSignal("");
|
||||||
@ -252,7 +246,7 @@ function ImportCommandsModal(props) {
|
|||||||
<option value="" selected>
|
<option value="" selected>
|
||||||
Please select
|
Please select
|
||||||
</option>
|
</option>
|
||||||
{Object.values(Command.PROTOCOLS).map(
|
{Object.keys(Command.Protocols).map(
|
||||||
(protocol) => (
|
(protocol) => (
|
||||||
<option
|
<option
|
||||||
value={protocol}
|
value={protocol}
|
||||||
@ -262,7 +256,7 @@ function ImportCommandsModal(props) {
|
|||||||
] === protocol
|
] === protocol
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Command.getProtocolString(protocol)}
|
{Command.Protocols[protocol]}
|
||||||
</option>
|
</option>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@ -278,7 +272,7 @@ function ImportCommandsModal(props) {
|
|||||||
<option value="" selected>
|
<option value="" selected>
|
||||||
Please select
|
Please select
|
||||||
</option>
|
</option>
|
||||||
{Object.values(Command.TYPES).map(
|
{Object.keys(Command.CommandTypes).map(
|
||||||
(commandType) => (
|
(commandType) => (
|
||||||
<option
|
<option
|
||||||
value={commandType}
|
value={commandType}
|
||||||
@ -288,7 +282,7 @@ function ImportCommandsModal(props) {
|
|||||||
] === commandType
|
] === commandType
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Command.getTypeString(commandType)}
|
{Command.CommandTypes[commandType]}
|
||||||
</option>
|
</option>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@ -324,8 +318,8 @@ function ImportCommandsModal(props) {
|
|||||||
if (!valueMapping[PROTOCOL_FIELD][protocolValue]) {
|
if (!valueMapping[PROTOCOL_FIELD][protocolValue]) {
|
||||||
let result = protocolsFuse.search(protocolValue).shift();
|
let result = protocolsFuse.search(protocolValue).shift();
|
||||||
if (result) {
|
if (result) {
|
||||||
let protocol = Object.values(Command.PROTOCOLS).find(
|
let protocol = Object.keys(Command.Protocols).find(
|
||||||
(protocol) => Command.getProtocolString(protocol) === result.item
|
(protocol) => Command.Protocols[protocol] === result.item
|
||||||
);
|
);
|
||||||
valueMapping[PROTOCOL_FIELD][protocolValue] = protocol;
|
valueMapping[PROTOCOL_FIELD][protocolValue] = protocol;
|
||||||
}
|
}
|
||||||
@ -333,9 +327,8 @@ function ImportCommandsModal(props) {
|
|||||||
if (!valueMapping[COMMAND_TYPE_FIELD][commandTypeValue]) {
|
if (!valueMapping[COMMAND_TYPE_FIELD][commandTypeValue]) {
|
||||||
let result = commandTypeFuse.search(commandTypeValue).shift();
|
let result = commandTypeFuse.search(commandTypeValue).shift();
|
||||||
if (result) {
|
if (result) {
|
||||||
let commandType = Object.values(Command.TYPES).find(
|
let commandType = Object.keys(Command.CommandTypes).find(
|
||||||
(commandType) =>
|
(commandType) => Command.CommandTypes[commandType] === result.item
|
||||||
Command.getTypeString(commandType) === result.item
|
|
||||||
);
|
);
|
||||||
valueMapping[COMMAND_TYPE_FIELD][commandTypeValue] = commandType;
|
valueMapping[COMMAND_TYPE_FIELD][commandTypeValue] = commandType;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,9 @@
|
|||||||
import Command from "../data/command";
|
import Command from "../data/command";
|
||||||
import Serializer from "../data/serializer";
|
import Serializer from "../data/serializer";
|
||||||
import Net from "../tools/net";
|
import Net from "../tools/net";
|
||||||
import WebRTCService from "./webrtc-service";
|
|
||||||
|
|
||||||
const MESSAGE_TYPE_COMMAND = "command";
|
|
||||||
|
|
||||||
function RemoteService() {
|
function RemoteService() {
|
||||||
async function getRemote(remoteId) {
|
let remotes = [];
|
||||||
let response = await Net.sendRequest({
|
|
||||||
method: "GET",
|
|
||||||
url: "/api/remotes/" + remoteId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
|
||||||
let responseData = JSON.parse(response.data);
|
|
||||||
throw new Error(responseData.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
let remoteObject = JSON.parse(response.data);
|
|
||||||
return Serializer.deserializeRemote(remoteObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getRemotes() {
|
async function getRemotes() {
|
||||||
let response = await Net.sendRequest({
|
let response = await Net.sendRequest({
|
||||||
@ -107,13 +91,7 @@ function RemoteService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCommand(command) {
|
|
||||||
let commandObject = Serializer.serializeCommand(command);
|
|
||||||
WebRTCService.sendDataJson({type: MESSAGE_TYPE_COMMAND, data: commandObject});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getRemote,
|
|
||||||
getRemotes,
|
getRemotes,
|
||||||
createRemote,
|
createRemote,
|
||||||
deleteRemote,
|
deleteRemote,
|
||||||
@ -121,7 +99,6 @@ function RemoteService() {
|
|||||||
createCommand,
|
createCommand,
|
||||||
createCommands,
|
createCommands,
|
||||||
deleteCommand,
|
deleteCommand,
|
||||||
sendCommand,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,10 @@ function WebRTCService() {
|
|||||||
const STATE_CLOSED = "closed";
|
const STATE_CLOSED = "closed";
|
||||||
const STATE_FAILED = "failed";
|
const STATE_FAILED = "failed";
|
||||||
const ICE_CONNECTION_STATE_CHANGE_EVENT = "iceconnectionstatechange";
|
const ICE_CONNECTION_STATE_CHANGE_EVENT = "iceconnectionstatechange";
|
||||||
const DATA_CHANNEL_OPEN_EVENT = "datachannelopen";
|
|
||||||
|
|
||||||
let videoElement;
|
let videoElement;
|
||||||
let peerConnection;
|
let peerConnection;
|
||||||
let peerId;
|
let peerId;
|
||||||
let dataChannel;
|
|
||||||
|
|
||||||
let eventEmitter = new EventEmitter();
|
let eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
@ -59,19 +57,12 @@ function WebRTCService() {
|
|||||||
console.log("Negotiation needed");
|
console.log("Negotiation needed");
|
||||||
negotiate(targetId);
|
negotiate(targetId);
|
||||||
};
|
};
|
||||||
dataChannel = peerConnection.createDataChannel("data");
|
|
||||||
dataChannel.addEventListener("open", () => {
|
|
||||||
eventEmitter.dispatchEvent(DATA_CHANNEL_OPEN_EVENT);
|
|
||||||
});
|
|
||||||
negotiate(targetId);
|
negotiate(targetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
peerConnection.close();
|
peerConnection.close();
|
||||||
eventEmitter.dispatchEvent(
|
eventEmitter.dispatchEvent(ICE_CONNECTION_STATE_CHANGE_EVENT, peerConnection.iceConnectionState);
|
||||||
ICE_CONNECTION_STATE_CHANGE_EVENT,
|
|
||||||
peerConnection.iceConnectionState
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function negotiate(targetId) {
|
async function negotiate(targetId) {
|
||||||
@ -134,27 +125,12 @@ function WebRTCService() {
|
|||||||
peerConnection.addIceCandidate(iceCandidate);
|
peerConnection.addIceCandidate(iceCandidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendDataString(data) {
|
|
||||||
if (!dataChannel) return;
|
|
||||||
if (dataChannel.readyState !== "open") return;
|
|
||||||
dataChannel.send(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendDataJson(data) {
|
|
||||||
let dataJson = JSON.stringify(data);
|
|
||||||
sendDataString(dataJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onStateChanged(callback) {
|
function onStateChanged(callback) {
|
||||||
eventEmitter.on(ICE_CONNECTION_STATE_CHANGE_EVENT, () => {
|
eventEmitter.on(ICE_CONNECTION_STATE_CHANGE_EVENT, () => {
|
||||||
callback(peerConnection.iceConnectionState);
|
callback(peerConnection.iceConnectionState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDataChannelOpen(callback) {
|
|
||||||
eventEmitter.on(DATA_CHANNEL_OPEN_EVENT, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getConfiguration() {
|
function getConfiguration() {
|
||||||
return {
|
return {
|
||||||
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
||||||
@ -179,10 +155,7 @@ function WebRTCService() {
|
|||||||
disconnect,
|
disconnect,
|
||||||
setVideoElement,
|
setVideoElement,
|
||||||
getVideoElement,
|
getVideoElement,
|
||||||
sendDataString,
|
|
||||||
sendDataJson,
|
|
||||||
onStateChanged,
|
onStateChanged,
|
||||||
onDataChannelOpen,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,8 @@
|
|||||||
import {
|
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
|
||||||
createEffect,
|
|
||||||
createMemo,
|
|
||||||
createResource,
|
|
||||||
createSignal,
|
|
||||||
onCleanup,
|
|
||||||
} from "solid-js";
|
|
||||||
import WebRTCService from "../../services/webrtc-service";
|
import WebRTCService from "../../services/webrtc-service";
|
||||||
import Integration from "../../data/integration";
|
import Integration from "../../data/integration";
|
||||||
import DeviceService from "../../services/device-service";
|
import DeviceService from "../../services/device-service";
|
||||||
import UrlUtils from "../../tools/url-utils";
|
import UrlUtils from "../../tools/url-utils";
|
||||||
import RemoteControl from "../../components/remote-control";
|
|
||||||
import RemoteService from "../../services/remotes-service";
|
|
||||||
|
|
||||||
const [integration, setIntegration] = createSignal(new Integration());
|
const [integration, setIntegration] = createSignal(new Integration());
|
||||||
|
|
||||||
@ -30,16 +22,8 @@ function IntegrationView(props) {
|
|||||||
connectionState() === WebRTCService.STATE_CLOSED
|
connectionState() === WebRTCService.STATE_CLOSED
|
||||||
);
|
);
|
||||||
WebRTCService.onStateChanged(handleConnectionStateChanged);
|
WebRTCService.onStateChanged(handleConnectionStateChanged);
|
||||||
WebRTCService.onDataChannelOpen(handleDataChannelOpen);
|
|
||||||
let videoElement = null;
|
let videoElement = null;
|
||||||
|
|
||||||
const [availableRemotes] = createResource(RemoteService.getRemotes, {
|
|
||||||
initialValue: [],
|
|
||||||
});
|
|
||||||
const [selectedRemote, setSelectedRemote] = createSignal();
|
|
||||||
|
|
||||||
createEffect(() => handleRemoteSelected(availableRemotes().shift()?.getId()));
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
let url = UrlUtils.getUrl();
|
let url = UrlUtils.getUrl();
|
||||||
url = UrlUtils.addQueryParameter(url, "id", integration()?.getId());
|
url = UrlUtils.addQueryParameter(url, "id", integration()?.getId());
|
||||||
@ -56,7 +40,7 @@ function IntegrationView(props) {
|
|||||||
async function setIntegrationFromUrl() {
|
async function setIntegrationFromUrl() {
|
||||||
let integrationId = UrlUtils.getQueryParameter("id");
|
let integrationId = UrlUtils.getQueryParameter("id");
|
||||||
if (!integrationId) return;
|
if (!integrationId) return;
|
||||||
let integration = await DeviceService.getIntegration(integrationId);
|
let integration = await DeviceService.getIntegration(integrationId)
|
||||||
setIntegration(integration);
|
setIntegration(integration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,22 +58,6 @@ function IntegrationView(props) {
|
|||||||
setConnectionState(state);
|
setConnectionState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDataChannelOpen() {
|
|
||||||
setInterval(() => {
|
|
||||||
WebRTCService.sendDataJson({ message: "ping" });
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleRemoteSelected(remoteId) {
|
|
||||||
if (!remoteId) return;
|
|
||||||
let remote = await RemoteService.getRemote(remoteId);
|
|
||||||
setSelectedRemote(remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRemoteButtonPressed(command) {
|
|
||||||
RemoteService.sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={
|
class={
|
||||||
@ -124,31 +92,13 @@ function IntegrationView(props) {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-fill d-flex flex-row overflow-hidden">
|
<div class="flex-fill d-flex flex-column justify-content-center align-items-center rounded overflow-hidden">
|
||||||
<div class="flex-fill rounded overflow-hidden">
|
|
||||||
<video
|
<video
|
||||||
ref={videoElement}
|
ref={videoElement}
|
||||||
class="w-100 h-100"
|
class="w-100 h-100"
|
||||||
style="background-color: #000"
|
style="background-color: #000"
|
||||||
></video>
|
></video>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column ps-3">
|
|
||||||
<select
|
|
||||||
class="form-select mb-3"
|
|
||||||
onChange={(e) => handleRemoteSelected(e.target.value)}
|
|
||||||
>
|
|
||||||
{availableRemotes().map((remote, index) => (
|
|
||||||
<option value={remote.getId()} selected={index === 0}>
|
|
||||||
{remote.getTitle()}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<RemoteControl
|
|
||||||
onCommand={handleRemoteButtonPressed}
|
|
||||||
remote={selectedRemote()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user