mqttdevicemanager/console/src/components/dashboard/DevicesWidget.vue

95 lines
2.7 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue'
import { inject } from 'vue'
const mqttRef = inject('mqtt')
const emit = defineEmits(['select'])
const devices = ref([])
const deviceMap = ref({}) // map deviceId => device object with all meta properties
const selected = ref(null)
// Add or update a device's meta property
function upsertDevice(deviceId, property, value) {
if (!deviceMap.value[deviceId]) {
deviceMap.value[deviceId] = {
id: deviceId,
meta: {}, // stores all meta properties
title: deviceId,
value: '', // main status display
subtitle: 'MQTT Device',
icon: 'pi-box',
}
}
// update the property
deviceMap.value[deviceId].meta[property] = value
// update card display values (example: show 'status' as main value)
if (property === 'status') {
deviceMap.value[deviceId].value = value.replace(/['"]+/g, '')
deviceMap.value[deviceId].icon = deviceMap.value[deviceId].value === 'ONLINE' ? 'pi-check-circle' : 'pi-times-circle'
}
// rebuild reactive array for rendering
devices.value = Object.values(deviceMap.value)
}
function selectDevice(device) {
selected.value = device.id
emit('select', device.id)
}
onMounted(() => {
mqttRef.value.subscribe('device/+/meta/+', (payload, topic) => {
const parts = topic.split('/')
const deviceId = parts[1]
const property = parts.slice(3).join('/') // handles nested properties if any
upsertDevice(deviceId, property, payload)
})
})
</script>
<template>
<div class="layout-grid-row">
<div
v-for="device in devices"
:key="device.id"
class="layout-card device-card"
:class="{ 'selected-device': device.id === selected }"
@click="selectDevice(device)"
>
<div class="stats-header">
<span class="stats-title">{{ device.meta['type'] }}</span>
<span :class="[ device.value === 'OFFLINE' ? 'offline' : '', 'stats-icon-box']">
<i
:class="['pi', device.icon]"
:style="{ color: device.value === 'ONLINE' ? 'inherit' : 'var(--p-secondary-color)' }"
></i>
</span>
</div>
<div class="stats-content">
<div class="stats-value">{{ device.meta['name'] }}</div>
<div class="stats-subtitle"><span class='italic'>{{ device.id }}</span> on <span class="font-bold">{{ device.meta['host'] }}</span></div>
<div class="stats-subtitle italic text-green-500"></div>
</div>
</div>
</div>
</template>
<style scoped>
.device-card {
cursor: pointer;
transition: all 0.2s ease;
}
.device-card:hover {
transform: translateY(-2px);
}
.selected-device {
border: 2px solid var(--p-primary-color);
box-shadow: 0 0 8px var(--p-primary-color);
}
</style>