[WIP] Zwave panel (#260)

* Add Node card

* Add Node selection dropdown

* Add refresh node service and auto visible options

* Add replace node and replace failed node to node options

* Add node information

* Add node group configuration

* Add Wakeup and fix group associations

* Make Node info expandable

* Add refresh entity

* Cleanup

* Add set_config and print_config parameter

* styling

* indention

* lint error

* thank you @adgelbfish

* Hide correctly

* Add Entity info

* Indents

* Add Group fetched from API

* Add API to get config options

* Initialize empty array

* Add config handling

* add OZW log

* missing style

* Bug in other groupmembers

* Fix set-configparameter

* Fix set-association

* Tweaks to associations

* Add usercodes

* Tweaks

* Tweaks

* Target node text

* Move computed servicedata to property

* Start splitting

* Split part 2, network

* Splitting 3

* cleanup

* refresh on setting with delay

* fix servicedata

* Splitting 4 groups

* Splitting 5 config and wakeup

* Splitting done.

* Final tweaks, lint and removal of logging
This commit is contained in:
John Arild Berentsen 2017-05-19 02:37:28 +02:00 committed by Paulus Schoutsen
parent ffe0b22d77
commit ad3b3ce3dc
7 changed files with 1266 additions and 39 deletions

View File

@ -4,9 +4,19 @@
<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html"> <link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html"> <link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html"> <link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href="../../bower_components/paper-listbox/paper-listbox.html">
<link rel='import' href="../../bower_components/paper-input/paper-input.html">
<link rel="import" href="../../src/components/buttons/ha-call-service-button.html"> <link rel="import" href="../../src/components/buttons/ha-call-service-button.html">
<link rel="import" href="../../src/resources/ha-style.html"> <link rel="import" href="../../src/resources/ha-style.html">
<link rel="import" href="./zwave-log.html">
<link rel="import" href="./zwave-network.html">
<link rel="import" href="./zwave-node-information.html">
<link rel="import" href="./zwave-node-config.html">
<link rel="import" href="./zwave-usercodes.html">
<link rel="import" href="./zwave-groups.html">
<dom-module id="ha-panel-zwave"> <dom-module id="ha-panel-zwave">
<template> <template>
@ -15,14 +25,28 @@
margin-top: 24px; margin-top: 24px;
} }
.node-info {
margin-left: 16px;
text-transform: capitalize;
}
.help-text {
padding-left: 24px;
padding-right: 24px;
}
paper-card { paper-card {
display: block; display: block;
margin: 0 auto; margin: 0 auto;
max-width: 600px; max-width: 600px;
} }
.card-actions.warning ha-call-service-button { .device-picker {
color: var(--google-red-500); @apply(--layout-horizontal);
@apply(--layout-center-center);
padding-left: 24px;
padding-right: 24px;
padding-bottom: 24px;
} }
</style> </style>
<app-header-layout has-scrolling-region> <app-header-layout has-scrolling-region>
@ -34,63 +58,152 @@
</app-header> </app-header>
<div class='content'> <div class='content'>
<paper-card heading='Z-Wave Network Management'> <zwave-network
<div class='card-content'> id='zwave-network'
Z-Wave Network controls. hass='[[hass]]'
></zwave-network>
</div> </div>
<!--Node card-->
<div class='content'>
<paper-card heading='Z-Wave Node Management'>
<div class='card-content'>
Z-Wave Node controls.
</div>
<div class='device-picker'>
<paper-dropdown-menu label="Nodes" class="flex">
<paper-listbox
class="dropdown-content"
selected='{{selectedNode}}'>
<template is='dom-repeat' items='[[nodes]]' as='state'>
<paper-item>[[computeSelectCaption(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is='dom-if' if='[[!computeIsNodeSelected(selectedNode)]]'>
<div class='card-actions'> <div class='card-actions'>
<ha-call-service-button <ha-call-service-button
hass='[[hass]]' hass='[[hass]]'
domain='zwave' domain='zwave'
service='add_node_secure' service='refresh_node'
>Add Node</ha-call-service-button> service-data=[[computeNodeServiceData(selectedNode)]]
>Refresh Node</ha-call-service-button>
<ha-call-service-button <ha-call-service-button
hass='[[hass]]' hass='[[hass]]'
domain='zwave' domain='zwave'
service='remove_failed_node' service='remove_failed_node'
service-data=[[computeNodeServiceData(selectedNode)]]
>Remove Failed Node</ha-call-service-button> >Remove Failed Node</ha-call-service-button>
<ha-call-service-button <ha-call-service-button
hass='[[hass]]' hass='[[hass]]'
domain='zwave' domain='zwave'
service='remove_node' service='replace_failed_node'
>Remove Node</ha-call-service-button> service-data=[[computeNodeServiceData(selectedNode)]]
</div> >Replace Failed Node</ha-call-service-button>
<div class='card-actions warning'>
<ha-call-service-button <ha-call-service-button
hass='[[hass]]' hass='[[hass]]'
domain='zwave' domain='zwave'
service='cancel_command' service='print_node'
>Cancel Command</ha-call-service-button> service-data=[[computeNodeServiceData(selectedNode)]]
>Print Node</ha-call-service-button>
</div> </div>
<div class='card-actions'>
<paper-input
float-label="New node name"
type=text
value={{newNodeNameInput}}
placeholder=[[computeGetNodeName(selectedNode)]]>
</paper-input>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='rename_node'
service-data=[[computeNodeNameServiceData(newNodeNameInput)]]
>Rename Node</ha-call-service-button>
</div>
<div class='device-picker'>
<paper-dropdown-menu label="Entities of this node" class="flex">
<paper-listbox
class="dropdown-content"
selected='{{selectedEntity}}'>
<template is='dom-repeat' items='[[entities]]' as='state'>
<paper-item>[[computeSelectCaptionEnt(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is='dom-if' if='[[!computeIsEntitySelected(selectedEntity)]]'>
<div class='card-actions'> <div class='card-actions'>
<ha-call-service-button <ha-call-service-button
hass='[[hass]]' hass='[[hass]]'
domain='zwave' domain='zwave'
service='heal_network' service='refresh_entity'
>Heal Network</ha-call-service-button> service-data=[[computeRefreshEntityServiceData(selectedEntity)]]
<ha-call-service-button >Refresh Entity</ha-call-service-button>
hass='[[hass]]'
domain='zwave'
service='start_network'
>Start Network</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='stop_network'
>Stop Network</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='soft_reset'
>Soft Reset</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='test_network'
>Test Network</ha-call-service-button>
</div> </div>
<div class='content'>
<div class='card-actions'>
<paper-button toggles raised noink active={{entityInfoActive}}>Entity Attributes</paper-button>
</div>
<template is='dom-if' if={{entityInfoActive}}>
<template is='dom-repeat' items='[[selectedEntityAttrs]]' as='state'>
<div class='node-info'>
<span>[[state]]</span>
</div>
</template>
</template>
</div>
</template>
</template>
</paper-card> </paper-card>
</div> </div>
<!--Node info card-->
<template is='dom-if' if='[[!computeIsNodeSelected(selectedNode)]]'>
<zwave-node-information
id='zwave-node-information'
nodes='[[nodes]]'
selected-node='[[selectedNode]]'
></zwave-node-information>
</template>
<!--Group card-->
<template is='dom-if' if='[[!computeIsNodeSelected(selectedNode)]]'>
<zwave-groups
hass='[[hass]]'
nodes='[[nodes]]'
selected-node='[[selectedNode]]'
groups='[[groups]]'
></zwave-groups>
</template>
<!--Config card-->
<template is='dom-if' if='[[!computeIsNodeSelected(selectedNode)]]'>
<zwave-node-config
hass='[[hass]]'
nodes='[[nodes]]'
selected-node='[[selectedNode]]'
config='[[config]]'
></zwave-node-config>
</template>
<!--User Codes-->
<template is='dom-if' if='{{hasNodeUserCodes}}'>
<zwave-usercodes
id='zwave-usercodes'
hass='[[hass]]'
nodes='[[nodes]]'
user-codes='[[userCodes]]'
selected-node='[[selectedNode]]'
></zwave-usercodes>
</template>
<!--Ozw log-->
<div class='content'>
<ozw-log
id='ozw-log'
hass='[[hass]]'
></ozw-log>
</div>
</app-header-layout> </app-header-layout>
</template> </template>
</dom-module> </dom-module>
@ -112,6 +225,196 @@ Polymer({
type: Boolean, type: Boolean,
value: false, value: false,
}, },
nodes: {
type: Array,
computed: 'computeNodes(hass)'
},
selectedNode: {
type: Number,
value: -1,
observer: 'selectedNodeChanged'
},
config: {
type: Array,
value: function () {
return [];
}
},
entities: {
type: Array,
computed: 'computeEntities(selectedNode)',
},
entityInfoActive: {
type: Boolean,
},
selectedEntity: {
type: Number,
value: -1,
observers: ['computeIsEntitySelected',
'computeRefreshEntityServiceData']
},
selectedEntityAttrs: {
type: Array,
computed: 'computeSelectedEntityAttrs(selectedEntity)'
},
groups: {
type: Array,
},
newNodeNameInput: {
type: String,
},
userCodes: {
type: Array,
value: function () {
return [];
},
},
hasNodeUserCodes: {
type: Boolean,
value: false
},
},
computeNodes: function (hass) {
return Object.keys(hass.states)
.map(function (key) { return hass.states[key]; })
.filter(function (ent) {
return (!ent.attributes.hidden &&
(ent.entity_id).match('zwave[.]'));
})
.sort(function (entityA, entityB) {
if (entityA.entity_id < entityB.entity_id) {
return -1;
}
if (entityA.entity_id > entityB.entity_id) {
return 1;
}
return 0;
});
},
computeEntities: function (selectedNode) {
if (!this.nodes || selectedNode === -1) return -1;
var hass = this.hass;
var nodeid = this.nodes[this.selectedNode].attributes.node_id;
return Object.keys(hass.states)
.map(function (key) { return hass.states[key]; })
.filter(function (ent) {
if (ent.attributes.node_id === undefined) {
return false;
}
return (!ent.attributes.hidden &&
'node_id' in ent.attributes &&
ent.attributes.node_id === nodeid &&
(!(ent.entity_id).match('zwave[.]')));
})
.sort(function (entityA, entityB) {
if (entityA.entity_id < entityB.entity_id) {
return -1;
}
if (entityA.entity_id > entityB.entity_id) {
return 1;
}
return 0;
});
},
selectedNodeChanged: function (selectedNode) {
if (selectedNode === -1) return;
this.selectedConfigParameter = -1;
this.selectedConfigParameterValue = -1;
this.selectedGroup = -1;
var configData = [];
this.hass.callApi('GET', 'zwave/config/' + this.nodes[selectedNode].attributes.node_id).then(function (configs) {
Object.entries(configs).forEach(([key, value]) => {
configData.push({ key, value });
});
this.config = configData;
}.bind(this));
var groupData = [];
this.hass.callApi('GET', 'zwave/groups/' + this.nodes[selectedNode].attributes.node_id).then(function (groups) {
Object.entries(groups).forEach(([key, value]) => {
groupData.push({ key, value });
});
this.groups = groupData;
}.bind(this));
var userCodes = [];
this.hasNodeUserCodes = false;
this.notifyPath('hasNodeUserCodes');
this.hass.callApi('GET', 'zwave/usercodes/' + this.nodes[selectedNode].attributes.node_id).then(function (usercodes) {
Object.entries(usercodes).forEach(([key, value]) => {
userCodes.push({ key, value });
});
this.userCodes = userCodes;
if (Object.keys(userCodes).length === 0) {
this.hasNodeUserCodes = false;
} else {
this.hasNodeUserCodes = true;
}
this.notifyPath('hasNodeUserCodes');
}.bind(this));
},
computeSelectedEntityAttrs: function (selectedEntity) {
if (selectedEntity === -1) return 'No entity selected';
var entityAttrs = this.entities[selectedEntity].attributes;
var att = [];
Object.entries(entityAttrs).forEach(([key, value]) => {
att.push(key + ': ' + value);
});
return att.sort();
},
computeSelectCaption: function (stateObj) {
return window.hassUtil.computeStateName(stateObj) + ' (Node:' +
stateObj.attributes.node_id + ' ' +
stateObj.attributes.query_stage + ')';
},
computeSelectCaptionEnt: function (stateObj) {
return (window.hassUtil.computeDomain(stateObj) + '.'
+ window.hassUtil.computeStateName(stateObj));
},
computeIsNodeSelected: function () {
return (!this.nodes || this.selectedNode === -1);
},
computeIsEntitySelected: function (selectedEntity) {
return (selectedEntity === -1);
},
computeNodeServiceData: function (selectedNode) {
return { node_id: this.nodes[selectedNode].attributes.node_id };
},
computeGetNodeName: function (selectedNode) {
if (this.selectedNode === -1 ||
!this.nodes[selectedNode].entity_id) return -1;
var str = (this.nodes[selectedNode].entity_id);
var name = str.replace('zwave.', '');
return name;
},
computeNodeNameServiceData: function (newNodeNameInput) {
return { node_id: this.nodes[this.selectedNode].attributes.node_id,
name: newNodeNameInput };
},
computeRefreshEntityServiceData: function (selectedEntity) {
if (selectedEntity === -1) return -1;
return { entity_id: this.entities[selectedEntity].entity_id };
}, },
}); });
</script> </script>

