Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LX Keypad to V2 #28

Merged
merged 6 commits into from
Jun 29, 2022
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
4 changes: 3 additions & 1 deletion src/api/outputModules/outputModulesRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ export const outputModulesRouter = (
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
payload: apiObject
): Promise<apiObject> => {
logger.debug('e131 scanner router has a request', { path, method, payload })
logger.debug('e131 router has a request', { path, method, payload })
return new Promise(resolve => {
if (path[0] === 'e131') {
if (path[1] === 'startSampling') {
return globalThis.e131.sampleE131().then(() => resolve({}))
} else if (path[1] === 'output') {
return globalThis.e131.update(payload.universe, payload.channelData, payload.fadeTime)
}
}
})
Expand Down
8 changes: 7 additions & 1 deletion src/api/preset/presetRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ export const presetRouter = (
e131.convertObjectToChannelData(value.data),
value.fadeTime * 1000
)
resolve({})
} else if (value.type === 'osc' && value.data !== null) {
Object.entries(value.data).forEach(presetData => {
osc.send(presetData)
})
resolve({})
} else if (value.type === 'http' && value.data !== null) {
// Make the HTTP request
axios({
Expand All @@ -42,15 +44,19 @@ export const presetRouter = (
}).catch(err => {
logger.info('Preset HTTP request failed', { err })
})
resolve({})
} else if (value.type === 'macro' && value.data !== null) {
value.data.forEach((step: { type: string; value: string; key: string }) => {
logger.info(step)
if (step.type === 'preset' && parseInt(step.value) !== value.id) {
// Trigger the presets in the macro
presetRouter(['recall', step.value], 'GET', {})
resolve({})
} else if (step.type === 'link') {
resolve({ redirect: step.value })
}
})
}
resolve({})
})
} else if (method === 'PUT') {
return PresetRepository.setAllFromApp(payload as Array<DatabasePreset>)
Expand Down
12 changes: 11 additions & 1 deletion src/app/Components/Admin/Controls/Presets/EditModal/Macro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export const MacroPresetEditModal = (props: GetInputProps<'input'>) => {
<Group key={item.key} mt="xs">
<Select
{...form.getListInputProps('steps', index, 'type')}
data={[{ value: 'preset', label: 'Trigger Preset' }]}
data={[
{ value: 'preset', label: 'Trigger Preset' },
{ value: 'link', label: 'Open a Page' },
]}
/>
{form.values.steps[index].type === 'preset' ? (
<Select
Expand All @@ -61,6 +64,13 @@ export const MacroPresetEditModal = (props: GetInputProps<'input'>) => {
data={presetsForSelect}
/>
) : null}
{form.values.steps[index].type === 'link' ? (
<Select
placeholder="Page"
{...form.getListInputProps('steps', index, 'value')}
data={[{ value: '/controlPanel/lxKeypad', label: 'Lighting Keypad' }]}
/>
) : null}
<ActionIcon color="red" variant="hover" onClick={() => form.removeListItem('steps', index)}>
<FaTrash />
</ActionIcon>
Expand Down
1 change: 1 addition & 0 deletions src/app/Navigation/ControlPanelNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { NavbarItem } from './NavbarItem'
import { useAppSelector } from './../apis/redux/mainStore'
import { DatabasePresetFolder } from './../../database/repository/presetFolder'
import { PresetFolderIcon } from './../Components/ControlPanel/PresetFolderIcon'
import { FaCogs } from '@react-icons/all-files/fa/FaCogs'

const TopLevelPresetFolders = ({
active,
Expand Down
198 changes: 198 additions & 0 deletions src/app/Pages/ControlPanel/Keypad.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { Button, SimpleGrid, TextInput, Space, Slider, Container, Title } from '@mantine/core'
import React, { useEffect, useState } from 'react'
import { ApiCall } from '../../../app/apis/wrapper'
import { channelData } from '../../../output/e131'

export const KeypadPage = () => {
const [faderDisabled, setFaderDisabled] = useState<boolean>(true)
const [intensity, setIntensity] = useState<number>(0)
const [commandText, setCommandText] = useState<string>('')

//Update e131 output on intensity change
useEffect(() => {
const command = commandText.split(' ')
const channels: Array<channelData> = []
if (validCommand(command)) {
setFaderDisabled(false)
if (command[2]) {
for (let i = parseInt(command[0]); i <= parseInt(command[2]); i++) {
channels.push({ channel: i, level: intensity })
}
} else {
channels.push({ channel: parseInt(command[0]), level: intensity })
}

sendLX(1, channels, 0)
} else {
setFaderDisabled(true)
}
}, [commandText, intensity])

/**
* Verify a command array will actually be able to control fixtures
* @param command - The command array to verify
* @returns True if the command array is valid, false otherwise
*/
function validCommand(command: string[]) {
if (
command.length === 0 ||
command.length > 3 ||
command.length === 2 ||
command[0] === '' ||
command[2] === '' ||
parseInt(command[0]) > 512 ||
parseInt(command[2]) > 512 ||
parseInt(command[2]) < parseInt(command[0])
) {
return false
}
return true
}

/**
* Actually update our e131 Data
* @param universe - Current Universe
* @param channels - Channels to update
* @param fadeTime - how long to fade the channels
*/
function sendLX(universe: number, channels: Array<channelData>, fadeTime: number) {
const data: apiObject = { universe: universe, channelData: channels, fadeTime: fadeTime * 1000 }
ApiCall.get('/outputModules/e131/output', data)
}

/**
* Update and verify the command array and enable intensity slider if valid
* @param commandValue - the new value to add
*/
function updateCommandString(commandValue: string) {
if (commandValue === 'clear') {
setCommandText('')
setIntensity(0)
} else {
setCommandText(commandText + commandValue)
}
}

return (
<Container>
<TextInput disabled aria-label="Command Output" size="xl" value={commandText} />
<Space h="xl" />
<SimpleGrid cols={3}>
<Button
size="lg"
onClick={() => {
updateCommandString('7')
}}
>
7
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('8')
}}
>
8
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('9')
}}
>
9
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('4')
}}
>
4
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('5')
}}
>
5
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('6')
}}
>
6
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('1')
}}
>
1
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('2')
}}
>
2
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('3')
}}
>
3
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('clear')
}}
>
Clear
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString('0')
}}
>
0
</Button>
<Button
size="lg"
onClick={() => {
updateCommandString(' thru ')
}}
>
Thru
</Button>
</SimpleGrid>
<Space h="xl" />
<Title order={2}>Intensity</Title>
<Slider
disabled={faderDisabled}
value={intensity}
onChange={setIntensity}
size="xl"
radius="xl"
min={0}
max={255}
marks={[
{ value: 0, label: '0%' },
{ value: 64, label: '25%' },
{ value: 128, label: '50%' },
{ value: 192, label: '75%' },
{ value: 255, label: '100%' },
]}
></Slider>
</Container>
)
}
10 changes: 8 additions & 2 deletions src/app/Pages/ControlPanel/Preset.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Link, useParams } from 'react-router-dom'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { DatabasePresetFolder } from './../../../database/repository/presetFolder'
import { Button, Paper } from '@mantine/core'
import { FaLevelUpAlt } from '@react-icons/all-files/fa/FaLevelUpAlt'
Expand All @@ -9,6 +9,8 @@ import { useAppSelector } from './../../apis/redux/mainStore'
import { ApiCall } from './../../apis/wrapper'
import { PresetFolderIconReact } from './../../Components/ControlPanel/PresetFolderIcon'
const PresetButton = ({ text, presetId, color }: { text: string; presetId: number; color: string }) => {
const navigate = useNavigate()

return (
<Button
variant="default"
Expand All @@ -18,7 +20,11 @@ const PresetButton = ({ text, presetId, color }: { text: string; presetId: numbe
color: pickTextColorBasedOnBgColor(color),
})}
onClick={() => {
ApiCall.get('/presets/recall/' + presetId, {})
ApiCall.get('/presets/recall/' + presetId, {}).then(function (value) {
if (value.redirect) {
navigate(value.redirect)
}
})
}}
size="xl"
mx="xs"
Expand Down
2 changes: 2 additions & 0 deletions src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { PresetsConfigurationPage } from './Pages/Admin/Presets'
import { FadersConfigurationPage } from './Pages/Admin/Faders'
import { ControlsConfigurationPage } from './Pages/Admin/Controls'
import { Locked } from './Components/Locked'
import { KeypadPage } from './Pages/ControlPanel/Keypad'

const Router = () => {
return (
Expand All @@ -28,6 +29,7 @@ const Router = () => {
>
<Route path="presetFolder/:folderId" element={<PresetPage />} />
<Route path="help" element={<HelpPage />} />
<Route path="lxkeypad" element={<KeypadPage />} />
</Route>
<Route path="e131sampler" element={<div>Sampling E1.31</div>} />
<Route path="admin" element={<MainNav navigation={<AdminNavigation />} />}>
Expand Down
2 changes: 1 addition & 1 deletion src/output/e131/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ip from 'ip'
import { broadcast } from './../../api/broadcast'
import { createDatabaseObject, Database, sendDatabaseObject } from './../../api/database'

interface channelData {
export interface channelData {
channel: number
level: number
}
Expand Down