Narete stvari da delajo

This commit is contained in:
Miha Frangež 2025-09-12 17:43:50 +02:00
parent 64a10b0512
commit 29b2beca5a
45 changed files with 809 additions and 1600 deletions

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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>

View file

@ -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>

View 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>

View file

@ -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>

View 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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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>

View file

@ -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 -->

View file

@ -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>

View file

@ -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>

View file

@ -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,
},
})
)