View File

@ -0,0 +1,222 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href="../../bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="../../src/components/buttons/ha-call-service-button.html">
<dom-module id='zwave-groups'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
.device-picker {
@apply(--layout-horizontal);
@apply(--layout-center-center);
padding-left: 24px;
padding-right: 24px;
padding-bottom: 24px;
}
.help-text {
padding-left: 24px;
padding-right: 24px;
}
</style>
<div class='content'>
<paper-card heading='Node group associations'>
<div class='device-picker'>
<paper-dropdown-menu label="Node to control" class='flex'>
<paper-listbox
class="dropdown-content"
selected='{{selectedTargetNode}}'>
<template is='dom-repeat' items='[[nodes]]' as='state'>
<paper-item>[[computeSelectCaption(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is='dom-if' if='[[!computeIsTargetNodeSelected(selectedTargetNode)]]'>
<!--TODO make api for getting groups and members-->
<div class='device-picker'>
<paper-dropdown-menu label="Group" class='flex'>
<paper-listbox
class="dropdown-content"
selected='{{selectedGroup}}'>
<template is='dom-repeat' items='[[groups]]' as='state'>
<paper-item>[[computeSelectCaptionGroup(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</template>
<template is='dom-if' if='[[!computeIsGroupSelected(selectedGroup)]]'>
<div class='help-text'>
<span>Other Nodes in this group:</span>
<template is='dom-repeat' items='[[otherGroupNodes]]' as='state'>
<span>[[state]]</span>
</template>
</div>
<div class='help-text'>
<span>Max Associations:</span>
<span>[[maxAssociations]]</span>
</div>
<div class='card-actions'>
<template is='dom-if' if='[[!noAssociationsLeft]]'>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='change_association'
service-data='[[computeAssocServiceData(selectedGroup, "add")]]'
>Add To Group</ha-call-service-button>
</template>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='change_association'
service-data='[[computeAssocServiceData(selectedGroup, "remove")]]'
>Remove From Group</ha-call-service-button>
</div>
</template>
</paper-card>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'zwave-groups',
properties: {
hass: {
type: Object,
},
nodes: {
type: Array,
},
groups: {
type: Array,
},
selectedNode: {
type: Number,
},
selectedTargetNode: {
type: Number,
value: -1
},
selectedGroup: {
type: Number,
value: -1,
observer: 'selectedGroupChanged'
},
otherGroupNodes: {
type: Array,
value: -1,
computed: 'computeOtherGroupNodes(selectedGroup)'
},
maxAssociations: {
type: String,
value: '',
computed: 'computeMaxAssociations(selectedGroup)'
},
noAssociationsLeft: {
type: Boolean,
value: true,
computed: 'computeAssociationsLeft(selectedGroup)'
},
},
listeners: {
'hass-service-called': 'serviceCalled',
},
serviceCalled: function (ev) {
if (ev.detail.success) {
var foo = this;
setTimeout(function () {
foo.refreshGroups(foo.selectedNode);
}, 5000);
}
},
computeAssociationsLeft: function (selectedGroup) {
if (selectedGroup === -1) return true;
return (this.maxAssociations === this.otherGroupNodes.length);
},
computeMaxAssociations: function (selectedGroup) {
if (selectedGroup === -1) return -1;
var maxAssociations = this.groups[selectedGroup].value.max_associations;
if (!maxAssociations) return ['None'];
return maxAssociations;
},
computeOtherGroupNodes: function (selectedGroup) {
if (selectedGroup === -1) return -1;
var associations = Object.values(this.groups[selectedGroup].value.associations);
if (!associations.length) return ['None'];
return associations;
},
computeSelectCaption: function (stateObj) {
return window.hassUtil.computeStateName(stateObj) + ' (Node:' +
stateObj.attributes.node_id + ' ' +
stateObj.attributes.query_stage + ')';
},
computeSelectCaptionGroup: function (stateObj) {
return (stateObj.key + ': ' + stateObj.value.label);
},
computeIsTargetNodeSelected: function (selectedTargetNode) {
return (!this.nodes || selectedTargetNode === -1);
},
computeIsGroupSelected: function (selectedGroup) {
return (!this.nodes || this.selectedNode === -1 || selectedGroup === -1);
},
computeAssocServiceData: function (selectedGroup, type) {
if (!this.groups === -1 || selectedGroup === -1 || this.selectedNode === -1) return -1;
return { node_id: this.nodes[this.selectedNode].attributes.node_id,
association: type,
target_node_id: this.nodes[this.selectedTargetNode].attributes.node_id,
group: this.groups[selectedGroup].key };
},
refreshGroups: function (selectedNode) {
var groupData = [];
this.hass.callApi('GET', 'zwave/groups/' + this.nodes[selectedNode].attributes.node_id).then(function (groups) {
Object.entries(groups).forEach(([key, value]) => {
groupData.push({ key, value });
});
this.groups = groupData;
this.selectedGroupChanged(this.selectedGroup);
}.bind(this));
},
selectedGroupChanged: function (selectedGroup) {
if (this.selectedGroup === -1 || selectedGroup === -1) return;
this.maxAssociations = this.groups[selectedGroup].value.max_associations;
this.otherGroupNodes = Object.values(this.groups[selectedGroup].value.associations);
},
});
</script>

View File

@ -0,0 +1,52 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<dom-module id='ozw-log'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
</style>
<paper-card heading='OZW Log'>
<div class='help-text'>
<pre>[[ozwLogs]]</pre>
</div>
<div class="card-actions">
<paper-button raised on-tap='refreshLog'>Refresh</paper-button>
</div>
</paper-card>
</template>
</dom-module>
<script>
Polymer({
is: 'ozw-log',
properties: {
hass: {
type: Object,
},
ozwLogs: {
type: String,
value: ''
},
},
refreshLog: function () {
this.ozwLogs = 'Loading ozw log...';
this.hass.callApi('GET', 'zwave/ozwlog')
.then(function (info) {
this.ozwLogs = info;
}.bind(this));
},
});
</script>

View File

@ -0,0 +1,86 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../src/components/buttons/ha-call-service-button.html">
<dom-module id='zwave-network'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
.card-actions.warning ha-call-service-button {
color: var(--google-red-500);
}
</style>
<paper-card heading='Z-Wave Network Management'>
<div class='card-content'>
Z-Wave Network controls.
</div>
<div class='card-actions'>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='add_node_secure'
>Add Node</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='remove_node'
>Remove Node</ha-call-service-button>
</div>
<div class='card-actions warning'>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='cancel_command'
>Cancel Command</ha-call-service-button>
</div>
<div class='card-actions'>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='heal_network'
>Heal Network</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='start_network'
>Start Network</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='stop_network'
>Stop Network</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='soft_reset'
>Soft Reset</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='test_network'
>Test Network</ha-call-service-button>
</div>
</paper-card>
</template>
</dom-module>
<script>
Polymer({
is: 'zwave-network',
properties: {
hass: {
type: Object,
},
},
});
</script>

View File

@ -0,0 +1,312 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href="../../bower_components/paper-listbox/paper-listbox.html">
<link rel='import' href="../../bower_components/paper-input/paper-input.html">
<link rel="import" href="../../src/components/buttons/ha-call-service-button.html">
<dom-module id='zwave-node-config'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
.device-picker {
@apply(--layout-horizontal);
@apply(--layout-center-center);
padding-left: 24px;
padding-right: 24px;
padding-bottom: 24px;
}
.help-text {
padding-left: 24px;
padding-right: 24px;
}
</style>
<div class='content'>
<paper-card heading='Node config options'>
<template is='dom-if' if='[[wakeupNode]]'>
<div class='card-actions'>
<paper-input
float-label="Wakeup Interval"
type=number
value={{wakeupInput}}
placeholder=[[computeGetWakeupValue(selectedNode)]]>
<div suffix>seconds</div>
</paper-input>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='set_wakeup'
service-data='[[computeWakeupServiceData(wakeupInput)]]'
>Set Wakeup</ha-call-service-button>
</div>
</template>
<div class='device-picker'>
<paper-dropdown-menu label="Config parameter" class='flex'>
<paper-listbox
class="dropdown-content"
selected='{{selectedConfigParameter}}'>
<template is='dom-repeat' items='[[config]]' as='state'>
<paper-item>[[computeSelectCaptionConfigParameter(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is='dom-if' if="[[isConfigParameterSelected(selectedConfigParameter, 'List')]]">
<div class='device-picker'>
<paper-dropdown-menu label="Config value" class='flex' placeholder='{{loadedConfigValue}}'>
<paper-listbox
class="dropdown-content"
selected='{{selectedConfigValue}}'>
<template is='dom-repeat' items='[[selectedConfigParameterValues]]' as='state'>
<paper-item>[[state]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</template>
<template is='dom-if' if="[[isConfigParameterSelected(selectedConfigParameter, 'Byte Short Int')]]">
<div class='card-actions'>
<paper-input
label='{{selectedConfigParameterNumValues}}'
type=number
value='{{selectedConfigValue}}'
max='{{configParameterMax}}'
min='{{configParameterMin}}'>
</paper-input>
</div>
</template>
<template is='dom-if' if="[[isConfigParameterSelected(selectedConfigParameter, 'Bool Button')]]">
<div class='device-picker'>
<paper-dropdown-menu label="Config value" class='flex' placeholder='{{loadedConfigValue}}'>
<paper-listbox
class="dropdown-content"
selected='{{selectedConfigValue}}'>
<template is='dom-repeat' items='[[selectedConfigParameterValues]]' as='state'>
<paper-item>[[state]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</template>
<div class='help-text'>
<span>[[configValueHelpText]]</span>
</div>
<template is='dom-if' if="[[isConfigParameterSelected(selectedConfigParameter, 'Bool Button Byte Short Int List')]]">
<div class='card-actions'>
<ha-call-service-button
hass='[[hass]]'
domain='zwave'
service='set_config_parameter'
service-data=[[computeSetConfigParameterServiceData(selectedConfigValue)]]
>Set Config Parameter</ha-call-service-button>
</div>
</template>
</paper-card>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'zwave-node-config',
properties: {
hass: {
type: Object,
},
nodes: {
type: Array,
observer: 'nodesChanged'
},
selectedNode: {
type: Number,
value: -1,
observer: 'nodesChanged'
},
config: {
type: Array,
value: function () {
return [];
}
},
selectedConfigParameter: {
type: Number,
value: -1,
observer: 'selectedConfigParameterChanged'
},
configParameterMax: {
type: Number,
value: -1
},
configParameterMin: {
type: Number,
value: -1
},
configValueHelpText: {
type: String,
value: '',
computed: 'computeConfigValueHelp(selectedConfigParameter)'
},
selectedConfigParameterType: {
type: String,
value: ''
},
selectedConfigValue: {
type: Number,
value: -1,
observer: 'computeSetConfigParameterServiceData'
},
selectedConfigParameterValues: {
type: Array,
value: function () {
return [];
}
},
selectedConfigParameterNumValues: {
type: String,
value: ''
},
loadedConfigValue: {
type: Number,
value: -1
},
wakeupInput: {
type: Number,
},
wakeupNode: {
type: Boolean,
value: false,
},
},
listeners: {
'hass-service-called': 'serviceCalled',
},
serviceCalled: function (ev) {
if (ev.detail.success) {
var foo = this;
setTimeout(function () {
foo.refreshConfig(foo.selectedNode);
}, 5000);
}
},
nodesChanged: function () {
if (!this.nodes) return;
this.wakeupNode = (this.nodes[this.selectedNode].attributes.wake_up_interval === 0 ||
this.nodes[this.selectedNode].attributes.wake_up_interval);
if (this.wakeupNode) {
if (this.nodes[this.selectedNode].attributes.wake_up_interval === 0) this.wakeupInput = '';
else this.wakeupInput = this.nodes[this.selectedNode].attributes.wake_up_interval;
}
},
computeGetWakeupValue: function (selectedNode) {
if (this.selectedNode === -1 ||
!this.nodes[selectedNode].attributes.wake_up_interval) return 'unknown';
return (this.nodes[selectedNode].attributes.wake_up_interval);
},
computeWakeupServiceData: function (wakeupInput) {
return { node_id: this.nodes[this.selectedNode].attributes.node_id,
value: wakeupInput };
},
computeConfigValueHelp: function (selectedConfigParameter) {
if (selectedConfigParameter === -1) return '';
var helpText = this.config[selectedConfigParameter].value.help;
if (!helpText) return ['No helptext available'];
return helpText;
},
computeSetConfigParameterServiceData: function (selectedConfigValue) {
if (this.selectedNode === -1 || this.selectedConfigParameter === -1) return -1;
var valueData = null;
if (('Short Byte Int').includes(this.selectedConfigParameterType)) {
valueData = parseInt(selectedConfigValue, 10);
} if (('Bool Button').includes(this.selectedConfigParameterType)) {
valueData = this.selectedConfigParameterValues[selectedConfigValue];
} if (this.selectedConfigParameterType === 'List') {
valueData = this.selectedConfigParameterValues[selectedConfigValue];
}
return { node_id: this.nodes[this.selectedNode].attributes.node_id,
parameter: this.config[this.selectedConfigParameter].key,
value: valueData };
},
selectedConfigParameterChanged: function (selectedConfigParameter) {
if (selectedConfigParameter === -1) return;
this.selectedConfigValue = -1;
this.loadedConfigValue = -1;
this.selectedConfigParameterValues = [];
this.selectedConfigParameterType = this.config[selectedConfigParameter].value.type;
this.configParameterMax = this.config[selectedConfigParameter].value.max;
this.configParameterMin = this.config[selectedConfigParameter].value.min;
this.loadedConfigValue = this.config[selectedConfigParameter].value.data;
this.configValueHelpText = this.config[selectedConfigParameter].value.help;
if (('Short Byte Int').includes(this.selectedConfigParameterType)) {
this.selectedConfigParameterNumValues = this.config[selectedConfigParameter].value.data_items;
this.selectedConfigValue = this.loadedConfigValue;
}
if (('Bool Button').includes(this.selectedConfigParameterType)) {
this.selectedConfigParameterValues = ['True', 'False'];
if (this.config[selectedConfigParameter].value.data) {
this.loadedConfigValue = 'True';
} else this.loadedConfigValue = 'False';
}
if (('List').includes(this.selectedConfigParameterType)) {
this.selectedConfigParameterValues = this.config[selectedConfigParameter].value.data_items;
}
},
isConfigParameterSelected: function (selectedConfigParameter, type) {
if (selectedConfigParameter === -1) return false;
if (this.config[selectedConfigParameter].value.type === type) return true;
if (type.includes(this.config[selectedConfigParameter].value.type)) return true;
return false;
},
computeSelectCaptionConfigParameter: function (stateObj) {
return (stateObj.key + ': ' + stateObj.value.label);
},
refreshConfig: function (selectedNode) {
var configData = [];
this.hass.callApi('GET', 'zwave/config/' + this.nodes[selectedNode].attributes.node_id).then(function (config) {
Object.entries(config).forEach(([key, value]) => {
configData.push({ key, value });
});
this.config = configData;
this.selectedConfigParameterChanged(this.selectedConfigParameter);
}.bind(this));
},
});
</script>

View File

@ -0,0 +1,81 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<dom-module id='zwave-node-information'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
.node-info {
margin-left: 16px;
text-transform: capitalize;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
paper-button[toggles][active] {
background: lightgray;
}
</style>
<div class='content'>
<paper-card heading='Node Information'>
<div class='card-actions'>
<paper-button toggles raised noink active={{nodeInfoActive}}>Show</paper-button>
</div>
<template is='dom-if' if={{nodeInfoActive}}>
<template is='dom-repeat' items='[[selectedNodeAttrs]]' as='state'>
<div class='node-info'>
<span>[[state]]</span>
</div>
</template>
</template>
</paper-card>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'zwave-node-information',
properties: {
nodes: {
type: Array,
observer: 'nodeChanged'
},
selectedNode: {
type: Number,
value: -1,
observer: 'nodeChanged'
},
selectedNodeAttrs: {
type: Array,
},
nodeInfoActive: {
type: Boolean,
},
},
nodeChanged: function (selectedNode) {
if (!this.nodes || selectedNode === -1) return;
var nodeAttrs = this.nodes[this.selectedNode].attributes;
var att = [];
Object.entries(nodeAttrs).forEach(([key, value]) => {
att.push(key + ': ' + value);
});
this.selectedNodeAttrs = att.sort();
},
});
</script>

View File

@ -0,0 +1,171 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href="../../bower_components/paper-listbox/paper-listbox.html">
<link rel='import' href="../../bower_components/paper-input/paper-input.html">
<link rel="import" href="../../src/components/buttons/ha-call-service-button.html">
<dom-module id='zwave-usercodes'>
<template>
<style include="iron-flex ha-style">
.content {
margin-top: 24px;
}
paper-card {
display: block;
margin: 0 auto;
max-width: 600px;
}
.device-picker {
@apply(--layout-horizontal);
@apply(--layout-center-center);
padding-left: 24px;
padding-right: 24px;
padding-bottom: 24px;
}
</style>
<div class='content'>
<paper-card heading='Node user codes'>
<div class='device-picker'>
<paper-dropdown-menu label="Code slot" class='flex'>
<paper-listbox
class="dropdown-content"
selected='{{selectedUserCode}}'>
<template is='dom-repeat' items='[[userCodes]]' as='state'>
<paper-item>[[computeSelectCaptionUserCodes(state)]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is='dom-if' if="[[isUserCodeSelected(selectedUserCode)]]">
<div class='card-actions'>
<paper-input
label='User code'
type=number
value='{{selectedUserCodeValue}}'
maxlength='{{userCodeMaxLen}}'
min='0'>
</paper-input>
</div>
<div class='card-actions'>
<ha-call-service-button
hass='[[hass]]'
domain='lock'
service='set_usercode'
service-data='[[computeUserCodeServiceData(selectedUserCodeValue, "Add")]]'
>Set Usercode</ha-call-service-button>
<ha-call-service-button
hass='[[hass]]'
domain='lock'
service='clear_usercode'
service-data='[[computeUserCodeServiceData(selectedUserCode, "Delete")]]'
>Delete Usercode</ha-call-service-button>
</div>
</template>
</paper-card>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'zwave-usercodes',
properties: {
hass: {
type: Object,
},
nodes: {
type: Array,
},
selectedNode: {
type: Number,
},
userCodes: {
type: Object,
},
userCodeMaxLen: {
type: Number,
value: 4
},
selectedUserCode: {
type: Number,
value: -1,
observer: 'selectedUserCodeChanged'
},
selectedUserCodeValue: {
type: Number,
value: -1
},
},
listeners: {
'hass-service-called': 'serviceCalled',
},
serviceCalled: function (ev) {
if (ev.detail.success) {
var foo = this;
setTimeout(function () {
foo.refreshUserCodes(foo.selectedNode);
}, 5000);
}
},
isUserCodeSelected: function (selectedUserCode) {
if (selectedUserCode === -1) return false;
return true;
},
computeSelectCaptionUserCodes: function (stateObj) {
return (stateObj.key + ': ' + stateObj.value.label);
},
selectedUserCodeChanged: function (selectedUserCode) {
if (this.selectedUserCode === -1 || selectedUserCode === -1) return;
var value = (parseInt((this.userCodes[selectedUserCode].value.code).trim()));
this.userCodeMaxLen = (this.userCodes[selectedUserCode].value.length);
if (isNaN(value)) this.selectedUserCodeValue = '';
else this.selectedUserCodeValue = value;
},
computeUserCodeServiceData: function (selectedUserCodeValue, type) {
if (this.selectedNode === -1 || !selectedUserCodeValue) return -1;
var serviceData = null;
var valueData = null;
if (type === 'Add') {
valueData = selectedUserCodeValue;
serviceData = { node_id: this.nodes[this.selectedNode].attributes.node_id,
code_slot: this.selectedUserCode,
usercode: valueData };
}
if (type === 'Delete') {
serviceData = { node_id: this.nodes[this.selectedNode].attributes.node_id,
code_slot: this.selectedUserCode };
}
return serviceData;
},
refreshUserCodes: function (selectedNode) {
this.selectedUserCodeValue = '';
var userCodes = [];
this.hass.callApi('GET', 'zwave/usercodes/' + this.nodes[selectedNode].attributes.node_id).then(function (usercodes) {
Object.entries(usercodes).forEach(([key, value]) => {
userCodes.push({ key, value });
});
this.userCodes = userCodes;
this.selectedUserCodeChanged(this.selectedUserCode);
}.bind(this));
},
});
</script>