[#340] Added `alarm` UI panel.

This commit is contained in:
Fabio Manganiello 2023-12-10 15:33:19 +01:00
parent 62054e83cc
commit dfa0727289
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
2 changed files with 207 additions and 8 deletions

View File

@ -0,0 +1,174 @@
<template>
<Loading v-if="loading" />
<NoItems v-else-if="!Object.keys(alarms).length">
No alarms configured
</NoItems>
<div class="alarms-container" v-else>
<div class="alarms items">
<div class="item" v-for="alarm in alarms" :key="alarm.external_id">
<Entity :value="alarm" @show-modal="selectedAlarm = alarm.external_id" />
</div>
</div>
</div>
<EntityModal
:entity="alarms[selectedAlarm]"
:visible="modalVisible"
:config-values="{}"
@close="selectedAlarm = null"
v-if="modalVisible" />
<FloatingButton icon-class="fa fa-plus" text="Add Alarm"
@click="addAlarmModalVisible = true" />
</template>
<script>
import Utils from "@/Utils";
import Loading from "@/components/Loading";
import EntityModal from "@/components/panels/Entities/Modal";
import Entity from "@/components/panels/Entities/Entity";
import FloatingButton from "@/components/elements/FloatingButton";
import NoItems from "@/components/elements/NoItems";
export default {
components: {Entity, EntityModal, FloatingButton, Loading, NoItems},
mixins: [Utils],
props: {
pluginName: {
type: String,
},
config: {
type: Object,
default: () => {},
},
},
data() {
return {
loading: false,
addAlarmModalVisible: false,
alarms: {},
selectedAlarm: null,
}
},
computed: {
modalVisible() {
return this.alarms[this.selectedAlarm] != null
},
},
methods: {
addAlarm(alarm) {
alarm.name = alarm?.meta?.name_override || alarm.name
alarm.meta = {
...alarm.meta,
icon: {
'class': (alarm.meta?.icon?.['class'] || 'fas fa-stopwatch'),
},
}
this.alarms[alarm.external_id] = alarm
},
async refresh() {
this.$emit('loading', true)
try {
(await this.request('entities.get', {plugins: [this.pluginName]})).forEach(
entity => this.addAlarm(entity)
)
} finally {
this.$emit('loading', false)
}
},
async onEntityUpdate(msg) {
const entity = msg?.entity
if (entity?.plugin !== this.pluginName)
return
this.addAlarm(entity)
},
async onEntityDelete(msg) {
const entity = msg?.entity
if (entity?.plugin !== this.pluginName)
return
if (this.selectedAlarm === entity.external_id)
this.selectedAlarm = null
if (this.alarms[entity.external_id])
delete this.alarms[entity.external_id]
},
},
mounted() {
this.refresh()
this.subscribe(
this.onEntityUpdate,
'on-alarm-entity-update',
'platypush.message.event.entities.EntityUpdateEvent'
)
this.subscribe(
this.onEntityDelete,
'on-alarm-entity-delete',
'platypush.message.event.entities.EntityDeleteEvent'
)
},
unmounted() {
this.unsubscribe('on-alarm-entity-update')
this.unsubscribe('on-alarm-entity-delete')
},
}
</script>
<style lang="scss" scoped>
.alarms-container {
display: flex;
height: 100%;
background: $default-bg-6;
flex-grow: 1;
overflow-y: auto;
justify-content: center;
.alarms {
@include until($tablet) {
width: 100%;
}
@include from($tablet) {
width: calc(100% - 2em);
margin-top: 1em;
border-radius: 1em;
}
max-width: 800px;
background: $default-bg-2;
display: flex;
flex-direction: column;
margin-bottom: auto;
box-shadow: $border-shadow-bottom-right;
:deep(.item) {
.entity-container {
&:first-child {
border-top-left-radius: 1em;
border-top-right-radius: 1em;
}
&:last-child {
border-bottom-left-radius: 1em;
border-bottom-right-radius: 1em;
}
}
}
}
}
</style>

View File

@ -5,11 +5,11 @@
<EntityIcon :entity="value" :loading="loading" :error="error" />
</div>
<div class="label col-6">
<div class="label col-5">
<div class="name" v-text="value.name" />
</div>
<div class="value-and-toggler col-7" @click.stop="collapsed = !collapsed">
<div class="value-and-toggler col-8" @click.stop="collapsed = !collapsed">
<div class="value" v-if="!value.enabled">Disabled</div>
<div class="value" v-else-if="isRunning">Running</div>
<div class="value" v-else-if="isSnoozed">Snoozed</div>
@ -27,22 +27,22 @@
<div class="body children" v-if="!collapsed" @click.stop="prevent">
<div class="child">
<label :for="enableInputId" class="label">
<div class="name col-s-12 col-m-6">Enabled</div>
<div class="value col-s-12 col-m-6">
<div class="name col-6">Enabled</div>
<div class="value col-6">
<ToggleSwitch :id="enableInputId" :value="value.enabled" @input="setEnabled" />
</div>
</label>
</div>
<div class="child buttons" v-if="isRunning || isSnoozed">
<label :for="snoozeInputId" class="label col-s-12 col-m-6" v-if="isRunning">
<label :for="snoozeInputId" class="label col-6" v-if="isRunning">
<div class="value">
<button class="btn btn-default" @click="snooze">Snooze</button>
</div>
</label>
<label :for="dismissInputId" class="label col-s-12"
:class="{'col-m-6': isRunning, 'col-m-12': isSnoozed}">
<label :for="dismissInputId" class="label"
:class="{'col-6': isRunning, 'col-12': !isRunning}">
<div class="value">
<button class="btn btn-default" @click="dismiss">Dismiss</button>
</div>
@ -107,7 +107,7 @@ export default {
await this.request(
'alarm.set_enabled',
{
name: this.value.name,
name: this.value.external_id,
enabled: !this.value.enabled,
}
)
@ -138,6 +138,31 @@ export default {
e.stopPropagation()
},
},
mounted() {
this.$watch(
() => this.value,
(newValue, oldValue) => {
if (newValue?.state !== oldValue?.state) {
const notif = {image: {icon: 'stopwatch'}}
switch (newValue?.state) {
case 'RUNNING':
notif.text = `Alarm ${newValue.name} is running`
break
case 'SNOOZED':
notif.text = `Alarm ${newValue.name} has been snoozed`
break
case 'DISMISSED':
notif.text = `Alarm ${newValue.name} has been dismissed`
break
}
if (notif.text)
this.notify(notif)
}
}
)
},
}
</script>