forked from platypush/platypush
Added support for values in Z-Wave web panel (see #123)
This commit is contained in:
parent
a0ceb560b4
commit
98727c4f31
9 changed files with 356 additions and 144 deletions
|
@ -17,6 +17,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
.no-items {
|
.no-items {
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
|
@ -156,9 +157,71 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 58%;
|
width: 58%;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
|
.value-edit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-data {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: .8em;
|
||||||
|
margin-left: 1em;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numeric {
|
||||||
|
input.slider {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
text-align: right;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
background: none;
|
||||||
|
&:hover {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-min, .value-max {
|
||||||
|
width: 50%;
|
||||||
|
font-size: .85em;
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-min {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-max {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-value-name-edit {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
.section {
|
.section {
|
||||||
.header {
|
.header {
|
||||||
|
|
|
@ -6,7 +6,8 @@ Vue.component('toggle-switch', {
|
||||||
toggled: function(event) {
|
toggled: function(event) {
|
||||||
this.$emit('toggled', {
|
this.$emit('toggled', {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
value: !this.value
|
value: !this.value,
|
||||||
|
event: event,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,31 +10,30 @@ Vue.component('zwave', {
|
||||||
nodes: {},
|
nodes: {},
|
||||||
groups: {},
|
groups: {},
|
||||||
scenes: {},
|
scenes: {},
|
||||||
values: {},
|
|
||||||
switches: new Set(),
|
|
||||||
dimmers: new Set(),
|
|
||||||
sensors: new Set(),
|
|
||||||
batteryLevels: new Set(),
|
|
||||||
powerLevels: new Set(),
|
|
||||||
bulbs: new Set(),
|
|
||||||
doorlocks: new Set(),
|
|
||||||
usercodes: new Set(),
|
|
||||||
thermostats: new Set(),
|
|
||||||
protections: new Set(),
|
|
||||||
commandRunning: false,
|
commandRunning: false,
|
||||||
|
values: {
|
||||||
|
switches: {},
|
||||||
|
dimmers: {},
|
||||||
|
sensors: {},
|
||||||
|
battery_levels: {},
|
||||||
|
power_levels: {},
|
||||||
|
bulbs: {},
|
||||||
|
doorlocks: {},
|
||||||
|
usercodes: {},
|
||||||
|
thermostats: {},
|
||||||
|
protections: {},
|
||||||
|
},
|
||||||
selected: {
|
selected: {
|
||||||
view: 'nodes',
|
view: 'nodes',
|
||||||
nodeId: undefined,
|
nodeId: undefined,
|
||||||
groupId: undefined,
|
groupId: undefined,
|
||||||
sceneId: undefined,
|
sceneId: undefined,
|
||||||
valueId: undefined,
|
|
||||||
},
|
},
|
||||||
loading: {
|
loading: {
|
||||||
status: false,
|
status: false,
|
||||||
nodes: false,
|
nodes: false,
|
||||||
groups: false,
|
groups: false,
|
||||||
scenes: false,
|
scenes: false,
|
||||||
values: false,
|
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
networkInfo: {
|
networkInfo: {
|
||||||
|
@ -198,28 +197,11 @@ Vue.component('zwave', {
|
||||||
methods: {
|
methods: {
|
||||||
refreshNodes: async function () {
|
refreshNodes: async function () {
|
||||||
this.loading.nodes = true;
|
this.loading.nodes = true;
|
||||||
this.loading.values = true;
|
|
||||||
|
|
||||||
this.nodes = await request('zwave.get_nodes');
|
this.nodes = await request('zwave.get_nodes');
|
||||||
this.loading.nodes = false;
|
|
||||||
|
|
||||||
this.values = Object.values(this.nodes).reduce((values, node) => {
|
if (Object.keys(this.nodes).length) {
|
||||||
values = {
|
Vue.set(this.views, 'values', true);
|
||||||
...Object.values(node.values).reduce((values, value) => {
|
}
|
||||||
values[value.value_id] = {
|
|
||||||
node_id: node.node_id,
|
|
||||||
...value,
|
|
||||||
};
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}, {}),
|
|
||||||
...values
|
|
||||||
};
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
this.loading.values = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshGroups: async function () {
|
refreshGroups: async function () {
|
||||||
|
@ -250,93 +232,16 @@ Vue.component('zwave', {
|
||||||
this.loading.scenes = false;
|
this.loading.scenes = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshSwitches: async function () {
|
refreshValues: async function(type) {
|
||||||
this.switches = new Set(Object.values(await request('zwave.get_switches'))
|
Vue.set(this.values, type, Object.values(await request('zwave.get_' + type))
|
||||||
.filter((sw) => sw.id_on_network).map((sw) => sw.value_id));
|
.filter((item) => item.id_on_network)
|
||||||
|
.reduce((values, value) => {
|
||||||
|
values[value.id_on_network] = true;
|
||||||
|
return values;
|
||||||
|
}, {}));
|
||||||
|
|
||||||
if (this.switches.size) {
|
if (Object.keys(this.values[type]).length) {
|
||||||
Vue.set(this.views, 'switches', true);
|
Vue.set(this.views, type, true);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshDimmers: async function () {
|
|
||||||
this.dimmers = new Set(Object.values(await request('zwave.get_dimmers'))
|
|
||||||
.filter((dimmer) => dimmer.id_on_network).map((dimmer) => dimmer.value_id));
|
|
||||||
|
|
||||||
if (this.dimmers.size) {
|
|
||||||
Vue.set(this.views, 'dimmers', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshSensors: async function () {
|
|
||||||
this.sensors = new Set(Object.values(await request('zwave.get_sensors'))
|
|
||||||
.filter((sensor) => sensor.id_on_network).map((sensor) => sensor.value_id));
|
|
||||||
|
|
||||||
if (this.sensors.size) {
|
|
||||||
Vue.set(this.views, 'sensors', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshBatteryLevels: async function () {
|
|
||||||
this.batteryLevels = new Set(Object.values(await request('zwave.get_battery_levels'))
|
|
||||||
.filter((battery) => battery.id_on_network).map((battery) => battery.value_id));
|
|
||||||
|
|
||||||
if (this.batteryLevels.size) {
|
|
||||||
Vue.set(this.views, 'batteryLevels', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshPowerLevels: async function () {
|
|
||||||
this.powerLevels = new Set(Object.values(await request('zwave.get_power_levels'))
|
|
||||||
.filter((power) => power.id_on_network).map((power) => power.value_id));
|
|
||||||
|
|
||||||
if (this.powerLevels.size) {
|
|
||||||
Vue.set(this.views, 'powerLevels', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshBulbs: async function () {
|
|
||||||
this.bulbs = new Set(Object.values(await request('zwave.get_bulbs'))
|
|
||||||
.filter((bulb) => bulb.id_on_network).map((bulb) => bulb.value_id));
|
|
||||||
|
|
||||||
if (this.bulbs.size) {
|
|
||||||
Vue.set(this.views, 'bulbs', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshDoorlocks: async function () {
|
|
||||||
this.doorlocks = new Set(Object.values(await request('zwave.get_doorlocks'))
|
|
||||||
.filter((lock) => lock.id_on_network).map((lock) => lock.value_id));
|
|
||||||
|
|
||||||
if (this.doorlocks.size) {
|
|
||||||
Vue.set(this.views, 'doorlocks', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshUsercodes: async function () {
|
|
||||||
this.doorlocks = new Set(Object.values(await request('zwave.get_usercodes'))
|
|
||||||
.filter((code) => code.id_on_network).map((code) => code.value_id));
|
|
||||||
|
|
||||||
if (this.usercodes.size) {
|
|
||||||
Vue.set(this.views, 'usercodes', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshThermostats: async function () {
|
|
||||||
this.thermostats = new Set(Object.values(await request('zwave.get_thermostats'))
|
|
||||||
.filter((th) => th.id_on_network).map((th) => th.value_id));
|
|
||||||
|
|
||||||
if (this.thermostats.size) {
|
|
||||||
Vue.set(this.views, 'thermostats', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshProtections: async function () {
|
|
||||||
this.protections = new Set(Object.values(await request('zwave.get_protections'))
|
|
||||||
.filter((p) => p.id_on_network).map((p) => p.value_id));
|
|
||||||
|
|
||||||
if (this.protections.size) {
|
|
||||||
Vue.set(this.views, 'protections', true);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -355,16 +260,17 @@ Vue.component('zwave', {
|
||||||
this.refreshNodes();
|
this.refreshNodes();
|
||||||
this.refreshGroups();
|
this.refreshGroups();
|
||||||
this.refreshScenes();
|
this.refreshScenes();
|
||||||
this.refreshSwitches();
|
this.refreshValues('switches');
|
||||||
this.refreshDimmers();
|
this.refreshValues('dimmers');
|
||||||
this.refreshSensors();
|
this.refreshValues('sensors');
|
||||||
this.refreshBulbs();
|
this.refreshValues('bulbs');
|
||||||
this.refreshDoorlocks();
|
this.refreshValues('doorlocks');
|
||||||
this.refreshUsercodes();
|
this.refreshValues('usercodes');
|
||||||
this.refreshThermostats();
|
this.refreshValues('thermostats');
|
||||||
this.refreshProtections();
|
this.refreshValues('protections');
|
||||||
this.refreshBatteryLevels();
|
this.refreshValues('battery_levels');
|
||||||
this.refreshPowerLevels();
|
this.refreshValues('power_levels');
|
||||||
|
this.refreshValues('node_config');
|
||||||
this.refreshStatus();
|
this.refreshStatus();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -428,6 +334,8 @@ Vue.component('zwave', {
|
||||||
|
|
||||||
created: function() {
|
created: function() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
this.bus.$on('refresh', this.refresh);
|
||||||
|
this.bus.$on('refreshNodes', this.refreshNodes);
|
||||||
this.bus.$on('nodeClicked', this.onNodeClicked);
|
this.bus.$on('nodeClicked', this.onNodeClicked);
|
||||||
this.bus.$on('groupClicked', this.onGroupClicked);
|
this.bus.$on('groupClicked', this.onGroupClicked);
|
||||||
this.bus.$on('openAddToGroupModal', () => {self.modal.group.visible = true});
|
this.bus.$on('openAddToGroupModal', () => {self.modal.group.visible = true});
|
||||||
|
@ -447,7 +355,11 @@ Vue.component('zwave', {
|
||||||
'platypush.message.event.zwave.ZwaveNodeEvent',
|
'platypush.message.event.zwave.ZwaveNodeEvent',
|
||||||
'platypush.message.event.zwave.ZwaveNodeAddedEvent',
|
'platypush.message.event.zwave.ZwaveNodeAddedEvent',
|
||||||
'platypush.message.event.zwave.ZwaveNodeRenamedEvent',
|
'platypush.message.event.zwave.ZwaveNodeRenamedEvent',
|
||||||
'platypush.message.event.zwave.ZwaveNodeReadyEvent');
|
'platypush.message.event.zwave.ZwaveNodeReadyEvent',
|
||||||
|
'platypush.message.event.zwave.ZwaveValueAddedEvent',
|
||||||
|
'platypush.message.event.zwave.ZwaveValueChangedEvent',
|
||||||
|
'platypush.message.event.zwave.ZwaveValueRemovedEvent',
|
||||||
|
'platypush.message.event.zwave.ZwaveValueRefreshedEvent');
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
|
|
73
platypush/backend/http/static/js/plugins/zwave/value.js
Normal file
73
platypush/backend/http/static/js/plugins/zwave/value.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
Vue.component('zwave-value', {
|
||||||
|
template: '#tmpl-zwave-value',
|
||||||
|
props: ['node','bus','selected','values'],
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onNodeClicked: function() {
|
||||||
|
this.bus.$emit('nodeClicked', {
|
||||||
|
nodeId: this.node.node_id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
disableForm: function(form) {
|
||||||
|
form.querySelector('input,button').readOnly = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
enableForm: function(form) {
|
||||||
|
form.querySelector('input,button').readOnly = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditMode: function(mode) {
|
||||||
|
Vue.set(this.editMode, mode, true);
|
||||||
|
const form = this.$refs[mode + 'Form'];
|
||||||
|
const input = form.querySelector('input[type=text]');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
input.focus();
|
||||||
|
input.select();
|
||||||
|
}, 10);
|
||||||
|
},
|
||||||
|
|
||||||
|
editName: function(event) {
|
||||||
|
const value = this.node.values[event.target.parentElement.dataset.idOnNetwork];
|
||||||
|
const name = prompt('New name', value.label);
|
||||||
|
|
||||||
|
if (!name || !name.length || name === value.label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request('zwave.set_value_label', {
|
||||||
|
id_on_network: value.id_on_network,
|
||||||
|
new_label: name,
|
||||||
|
}).then(() => {
|
||||||
|
this.bus.$emit('refreshNodes');
|
||||||
|
createNotification({
|
||||||
|
text: 'Value successfully renamed',
|
||||||
|
image: { icon: 'check' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onValueChanged: function(event) {
|
||||||
|
const target = event.target ? event.target : event.event.target.parentElement;
|
||||||
|
const value = this.node.values[target.dataset.idOnNetwork];
|
||||||
|
const data = value.type === 'List' ? value.data_items[event.target.value] : (target.value || event.value);
|
||||||
|
|
||||||
|
request('zwave.set_value', {
|
||||||
|
id_on_network: value.id_on_network,
|
||||||
|
data: data,
|
||||||
|
}).then(() => {
|
||||||
|
this.bus.$emit('refreshNodes');
|
||||||
|
createNotification({
|
||||||
|
text: 'Value successfully modified',
|
||||||
|
image: { icon: 'check' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% include 'plugins/zwave/node.html' %}
|
{% include 'plugins/zwave/node.html' %}
|
||||||
{% include 'plugins/zwave/group.html' %}
|
{% include 'plugins/zwave/group.html' %}
|
||||||
|
{% include 'plugins/zwave/value.html' %}
|
||||||
|
|
||||||
<script type="text/x-template" id="tmpl-zwave">
|
<script type="text/x-template" id="tmpl-zwave">
|
||||||
<div class="zwave-container">
|
<div class="zwave-container">
|
||||||
|
@ -10,7 +11,7 @@
|
||||||
<div class="view-selector col-s-9 col-m-10 col-l-11">
|
<div class="view-selector col-s-9 col-m-10 col-l-11">
|
||||||
<select @change="onViewChange">
|
<select @change="onViewChange">
|
||||||
<option v-for="_, view in views"
|
<option v-for="_, view in views"
|
||||||
v-text="view[0].toUpperCase() + view.slice(1)"
|
v-text="(view[0].toUpperCase() + view.slice(1)).replace('_', ' ')"
|
||||||
:key="view"
|
:key="view"
|
||||||
:selected="view == selected.view"
|
:selected="view == selected.view"
|
||||||
:value="view">
|
:value="view">
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
</zwave-node>
|
</zwave-node>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view groups" v-if="selected.view == 'groups'">
|
<div class="view groups" v-else-if="selected.view == 'groups'">
|
||||||
<div class="no-items" v-if="Object.keys(groups).length == 0">
|
<div class="no-items" v-if="Object.keys(groups).length == 0">
|
||||||
<div class="loading" v-if="loading.groups">Loading groups...</div>
|
<div class="loading" v-if="loading.groups">Loading groups...</div>
|
||||||
<div class="empty" v-else>No groups available on the network</div>
|
<div class="empty" v-else>No groups available on the network</div>
|
||||||
|
@ -81,7 +82,7 @@
|
||||||
</zwave-group>
|
</zwave-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view scenes" v-if="selected.view == 'scenes'">
|
<div class="view scenes" v-else-if="selected.view == 'scenes'">
|
||||||
<div class="no-items" v-if="Object.keys(scenes).length == 0">
|
<div class="no-items" v-if="Object.keys(scenes).length == 0">
|
||||||
<div class="loading" v-if="loading.scenes">Loading scenes...</div>
|
<div class="loading" v-if="loading.scenes">Loading scenes...</div>
|
||||||
<div class="empty" v-else>No scenes configured on the network</div>
|
<div class="empty" v-else>No scenes configured on the network</div>
|
||||||
|
@ -94,6 +95,23 @@
|
||||||
<!-- :bus="bus">-->
|
<!-- :bus="bus">-->
|
||||||
<!-- </zwave-scene>-->
|
<!-- </zwave-scene>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="view values" v-else>
|
||||||
|
<div class="no-items" v-if="Object.keys(nodes).length == 0">
|
||||||
|
<div class="loading" v-if="loading.nodes">Loading nodes...</div>
|
||||||
|
<div class="empty" v-else>No nodes found on the network</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<zwave-value
|
||||||
|
v-for="node, nodeId in nodes"
|
||||||
|
v-if="selected.view === 'values' || Object.values(node.values).filter((value) => value.id_on_network in values[selected.view]).length > 0"
|
||||||
|
:key="nodeId"
|
||||||
|
:values="Object.values(node.values).filter((value) => selected.view === 'values' || value.id_on_network in values[selected.view]).map((value) => value.id_on_network)"
|
||||||
|
:node="node"
|
||||||
|
:selected="selected.nodeId == nodeId"
|
||||||
|
:bus="bus">
|
||||||
|
</zwave-value>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
110
platypush/backend/http/templates/plugins/zwave/value.html
Normal file
110
platypush/backend/http/templates/plugins/zwave/value.html
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<script type="text/x-template" id="tmpl-zwave-value">
|
||||||
|
<div class="item node" :class="{selected: selected}">
|
||||||
|
<div class="row name vertical-center" :class="{selected: selected}"
|
||||||
|
v-text="node.name" @click="onNodeClicked"></div>
|
||||||
|
|
||||||
|
<div class="params" v-if="selected">
|
||||||
|
<div class="section value"
|
||||||
|
v-for="value in node.values"
|
||||||
|
v-if="values.indexOf(value.id_on_network) >= 0"
|
||||||
|
:key="value.id_on_network">
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">
|
||||||
|
<button class="btn btn-default btn-value-name-edit" title="Edit value name"
|
||||||
|
:data-id-on-network="value.id_on_network" @click="editName">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
{% raw %}{{ value.label }}{% endraw %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="param-name">Value</div>
|
||||||
|
<div class="param-value">
|
||||||
|
<div class="value-view" v-if="value.is_read_only">
|
||||||
|
<div class="value-data" v-text="value.data" ></div>
|
||||||
|
<div class="unit" v-text="value.units" v-if="value.units && value.units.length">
|
||||||
|
{% raw %}{{ value.units }}{% endraw %}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="value-edit" v-else>
|
||||||
|
<div :class="['col-' + (value.units && value.units.length ? '11' : '12')]">
|
||||||
|
<div class="list" v-if="value.type === 'List'">
|
||||||
|
<select @change="onValueChanged"
|
||||||
|
:data-id-on-network="value.id_on_network">
|
||||||
|
<option v-for="data, index in value.data_items"
|
||||||
|
v-text="data"
|
||||||
|
:key="index"
|
||||||
|
:selected="value.data == data"
|
||||||
|
:value="index">
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="numeric slider-container" v-else-if="['Byte', 'Decimal', 'Short'].indexOf(value.type) >= 0">
|
||||||
|
<div class="col-10">
|
||||||
|
<div class="row">
|
||||||
|
<span class="value-min" v-text="value.min"></span>
|
||||||
|
<span class="value-max" v-text="value.max"></span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<input class="slider" type="range" :min="value.min" :max="value.max"
|
||||||
|
:value="value.data" :data-id-on-network="value.id_on_network"
|
||||||
|
@change="onValueChanged">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<input type="text" :data-id-on-network="value.id_on_network" :value="value.data"
|
||||||
|
@change="onValueChanged">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="boolean" v-else-if="['Bool', 'Button'].indexOf(value.type) >= 0">
|
||||||
|
<toggle-switch :value="value.data" :data-id-on-network="value.id_on_network"
|
||||||
|
@toggled="onValueChanged"></toggle-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="value-data" v-text="value.data" v-else></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-1 unit" v-text="value.units" v-if="value.units && value.units.length">
|
||||||
|
{% raw %}{{ value.units }}{% endraw %}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" v-if="value.help && value.help.length">
|
||||||
|
<div class="param-name">Help</div>
|
||||||
|
<div class="param-value" v-text="value.help"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="param-name">Value ID</div>
|
||||||
|
<div class="param-value" v-text="value.value_id"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="param-name">ID on Network</div>
|
||||||
|
<div class="param-value" v-text="value.id_on_network"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="param-name">Command Class</div>
|
||||||
|
<div class="param-value" v-text="value.command_class"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" v-if="value.last_update">
|
||||||
|
<div class="param-name">Last Update</div>
|
||||||
|
<div class="param-value" v-text="value.last_update"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/zwave/value.js') }}"></script>
|
||||||
|
|
|
@ -241,6 +241,8 @@ class ZwaveBackend(Backend):
|
||||||
event = ZwaveValueRemovedEvent(device=self.device,
|
event = ZwaveValueRemovedEvent(device=self.device,
|
||||||
node=ZwavePlugin.node_to_dict(event.args['node']),
|
node=ZwavePlugin.node_to_dict(event.args['node']),
|
||||||
value=ZwavePlugin.value_to_dict(event.args['value']))
|
value=ZwavePlugin.value_to_dict(event.args['value']))
|
||||||
|
else:
|
||||||
|
self.logger.info('Received unhandled ZWave event: {}'.format(event))
|
||||||
|
|
||||||
if isinstance(event, ZwaveEvent):
|
if isinstance(event, ZwaveEvent):
|
||||||
self.bus.post(event)
|
self.bus.post(event)
|
||||||
|
|
|
@ -17,6 +17,9 @@ class Message(object):
|
||||||
isinstance(obj, datetime.time):
|
isinstance(obj, datetime.time):
|
||||||
return obj.isoformat()
|
return obj.isoformat()
|
||||||
|
|
||||||
|
if isinstance(obj, set):
|
||||||
|
return list(obj)
|
||||||
|
|
||||||
return super().default(obj)
|
return super().default(obj)
|
||||||
|
|
||||||
def __init__(self, timestamp=None, *args, **kwargs):
|
def __init__(self, timestamp=None, *args, **kwargs):
|
||||||
|
|
|
@ -150,13 +150,14 @@ class ZwavePlugin(Plugin):
|
||||||
controller.request_node_neighbor_update(node.node_id)
|
controller.request_node_neighbor_update(node.node_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def value_to_dict(value) -> Dict[str, Any]:
|
def value_to_dict(value: Optional[ZWaveValue]) -> Dict[str, Any]:
|
||||||
if not value:
|
if not value:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'command_class': value.command_class,
|
'command_class': value.node.get_command_class_as_string(value.command_class),
|
||||||
'data': value.data,
|
'data': value.data,
|
||||||
|
'data_as_string': value.data_as_string,
|
||||||
'data_items': list(value.data_items) if isinstance(value.data_items, set) else value.data_items,
|
'data_items': list(value.data_items) if isinstance(value.data_items, set) else value.data_items,
|
||||||
'genre': value.genre,
|
'genre': value.genre,
|
||||||
'help': value.help,
|
'help': value.help,
|
||||||
|
@ -185,7 +186,7 @@ class ZwavePlugin(Plugin):
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def group_to_dict(group) -> Dict[str, Any]:
|
def group_to_dict(group: Optional[ZWaveGroup]) -> Dict[str, Any]:
|
||||||
if not group:
|
if not group:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ class ZwavePlugin(Plugin):
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def node_to_dict(cls, node) -> Dict[str, Any]:
|
def node_to_dict(cls, node: Optional[ZWaveNode]) -> Dict[str, Any]:
|
||||||
if not node:
|
if not node:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ class ZwavePlugin(Plugin):
|
||||||
'use_cache': node.use_cache,
|
'use_cache': node.use_cache,
|
||||||
'version': node.version,
|
'version': node.version,
|
||||||
'values': {
|
'values': {
|
||||||
value_id: cls.value_to_dict(value)
|
value.id_on_network: cls.value_to_dict(value)
|
||||||
for value_id, value in (node.values or {}).items()
|
for value_id, value in (node.values or {}).items()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -445,16 +446,22 @@ class ZwavePlugin(Plugin):
|
||||||
node_id: Optional[int] = None, node_name: Optional[str] = None, value_label: Optional[str] = None) \
|
node_id: Optional[int] = None, node_name: Optional[str] = None, value_label: Optional[str] = None) \
|
||||||
-> ZWaveValue:
|
-> ZWaveValue:
|
||||||
assert (value_id is not None or id_on_network is not None) or \
|
assert (value_id is not None or id_on_network is not None) or \
|
||||||
(node_id is not None and node_name is not None and value_label is not None),\
|
((node_id is not None or node_name is not None) and value_label is not None),\
|
||||||
'Specify either value_id, id_on_network, or [node_id/node_name, value_label]'
|
'Specify either value_id, id_on_network, or [node_id/node_name, value_label]'
|
||||||
|
|
||||||
if value_id is not None:
|
if value_id is not None:
|
||||||
return self._get_network().get_value(value_id)
|
return self._get_network().get_value(value_id)
|
||||||
if id_on_network is not None:
|
if id_on_network is not None:
|
||||||
return self._get_network().get_value_from_id_on_network(id_on_network)
|
values = [value
|
||||||
|
for node in self._get_network().nodes.values()
|
||||||
|
for value in node.values.values()
|
||||||
|
if value.id_on_network == id_on_network]
|
||||||
|
|
||||||
|
assert values, 'No such value ID: {}'.format(id_on_network)
|
||||||
|
return values[0]
|
||||||
|
|
||||||
node = self._get_node(node_id=node_id, node_name=node_name)
|
node = self._get_node(node_id=node_id, node_name=node_name)
|
||||||
values = [v for v in node.values if v.label == value_label]
|
values = [v for v in node.values.values() if v.label == value_label]
|
||||||
assert values, 'No such value on node "{}": "{}"'.format(node.name, value_label)
|
assert values, 'No such value on node "{}": "{}"'.format(node.name, value_label)
|
||||||
return values[0]
|
return values[0]
|
||||||
|
|
||||||
|
@ -489,7 +496,30 @@ class ZwavePlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
value = self._get_value(value_id=value_id, id_on_network=id_on_network,
|
value = self._get_value(value_id=value_id, id_on_network=id_on_network,
|
||||||
node_id=node_id, node_name=node_name, value_label=value_label)
|
node_id=node_id, node_name=node_name, value_label=value_label)
|
||||||
value.data = data
|
new_val = value.check_data(data)
|
||||||
|
assert new_val is not None, 'Invalid value passed to the property'
|
||||||
|
node: ZWaveNode = self._get_network().nodes[value.node.node_id]
|
||||||
|
node.values[value.value_id].data = new_val
|
||||||
|
self.write_config()
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_value_label(self, new_label: str, value_id: Optional[int] = None, id_on_network: Optional[str] = None,
|
||||||
|
value_label: Optional[str] = None, node_id: Optional[int] = None,
|
||||||
|
node_name: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
Change the label/name of a value.
|
||||||
|
|
||||||
|
:param new_label: New value label.
|
||||||
|
:param value_id: Select value by value_id.
|
||||||
|
:param id_on_network: Select value by id_on_network.
|
||||||
|
:param value_label: Select value by [node_id/node_name, value_label]
|
||||||
|
:param node_id: Select value by [node_id/node_name, value_label]
|
||||||
|
:param node_name: Select value by [node_id/node_name, value_label]
|
||||||
|
"""
|
||||||
|
value = self._get_value(value_id=value_id, id_on_network=id_on_network,
|
||||||
|
node_id=node_id, node_name=node_name, value_label=value_label)
|
||||||
|
value.label = new_label
|
||||||
|
self.write_config()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def node_add_value(self, value_id: Optional[int] = None, id_on_network: Optional[str] = None,
|
def node_add_value(self, value_id: Optional[int] = None, id_on_network: Optional[str] = None,
|
||||||
|
@ -579,7 +609,7 @@ class ZwavePlugin(Plugin):
|
||||||
else self._get_network().nodes.values()
|
else self._get_network().nodes.values()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value_id: {
|
value.id_on_network: {
|
||||||
'node_id': node.node_id,
|
'node_id': node.node_id,
|
||||||
'node_name': node.name,
|
'node_name': node.name,
|
||||||
**self.value_to_dict(value)
|
**self.value_to_dict(value)
|
||||||
|
|
Loading…
Reference in a new issue