mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-25 11:45:49 +01:00
Some updates to the HomeBuilder (#417)
* Added semantic labels to tags Floors, Rooms and objects * Added a Cellar as a Floor * Added support for more languages which where already available as translation * Integrated the webpack build inside Maven * Only allow minor updates in the package.json to make sure new version will not easily break the build, if semver fails again * Remove dist folder because it should be generated through the build * Switched from 'hidden-xs-up' to 'd-none' as proposed in bootstrap migration guide * Switched from 'scope' to 'slot-scope' as suggested by vue 2.5 migration * Added some additional parents in the vue schema because they were needed * Removed osgi import from manifest * Switched to a computed setup and the build in fieldMultiselect * Switched to a 'computed' setup * Remove the package fieldMultiselect and switch to the build-in * Applied changes from review. * Formatted JS files * Re-introduced (customized) fieldMultiselect and minor fixes * Re-introduce custom multiselect because this is the one that offers the icons * Add the floor name to the label of room-based object selector * Make sure that generating tags can be switched off * Switched from floor-count to choosing custom floors * Add no-save and corrected some js warnings Signed-off-by: Martin van Wingerden <martin@martinvw.nl>
This commit is contained in:
parent
84d97270fd
commit
deb2437a6b
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,5 +4,8 @@
|
||||
/*features*/*/src/main/history/
|
||||
maven-metadata-local.xml
|
||||
dependency-reduced-pom.xml
|
||||
**/node_modules/
|
||||
npm-debug.log
|
||||
|
||||
bundles/org.openhab.ui.homebuilder/web/dist
|
||||
bundles/org.openhab.ui.homebuilder/npm_cache
|
||||
bundles/org.openhab.ui.homebuilder/web/node
|
||||
|
@ -17,7 +17,6 @@ Import-Package: com.google.gson,
|
||||
org.osgi.framework,
|
||||
org.osgi.service.cm,
|
||||
org.osgi.service.http,
|
||||
org.osgi.service.component.annotations;resolution:=optional,
|
||||
org.slf4j
|
||||
Bundle-Vendor: openHAB
|
||||
Bundle-ActivationPolicy: lazy
|
||||
|
@ -13,5 +13,86 @@
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
<name>openHAB HomeBuilder UI Dashboard Integration</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>${basedir}/web</directory>
|
||||
<includes>
|
||||
<include>node_modules/**</include>
|
||||
<include>npm_cache/**</include>
|
||||
<include>npm_tmp/**</include>
|
||||
<include>dist/**</include>
|
||||
</includes>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<configuration>
|
||||
<nodeVersion>v10.4.0</nodeVersion>
|
||||
<npmVersion>6.1.0</npmVersion>
|
||||
<nodeDownloadRoot>http://nodejs.org/dist/</nodeDownloadRoot>
|
||||
<npmDownloadRoot>http://registry.npmjs.org/npm/-/</npmDownloadRoot>
|
||||
<environmentVariables>
|
||||
<npm_config_cache>${project.basedir}/npm_cache</npm_config_cache>
|
||||
<npm_config_tmp>${project.basedir}/npm_tmp</npm_config_tmp>
|
||||
</environmentVariables>
|
||||
<workingDirectory>web</workingDirectory>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>Install node and npm</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>npm clean</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<phase>clean</phase>
|
||||
<configuration>
|
||||
<arguments>cache clean --force</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>install --no-save</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>webpack build</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run build</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
17027
bundles/org.openhab.ui.homebuilder/web/package-lock.json
generated
17027
bundles/org.openhab.ui.homebuilder/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,16 +12,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ascii-table": "0.0.9",
|
||||
"bootstrap-vue": "^0.18.0",
|
||||
"daemonite-material": "^4.0.0-alpha.6",
|
||||
"bootstrap-vue": "~0.18.0",
|
||||
"daemonite-material": "~4.1.1",
|
||||
"lodash": "^4.17.4",
|
||||
"pickadate": "^3.5.6",
|
||||
"popper.js": "^1.14.4",
|
||||
"underscore.string": "^3.3.4",
|
||||
"v-clipboard": "^1.0.4",
|
||||
"vue": "^2.3.3",
|
||||
"vue-form-generator": "^2.0.0",
|
||||
"vue-i18n": "^7.1.1",
|
||||
"vue-multiselect": "^2.0.2",
|
||||
"vue-resource": "^1.3.4"
|
||||
"vue": "~2.5.17",
|
||||
"vue-form-generator": "~2.3.1",
|
||||
"vue-i18n": "~7.8.1",
|
||||
"vue-multiselect": "~2.1.3",
|
||||
"vue-resource": "~1.5.1",
|
||||
"vue-style-loader": "~1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.0.0",
|
||||
@ -30,6 +33,8 @@
|
||||
"cross-env": "^3.0.0",
|
||||
"css-loader": "^0.25.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"jquery": "^1.12.4",
|
||||
"lodash-webpack-plugin": "^0.11.5",
|
||||
"node-sass": "^4.5.0",
|
||||
"sass-loader": "^5.0.1",
|
||||
"style-loader": "^0.18.2",
|
||||
|
@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<app-nav></app-nav>
|
||||
<div class="container" id="app">
|
||||
<app-form></app-form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<app-nav></app-nav>
|
||||
<div class="container" id="app">
|
||||
<app-form></app-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppNav from './AppNav.vue'
|
||||
import AppForm from './AppForm.vue'
|
||||
import AppNav from './AppNav.vue'
|
||||
import AppForm from './AppForm.vue'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
AppNav,
|
||||
AppForm
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
appNav: AppNav,
|
||||
appForm: AppForm
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,201 +1,226 @@
|
||||
<template>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6' id="form">
|
||||
<vue-form-generator :schema='schema' :model='model' :options='formOptions'>
|
||||
</vue-form-generator>
|
||||
<pre class='language-json hidden-xs-up'><code>{{ model }}</code></pre>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
<affix id="preview" relative-element-selector="#form" :offset="{ top: 65, bottom: 0 }">
|
||||
<collapse v-if="model && model.filesGenerated.includes('items')" :uid="'items'" :heading="'Items'" :content='model.itemsType === "text" ? generateItems(model) : generateItemsJson(model)'></collapse>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6' id="form">
|
||||
<vue-form-generator :schema='schema' :model='model' :options='formOptions'>
|
||||
</vue-form-generator>
|
||||
<pre class='language-json d-none'><code>{{ model }}</code></pre>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
<affix id="preview" relative-element-selector="#form" :offset="{ top: 65, bottom: 0 }">
|
||||
<collapse v-if="model && model.filesGenerated.includes('items')" :uid="'items'" :heading="'Items'"
|
||||
:content='model.itemsType === "text" ? generateItems(model) : generateItemsJson(model)'></collapse>
|
||||
|
||||
<collapse v-if="model && model.filesGenerated.includes('sitemap')" :uid="'sitemap'" :heading="'Sitemap'" :content='generateSitemap(model)'></collapse>
|
||||
<collapse v-if="model && model.filesGenerated.includes('sitemap')" :uid="'sitemap'" :heading="'Sitemap'"
|
||||
:content='generateSitemap(model)'></collapse>
|
||||
|
||||
<collapse v-if="model && model.filesGenerated.includes('habpanel')" :uid="'habpanel'" :heading="'HABPanel Dashboard'" :content='generateDashboard(model)'></collapse>
|
||||
</affix>
|
||||
<collapse v-if="model && model.filesGenerated.includes('habpanel')" :uid="'habpanel'" :heading="'HABPanel Dashboard'"
|
||||
:content='generateDashboard(model)'></collapse>
|
||||
</affix>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" src="./scss/app.scss">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import * as _ from 'lodash'
|
||||
import * as s from 'underscore.string'
|
||||
import Vue from 'vue'
|
||||
import Collapse from './Collapse.vue'
|
||||
import { component as VueFormGenerator } from 'vue-form-generator'
|
||||
import { floors, rooms, objects, languages, OBJECTS_SUFFIX } from './definitions'
|
||||
import * as schema from './formSchema'
|
||||
import { generateItems } from './textItems'
|
||||
import { generateItemsJson } from './restItems'
|
||||
import { generateDashboard } from './habpanel'
|
||||
import { sitemapName, generateSitemap } from './sitemap'
|
||||
import * as _ from 'lodash'
|
||||
import Collapse from './Collapse.vue'
|
||||
import {component as VueFormGenerator} from 'vue-form-generator'
|
||||
import {floors, languages, objects, OBJECTS_SUFFIX, rooms} from './definitions'
|
||||
import * as schema from './formSchema'
|
||||
import {roomsSelect} from './formSchema'
|
||||
import {objectSelect} from './formSchema'
|
||||
import {generateItems} from './textItems'
|
||||
import {generateItemsJson} from './restItems'
|
||||
import {generateDashboard} from './habpanel'
|
||||
import {generateSitemap} from './sitemap'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueFormGenerator,
|
||||
Collapse
|
||||
},
|
||||
export default {
|
||||
components: {
|
||||
vueFormGenerator: VueFormGenerator,
|
||||
collapse: Collapse
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
model: {
|
||||
language: 'en-UK',
|
||||
homeName: 'Our Home',
|
||||
filesGenerated: ['items', 'sitemap'],
|
||||
itemsType: 'text',
|
||||
itemsChannel: true,
|
||||
itemsIcons: true,
|
||||
floorsCount: 1,
|
||||
GroundFloor: []
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
model: {
|
||||
language: 'en-UK',
|
||||
homeName: 'Our Home',
|
||||
filesGenerated: ['items', 'sitemap'],
|
||||
itemsType: 'text',
|
||||
itemsChannel: true,
|
||||
itemsIcons: true,
|
||||
itemsTags: true,
|
||||
floors: []
|
||||
},
|
||||
|
||||
schema: {
|
||||
groups: [
|
||||
{ legend: '', fields: schema.basicFields },
|
||||
{ legend: 'Floors', fields: schema.floorsFields },
|
||||
{ legend: 'Rooms', fields: schema.roomsFields },
|
||||
{ legend: 'Objects', fields: schema.objectsFields },
|
||||
{ legend: '', fields: schema.settingsFields }
|
||||
],
|
||||
},
|
||||
|
||||
formOptions: {
|
||||
validateAfterLoad: true,
|
||||
validateAfterChanged: true,
|
||||
fieldIdPrefix: 'user-'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Generates textual Items
|
||||
*/
|
||||
generateItems,
|
||||
|
||||
/**
|
||||
* Generates JSON array of Items,
|
||||
* understandable by REST API
|
||||
*/
|
||||
generateItemsJson,
|
||||
|
||||
/**
|
||||
* Generates textual Sitemap
|
||||
*/
|
||||
generateSitemap,
|
||||
|
||||
/**
|
||||
* Generates HABPanel dashboard JSON config
|
||||
*/
|
||||
generateDashboard,
|
||||
|
||||
/**
|
||||
* Gets i18n configuration from ESH service
|
||||
*/
|
||||
getLocale: function() {
|
||||
const DEFAULT_LOCALE = 'en-UK';
|
||||
this.$http
|
||||
.get('services/org.eclipse.smarthome.core.i18nprovider/config')
|
||||
.then(response => {
|
||||
const body = response.body;
|
||||
let selectedLang = DEFAULT_LOCALE;
|
||||
|
||||
if (body.language && body.region) {
|
||||
let langId = body.language + '-' + body.region;
|
||||
let lang = _.find(languages, { id: langId });
|
||||
selectedLang = lang ? langId : DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
this.$data.model.language = selectedLang;
|
||||
this.fetchTranslations(selectedLang);
|
||||
})
|
||||
.catch(reason => {
|
||||
this.$data.model.language = DEFAULT_LOCALE;
|
||||
this.fetchTranslations(DEFAULT_LOCALE);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads translation file from the `/i18n/` folder
|
||||
* and assigns new `name` properties to the definitions.
|
||||
*/
|
||||
fetchTranslations: function(language) {
|
||||
let root = window.location.href.replace('/index.html', '');
|
||||
this.$http
|
||||
.get(root + '/i18n/' + language + '.json')
|
||||
.then(response => {
|
||||
this.$i18n.locale = language;
|
||||
this.$i18n.setLocaleMessage(language, response.body);
|
||||
|
||||
let stack = [...rooms, ...objects];
|
||||
let roomsModel = _.chain(this.$data.model)
|
||||
.pickBy((value, key) => _.endsWith(key, OBJECTS_SUFFIX))
|
||||
.value();
|
||||
|
||||
floors.forEach(function(item) {
|
||||
stack = [...stack, item];
|
||||
|
||||
if (this.$data.model[item.value]) {
|
||||
stack = [...stack, ...this.$data.model[item.value]];
|
||||
formOptions: {
|
||||
validateAfterLoad: true,
|
||||
validateAfterChanged: true,
|
||||
fieldIdPrefix: 'user-'
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
if (!_.isEmpty(roomsModel)) {
|
||||
_.forOwn(roomsModel, (room) => { stack = [...stack, ...room] });
|
||||
}
|
||||
computed: {
|
||||
schema() {
|
||||
let roomsFields = [];
|
||||
this.model.floors.forEach((floor) => {
|
||||
roomsFields.push(roomsSelect(floor.value, floor.name || floor.value));
|
||||
});
|
||||
|
||||
stack.forEach(item => {
|
||||
if (!item.custom) {
|
||||
item.name = this.$i18n.t(item.value);
|
||||
let objectsFields = [];
|
||||
this.model.floors.forEach((floor) => {
|
||||
if (this.model[floor.value]) {
|
||||
this.model[floor.value].forEach((element) => {
|
||||
let name = (element.name || element.value) + ' (' + floor.name + ')';
|
||||
let fieldName = floor.value + '_' + element.value + OBJECTS_SUFFIX;
|
||||
objectsFields.push(objectSelect(fieldName, name));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
groups: [
|
||||
{legend: '', fields: schema.basicFields},
|
||||
{legend: 'Floors', fields: schema.floorsFields},
|
||||
{legend: 'Rooms', fields: roomsFields},
|
||||
{legend: 'Objects', fields: objectsFields},
|
||||
{legend: '', fields: schema.settingsFields}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
this.$forceUpdate();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
resizeAffix(event) {
|
||||
let bodyWidth = document.body && document.body.clientWidth;
|
||||
let formEl = document.getElementById('form');
|
||||
let previewEl = document.getElementById('preview');
|
||||
let formWidth = formEl && form.clientWidth;
|
||||
let hidden = 'hidden-xs-up';
|
||||
methods: {
|
||||
/**
|
||||
* Generates textual Items
|
||||
*/
|
||||
generateItems,
|
||||
|
||||
previewEl.style.width = formWidth + 'px';
|
||||
/**
|
||||
* Generates JSON array of Items,
|
||||
* understandable by REST API
|
||||
*/
|
||||
generateItemsJson,
|
||||
|
||||
if (bodyWidth <= 599) {
|
||||
previewEl.classList.add(hidden);
|
||||
document.querySelector('.navbar-toggler-icon').classList.remove(hidden);
|
||||
document.querySelector('.navbar-close-icon').classList.add(hidden);
|
||||
} else {
|
||||
previewEl.classList.remove(hidden);
|
||||
}
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Generates textual Sitemap
|
||||
*/
|
||||
generateSitemap,
|
||||
|
||||
http: {
|
||||
root: window.location.origin + '/rest/'
|
||||
},
|
||||
/**
|
||||
* Generates HABPanel dashboard JSON config
|
||||
*/
|
||||
generateDashboard,
|
||||
|
||||
mounted: function() {
|
||||
this.getLocale();
|
||||
/**
|
||||
* Gets i18n configuration from ESH service
|
||||
*/
|
||||
getLocale: function () {
|
||||
const DEFAULT_LOCALE = 'en-UK';
|
||||
this.$http
|
||||
.get('services/org.eclipse.smarthome.core.i18nprovider/config')
|
||||
.then(response => {
|
||||
const body = response.body;
|
||||
let selectedLang = DEFAULT_LOCALE;
|
||||
|
||||
this.$nextTick(function() {
|
||||
window.addEventListener('resize', this.resizeAffix);
|
||||
this.resizeAffix();
|
||||
})
|
||||
},
|
||||
if (body.language && body.region) {
|
||||
let langId = body.language + '-' + body.region;
|
||||
let lang = _.find(languages, {id: langId});
|
||||
selectedLang = lang ? langId : DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
watch: {
|
||||
locale(val) {
|
||||
this.$i18n.locale = val
|
||||
this.$data.model.language = selectedLang;
|
||||
this.fetchTranslations(selectedLang);
|
||||
})
|
||||
.catch(reason => {
|
||||
this.$data.model.language = DEFAULT_LOCALE;
|
||||
this.fetchTranslations(DEFAULT_LOCALE);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads translation file from the `/i18n/` folder
|
||||
* and assigns new `name` properties to the definitions.
|
||||
*/
|
||||
fetchTranslations: function (language) {
|
||||
let root = window.location.href.replace(/\/index.html.*/, '');
|
||||
this.$http
|
||||
.get(root + '/i18n/' + language + '.json')
|
||||
.then(response => {
|
||||
this.$i18n.locale = language;
|
||||
this.$i18n.setLocaleMessage(language, response.body);
|
||||
|
||||
let stack = [...rooms, ...objects];
|
||||
let roomsModel = _.chain(this.$data.model)
|
||||
.pickBy((value, key) => _.endsWith(key, OBJECTS_SUFFIX))
|
||||
.value();
|
||||
|
||||
floors.forEach(function (item) {
|
||||
stack = [...stack, item];
|
||||
|
||||
if (this.$data.model[item.value]) {
|
||||
stack = [...stack, ...this.$data.model[item.value]];
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
if (!_.isEmpty(roomsModel)) {
|
||||
_.forOwn(roomsModel, (room) => {
|
||||
stack = [...stack, ...room]
|
||||
});
|
||||
}
|
||||
|
||||
stack.forEach(item => {
|
||||
if (!item.custom) {
|
||||
item.name = this.$i18n.t(item.value);
|
||||
}
|
||||
});
|
||||
this.$forceUpdate();
|
||||
});
|
||||
},
|
||||
|
||||
resizeAffix(event) {
|
||||
let bodyWidth = document.body && document.body.clientWidth;
|
||||
let formEl = document.getElementById('form');
|
||||
let previewEl = document.getElementById('preview');
|
||||
let formWidth = formEl && form.clientWidth;
|
||||
let hidden = 'd-none';
|
||||
|
||||
previewEl.style.width = formWidth + 'px';
|
||||
|
||||
if (bodyWidth <= 599) {
|
||||
previewEl.classList.add(hidden);
|
||||
document.querySelector('.navbar-toggler-icon').classList.remove(hidden);
|
||||
document.querySelector('.navbar-close-icon').classList.add(hidden);
|
||||
} else {
|
||||
previewEl.classList.remove(hidden);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
http: {
|
||||
root: window.location.origin + '/rest/'
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this.getLocale();
|
||||
|
||||
this.$nextTick(function () {
|
||||
window.addEventListener('resize', this.resizeAffix);
|
||||
this.resizeAffix();
|
||||
})
|
||||
},
|
||||
|
||||
watch: {
|
||||
locale(val) {
|
||||
this.$i18n.locale = val
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resizeAffix);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resizeAffix);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -1,41 +1,41 @@
|
||||
<template>
|
||||
<nav class="navbar fixed-top">
|
||||
<a class="navbar-brand mr-auto" href="#">HomeBuilder</a>
|
||||
<nav class="navbar fixed-top">
|
||||
<a class="navbar-brand mr-auto" href="#">HomeBuilder</a>
|
||||
|
||||
<button @click="togglePreview" type="button" aria-label="Toggle preview" class="navbar-toggler navbar-toggler-right">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<span class="navbar-close-icon hidden-xs-up">×</span>
|
||||
</button>
|
||||
<button @click="togglePreview" type="button" aria-label="Toggle preview" class="navbar-toggler navbar-toggler-right">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<span class="navbar-close-icon d-none">×</span>
|
||||
</button>
|
||||
|
||||
</nav>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.navbar {
|
||||
background: #E64A19;
|
||||
color: #fff;
|
||||
}
|
||||
.navbar {
|
||||
background: #E64A19;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-close-icon {
|
||||
color: #200f04;
|
||||
font-size: 36px;
|
||||
padding-left: 1px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.navbar-close-icon {
|
||||
color: #200f04;
|
||||
font-size: 36px;
|
||||
padding-left: 1px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
togglePreview() {
|
||||
document.getElementById('preview').classList.toggle('hidden-xs-up');
|
||||
document.querySelector('.navbar-close-icon').classList.toggle('hidden-xs-up');
|
||||
document.querySelector('.navbar-toggler-icon').classList.toggle('hidden-xs-up');
|
||||
export default {
|
||||
methods: {
|
||||
togglePreview() {
|
||||
document.getElementById('preview').classList.toggle('d-none');
|
||||
document.querySelector('.navbar-close-icon').classList.toggle('d-none');
|
||||
document.querySelector('.navbar-toggler-icon').classList.toggle('d-none');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -6,12 +6,14 @@
|
||||
<div class="text-center"
|
||||
v-if="uid === 'items' && content instanceof Array">
|
||||
<button @click="createItems"
|
||||
class="btn btn-create-items btn-outline-info">{{ createText }}</button>
|
||||
class="btn btn-create-items btn-outline-info">{{ createText }}
|
||||
</button>
|
||||
</div>
|
||||
<button @click="copy"
|
||||
v-if="!(uid === 'items' && content instanceof Array)"
|
||||
class="btn-clipboard"
|
||||
title="Copy to clipboard">{{ copyText }}</button>
|
||||
title="Copy to clipboard">{{ copyText }}
|
||||
</button>
|
||||
|
||||
<span class="pb-1" v-if="uid === 'habpanel'">
|
||||
Paste the content in
|
||||
@ -25,69 +27,69 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.card-block {
|
||||
padding: 0.5rem !important;
|
||||
.card-block {
|
||||
padding: 0.5rem !important;
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
pre {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Clipboard from 'v-clipboard'
|
||||
import Vue from 'vue'
|
||||
import Clipboard from 'v-clipboard'
|
||||
|
||||
Vue.use(Clipboard);
|
||||
Vue.use(Clipboard);
|
||||
|
||||
export default {
|
||||
props: ['uid', 'heading', 'content'],
|
||||
data: () => ({
|
||||
copyText: 'Copy',
|
||||
createText: 'Create Items'
|
||||
}),
|
||||
http: {
|
||||
root: window.location.origin + '/rest/'
|
||||
},
|
||||
methods: {
|
||||
copy() {
|
||||
let content = this.content;
|
||||
|
||||
if (typeof content === 'object') {
|
||||
content = JSON.stringify(this.content, null, 2);
|
||||
} else {
|
||||
content = content
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
this.$clipboard(content);
|
||||
|
||||
this.copyText = 'Copied!';
|
||||
|
||||
setTimeout(() => {
|
||||
this.copyText = 'Copy';
|
||||
}, 500);
|
||||
export default {
|
||||
props: ['uid', 'heading', 'content'],
|
||||
data: () => ({
|
||||
copyText: 'Copy',
|
||||
createText: 'Create Items'
|
||||
}),
|
||||
http: {
|
||||
root: window.location.origin + '/rest/'
|
||||
},
|
||||
createItems() {
|
||||
let content = this.content;
|
||||
if (typeof content === 'object') {
|
||||
this.$http
|
||||
.put('items', content)
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
this.createText = 'Done!';
|
||||
} else {
|
||||
this.createText = 'Error!';
|
||||
}
|
||||
methods: {
|
||||
copy() {
|
||||
let content = this.content;
|
||||
|
||||
setTimeout(() => {
|
||||
this.createText = 'Create Items';
|
||||
}, 500);
|
||||
});
|
||||
if (typeof content === 'object') {
|
||||
content = JSON.stringify(this.content, null, 2);
|
||||
} else {
|
||||
content = content
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
this.$clipboard(content);
|
||||
|
||||
this.copyText = 'Copied!';
|
||||
|
||||
setTimeout(() => {
|
||||
this.copyText = 'Copy';
|
||||
}, 500);
|
||||
},
|
||||
createItems() {
|
||||
let content = this.content;
|
||||
if (typeof content === 'object') {
|
||||
this.$http
|
||||
.put('items', content)
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
this.createText = 'Done!';
|
||||
} else {
|
||||
this.createText = 'Error!';
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.createText = 'Create Items';
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,89 +2,95 @@
|
||||
* i18n definitions
|
||||
*/
|
||||
export var languages = [
|
||||
{ name: 'English', id: 'en-UK' },
|
||||
{ name: 'French', id: 'fr-FR' },
|
||||
{ name: 'German', id: 'de-DE' },
|
||||
{ name: 'Italian', id: 'it-IT' },
|
||||
{ name: 'Korean', id: 'ko-KR' },
|
||||
{ name: 'Polish', id: 'pl-PL' },
|
||||
{ name: 'Russian', id: 'ru-RU' },
|
||||
{ name: 'Spanish', id: 'es-ES' }
|
||||
{name: 'Czech', id: 'cs-CZ'},
|
||||
{name: 'Dutch', id: 'nl-NL'},
|
||||
{name: 'English', id: 'en-UK'},
|
||||
{name: 'French', id: 'fr-FR'},
|
||||
{name: 'German', id: 'de-DE'},
|
||||
{name: 'Hungarian', id: 'hu-HU'},
|
||||
{name: 'Italian', id: 'it-IT'},
|
||||
{name: 'Korean', id: 'ko-KR'},
|
||||
{name: 'Polish', id: 'pl-PL'},
|
||||
{name: 'Portuguese (BR)', id: 'pt-BR'},
|
||||
{name: 'Russian', id: 'ru-RU'},
|
||||
{name: 'Spanish', id: 'es-ES'},
|
||||
{name: 'Tagalog (Philippines)', id: 'tl-PH'},
|
||||
{name: 'Turkish', id: 'tr-TR'}
|
||||
];
|
||||
|
||||
/**
|
||||
* Structure definitions
|
||||
*/
|
||||
export var floors = [
|
||||
{ abbr: 'GF', value: 'GroundFloor', icon: 'groundfloor' },
|
||||
{ abbr: 'FF', value: 'FirstFloor', icon: 'firstfloor' },
|
||||
{ abbr: 'F2', value: 'SecondFloor', icon: 'attic' },
|
||||
{ abbr: 'F3', value: 'ThirdFloor', icon: 'attic' },
|
||||
{ abbr: 'F4', value: 'FourthFloor', icon: 'attic' }
|
||||
{abbr: 'C', value: 'Cellar', icon: 'cellar', tags: ['Basement']},
|
||||
{abbr: 'OU', value: 'Outside', icon: 'garden', tags: ['Outdoor']},
|
||||
{abbr: 'GF', value: 'GroundFloor', icon: 'groundfloor', tags: ['GroundFloor']},
|
||||
{abbr: 'FF', value: 'FirstFloor', icon: 'firstfloor', tags: ['FirstFloor']},
|
||||
{abbr: 'F2', value: 'SecondFloor', icon: 'attic', tags: ['Attic']},
|
||||
{abbr: 'F3', value: 'ThirdFloor', icon: 'attic', tags: ['Attic']},
|
||||
{abbr: 'F4', value: 'FourthFloor', icon: 'attic', tags: ['Attic']},
|
||||
{abbr: 'AT', value: 'Attic', icon: 'attic', tags: ['Attic']}
|
||||
];
|
||||
|
||||
export var rooms = [
|
||||
{ value: 'Attic', icon: 'attic' },
|
||||
{ value: 'Balcony', icon: '' },
|
||||
{ value: 'Backyard', icon: 'lawnmower' },
|
||||
{ value: 'Basement', icon: 'cellar' },
|
||||
{ value: 'Bathroom', icon: 'bath' },
|
||||
{ value: 'Bedroom', icon: 'bedroom' },
|
||||
{ value: 'Boiler', icon: 'gas' },
|
||||
{ value: 'Wardrobe', icon: 'wardrobe' },
|
||||
{ value: 'Cellar', icon: 'cellar' },
|
||||
{ value: 'Corridor', icon: 'corridor' },
|
||||
{ value: 'Deck', icon: '' },
|
||||
{ value: 'Dining', icon: '' },
|
||||
{ value: 'Downstairs', icon: 'cellar' },
|
||||
{ value: 'Driveway', icon: '' },
|
||||
{ value: 'Entryway', icon: 'frontdoor' },
|
||||
{ value: 'FamilyRoom', icon: 'parents_2_4' },
|
||||
{ value: 'FrontYard', icon: 'lawnmower' },
|
||||
{ value: 'Garage', icon: 'garage' },
|
||||
{ value: 'GuestHouse', icon: 'house' },
|
||||
{ value: 'GuestRoom', icon: 'parents_4_3' },
|
||||
{ value: 'Hallway', icon: 'corridor' },
|
||||
{ value: 'HomeCinema', icon: 'screen' },
|
||||
{ value: 'KidsRoom', icon: 'girl_3' },
|
||||
{ value: 'Kitchen', icon: 'kitchen' },
|
||||
{ value: 'LaundryRoom', icon: 'washingmachine' },
|
||||
{ value: 'Library', icon: 'office' },
|
||||
{ value: 'LivingRoom', icon: 'sofa' },
|
||||
{ value: 'LivingDining', icon: 'sofa' },
|
||||
{ value: 'Loft', icon: 'attic' },
|
||||
{ value: 'Lounge', icon: 'sofa' },
|
||||
{ value: 'MasterBedroom', icon: 'bedroom_red' },
|
||||
{ value: 'NannyRoom', icon: 'woman_1' },
|
||||
{ value: 'Office', icon: 'office' },
|
||||
{ value: 'Outside', icon: 'garden' },
|
||||
{ value: 'Patio', icon: 'terrace' },
|
||||
{ value: 'Porch', icon: 'group' },
|
||||
{ value: 'Stairwell', icon: 'qualityofservice' },
|
||||
{ value: 'StorageRoom', icon: 'suitcase' },
|
||||
{ value: 'Studio', icon: 'pantry' },
|
||||
{ value: 'Shed', icon: 'greenhouse' },
|
||||
{ value: 'Toilet', icon: 'toilet' },
|
||||
{ value: 'Terrace', icon: 'terrace' },
|
||||
{ value: 'Upstairs', icon: 'firstfloor' }
|
||||
{value: 'Balcony', icon: '', tags: ['Outdoor']},
|
||||
{value: 'Backyard', icon: 'lawnmower', tags: ['Garden']},
|
||||
{value: 'Basement', icon: 'cellar', tags: ['Basement']},
|
||||
{value: 'Bathroom', icon: 'bath', tags: ['Bathroom']},
|
||||
{value: 'Bedroom', icon: 'bedroom', tags: ['Bedroom']},
|
||||
{value: 'Boiler', icon: 'gas', tags: ['Room']},
|
||||
{value: 'Wardrobe', icon: 'wardrobe', tags: []},
|
||||
{value: 'Cellar', icon: 'cellar', tags: ['Cellar']},
|
||||
{value: 'Corridor', icon: 'corridor', tags: ['Corridor']},
|
||||
{value: 'Deck', icon: '', tags: ['Terrace']},
|
||||
{value: 'Dining', icon: '', tags: ['Room']},
|
||||
{value: 'Driveway', icon: '', tags: ['Outdoor']},
|
||||
{value: 'Entryway', icon: 'frontdoor', tags: ['Room']},
|
||||
{value: 'FamilyRoom', icon: 'parents_2_4', tags: ['Room']},
|
||||
{value: 'FrontYard', icon: 'lawnmower', tags: ['Garden']},
|
||||
{value: 'Garage', icon: 'garage', tags: ['Garage']},
|
||||
{value: 'GuestHouse', icon: 'house', tags: ['House']},
|
||||
{value: 'GuestRoom', icon: 'parents_4_3', tags: ['Room']},
|
||||
{value: 'Hallway', icon: 'corridor', tags: ['Corridor']},
|
||||
{value: 'HomeCinema', icon: 'screen', tags: ['Room']},
|
||||
{value: 'KidsRoom', icon: 'girl_3', tags: ['Room']},
|
||||
{value: 'Kitchen', icon: 'kitchen', tags: ['Kitchen']},
|
||||
{value: 'LaundryRoom', icon: 'washingmachine', tags: ['Room']},
|
||||
{value: 'Library', icon: 'office', tags: ['Room']},
|
||||
{value: 'LivingRoom', icon: 'sofa', tags: ['LivingRoom']},
|
||||
{value: 'LivingDining', icon: 'sofa', tags: ['LivingRoom']},
|
||||
{value: 'Loft', icon: 'attic', tags: ['Room']},
|
||||
{value: 'Lounge', icon: 'sofa', tags: ['Room']},
|
||||
{value: 'MasterBedroom', icon: 'bedroom_red', tags: ['Bedroom']},
|
||||
{value: 'NannyRoom', icon: 'woman_1', tags: ['Room']},
|
||||
{value: 'Office', icon: 'office', tags: ['Room']},
|
||||
{value: 'Outside', icon: 'garden', tags: ['Outside']},
|
||||
{value: 'Patio', icon: 'terrace', tags: ['Outside']},
|
||||
{value: 'Porch', icon: 'group', tags: ['Outside']},
|
||||
{value: 'Stairwell', icon: 'qualityofservice', tags: []},
|
||||
{value: 'StorageRoom', icon: 'suitcase', tags: ['Room']},
|
||||
{value: 'Studio', icon: 'pantry', tags: ['Room']},
|
||||
{value: 'Shed', icon: 'greenhouse', tags: ['Garage']},
|
||||
{value: 'Toilet', icon: 'toilet', tags: ['Bathroom']},
|
||||
{value: 'Terrace', icon: 'terrace', tags: ['Terrace']}
|
||||
];
|
||||
|
||||
/**
|
||||
* Collection of objects (sensors, smart devices etc.) controllable by openHAB.
|
||||
*/
|
||||
export var objects = [
|
||||
{ value: 'Light', icon: 'light', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]' },
|
||||
{ value: 'Window', icon: 'window', type: 'Contact:OR(OPEN, CLOSED)', unit: '[MAP(en.map):%s]' },
|
||||
{ value: 'Door', icon: 'door', type: 'Contact:OR(OPEN, CLOSED)', unit: '[MAP(en.map):%s]' },
|
||||
{ value: 'Motion', icon: 'motion', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]' },
|
||||
{ value: 'Power', icon: 'poweroutlet', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]' },
|
||||
{ value: 'Shutter', icon: 'rollershutter', type: 'Rollershutter:OR(UP, DOWN)', unit: '[(%d)]' },
|
||||
{ value: 'Blind', icon: 'blinds', type: 'Dimmer', unit: '[%d %%]' },
|
||||
{ value: 'Fan', icon: 'fan_ceiling', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]' },
|
||||
{ value: 'AirCon', icon: 'snow', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]' },
|
||||
{ value: 'Heating', icon: 'heating', type: 'Number:AVG', unit: '[%.1f °C]' },
|
||||
{ value: 'Temperature', icon: 'temperature', type: 'Number:AVG', unit: '[%.1f °C]' },
|
||||
{ value: 'Humidity', icon: 'humidity', type: 'Number:AVG', unit: '[%d %%]' },
|
||||
{value: 'Light', icon: 'light', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]', tags: ['Lighting', 'Switchable']},
|
||||
{value: 'Window', icon: 'window', type: 'Contact:OR(OPEN, CLOSED)', unit: '[MAP(en.map):%s]', tags: ['Window']},
|
||||
{value: 'Door', icon: 'door', type: 'Contact:OR(OPEN, CLOSED)', unit: '[MAP(en.map):%s]', tags: ['Door']},
|
||||
{value: 'Motion', icon: 'motion', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]', tags: ['MotionDetector', 'Switchable']},
|
||||
{value: 'Power', icon: 'poweroutlet', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]', tags: ['Switch', 'Switchable']},
|
||||
{value: 'Shutter', icon: 'rollershutter', type: 'Rollershutter:OR(UP, DOWN)', unit: '[(%d)]', tags: ['Rollershutter']},
|
||||
{value: 'Blind', icon: 'blinds', type: 'Dimmer', unit: '[%d %%]', tags: ['Blinds', 'Switchable']},
|
||||
{value: 'Fan', icon: 'fan_ceiling', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]', tags: ['Switchable']},
|
||||
{value: 'AirCon', icon: 'snow', type: 'Switch:OR(ON, OFF)', unit: '[(%d)]', tags: ['HVAC', 'Switchable']},
|
||||
{value: 'Heating', icon: 'heating', type: 'Number:AVG', unit: '[%.1f °C]', tags: ['HVAC']},
|
||||
{value: 'Temperature', icon: 'temperature', type: 'Number:AVG', unit: '[%.1f °C]', tags: ['Temperature']},
|
||||
{value: 'Humidity', icon: 'humidity', type: 'Number:AVG', unit: '[%d %%]', tags: ['Humidity']}
|
||||
];
|
||||
|
||||
export const OBJECTS_SUFFIX = '_objects';
|
||||
|
@ -1,25 +1,25 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as s from 'underscore.string'
|
||||
import { validators } from 'vue-form-generator'
|
||||
import { languages, floors, rooms, objects, OBJECTS_SUFFIX } from './definitions'
|
||||
import {validators} from 'vue-form-generator'
|
||||
import {floors, languages, objects, OBJECTS_SUFFIX, rooms} from './definitions'
|
||||
|
||||
/**
|
||||
* Invoked when language select has changed its value.
|
||||
*
|
||||
*
|
||||
* @param {Object} model
|
||||
* @param {Object} newVal
|
||||
*/
|
||||
function languageChanged(model, newVal) {
|
||||
this.$parent.$parent.fetchTranslations(newVal);
|
||||
this.$parent.$parent.$parent.fetchTranslations(newVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom room entry
|
||||
*
|
||||
* @param {string} newTag
|
||||
* @param {string} id
|
||||
* @param {Object} options
|
||||
* @param {string} value
|
||||
*
|
||||
* @param {string} newTag
|
||||
* @param {string} id
|
||||
* @param {Object} options
|
||||
* @param {string} value
|
||||
*/
|
||||
function newRoomTag(newTag, id, options, value) {
|
||||
const tag = {
|
||||
@ -32,15 +32,17 @@ function newRoomTag(newTag, id, options, value) {
|
||||
.cleanDiacritics()
|
||||
.classify()
|
||||
.value()
|
||||
}
|
||||
};
|
||||
rooms.push(tag);
|
||||
value.push(tag);
|
||||
};
|
||||
if (value) {
|
||||
value.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group function for a given
|
||||
* Item's type
|
||||
* @param {*} type
|
||||
* @param {*} type
|
||||
*/
|
||||
function getGroupFunc(type) {
|
||||
let func = '';
|
||||
@ -67,11 +69,11 @@ function getGroupFunc(type) {
|
||||
|
||||
/**
|
||||
* Creates a custom object entry
|
||||
*
|
||||
* @param {string} newTag
|
||||
* @param {string} id
|
||||
* @param {Object} options
|
||||
* @param {string} value
|
||||
*
|
||||
* @param {string} newTag
|
||||
* @param {string} id
|
||||
* @param {Object} options
|
||||
* @param {string} value
|
||||
*/
|
||||
function newObjectTag(newTag, id, options, value) {
|
||||
let split = newTag.split(':');
|
||||
@ -91,65 +93,71 @@ function newObjectTag(newTag, id, options, value) {
|
||||
.cleanDiacritics()
|
||||
.classify()
|
||||
.value()
|
||||
}
|
||||
};
|
||||
|
||||
objects.push(tag);
|
||||
value.push(tag);
|
||||
};
|
||||
if (value) {
|
||||
value.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is being executed when
|
||||
* collection of rooms in floor multiselect field
|
||||
* Creates a custom floor entry
|
||||
*
|
||||
* @param {string} newTag
|
||||
* @param {string} id
|
||||
* @param {Object} options
|
||||
* @param {string} value
|
||||
*/
|
||||
function newFloorTag(newTag, id, options, value) {
|
||||
const tag = {
|
||||
abbr: s(newTag)
|
||||
.trim()
|
||||
.toUpperCase()
|
||||
.cleanDiacritics()
|
||||
.substr(0, 1)
|
||||
.value(),
|
||||
name: newTag,
|
||||
icon: 'none',
|
||||
custom: true,
|
||||
value: s(newTag)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.cleanDiacritics()
|
||||
.classify()
|
||||
.value()
|
||||
};
|
||||
floors.push(tag);
|
||||
if (value) {
|
||||
value.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is being executed when
|
||||
* collection of rooms in floor vueMultiSelect field
|
||||
* has changed.
|
||||
*
|
||||
*
|
||||
* If there's a new `room` in collection,
|
||||
* a new dynamic field is added to the floor object, e.g.
|
||||
* `"GroundFloor": [ { name: 'Bedroom', value: 'Bedroom', icon: 'bed' }]`
|
||||
*
|
||||
*
|
||||
* If an entry was removed from the collection,
|
||||
* a dynamic field is removed accordingly.
|
||||
*
|
||||
* @param {Object} model
|
||||
*
|
||||
* @param {Object} model
|
||||
* @param {Array} newVal
|
||||
* @param {Array} oldVal
|
||||
* @param {Object} field
|
||||
* @param {Array} oldVal
|
||||
* @param {Object} field
|
||||
*/
|
||||
function roomsChanged(model, newVal, oldVal, field) {
|
||||
let objectsFields = _.find(this.$options.parent.groups, { legend: 'Objects' }).fields;
|
||||
let floor = field.model;
|
||||
let oldList = oldVal ? _.map(oldVal, 'value') : [];
|
||||
let newList = _.map(newVal, 'value');
|
||||
let lastRemoved = _.first(_.difference(oldList, newList));
|
||||
let lastItem = _.last(newList);
|
||||
let roomName = '';
|
||||
|
||||
if (lastItem && !lastRemoved) {
|
||||
let room = _.find(rooms, { value: lastItem });
|
||||
roomName = floor + '_' + room.value + OBJECTS_SUFFIX;
|
||||
objectsFields.push({
|
||||
type: 'multiselect',
|
||||
label: (room.name || room.value) + ' (' + _.find(floors, { value: floor }).name + ')',
|
||||
styleClasses: 'rooms-list',
|
||||
model: roomName,
|
||||
placeholder: 'Type to search object',
|
||||
selectOptions: {
|
||||
multiple: true,
|
||||
hideSelected: true,
|
||||
closeOnSelect: false,
|
||||
selectLabel: '',
|
||||
trackBy: 'value',
|
||||
label: 'name',
|
||||
searchable: true,
|
||||
taggable: true,
|
||||
onNewTag: newObjectTag
|
||||
},
|
||||
values: objects
|
||||
});
|
||||
}
|
||||
let floor = field.model;
|
||||
|
||||
if (lastRemoved) {
|
||||
roomName = floor + '_' + lastRemoved + OBJECTS_SUFFIX;
|
||||
_.remove(objectsFields, { model: roomName });
|
||||
let roomName = floor + '_' + lastRemoved + OBJECTS_SUFFIX;
|
||||
delete model[roomName];
|
||||
}
|
||||
}
|
||||
@ -158,11 +166,12 @@ function roomsChanged(model, newVal, oldVal, field) {
|
||||
* Schema describing the basic form
|
||||
* generated by vue-form-generator
|
||||
*/
|
||||
export var basicFields = [{
|
||||
export var basicFields = [
|
||||
{
|
||||
type: 'select',
|
||||
model: 'language',
|
||||
label: 'Please select your language',
|
||||
values: function() {
|
||||
values: function () {
|
||||
return languages;
|
||||
},
|
||||
selectOptions: {
|
||||
@ -170,7 +179,6 @@ export var basicFields = [{
|
||||
},
|
||||
onChanged: languageChanged
|
||||
},
|
||||
|
||||
{
|
||||
type: 'input',
|
||||
inputType: 'text',
|
||||
@ -184,36 +192,42 @@ export var basicFields = [{
|
||||
];
|
||||
|
||||
export var floorsFields = [{
|
||||
type: 'radios',
|
||||
model: 'floorsCount',
|
||||
label: 'Number of floors',
|
||||
styleClasses: 'floors-number',
|
||||
values: [1, 2, 3, 4, 5],
|
||||
onChanged: function(model, newVal, oldVal, field) {
|
||||
let roomsFields = _.find(this.$options.parent.groups, { legend: 'Rooms' }).fields;
|
||||
if (newVal <= 5 && newVal > oldVal) {
|
||||
for (var i = oldVal; i < newVal; i++) {
|
||||
let floor = floors[i];
|
||||
let floorName = floor.value;
|
||||
let fieldExists = _.find(roomsFields, (field) => field.model === floorName);
|
||||
type: 'multiselect',
|
||||
label: 'Floors',
|
||||
styleClasses: 'rooms-list',
|
||||
model: 'floors',
|
||||
values: floors,
|
||||
placeholder: 'Type to search or add floor',
|
||||
selectOptions: {
|
||||
multiple: true,
|
||||
hideSelected: true,
|
||||
closeOnSelect: false,
|
||||
selectLabel: '',
|
||||
trackBy: 'value',
|
||||
label: 'name',
|
||||
searchable: true,
|
||||
taggable: true,
|
||||
tagPlaceholder: 'Add this as a new floor',
|
||||
onNewTag: newFloorTag
|
||||
},
|
||||
onChanged: function (model, newVal, oldVal, field) {
|
||||
let oldList = oldVal ? _.map(oldVal, 'value') : [];
|
||||
let newList = _.map(newVal, 'value');
|
||||
let lastRemoved = _.first(_.difference(oldList, newList));
|
||||
|
||||
if (!fieldExists) {
|
||||
roomsFields.push(roomsSelect(floorName, floor.name || floor.value));
|
||||
if (lastRemoved) {
|
||||
delete model[lastRemoved];
|
||||
for (let property in model) {
|
||||
if (model.hasOwnProperty(property) && property.startsWith(lastRemoved + '_')) {
|
||||
delete model[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newVal < oldVal) {
|
||||
for (var j = oldVal; j > newVal; j--) {
|
||||
let floorName = floors[j - 1].value;
|
||||
_.remove(roomsFields, { model: floorName });
|
||||
delete model[floorName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
export var settingsFields = [{
|
||||
export var settingsFields = [
|
||||
{
|
||||
type: 'checklist',
|
||||
model: 'filesGenerated',
|
||||
label: 'What would you like to generate?',
|
||||
@ -221,9 +235,9 @@ export var settingsFields = [{
|
||||
listBox: true,
|
||||
multiSelect: true,
|
||||
values: [
|
||||
{ name: 'Items', value: 'items' },
|
||||
{ name: 'Sitemap', value: 'sitemap' },
|
||||
{ name: 'Dashboard', value: 'habpanel' }
|
||||
{name: 'Items', value: 'items'},
|
||||
{name: 'Sitemap', value: 'sitemap'},
|
||||
{name: 'Dashboard', value: 'habpanel'}
|
||||
]
|
||||
},
|
||||
|
||||
@ -235,8 +249,8 @@ export var settingsFields = [{
|
||||
return model && model.filesGenerated.includes('items');
|
||||
},
|
||||
values: [
|
||||
{ name: 'Textual Configuration Files', value: 'text' },
|
||||
{ name: 'Internal Database', value: 'rest' }
|
||||
{name: 'Textual Configuration Files', value: 'text'},
|
||||
{name: 'Internal Database', value: 'rest'}
|
||||
]
|
||||
},
|
||||
|
||||
@ -275,7 +289,7 @@ export var settingsFields = [{
|
||||
type: 'switch',
|
||||
label: 'Include tags',
|
||||
model: 'itemsTags',
|
||||
default: false,
|
||||
default: true,
|
||||
textOn: 'Yes',
|
||||
textOff: 'No',
|
||||
valueOn: true,
|
||||
@ -287,13 +301,12 @@ export var settingsFields = [{
|
||||
];
|
||||
|
||||
/**
|
||||
* Generates a multiselect input with
|
||||
* rooms for specific floor (model)
|
||||
*
|
||||
* @param {string} model
|
||||
* @param {string} label
|
||||
* Generates a vueMultiSelect input with rooms for specific floor (model)
|
||||
*
|
||||
* @param {string} model
|
||||
* @param {string} label
|
||||
*/
|
||||
function roomsSelect(model, label) {
|
||||
export function roomsSelect(model, label) {
|
||||
return {
|
||||
type: 'multiselect',
|
||||
label: label,
|
||||
@ -318,13 +331,30 @@ function roomsSelect(model, label) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Field group schema for the rooms
|
||||
* Generates a vueMultiSelect input with objects for specific rooms (model)
|
||||
*
|
||||
* @param {string} model
|
||||
* @param {string} label
|
||||
*/
|
||||
export var roomsFields = [
|
||||
roomsSelect('GroundFloor', 'Ground Floor')
|
||||
];
|
||||
|
||||
/**
|
||||
* Field group schema for the objects
|
||||
*/
|
||||
export var objectsFields = [];
|
||||
export function objectSelect(model, label) {
|
||||
return {
|
||||
type: 'multiselect',
|
||||
label: label,
|
||||
styleClasses: 'rooms-list',
|
||||
model: model,
|
||||
values: objects,
|
||||
placeholder: 'Type to search or add object',
|
||||
selectOptions: {
|
||||
multiple: true,
|
||||
hideSelected: true,
|
||||
closeOnSelect: false,
|
||||
selectLabel: '',
|
||||
trackBy: 'value',
|
||||
label: 'name',
|
||||
searchable: true,
|
||||
taggable: true,
|
||||
tagPlaceholder: 'Add this as a new object',
|
||||
onNewTag: newObjectTag
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -62,10 +62,8 @@ function getWidgetType(type) {
|
||||
function makeWidgets(object, model) {
|
||||
let widgets = [];
|
||||
|
||||
for (var i = 0; i < model.floorsCount; i++) {
|
||||
var floor = floors[i];
|
||||
|
||||
if (floor && floor.value && !_.isUndefined(model[floor.value])) {
|
||||
model.floors.forEach((floor) => {
|
||||
if (!_.isUndefined(model[floor.value])) {
|
||||
model[floor.value].forEach(function(room) {
|
||||
let roomObjects = floor.value + '_' + room.value + OBJECTS_SUFFIX;
|
||||
let objectCollection = model[roomObjects] || [];
|
||||
@ -73,7 +71,7 @@ function makeWidgets(object, model) {
|
||||
|
||||
if (obj) {
|
||||
widgets.push(makeWidget({
|
||||
item: (model.floorsCount > 1 ? floor.abbr + '_' : '') + room.value + '_' + obj.value,
|
||||
item: (model.floors.length > 1 ? floor.abbr + '_' : '') + room.value + '_' + obj.value,
|
||||
name: room.name,
|
||||
type: getWidgetType(_.first(object.type.split(':'))),
|
||||
row: _.chunk(widgets, 6).length - 1,
|
||||
@ -83,10 +81,11 @@ function makeWidgets(object, model) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return widgets;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a full HABPanel dashboard set
|
||||
|
@ -29,4 +29,3 @@ const vm = new Vue({
|
||||
el: '#app',
|
||||
render: h => h(App)
|
||||
});
|
||||
|
||||
|
@ -1,22 +1,18 @@
|
||||
import * as _ from 'lodash'
|
||||
import { floors, objects, OBJECTS_SUFFIX } from './definitions'
|
||||
import {floors, objects, OBJECTS_SUFFIX} from './definitions'
|
||||
|
||||
export const GROUP_PREFIX = 'g';
|
||||
|
||||
export function addFloors(floor, model) {
|
||||
let items = [];
|
||||
if (model.floorsCount > 1) {
|
||||
items.push({
|
||||
type: 'Group',
|
||||
name: floor.abbr,
|
||||
label: floor.name || floor.value,
|
||||
category: model.itemsIcons ? floor.icon : '',
|
||||
groupNames: ['Home'],
|
||||
entryType: 'floor'
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
return [{
|
||||
type: 'Group',
|
||||
name: floor.abbr,
|
||||
label: floor.name || floor.value,
|
||||
category: model.itemsIcons ? floor.icon : '',
|
||||
groupNames: ['Home'],
|
||||
entryType: 'floor',
|
||||
tags: getTags(model, floor)
|
||||
}];
|
||||
}
|
||||
|
||||
export function addRooms(floor, model) {
|
||||
@ -25,7 +21,7 @@ export function addRooms(floor, model) {
|
||||
if (floor && floor.value && !_.isUndefined(model[floor.value])) {
|
||||
model[floor.value].forEach((room) => {
|
||||
let roomObjects = floor.value + '_' + room.value + OBJECTS_SUFFIX;
|
||||
let floorPrefix = model.floorsCount > 1 ? floor.abbr + '_' : '';
|
||||
let floorPrefix = model.floors.length > 1 ? floor.abbr + '_' : '';
|
||||
|
||||
items.push({
|
||||
type: 'Group',
|
||||
@ -34,9 +30,10 @@ export function addRooms(floor, model) {
|
||||
category: model.itemsIcons ? room.icon : '',
|
||||
groupNames: _.compact([
|
||||
'Home',
|
||||
model.floorsCount > 1 ? floor.abbr : ''
|
||||
model.floors.length > 1 ? floor.abbr : ''
|
||||
]),
|
||||
entryType: 'room'
|
||||
entryType: 'room',
|
||||
tags: getTags(model, room)
|
||||
});
|
||||
|
||||
items = [
|
||||
@ -65,23 +62,23 @@ export function addObjects(room, model, floorPrefix, roomObjects) {
|
||||
floorPrefix + room.value,
|
||||
GROUP_PREFIX + object.value
|
||||
],
|
||||
tags: addTags(object, model),
|
||||
tags: getTags(model, object),
|
||||
entryType: 'object'
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of object groups
|
||||
*
|
||||
* @param {Object} model
|
||||
*
|
||||
* @param {Object} model
|
||||
* @return {string}
|
||||
*/
|
||||
export function addObjectGroups(model) {
|
||||
let items = [];
|
||||
let chosenObjects = getChosenObjects(model);
|
||||
|
||||
chosenObjects.forEach(function(dev) {
|
||||
let object = _.find(objects, { value: dev });
|
||||
chosenObjects.forEach(function (dev) {
|
||||
let object = _.find(objects, {value: dev});
|
||||
|
||||
if (object) {
|
||||
let type = object.type.split(':');
|
||||
@ -98,7 +95,8 @@ export function addObjectGroups(model) {
|
||||
category: model.itemsIcons ? object.icon : '',
|
||||
groupNames: ['Home'],
|
||||
groupType: groupType,
|
||||
entryType: 'objectGroup'
|
||||
entryType: 'objectGroup',
|
||||
tags: getTags(model, object)
|
||||
};
|
||||
|
||||
if (groupFuncName) {
|
||||
@ -123,8 +121,8 @@ export function addObjectGroups(model) {
|
||||
/**
|
||||
* Gets list of objects chosen
|
||||
* from collection
|
||||
*
|
||||
* @param {*} model
|
||||
*
|
||||
* @param {*} model
|
||||
* @return {Array}
|
||||
*/
|
||||
export function getChosenObjects(model) {
|
||||
@ -136,41 +134,8 @@ export function getChosenObjects(model) {
|
||||
.value() || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object it creates a HomeKit-compatible
|
||||
* set of tags.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @param {Object} model
|
||||
* @return {Array}
|
||||
*/
|
||||
function addTags(object, model) {
|
||||
var type = _.first(object.type.split(':'));
|
||||
var tags = [];
|
||||
|
||||
switch (type) {
|
||||
case 'Switch':
|
||||
case 'Dimmer':
|
||||
case 'Color':
|
||||
tags.push('Switchable');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (object.value) {
|
||||
case 'Lamp':
|
||||
case 'Light':
|
||||
tags = ['Lighting'];
|
||||
break;
|
||||
case 'Motion':
|
||||
tags = [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return model.itemsTags ? tags : [];
|
||||
function getTags(model, entry) {
|
||||
return model.itemsTags ? entry.tags || [] : [];
|
||||
}
|
||||
|
||||
export function getItems(model) {
|
||||
@ -179,23 +144,23 @@ export function getItems(model) {
|
||||
name: 'Home',
|
||||
label: model.homeName,
|
||||
category: model.itemsIcons ? 'house' : '',
|
||||
entryType: 'home'
|
||||
entryType: 'home',
|
||||
tags: model.itemsTags ? ['Building'] : []
|
||||
}];
|
||||
|
||||
for (var i = 0; i < model.floorsCount; i++) {
|
||||
var floor = floors[i];
|
||||
model.floors.forEach((floor) => {
|
||||
|
||||
items = [
|
||||
...items,
|
||||
...addFloors(floor, model),
|
||||
...addRooms(floor, model)
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
items = [
|
||||
...items,
|
||||
...addObjectGroups(model)
|
||||
]
|
||||
];
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -204,8 +169,8 @@ export function getItems(model) {
|
||||
* Returns array of Items
|
||||
* without `entryType` which is not a valid parameter
|
||||
* for the request
|
||||
*
|
||||
* @param {*} model
|
||||
*
|
||||
* @param {*} model
|
||||
*/
|
||||
export function generateItemsJson(model) {
|
||||
let items = getItems(model);
|
||||
|
@ -8,7 +8,7 @@ export let sitemapName = '';
|
||||
function getFloorItem(floor, model) {
|
||||
let floorFrame = 'Frame ';
|
||||
|
||||
if (model.floorsCount > 1) {
|
||||
if (model.floors.length > 1) {
|
||||
let icon = model.itemsIcons && floor.icon ? '" icon="' + floor.icon : '';
|
||||
floorFrame += 'label="' + (floor.name || floor.value) + icon + '" ';
|
||||
}
|
||||
@ -29,11 +29,10 @@ function getRoomGroups(floor, model, floorPrefix) {
|
||||
function addFloorFrames(model) {
|
||||
let lines = [];
|
||||
|
||||
for (let i = 0; i < model.floorsCount; i++) {
|
||||
let floor = floors[i];
|
||||
model.floors.forEach((floor) => {
|
||||
let floorPrefix = '';
|
||||
|
||||
if (model.floorsCount > 1) {
|
||||
if (model.floors.length > 1) {
|
||||
floorPrefix = floor.abbr + '_';
|
||||
}
|
||||
|
||||
@ -44,10 +43,8 @@ function addFloorFrames(model) {
|
||||
s.pad(' ', 4) + '}'
|
||||
];
|
||||
|
||||
if (i < model.floorsCount) {
|
||||
lines.push('');
|
||||
}
|
||||
}
|
||||
lines.push('');
|
||||
});
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as AsciiTable from 'ascii-table'
|
||||
import { getItems } from './restItems'
|
||||
import {getItems} from './restItems'
|
||||
|
||||
/**
|
||||
* Generates item's type
|
||||
* e.g. `Switch` or `Group`
|
||||
* or `Group:Switch:OR(ON, OFF)`
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Object} model
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Object} model
|
||||
* @return {string}
|
||||
*/
|
||||
function generateType(item) {
|
||||
@ -27,7 +27,7 @@ function generateType(item) {
|
||||
|
||||
/**
|
||||
* Generates a label for the Item
|
||||
* @param {Object} item
|
||||
* @param {Object} item
|
||||
* @return {string}
|
||||
*/
|
||||
function generateLabel(item) {
|
||||
@ -36,9 +36,9 @@ function generateLabel(item) {
|
||||
|
||||
/**
|
||||
* Generates an icon if there's any.
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Object} model
|
||||
*
|
||||
* @param {Object} item
|
||||
* @param {Object} model
|
||||
* @return {string}
|
||||
*/
|
||||
function generateIcon(item, model) {
|
||||
@ -80,8 +80,8 @@ function generateChannel(item, model) {
|
||||
/**
|
||||
* Generates an array or items
|
||||
* to be later processed by AsciiTable
|
||||
*
|
||||
* @param {*} items
|
||||
*
|
||||
* @param {*} items
|
||||
*/
|
||||
function generateTextualItems(items, model) {
|
||||
let result = items.map(item => {
|
||||
@ -105,17 +105,17 @@ function generateTextualItems(items, model) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array or items
|
||||
* Generates an array or items
|
||||
* for a given type
|
||||
*
|
||||
* @param {string} entryType
|
||||
* @param {Object} model
|
||||
*
|
||||
* @param {string} entryType
|
||||
* @param {Object} model
|
||||
* @return {Array}
|
||||
*/
|
||||
function getItemsOfType(entryType, model) {
|
||||
let allItems = getItems(model);
|
||||
let items = _(allItems)
|
||||
.filter({ entryType: entryType })
|
||||
.filter({entryType: entryType})
|
||||
.uniq()
|
||||
.value() || [];
|
||||
|
||||
@ -125,7 +125,7 @@ function getItemsOfType(entryType, model) {
|
||||
/**
|
||||
* Transforms array of lines
|
||||
* into column-aligned table
|
||||
* @param {Array} lines
|
||||
* @param {Array} lines
|
||||
*/
|
||||
function toTable(lines) {
|
||||
let table = new AsciiTable();
|
||||
@ -149,7 +149,7 @@ function toTable(lines) {
|
||||
* Generates a textual Items file based on user input
|
||||
* Needs several datapoints from the model such as
|
||||
* floorsCount, rooms collection, objects etc.
|
||||
* @param {*} model
|
||||
* @param {*} model
|
||||
* @return {string}
|
||||
*/
|
||||
export function generateItems(model) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
var path = require('path')
|
||||
var webpack = require('webpack')
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/main.js',
|
||||
@ -59,10 +59,10 @@ module.exports = {
|
||||
hints: false
|
||||
},
|
||||
devtool: '#eval-source-map'
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports.devtool = '#source-map'
|
||||
module.exports.devtool = '#source-map';
|
||||
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
|
Loading…
Reference in New Issue
Block a user