Narete stvari da delajo
This commit is contained in:
parent
64a10b0512
commit
29b2beca5a
45 changed files with 809 additions and 1600 deletions
|
@ -1,19 +1,3 @@
|
|||
# vju_display
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<title>MMM krmilnik</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
|
@ -14,19 +14,10 @@ document.addEventListener('contextmenu', event => event.preventDefault());
|
|||
|
||||
|
||||
let urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const currentRoom = ref(urlParams.get('room') || 'none') // if no param specified
|
||||
|
||||
const debugFlag = ref(urlParams.get('debug') == 'true');
|
||||
|
||||
// should also check if valid room parameter ampak se ne mudi
|
||||
// TODO does this make sense al se naj naprej defaulta kr na p01?
|
||||
|
||||
const currentRoom = ref(urlParams.get('room') || 'none')
|
||||
|
||||
const pageNum = ref(0)
|
||||
|
||||
|
||||
|
||||
const mqttStat = ref($mqtt.status())
|
||||
|
||||
watch(mqttStat, (_, newState) => {
|
||||
|
@ -47,7 +38,7 @@ watch(pageNum, (_, newState) => {
|
|||
|
||||
<template>
|
||||
<div v-if="currentRoom == 'none'">
|
||||
<h1>Incorrect or missing room parameter!</h1>
|
||||
<h1>Missing room parameter! Add e.g. <code>?room=P01</code> to the URL</h1>
|
||||
</div>
|
||||
<div v-else id="wrapper">
|
||||
<header class="sidebar">
|
||||
|
@ -62,6 +53,7 @@ watch(pageNum, (_, newState) => {
|
|||
<Tab @click="pageNum=4" :selected="pageNum==4">Servis</Tab>
|
||||
</VerticalTabs>
|
||||
<div class="mstatus" v-if="$mqtt.status() != 'connected'">{{ $mqtt.status()?.toUpperCase() }}</div>
|
||||
<button class="reload" v-if="$mqtt.status() != 'connected'" onclick="window.location.reload()">RELOAD</button>
|
||||
|
||||
</header>
|
||||
<main>
|
||||
|
@ -75,14 +67,19 @@ watch(pageNum, (_, newState) => {
|
|||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reload {
|
||||
opacity: .8;
|
||||
font-size: .8em;
|
||||
margin: 0 3em;
|
||||
}
|
||||
|
||||
.hiddenPage {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
@ -99,12 +96,13 @@ main > * {
|
|||
}
|
||||
|
||||
.sidebar {
|
||||
max-width: 20vw;
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.logo {
|
||||
max-width: 100%;
|
||||
padding: .4rem;
|
||||
padding-left: .8rem;
|
||||
padding-top: .8rem;
|
||||
}
|
||||
|
||||
.mstatus {
|
||||
|
|
|
@ -37,14 +37,17 @@ body {
|
|||
}
|
||||
html, body {
|
||||
touch-action: manipulation;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
height: 100vh;
|
||||
margin: 0 auto;
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
color: #58595b;
|
||||
}
|
||||
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
|
@ -74,31 +77,30 @@ a,
|
|||
}
|
||||
|
||||
button {
|
||||
background: lightgray;
|
||||
--bg-default: #ffffff;
|
||||
--bg-active: lightgray;
|
||||
background: var(--bg-default);
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button:focus:not(:active) {
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
button:focus-visible(:active) {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
button {
|
||||
border: none;
|
||||
background-color: lightgray;
|
||||
margin: .1rem;
|
||||
border: 1px solid #000000;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
/* The last touched button keeps focus, so we shoudln't highlight that */
|
||||
button:focus:not(:active) {
|
||||
background: var(--bg-default);
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: gray;
|
||||
background: var(--bg-active);
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -108,4 +110,4 @@ h3 {
|
|||
|
||||
.currentlyActive {
|
||||
background-color: orange !important
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,31 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive, watch} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import ProjectorShutter from './ProjectorShutter.vue'
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
})
|
||||
|
||||
const projUnreachable = ref(false)
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + '/status/power',
|
||||
props.room + '/projectors/' + props.position + '/platno/status',
|
||||
props.room + '/projectors/' + props.position + '/lift/status',
|
||||
props.room + '/projectors/' + props.position + '/status'
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
let typ: string = ''
|
||||
if (topic.includes('power')) {
|
||||
typ = topic.split('/')[4]
|
||||
} else if (topic.includes('platno')) {
|
||||
typ = topic.split('/')[3]
|
||||
} else if (topic.includes('lift')) {
|
||||
typ = topic.split('/')[3]
|
||||
} else if (topic.endsWith('error')) {
|
||||
projUnreachable.value = msg == "UNREACHABLE"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
handleIncStatus(typ, msg)
|
||||
}
|
||||
|
||||
function handleIncStatus(typ: string, msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
if (typ == 'power') {
|
||||
roomStatus.proj_power = msg == '1'
|
||||
} else if (typ == 'platno') {
|
||||
roomStatus.platno_state = msg
|
||||
}
|
||||
//else if (typ == 'lift') { roomStatus.lift_state = msg }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
|
||||
const status = reactive({
|
||||
power: '?',
|
||||
platno: '?',
|
||||
lift: '?',
|
||||
wait: false,
|
||||
})
|
||||
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/status/power', (message) => {
|
||||
console.debug("a")
|
||||
status.power = message;
|
||||
});
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/platno/status', (message) => {
|
||||
status.platno = message;
|
||||
});
|
||||
$mqtt.subscribe(props.room + '/projectors/' + props.position + '/lift/status', (message) => {
|
||||
status.lift = message;
|
||||
});
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms)
|
||||
|
@ -68,102 +33,67 @@ function sleep(ms: number) {
|
|||
}
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Fnr') //todo refactor to 1 or 0 maybe
|
||||
$mqtt.publish(topic, msg, 'Fnr')
|
||||
console.log('sent')
|
||||
}
|
||||
|
||||
async function setLecture(pos?: String) {
|
||||
if (!pos) {
|
||||
return
|
||||
}
|
||||
let topicPref = props.room + "/projectors/" + props.position + "/"
|
||||
let command = '0'
|
||||
if (roomState.value == 0 || roomState.value == 3) {
|
||||
command = '1'
|
||||
} // else if (roomState.value == 1) {
|
||||
// command = '0'
|
||||
// } else { return }
|
||||
publishMQTTMsg((props.room + '/power/master/set'), '1')
|
||||
publishMQTTMsg((props.room + '/power/projectors/set'), '1')
|
||||
async function startLecture() {
|
||||
publishMQTTMsg(props.room + '/power/master/set', '1')
|
||||
publishMQTTMsg(props.room + '/power/audio/set', '1')
|
||||
publishMQTTMsg(props.room + '/power/projectors/set', '1')
|
||||
await sleep(500)
|
||||
publishMQTTMsg((topicPref + 'command/power'), command)
|
||||
publishMQTTMsg((topicPref + 'platno/goto'), command == '1' ? 'DOWN' : 'UP')
|
||||
publishMQTTMsg((topicPref + 'lift/move/' + (command == '1' ? 'down' : 'down')), '1')
|
||||
await sleep(1000)
|
||||
// publishMQTTMsg((topicPref + 'platno/goto'), 'STOP')
|
||||
publishMQTTMsg((topicPref + 'lift/move/' + (command == '1' ? 'down' : 'down')), '0')
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/set/power', '1')
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/platno/goto', "DOWN")
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/lift/goto', 'DOWN')
|
||||
|
||||
status.wait = true
|
||||
setTimeout(() => {
|
||||
status.wait = false
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
async function stopLecture() {
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/set/power', '0')
|
||||
publishMQTTMsg(props.room + '/projectors/' + props.position + '/platno/goto', "UP")
|
||||
// publishMQTTMsg(props.room + '/projectors/' + props.position + 'lift/goto', 'DOWN')
|
||||
status.wait = true
|
||||
setTimeout(() => {
|
||||
status.wait = false
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
const roomStatus = reactive({
|
||||
proj_power: false,
|
||||
//lift_state: 'UNKNOWN',
|
||||
platno_state: 'UNKNOWN',
|
||||
})
|
||||
|
||||
enum RoomStatus {
|
||||
OFF = 0,
|
||||
ON = 1,
|
||||
UNKNOWN = 2,
|
||||
ERROR = 3,
|
||||
}
|
||||
|
||||
const roomState = ref(0)
|
||||
// OFF -> 0; ON -> 1; IN BETWEEN -> 2
|
||||
|
||||
watch(roomStatus, (_, _newState) => {
|
||||
if (!_newState) {
|
||||
return
|
||||
}
|
||||
let allOn = _newState.proj_power && _newState.platno_state == 'DOWN' //&& _newState.lift_state == 'DOWN'
|
||||
let allOff = !_newState.proj_power && _newState.platno_state == 'UP' //&& _newState.lift_state == 'UP'
|
||||
let anyUnknown = _newState.platno_state == 'UNKNOWN'
|
||||
if (_newState.platno_state == 'DOWN') {
|
||||
roomState.value = RoomStatus.ON
|
||||
} else if (_newState.platno_state == 'UP') {
|
||||
roomState.value = 0
|
||||
} else if (anyUnknown) {
|
||||
roomState.value = 3
|
||||
} else {
|
||||
roomState.value = 2
|
||||
}
|
||||
}, {immediate: true})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="projUnreachable">CONNECTION FAILED</span>
|
||||
<div>
|
||||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<div class="status" v-if="roomState == 1">VKLOPLJENO</div>
|
||||
<div class="status" v-else-if="roomState == 0">IZKLOPLJENO</div>
|
||||
<div class="status" v-else-if="roomState == 2">POČAKAJTE</div>
|
||||
<div class="status" v-else-if="roomState == 3">NEZNANO STANJE</div>
|
||||
<div class="status" v-else>NAPAKA</div>
|
||||
<button style="" @click="setLecture(props.position)" :disabled="roomState == 2">
|
||||
{{ roomState == 1 ? 'IZKLOP' : (roomState == 0 || roomState == 3 ? 'VKLOP' : '...') }}
|
||||
<small class="status">
|
||||
PROJ: {{ status.power }}
|
||||
PLAT: {{ status.platno }}
|
||||
LIFT: {{ status.lift }}
|
||||
</small>
|
||||
|
||||
<button class="currentlyActive" @click="stopLecture()" v-if="status.power == '1' || status.platno == 'DOWN'" :class="{waiting: status.wait}">
|
||||
IZKLOP
|
||||
</button>
|
||||
<button @click="startLecture()" v-else="" :class="{waiting: status.wait}">
|
||||
VKLOP
|
||||
</button>
|
||||
|
||||
<ProjectorShutter :room="props.room" :position="props.position"/>
|
||||
</div>
|
||||
|
||||
<!--TODO ce hoces naj bo kot nek debug flag -->
|
||||
|
||||
<!-- <div>Projektor status: {{ roomStatus.proj_power ? "ON" : "OFF" }}</div>
|
||||
<div>Platno pozicija: {{ roomStatus.platno_state }}</div>-->
|
||||
|
||||
<!-- <h6>Dvigalo pozicija: {{ roomStatus.lift_state }}</h6>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.status {
|
||||
font-size: 1.1em;
|
||||
font-weight: normal;
|
||||
font-size: .8em;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
@ -175,7 +105,24 @@ button {
|
|||
padding: 1rem;
|
||||
width: 100%;
|
||||
|
||||
height: 10rem;
|
||||
height: 9rem;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.wait {
|
||||
animation: fade-in-out 1s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes fade-in-out {
|
||||
0% {
|
||||
opacity: .2;
|
||||
}
|
||||
50% {
|
||||
opacity: .8;
|
||||
}
|
||||
100% {
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,69 +11,19 @@ const props = defineProps({
|
|||
position: String
|
||||
})
|
||||
|
||||
const topicstrs = [ //TODO everything else
|
||||
props.room + '/projectors/' + props.position + 'lift/status',
|
||||
props.room + '/projectors/' + props.position + 'lift/status',
|
||||
]
|
||||
|
||||
const subscriptions =
|
||||
topicstrs.map(topic => {
|
||||
// console.log('subbing to', topic)
|
||||
$mqtt.subscribe(topic, (msg) => {
|
||||
// console.log('received:', topic, msg)
|
||||
handleIncomingMQTT(topic, msg)
|
||||
})
|
||||
})
|
||||
|
||||
function handleIncomingMQTT(topic: string, msg: string) {
|
||||
console.log('Received on', topic, 'with message', msg)
|
||||
if (topic.includes('status')) {
|
||||
//console.log(topic.split('/'))
|
||||
let typ = topic.split('/')[4]
|
||||
// handlePlatnoStatus(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// enum firankState {
|
||||
// UP,
|
||||
// DOWN,
|
||||
// MOVING,
|
||||
// STOPPED
|
||||
// }
|
||||
// const firankStatus = ref(firankState.STOPPED)
|
||||
|
||||
// function handlePlatnoStatus(msg: string) {
|
||||
// console.log('handling status')
|
||||
// //console.log(projStatus)
|
||||
// let newState: firankState
|
||||
// switch (msg) {
|
||||
// case "UP":
|
||||
// newState = firankState.UP
|
||||
// break
|
||||
// case "DOWN":
|
||||
// newState = firankState.DOWN
|
||||
// break
|
||||
// case "MOVING":
|
||||
// newState = firankState.MOVING
|
||||
// break
|
||||
// default:
|
||||
// newState = firankState.STOPPED
|
||||
// break
|
||||
// }
|
||||
// firankStatus.value = newState
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('test')
|
||||
//$mqtt.publish('peepee', 'poopoo', 'Qr') // dela
|
||||
const topicPrefix = `${props.room}/projectors/${props.position}`
|
||||
|
||||
const status = reactive({
|
||||
status: "-",
|
||||
})
|
||||
|
||||
$mqtt.subscribe(`${topicPrefix}/lift/status`, (message) => {
|
||||
status.status = message;
|
||||
});
|
||||
|
||||
function publishMQTTMsg(topic: string, msg: string) {
|
||||
//msg = msg.toString()
|
||||
console.log('Sending to [', topic, '] with message [', msg, ']')
|
||||
$mqtt.publish(topic, msg, 'Fnr') //todo refactor to 1 or 0 maybe
|
||||
console.log('sent')
|
||||
$mqtt.publish(topic, msg, 'Fnr')
|
||||
}
|
||||
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/lift/')
|
||||
|
@ -88,20 +38,20 @@ TODO: NE HARDCODANO
|
|||
<template>
|
||||
<div style="display:flex; gap: 1rem">
|
||||
<div>
|
||||
<h4>Lifti</h4>
|
||||
<h4>Lifti ({{status.status}})</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move/up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move/up', '0')">
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/up', '0')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move/down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move/down', '0')">
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/down', '0')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -109,21 +59,35 @@ TODO: NE HARDCODANO
|
|||
<h4>Servis</h4>
|
||||
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/service_up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move/service_up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/service_up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move/service_up', '0')">
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/service_up', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/service_up', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/service_up', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/service_up', '0')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'move/service_down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'move/service_down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'move/service_down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'move/service_down', '0')">
|
||||
@mousedown="publishMQTTMsg(publishPrefix + 'manual/service_down', '1')"
|
||||
@touchstart="publishMQTTMsg(publishPrefix + 'manual/service_down', '1')"
|
||||
@mouseup="publishMQTTMsg(publishPrefix + 'manual/service_down', '0')"
|
||||
@touchend="publishMQTTMsg(publishPrefix + 'manual/service_down', '0')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h4>GOTO</h4>
|
||||
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'UP')">
|
||||
<UpIcon/>
|
||||
</button>
|
||||
<button
|
||||
@click="publishMQTTMsg(publishPrefix + 'goto', 'DOWN')">
|
||||
<DownIcon/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
122
frontend/vju_display/src/components/LightControl.vue
Normal file
122
frontend/vju_display/src/components/LightControl.vue
Normal file
|
@ -0,0 +1,122 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { $mqtt } from "vue-paho-mqtt";
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
id: Number,
|
||||
name: String,
|
||||
dimmable: Boolean,
|
||||
});
|
||||
|
||||
const topicPrefix = `${props.room}/lucke`
|
||||
|
||||
const status = reactive({
|
||||
brightness: 0,
|
||||
})
|
||||
|
||||
$mqtt.subscribe(`${topicPrefix}/brightness/${props.id}`, (message) => {
|
||||
status.brightness = parseInt(message);
|
||||
});
|
||||
|
||||
function doBrightness(brightness: Number) {
|
||||
$mqtt.publish(`${topicPrefix}/set/${props.id}`, brightness.toFixed(0), 'Fnr');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="props.dimmable">
|
||||
<label>{{props.name}}</label>
|
||||
<input type="range" min="0" max="100" step="10" :value="status.brightness" @input="doBrightness(parseInt(($event.target as HTMLInputElement).value))" >
|
||||
</div>
|
||||
<span v-else>
|
||||
<label>{{props.name}}</label>
|
||||
<button @click="doBrightness(status.brightness != 0 ? 0 : 100)" :class="{currentlyActive: status.brightness != 0 }">
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-flex
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: .5rem
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
flex: 1;
|
||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||
--width: 100%; /* Specific width is required for Firefox. */
|
||||
background: transparent; /* Otherwise white in Chrome */
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: lightgray;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: lightgray;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
|
||||
|
||||
input[type=range]::-moz-range-thumb {
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 32px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 32px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
margin-top: -14px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
|
||||
}
|
||||
</style>
|
|
@ -4,6 +4,7 @@ import {ref, computed} from 'vue'
|
|||
const emit = defineEmits(['submitPasscode'])
|
||||
defineProps([])
|
||||
|
||||
// TODO: unhardocde this shit
|
||||
const correctCode = '1337'
|
||||
const passcodeLength = correctCode.length
|
||||
const enteredCode = ref('')
|
||||
|
@ -45,14 +46,13 @@ const submit = () => {
|
|||
<div class="keypadFeedback">
|
||||
<span v-show="showStatus">Access Denied</span>
|
||||
<span v-show="!showStatus" v-for="(_, i) in passcodeLength" :key="i">
|
||||
<span class="">
|
||||
<span>
|
||||
{{ enteredCode[i] ? '•' : ' ' }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
|
||||
<div class="keypadButtons">
|
||||
<div class="keypadRow">
|
||||
<button @click="appendDigit('1')">1</button>
|
||||
|
@ -77,10 +77,6 @@ const submit = () => {
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <div v-if="status" class="">-->
|
||||
<!-- {{ status }}-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -89,19 +85,17 @@ const submit = () => {
|
|||
button {
|
||||
margin: 0.1em;
|
||||
flex: max-content;
|
||||
padding: 1.5em;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
align-self: inherit;
|
||||
justify-content: space-evenly;
|
||||
|
||||
}
|
||||
|
||||
.keypad {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 2em;
|
||||
margin: 1.5em;
|
||||
align-content: space-evenly;
|
||||
|
||||
}
|
||||
|
||||
.keypadRow {
|
||||
|
@ -115,11 +109,15 @@ button {
|
|||
.keypadFeedback {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
//border: 2px solid darkgray;
|
||||
background-color: lightgray;
|
||||
background-clip: border-box;
|
||||
font-size: 2em;
|
||||
min-height: 2em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
border: 1px solid #000000;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
</style>
|
||||
|
|
63
frontend/vju_display/src/components/ProjectorShutter.vue
Normal file
63
frontend/vju_display/src/components/ProjectorShutter.vue
Normal file
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { $mqtt } from "vue-paho-mqtt";
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
position: String,
|
||||
ctrltype: [String, null],
|
||||
});
|
||||
|
||||
const topicPrefix = `${props.room}/projectors/${props.position}`
|
||||
|
||||
const status = reactive({
|
||||
power: false,
|
||||
shutter: false,
|
||||
freeze: false,
|
||||
})
|
||||
|
||||
function bindBoolean(name: keyof typeof status) {
|
||||
$mqtt.subscribe(`${topicPrefix}/status/${name}`, (message) => {
|
||||
status[name] = message == "1";
|
||||
console.debug(name, "=", message == "1")
|
||||
});
|
||||
}
|
||||
|
||||
bindBoolean("power")
|
||||
bindBoolean("shutter")
|
||||
bindBoolean("freeze")
|
||||
|
||||
function doCommand(command: keyof typeof status, shutter: boolean) {
|
||||
$mqtt.publish(`${topicPrefix}/set/${command}`, shutter ? "1" : "0", 'Fnr');
|
||||
console.debug("SET SHUTTER", shutter)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="btns">
|
||||
<button @click="doCommand('shutter', !status.shutter)" :class="{ disabled: !status.power, currentlyActive: status.power && status.shutter }">
|
||||
SHUTTER
|
||||
</button>
|
||||
<button @click="doCommand('freeze', !status.freeze)" :class="{ disabled: !status.power, currentlyActive: status.power && status.freeze }">
|
||||
FREEZE
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btns {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: .5em;
|
||||
}
|
||||
button {
|
||||
padding: 1rem;
|
||||
min-width: 40%;
|
||||
}
|
||||
</style>
|
|
@ -42,6 +42,8 @@ function handleIncomingMQTT(topic: string, msg: string) {
|
|||
function handleProjectorStatus(typ: string, msg: string) {
|
||||
console.log('handling status')
|
||||
//console.log(projStatus)
|
||||
console.debug(props.room, projStatus.power, projStatus.shutter)
|
||||
|
||||
if (typ == 'power') { projStatus.power = msg == '1' }
|
||||
else if (typ == 'shutter') { projStatus.shutter = msg == '1' }
|
||||
else if (typ == 'freeze') { projStatus.freeze = msg == '1' }
|
||||
|
@ -62,7 +64,7 @@ function publishMQTTMsg(topic: string, msg: string) {
|
|||
|
||||
|
||||
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/command/')
|
||||
const publishPrefix = ref(props.room + '/projectors/' + props.position + '/set/')
|
||||
|
||||
//TODO organize better, binds, etc.
|
||||
|
||||
|
@ -80,18 +82,18 @@ const projStatus = reactive({
|
|||
<!-- TODO lepš -->
|
||||
<div>
|
||||
<h4>Power</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'power', (!projStatus.power ? '1' : '0'))">
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'power', (!projStatus.power ? '1' : '0'))" :class="{currentlyActive: projStatus.power}">
|
||||
Turn {{ projStatus.power ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
<div :class="{ disabled: !projStatus.power }">
|
||||
<div>
|
||||
<h4>Shutter</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'shutter', (!projStatus.shutter ? '1' : '0'))">
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'shutter', (!projStatus.shutter ? '1' : '0'))" :class="{currentlyActive: projStatus.shutter}">
|
||||
Turn {{ projStatus.shutter ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Freeze image</h4>
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'freeze', (!projStatus.freeze ? '1' : '0'))">
|
||||
<button @click="publishMQTTMsg(publishPrefix + 'freeze', (!projStatus.freeze ? '1' : '0'))" :class="{currentlyActive: projStatus.freeze}">
|
||||
Turn {{ projStatus.freeze ? 'OFF' : 'ON' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ import {ref, onMounted, reactive} from 'vue'
|
|||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import DownIcon from '../icons/DownIcon.vue';
|
||||
import UpIcon from '../icons/UpIcon.vue';
|
||||
import LightControl from '../LightControl.vue';
|
||||
|
||||
const props = defineProps({
|
||||
room: String,
|
||||
|
@ -85,6 +86,42 @@ function publishMQTTMsg(topic: string, msg: string) {
|
|||
|
||||
const publishPrefix = ref(props.room + '/')
|
||||
|
||||
// TODO: un-hard-code this
|
||||
const lights = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tabla",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 5,
|
||||
name: "Začetek",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 2,
|
||||
name: "Sredina",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 3,
|
||||
name: "Vrh",
|
||||
dimmable: true,
|
||||
}, {
|
||||
id: 4,
|
||||
name: "Vhod 1",
|
||||
dimmable: false,
|
||||
}, {
|
||||
id: 7,
|
||||
name: "Vhod 2",
|
||||
dimmable: false,
|
||||
// }, {
|
||||
// id: 6,
|
||||
// name: "Reflektorji 2 (ne dela)",
|
||||
// dimmable: false,
|
||||
}, {
|
||||
id: 8,
|
||||
name: "Stopnice",
|
||||
dimmable: false,
|
||||
}
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -93,7 +130,6 @@ const publishPrefix = ref(props.room + '/')
|
|||
|
||||
<div class="razsvetljava">
|
||||
<div style="display: flex">
|
||||
<h3>Razsvetljava</h3>
|
||||
</div>
|
||||
<div class="lightButtons" style="display: flex; flex-direction: column; align-items: stretch">
|
||||
<div style="display: flex;">
|
||||
|
@ -116,6 +152,10 @@ const publishPrefix = ref(props.room + '/')
|
|||
</div>
|
||||
|
||||
<div style="justify-content: center">
|
||||
<LightControl v-for="light in lights" :key="light.id" v-bind="light" :room="room" />
|
||||
</div>
|
||||
|
||||
<div style="justify-content: center" class="sencila">
|
||||
<h3>Senčila</h3>
|
||||
<button
|
||||
:class="{currentlyActive: (platnoStatus == 'MOVING_UP')}"
|
||||
|
@ -141,10 +181,13 @@ const publishPrefix = ref(props.room + '/')
|
|||
|
||||
<style scoped>
|
||||
|
||||
.sencila button {
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 0.1rem;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
|
||||
.lightButtons {
|
||||
|
@ -154,7 +197,7 @@ button {
|
|||
.lightButtons button {
|
||||
width: fit-content;
|
||||
flex: 1;
|
||||
height: 4em;
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, reactive} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
import Projektor from '../Projektor.vue';
|
||||
import Platno from '../Platno.vue';
|
||||
import Lift from '../Lift.vue';
|
||||
import LectureModule from '../LectureModule.vue';
|
||||
import AudioControlModule from '../AudioControlModule.vue';
|
||||
|
||||
|
@ -11,12 +6,6 @@ const props = defineProps({
|
|||
room: String
|
||||
})
|
||||
|
||||
const _glavni_position = ref('main')
|
||||
const _stranski_position = ref('side')
|
||||
|
||||
let _glavni = ref('main')
|
||||
let _stranski = ref('side')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -24,11 +13,11 @@ let _stranski = ref('side')
|
|||
<div style="display: flex; gap: 1rem; margin:inherit">
|
||||
<div style="width: 50%;">
|
||||
<h3>Glavni projektor</h3>
|
||||
<LectureModule :room="props.room" :position="_glavni"/>
|
||||
<LectureModule :room="props.room" position="main"/>
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
<h3>Stranski projektor</h3>
|
||||
<LectureModule :room="props.room" :position="_stranski"/>
|
||||
<LectureModule :room="props.room" position="side"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
|
@ -44,4 +33,4 @@ let _stranski = ref('side')
|
|||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
//import HelloWorld from './components/HelloWorld.vue'
|
||||
//import TheWelcome from './components/TheWelcome.vue'
|
||||
import {ref, onMounted, reactive, watch} from 'vue'
|
||||
import {$mqtt} from 'vue-paho-mqtt'
|
||||
// import Projektor from '../Projektor.vue'
|
||||
import Platno from '../Platno.vue'
|
||||
import Lift from '../Lift.vue';
|
||||
import Projektor from '../Projektor.vue';
|
||||
|
@ -63,6 +60,7 @@ watch(props, (ca) => {
|
|||
</div>
|
||||
<div></div>
|
||||
<ResetButton :room="props.room"/>
|
||||
<button onclick="window.location.reload()">RELOAD PAGE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,4 +71,4 @@ watch(props, (ca) => {
|
|||
</style>
|
||||
|
||||
<!-- scaling UIja, buttons as big as can, text as big (within reason), clear feedback, mqtt defaults to host font family noto sans, sans serif, chrome disable -->
|
||||
<!-- naj se odziva ono sam na platno status fuj fej -->
|
||||
<!-- naj se odziva ono sam na platno status fuj fej -->
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
selected: Boolean
|
||||
})
|
||||
selected: Boolean,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="tab" :class="{selected: props.selected}">
|
||||
<slot/>
|
||||
</div>
|
||||
<div class="tab" :class="{ selected: props.selected }">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -14,9 +14,13 @@ const props = defineProps({
|
|||
background-color: var(--color-brand-ul-medium-grey);
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab:hover {
|
||||
|
||||
|
||||
border: 1px solid #000000;
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-left: none;
|
||||
background: lightgray;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
.tab.selected {
|
||||
|
@ -24,13 +28,12 @@ const props = defineProps({
|
|||
}
|
||||
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: .3rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
|
||||
|
||||
<template>
|
||||
<div class="vertical-tabs">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tab:hover {
|
||||
|
||||
}
|
||||
|
||||
.vertical-tabs .tab {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vertical-tabs .tab {
|
||||
width: 100%;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
}
|
||||
|
||||
.vertical-tabs .tab:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -12,7 +12,7 @@ createApp(App)
|
|||
autoConnect: true,
|
||||
showNotifications: false,
|
||||
},
|
||||
|
||||
|
||||
MqttOptions: {
|
||||
//host: import.meta.env.VITE_MQTT_HOST,
|
||||
//host: "localhost",
|
||||
|
@ -24,6 +24,7 @@ createApp(App)
|
|||
clientId: `vju-${Math.random() * 9999}`,
|
||||
//mainTopic: '',
|
||||
enableMainTopic: false,
|
||||
|
||||
},
|
||||
})
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue