zigbee2mqtt web panel migration [WIP]
This commit is contained in:
parent
1eedcaf2be
commit
db80240209
4 changed files with 459 additions and 207 deletions
|
@ -110,15 +110,16 @@
|
||||||
<div class="row value" v-for="(value, property) in displayedValues" :key="property">
|
<div class="row value" v-for="(value, property) in displayedValues" :key="property">
|
||||||
<div class="param-name">
|
<div class="param-name">
|
||||||
{{ value.description }}
|
{{ value.description }}
|
||||||
<span class="text" v-if="value.value?.x != null && value.value?.y != null">Color (XY coordinates)</span>
|
<span class="text" v-if="rgbColor != null && (value.value?.x != null && value.value?.y != null) ||
|
||||||
<span class="text" v-else-if="value.value?.hue != null && value.value?.saturation != null">Color (Hue/saturation)</span>
|
(value.value?.hue != null && value.value?.saturation != null)">Color</span>
|
||||||
<span class="name" v-text="value.property" v-if="value.property" />
|
<span class="name" v-text="value.property" v-if="value.property" />
|
||||||
<span class="unit" v-text="value.unit" v-if="value.unit" />
|
<span class="unit" v-text="value.unit" v-if="value.unit" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="param-value">
|
<div class="param-value">
|
||||||
<ToggleSwitch :value="value.value_on != null ? value.value === value.value_on : !!value.value"
|
<ToggleSwitch :value="value.value_on != null ? value.value === value.value_on : !!value.value"
|
||||||
v-if="value.type === 'binary'" @input="setValue(value, $event)" />
|
:disabled="!value.writable" v-if="value.type === 'binary'"
|
||||||
|
@input="setValue(value, $event)" />
|
||||||
|
|
||||||
<Slider :with-label="true" :range="[value.value_min, value.value_max]" :value="value.value"
|
<Slider :with-label="true" :range="[value.value_min, value.value_max]" :value="value.value"
|
||||||
:disabled="!value.writable" @change="setValue(value, $event)"
|
:disabled="!value.writable" @change="setValue(value, $event)"
|
||||||
|
@ -138,8 +139,10 @@
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label v-else-if="(value.value?.x != null && value.value?.y != null) || (value.value?.hue != null && value.value?.saturation != null)">
|
<label v-else-if="rgbColor != null && (value.value?.x != null && value.value?.y != null) ||
|
||||||
<!-- <input type="color" :value="rgbColor" @change.stop="onColorSelect" />-->
|
(value.value?.hue != null && value.value?.saturation != null)">
|
||||||
|
<input type="color" @change.stop="setValue(value, $event)"
|
||||||
|
:value="'#' + rgbColor.map((i) => { i = Number(i).toString(16); return i.length === 1 ? '0' + i : i }).join('')" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label v-else>
|
<label v-else>
|
||||||
|
@ -156,33 +159,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<!-- <div class="row" @click="removeDevice(false)">-->
|
<div class="row" @click="remove(false)">
|
||||||
<!-- <div class="param-name">Remove Device</div>-->
|
<div class="param-name">Remove Device</div>
|
||||||
<!-- <div class="param-value">-->
|
<div class="param-value">
|
||||||
<!-- <i class="fa fa-trash"></i>-->
|
<i class="fa fa-trash"></i>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
|
|
||||||
<!-- <div class="row error" @click="removeDevice(true)">-->
|
<div class="row error" @click="remove(true)">
|
||||||
<!-- <div class="param-name">Force Remove Device</div>-->
|
<div class="param-name">Force Remove Device</div>
|
||||||
<!-- <div class="param-value">-->
|
<div class="param-value">
|
||||||
<!-- <i class="fa fa-trash"></i>-->
|
<i class="fa fa-trash"></i>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
|
|
||||||
<!-- <div class="row" @click="banDevice">-->
|
|
||||||
<!-- <div class="param-name">Ban Device</div>-->
|
|
||||||
<!-- <div class="param-value">-->
|
|
||||||
<!-- <i class="fa fa-ban"></i>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
|
|
||||||
<!-- <div class="row" @click="whitelistDevice">-->
|
|
||||||
<!-- <div class="param-name">Whitelist Device</div>-->
|
|
||||||
<!-- <div class="param-value">-->
|
|
||||||
<!-- <i class="fa fa-list"></i>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -194,13 +183,13 @@ import Loading from "@/components/Loading";
|
||||||
import Slider from "@/components/elements/Slider";
|
import Slider from "@/components/elements/Slider";
|
||||||
import ToggleSwitch from "@/components/elements/ToggleSwitch";
|
import ToggleSwitch from "@/components/elements/ToggleSwitch";
|
||||||
import Utils from "@/Utils";
|
import Utils from "@/Utils";
|
||||||
// import {ColorConverter} from "@/components/panels/Light/color";
|
import {ColorConverter} from "@/components/panels/Light/color";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Device",
|
name: "Device",
|
||||||
components: {ToggleSwitch, Slider, Loading},
|
components: {ToggleSwitch, Slider, Loading},
|
||||||
mixins: [Utils],
|
mixins: [Utils],
|
||||||
emits: ['select', 'rename'],
|
emits: ['select', 'rename', 'remove'],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
device: {
|
device: {
|
||||||
|
@ -278,6 +267,32 @@ export default {
|
||||||
Object.entries(this.values).reduce(mergeValues, ret)
|
Object.entries(this.values).reduce(mergeValues, ret)
|
||||||
return ret
|
return ret
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rgbColor() {
|
||||||
|
if (!this.displayedValues.color)
|
||||||
|
return
|
||||||
|
|
||||||
|
const color = this.displayedValues.color.value
|
||||||
|
if (color.x != null && color.y != null) {
|
||||||
|
const converter = new ColorConverter({
|
||||||
|
bri: [this.displayedValues.brightness?.value_min || 0, this.displayedValues.brightness?.value_max || 255],
|
||||||
|
})
|
||||||
|
|
||||||
|
return converter.xyToRgb(color.x, color.y, this.displayedValues.brightness.value)
|
||||||
|
} else
|
||||||
|
if (color.hue != null && (color.saturation != null || color.sat != null)) {
|
||||||
|
const satAttr = color.saturation != null ? 'saturation' : 'sat'
|
||||||
|
const converter = new ColorConverter({
|
||||||
|
hue: [this.displayedValues.color.hue?.value_min || 0, this.displayedValues.color.hue.value_max || 65535],
|
||||||
|
sat: [this.displayedValues.color[satAttr]?.value_min || 0, this.displayedValues.color[satAttr].value_max || 255],
|
||||||
|
bri: [this.displayedValues.brightness?.value_min || 0, this.displayedValues.brightness?.value_max || 255],
|
||||||
|
})
|
||||||
|
|
||||||
|
return converter.hslToRgb(color.hue, color[satAttr], this.displayedValues.brightness.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -310,6 +325,24 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async remove(force) {
|
||||||
|
if (!confirm('Are you really sure that you want to remove this device from the network?'))
|
||||||
|
return
|
||||||
|
|
||||||
|
force = !!force
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
await this.request('zigbee.mqtt.device_remove', {
|
||||||
|
device: this.device.friendly_name?.length ? this.device.friendly_name : this.device.ieee_address,
|
||||||
|
force: force,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('remove', {device: this.device.friendly_name || this.device.ieee_address});
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async setValue(value, event) {
|
async setValue(value, event) {
|
||||||
const request = {
|
const request = {
|
||||||
device: this.device.friendly_name || this.device.ieee_address,
|
device: this.device.friendly_name || this.device.ieee_address,
|
||||||
|
@ -337,6 +370,46 @@ export default {
|
||||||
request.value = event.target.value
|
request.value = event.target.value
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
if ((value.x != null && value.y != null) || (value.hue != null && (value.saturation != null || value.sat != null))) {
|
||||||
|
request.property = 'color'
|
||||||
|
const rgb = event.target.value.slice(1)
|
||||||
|
.split(/([0-9a-fA-F]{2})/)
|
||||||
|
.filter((_, i) => i % 2)
|
||||||
|
.map((i) => parseInt(i, 16))
|
||||||
|
|
||||||
|
if ((value.x != null && value.y != null)) {
|
||||||
|
const converter = new ColorConverter({
|
||||||
|
bri: [this.displayedValues.brightness?.value_min || 0, this.displayedValues.brightness?.value_max || 255],
|
||||||
|
})
|
||||||
|
|
||||||
|
const xy = converter.rgbToXY(...rgb)
|
||||||
|
request.value = {
|
||||||
|
color: {
|
||||||
|
x: xy[0],
|
||||||
|
y: xy[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const satAttr = this.displayedValues.color.saturation != null ? 'saturation' : 'sat'
|
||||||
|
const converter = new ColorConverter({
|
||||||
|
hue: [this.displayedValues.color.hue?.value_min || 0, this.displayedValues.color.hue.value_max || 65535],
|
||||||
|
sat: [this.displayedValues.color[satAttr]?.value_min || 0, this.displayedValues.color[satAttr].value_max || 255],
|
||||||
|
bri: [this.displayedValues.brightness?.value_min || 0, this.displayedValues.brightness?.value_max || 255],
|
||||||
|
})
|
||||||
|
|
||||||
|
const hsl = converter.rgbToHsl(...rgb)
|
||||||
|
request.value = {
|
||||||
|
brightness: hsl[2],
|
||||||
|
color: {
|
||||||
|
hue: hsl[0],
|
||||||
|
'`${satAttr}': hsl[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.value == null)
|
if (request.value == null)
|
||||||
|
@ -364,135 +437,4 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "common";
|
@import "common";
|
||||||
|
|
||||||
.item {
|
|
||||||
&.selected {
|
|
||||||
box-shadow: $selected-item-box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name.header {
|
|
||||||
padding: 1em !important;
|
|
||||||
cursor: pointer;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: .06em;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
border-radius: 1.5em;
|
|
||||||
}
|
|
||||||
&.selected {
|
|
||||||
background: $selected-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 1.2em;
|
|
||||||
padding-left: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $hover-bg;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background: $selected-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
border-bottom: $item-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-radius: 1.5em 1.5em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-radius: 0 0 1.5em 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.params {
|
|
||||||
.section {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
.param-name {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: .8em;
|
|
||||||
text-transform: unset;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '[';
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit {
|
|
||||||
font-size: .8em;
|
|
||||||
&:before {
|
|
||||||
content: ' [unit: ';
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-value {
|
|
||||||
label {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: 0;
|
|
||||||
background: none;
|
|
||||||
padding: 0 .5em;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $default-hover-fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-edit {
|
|
||||||
width: 100%;
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: right;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
form {
|
|
||||||
width: 100%;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items:center;
|
|
||||||
justify-content: right;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: right;
|
|
||||||
margin: 0 0 0 .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<div class="item group" :class="{selected: selected}">
|
||||||
|
<Loading v-if="loading" />
|
||||||
|
<div class="row name header vertical-center" :class="{selected: selected}"
|
||||||
|
v-text="group.friendly_name" @click="$emit('select')" />
|
||||||
|
|
||||||
|
<div class="params" v-if="selected">
|
||||||
|
<div class="section values">
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">Values</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<!-- <div class="row" v-for="(value, name) in properties" :key="name">-->
|
||||||
|
<!-- <div class="param-name" v-text="name"></div>-->
|
||||||
|
<!-- <div class="param-value">-->
|
||||||
|
<!-- <div v-if="name === 'state'">-->
|
||||||
|
<!-- <toggle-switch :value="value" @toggled="toggleState"></toggle-switch>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div v-else>-->
|
||||||
|
<!-- <input type="text" :value="value" :data-name="name" @change="setValue">-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="section devices">-->
|
||||||
|
<!-- <div class="header">-->
|
||||||
|
<!-- <div class="title col-10">Devices</div>-->
|
||||||
|
<!-- <div class="buttons col-2">-->
|
||||||
|
<!-- <button class="btn btn-default" title="Add Devices" @click="bus.$emit('openAddToGroupModal')">-->
|
||||||
|
<!-- <i class="fa fa-plus"></i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- <div class="body">-->
|
||||||
|
<!-- <div class="row" v-for="device in group.devices">-->
|
||||||
|
<!-- <div class="col-10" v-text="device.friendly_name"></div>-->
|
||||||
|
<!-- <div class="buttons col-2">-->
|
||||||
|
<!-- <button class="btn btn-default" title="Remove from group" @click="removeFromGroup(device.friendly_name)">-->
|
||||||
|
<!-- <i class="fa fa-trash"></i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<div class="section actions">
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">Actions</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<div class="row" @click="rename">
|
||||||
|
<div class="col-10">Rename Group</div>
|
||||||
|
<div class="buttons col-2 pull-right">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" @click="remove">
|
||||||
|
<div class="col-10">Remove Group</div>
|
||||||
|
<div class="buttons col-2 pull-right">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Loading from "@/components/Loading";
|
||||||
|
// import ToggleSwitch from "@/components/elements/ToggleSwitch";
|
||||||
|
import Utils from "@/Utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Group",
|
||||||
|
emits: ['select', 'remove'],
|
||||||
|
mixins: [Utils],
|
||||||
|
// components: {Loading, ToggleSwitch},
|
||||||
|
components: {Loading},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
group: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
selected: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async remove() {
|
||||||
|
if (!confirm('Are you sure that you want to remove this group?'))
|
||||||
|
return
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
await this.request('zigbee.mqtt.group_remove', {name: this.group.friendly_name})
|
||||||
|
this.$emit('remove', {name: this.group.friendly_name})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async rename() {
|
||||||
|
const name = prompt('New group name', this.group.friendly_name).trim()
|
||||||
|
if (!name.length)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.request('zigbee.mqtt.group_rename', {
|
||||||
|
group: this.group.friendly_name || this.group.id,
|
||||||
|
name: name,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('rename', {name: this.group.friendly_name, newName: name})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "common";
|
||||||
|
</style>
|
|
@ -23,10 +23,7 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Dropdown ref="networkCommandsDropdown" icon-class="fa fa-cog" title="Network commands">
|
<Dropdown ref="networkCommandsDropdown" icon-class="fa fa-cog" title="Network commands">
|
||||||
<DropdownItem text="Start Network" :disabled="loading" @click="startNetwork" />
|
|
||||||
<DropdownItem text="Stop Network" :disabled="loading" @click="stopNetwork" />
|
|
||||||
<DropdownItem text="Permit Join" :disabled="loading" @click="permitJoin(true)" />
|
<DropdownItem text="Permit Join" :disabled="loading" @click="permitJoin(true)" />
|
||||||
<DropdownItem text="Reset" :disabled="loading" @click="reset" />
|
|
||||||
<DropdownItem text="Factory Reset" :disabled="loading" @click="factoryReset" />
|
<DropdownItem text="Factory Reset" :disabled="loading" @click="factoryReset" />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
|
@ -43,10 +40,10 @@
|
||||||
<div class="empty" v-else>No devices found on the network</div>
|
<div class="empty" v-else>No devices found on the network</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ZigbeeDevice v-for="(device, id) in devices" :key="id"
|
<Device v-for="(device, id) in devices" :key="id"
|
||||||
:device="device" :selected="selected.deviceId === id"
|
:device="device" :selected="selected.deviceId === id"
|
||||||
@select="selected.deviceId = selected.deviceId === id ? null : id"
|
@select="selected.deviceId = selected.deviceId === id ? null : id"
|
||||||
@rename="refreshDevices" />
|
@rename="refreshDevices" @remove="refreshDevices" />
|
||||||
|
|
||||||
<!-- <dropdown ref="addToGroupDropdown" :items="addToGroupDropdownItems"></dropdown>-->
|
<!-- <dropdown ref="addToGroupDropdown" :items="addToGroupDropdownItems"></dropdown>-->
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,16 +54,13 @@
|
||||||
<div class="empty" v-else>No groups available on the network</div>
|
<div class="empty" v-else>No groups available on the network</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <zigbee-group-->
|
<Group v-for="(group, id) in groups" :key="id" :group="group"
|
||||||
<!-- v-for="(group, groupId) in groups"-->
|
:selected="selected.groupId === id"
|
||||||
<!-- :key="groupId"-->
|
@select="selected.groupId = selected.groupId === id ? null : id"
|
||||||
<!-- :group="group"-->
|
@rename="refreshGroups" @remove="refreshGroups" />
|
||||||
<!-- :selected="selected.groupId === groupId"-->
|
|
||||||
<!-- :bus="bus">-->
|
|
||||||
<!-- </zigbee-group>-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -75,11 +69,12 @@ import DropdownItem from "@/components/elements/DropdownItem"
|
||||||
import Loading from "@/components/Loading"
|
import Loading from "@/components/Loading"
|
||||||
import Utils from "@/Utils"
|
import Utils from "@/Utils"
|
||||||
|
|
||||||
import ZigbeeDevice from "@/components/panels/ZigbeeMqtt/Device";
|
import Device from "@/components/panels/ZigbeeMqtt/Device";
|
||||||
|
import Group from "@/components/panels/ZigbeeMqtt/Group";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ZigbeeMqtt",
|
name: "ZigbeeMqtt",
|
||||||
components: {Dropdown, DropdownItem, Loading, ZigbeeDevice},
|
components: {Dropdown, DropdownItem, Loading, Device, Group},
|
||||||
mixins: [Utils],
|
mixins: [Utils],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -208,12 +203,8 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async permitJoin(permit) {
|
async permitJoin(permit) {
|
||||||
let seconds = prompt('Join allow period in seconds (type 0 for no time limits)', '60')
|
let seconds = prompt('Join allow period in seconds (0 or empty for no time limits)', '60')
|
||||||
if (!seconds) {
|
seconds = seconds.length ? parseInt(seconds) : null
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
seconds = parseInt(seconds)
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -223,19 +214,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async reset() {
|
|
||||||
if (!confirm('Are you sure that you want to reset the device?')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true
|
|
||||||
try {
|
|
||||||
await this.request('zigbee.mqtt.reset')
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async factoryReset() {
|
async factoryReset() {
|
||||||
if (!confirm('Are you SURE that you want to do a device factory reset?')) {
|
if (!confirm('Are you SURE that you want to do a device factory reset?')) {
|
||||||
if (!confirm('Are you REALLY sure? ALL network information and custom firmware will be lost!!'))
|
if (!confirm('Are you REALLY sure? ALL network information and custom firmware will be lost!!'))
|
||||||
|
@ -439,19 +417,39 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding-top: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.view {
|
.view {
|
||||||
min-width: 400pt;
|
|
||||||
max-width: 750pt;
|
|
||||||
height: max-content;
|
height: max-content;
|
||||||
background: $view-bg;
|
background: $view-bg;
|
||||||
border: $view-border;
|
border: $view-border;
|
||||||
border-radius: 1.5em;
|
|
||||||
box-shadow: $view-box-shadow;
|
box-shadow: $view-box-shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
.view {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $tablet) {
|
||||||
|
.view {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $desktop) {
|
||||||
|
.view {
|
||||||
|
min-width: 400pt;
|
||||||
|
max-width: 750pt;
|
||||||
|
border-radius: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-container {
|
||||||
|
padding-top: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.params {
|
.params {
|
||||||
background: $params-bg;
|
background: $params-bg;
|
||||||
padding-bottom: 1em;
|
padding-bottom: 1em;
|
||||||
|
|
|
@ -20,12 +20,183 @@
|
||||||
background: $hover-bg;
|
background: $hover-bg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
&.selected {
|
||||||
|
box-shadow: $selected-item-box-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name.header {
|
||||||
|
padding: 1em !important;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .06em;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-radius: 1.5em;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
background: $selected-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $hover-bg;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: $selected-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: $item-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 1.5em 1.5em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 0 1.5em 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.params {
|
||||||
|
.section {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
.param-name {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: .8em;
|
||||||
|
text-transform: unset;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '[';
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: .8em;
|
||||||
|
&:before {
|
||||||
|
content: ' [unit: ';
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-value {
|
||||||
|
label {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
padding: 0 .5em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $default-hover-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
.name-edit {
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $tablet) {
|
||||||
|
.name-edit {
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-edit {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content: right;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: right;
|
||||||
|
margin: 0 0 0 .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
.param-name {
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-value {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 1%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $tablet) {
|
||||||
|
.param-name {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-value {
|
||||||
|
width: 58%;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.param-name {
|
.param-name {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
width: 40%;
|
|
||||||
margin-left: 1%;
|
margin-left: 1%;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
letter-spacing: .04em;
|
letter-spacing: .04em;
|
||||||
|
@ -33,8 +204,6 @@
|
||||||
|
|
||||||
.param-value {
|
.param-value {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
width: 58%;
|
|
||||||
justify-content: right;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.value-edit {
|
.value-edit {
|
||||||
|
|
Loading…
Reference in a new issue