Add wizards to help adding rule modules (#622)
Replace the default flat grouped list of module types with a way user-friendlier UI that is aware of the core modules and offers an improved experience. Examples include directly jumping into the script editor, in Blockly mode, or pick the item from the model before deciding which event to consider. Also now the behavior of the icons on the cron triggers and script actions/conditions is now reversed: clicking on the bar will launch the special action, and clicking on the little icon will bring up the generic module editor popup. Fix time of day parameter, replaced day of week with an inline list. Move the Blockly button to the bottom in the script editor. Support system start level in triggers > system triggers. Signed-off-by: Yannick Schaus <github@schaus.net>pull/631/head
parent
9ea63a0085
commit
016e3b091d
|
@ -18,6 +18,8 @@
|
|||
|
||||
<style lang="stylus">
|
||||
.item-picker-container
|
||||
.item-content
|
||||
padding-left calc(var(--f7-list-item-padding-horizontal)/2 + var(--f7-safe-area-left))
|
||||
.item-media
|
||||
padding 0
|
||||
.item-inner:after
|
||||
|
@ -67,12 +69,14 @@ export default {
|
|||
this.$f7.input.validateInputs(this.$refs.smartSelect.$el)
|
||||
const value = this.$refs.smartSelect.f7SmartSelect.getValue()
|
||||
this.$emit('input', value)
|
||||
if (!this.multiple) this.$emit('itemSelected', this.items.find((i) => i.name === value))
|
||||
},
|
||||
updateFromModelPicker (value) {
|
||||
if (this.multiple) {
|
||||
this.$emit('input', value.map((i) => i.name))
|
||||
} else {
|
||||
this.$emit('input', value.name)
|
||||
this.$emit('itemSelected', value)
|
||||
}
|
||||
this.ready = false
|
||||
this.$nextTick(() => { this.ready = true })
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
<template>
|
||||
<ul>
|
||||
<f7-list-item
|
||||
:title="configDescription.label" smart-select :smart-select-params="{ view: $f7.views.main, openIn: 'popover', multiple: configDescription.multiple, closeOnSelect: !configDescription.multiple }" ref="item">
|
||||
<select :name="configDescription.name" @change="updateValue" :multiple="configDescription.multiple">
|
||||
<option v-for="(day, $idx) in values" :value="day" :key="day" :selected="isSelected(day)">{{labels[$idx]}}</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
<f7-list-item v-for="(day, $idx) in values" :value="day" :key="day"
|
||||
:title="labels[$idx]" checkbox :checked="isSelected(day)" @change="(evt) => select(day, evt.target.checked)" />
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
@ -19,10 +15,6 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue (event) {
|
||||
let value = this.$refs.item.f7SmartSelect.getValue()
|
||||
this.$emit('input', value)
|
||||
},
|
||||
isSelected (option) {
|
||||
if (this.value === null || this.value === undefined) return
|
||||
if (!this.configDescription.multiple) {
|
||||
|
@ -30,6 +22,15 @@ export default {
|
|||
} else {
|
||||
return this.value && this.value.indexOf(option) >= 0
|
||||
}
|
||||
},
|
||||
select (day, value) {
|
||||
const newValuesSet = (this.value) ? new Set([...this.value]) : new Set()
|
||||
if (value) newValuesSet.add(day)
|
||||
if (!value) newValuesSet.delete(day)
|
||||
let newValues = new Array(...newValuesSet).sort((a, b) => this.values.indexOf(a) < this.values.indexOf(b))
|
||||
newValues.sort((a, b) => this.values.indexOf(a) - this.values.indexOf(b))
|
||||
console.log(newValues)
|
||||
this.$emit('input', newValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
:required="configDescription.required" validate
|
||||
:clear-button="!configDescription.required"
|
||||
@input="updateValue" />
|
||||
<div slot="content-end" ref="picker" />
|
||||
<div slot="content-end" class="display-flex justify-content-center">
|
||||
<div ref="picker"></div>
|
||||
</div>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
@ -27,44 +29,46 @@ export default {
|
|||
const containerControl = this.$refs.picker
|
||||
if (!inputControl || !inputControl.$el || !containerControl) return
|
||||
const inputElement = this.$$(inputControl.$el).find('input')
|
||||
this.picker = this.$f7.picker.create({
|
||||
containerEl: containerControl,
|
||||
inputEl: inputElement,
|
||||
toolbar: false,
|
||||
inputReadOnly: false,
|
||||
rotateEffect: true,
|
||||
value: (self.value && self.value.indexOf(':') >= 0) ? self.value.split(':') : ['00', '00'],
|
||||
formatValue: function (values, displayValues) {
|
||||
return values[0] + ':' + values[1]
|
||||
},
|
||||
cols: [
|
||||
// Hours
|
||||
{
|
||||
values: (function () {
|
||||
var arr = []
|
||||
for (var i = 0; i <= 23; i++) { arr.push(i < 10 ? `0${i}` : i) }
|
||||
return arr
|
||||
})()
|
||||
this.$nextTick(() => {
|
||||
this.picker = this.$f7.picker.create({
|
||||
containerEl: containerControl,
|
||||
inputEl: inputElement,
|
||||
toolbar: false,
|
||||
inputReadOnly: false,
|
||||
rotateEffect: true,
|
||||
value: (self.value && self.value.indexOf(':') >= 0) ? self.value.split(':') : ['00', '00'],
|
||||
formatValue: function (values, displayValues) {
|
||||
return values[0] + ':' + values[1]
|
||||
},
|
||||
// Divider
|
||||
{
|
||||
divider: true,
|
||||
content: ':'
|
||||
},
|
||||
// Minutes
|
||||
{
|
||||
values: (function () {
|
||||
var arr = []
|
||||
for (var i = 0; i <= 59; i++) { arr.push(i < 10 ? `0${i}` : i) }
|
||||
return arr
|
||||
})()
|
||||
cols: [
|
||||
// Hours
|
||||
{
|
||||
values: (function () {
|
||||
var arr = []
|
||||
for (var i = 0; i <= 23; i++) { arr.push(i < 10 ? `0${i}` : i) }
|
||||
return arr
|
||||
})()
|
||||
},
|
||||
// Divider
|
||||
{
|
||||
divider: true,
|
||||
content: ':'
|
||||
},
|
||||
// Minutes
|
||||
{
|
||||
values: (function () {
|
||||
var arr = []
|
||||
for (var i = 0; i <= 59; i++) { arr.push(i < 10 ? `0${i}` : i) }
|
||||
return arr
|
||||
})()
|
||||
}
|
||||
],
|
||||
on: {
|
||||
change: function (picker, values, displayValues) {
|
||||
self.$emit('input', displayValues[0] + ':' + displayValues[1])
|
||||
}
|
||||
}
|
||||
],
|
||||
on: {
|
||||
change: function (picker, values, displayValues) {
|
||||
self.$emit('input', displayValues[0] + ':' + displayValues[1])
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ['title', 'name', 'value', 'multiple', 'required', 'filterType', 'filterUid'],
|
||||
props: ['title', 'name', 'value', 'multiple', 'required', 'filterType', 'filterUid', 'openOnReady'],
|
||||
data () {
|
||||
return {
|
||||
ready: false,
|
||||
|
@ -51,9 +51,17 @@ export default {
|
|||
this.things = this.things.filter((t) => this.filterUid.indexOf(t.UID) >= 0)
|
||||
}
|
||||
this.ready = true
|
||||
if (this.openOnReady) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.smartSelect.f7SmartSelect.open()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
open () {
|
||||
this.$refs.smartSelect.f7SmartSelect.open()
|
||||
},
|
||||
select (e) {
|
||||
this.$f7.input.validateInputs(this.$refs.smartSelect.$el)
|
||||
this.$emit('input', e.target.value)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<f7-list-item :title="title || 'Thing'" smart-select :smart-select-params="smartSelectParams" v-if="ready" ref="smartSelect">
|
||||
<select :name="name" :multiple="multiple" @change="select" :required="required">
|
||||
<option v-if="!multiple" value=""></option>
|
||||
<optgroup v-for="thing in things" :label="thing.label" :key="thing.UID">
|
||||
<optgroup v-for="thing in things.filter((t) => (filterThing) ? t.UID === filterThing : true)" :label="thing.label" :key="thing.UID">
|
||||
<option v-for="channel in thing.triggerChannels" :value="channel.uid" :key="channel.uid" :selected="(multiple) ? value.indexOf(channel.uid) >= 0 : value === channel.uid">
|
||||
{{channel.id}} ({{channel.label}})
|
||||
</option>
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ['title', 'name', 'value', 'multiple', 'required', 'filterType'],
|
||||
props: ['title', 'name', 'value', 'multiple', 'required', 'filterThing'],
|
||||
data () {
|
||||
return {
|
||||
ready: false,
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<template>
|
||||
<f7-block v-if="!category">
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseItemCategory">
|
||||
<f7-icon size="35" f7="square_on_circle" class="margin" />
|
||||
Item<br />Action
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseScriptCategory">
|
||||
<f7-icon size="35" f7="doc_plaintext" class="margin" />
|
||||
Run<br />Script
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseRulesCategory">
|
||||
<f7-icon size="35" f7="wand_stars" class="margin" />
|
||||
Other<br />Rules
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseMediaCategory">
|
||||
<f7-icon size="35" f7="music_note_list" class="margin" />
|
||||
Audio &<br />Voice
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-list>
|
||||
<f7-list-button title="Show All" color="blue" @click="$emit('showAdvanced')"></f7-list-button>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'item'">
|
||||
<f7-list>
|
||||
<item-picker :value="currentModule.configuration.itemName" title="Item" @input="(val) => $set(currentModule.configuration, 'itemName', val)" @itemSelected="(value) => { $set(this, 'currentItem', value); updateItemEventType('command') }" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-input
|
||||
label="Command to send"
|
||||
name="command"
|
||||
type="text"
|
||||
:value="currentModule.configuration.command"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'command', evt.target.value)"
|
||||
/>
|
||||
</f7-list>
|
||||
<f7-list v-if="commandSuggestions.length">
|
||||
<f7-list-item radio :checked="currentModule.configuration.command === suggestion.command" v-for="suggestion in commandSuggestions" :key="suggestion.command"
|
||||
:title="suggestion.label" @click="$set(currentModule.configuration, 'command', suggestion.command)" />
|
||||
</f7-list>
|
||||
<f7-block v-if="currentItem && (currentItem.type === 'Dimmer' || currentItem.type === 'Rollershutter' || (currentItem.type === 'Number' && currentItem.stateDescription && currentItem.stateDescription.minimum !== undefined))">
|
||||
<f7-range :value="currentModule.configuration.command" @range:changed="(val) => $set(currentModule.configuration, 'command', val)"
|
||||
:min="(currentItem.stateDescription && currentItem.stateDescription.minimum) ? currentItem.stateDescription.minimum : 0"
|
||||
:max="(currentItem.stateDescription && currentItem.stateDescription.maximum) ? currentItem.stateDescription.maximum : 100"
|
||||
:step="(currentItem.stateDescription && currentItem.stateDescription.step) ? currentItem.stateDescription.step : 1"
|
||||
:scale="true" :label="true" :scaleSubSteps="5" />
|
||||
</f7-block>
|
||||
<f7-list v-if="currentItem && currentItem.type === 'Color'" media-list>
|
||||
<f7-list-input media-item type="colorpicker" label="Pick a color" :color-picker-params="{
|
||||
targetEl: '#color-picker-value',
|
||||
targetElSetBackgroundColor: true,
|
||||
openIn: 'auto',
|
||||
modules: ['hsb-sliders', 'wheel', 'palette'],
|
||||
sliderValue: true,
|
||||
sliderValueEditable: true,
|
||||
sliderLabel: true,
|
||||
formatValue: colorToCommand
|
||||
}"
|
||||
:value="commandToColor()"
|
||||
@change="updateColorCommand"
|
||||
>
|
||||
<i slot="media" style="width: 32px; height: 32px" class="icon demo-list-icon" id="color-picker-value"></i>
|
||||
</f7-list-input>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'script'">
|
||||
<f7-block-title class="padding-horizontal">Run a script</f7-block-title>
|
||||
<f7-list media-list>
|
||||
<f7-list-item media-item
|
||||
title="Design with Blockly"
|
||||
footer="A beginner-friendly way to build scripts visually by assembling blocks"
|
||||
link="" @click="scriptLanguagePicked('blockly')">
|
||||
<img src="res/img/blockly.svg" height="32" width="32" slot="media" />
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal margin-vertical">or choose the scripting language:</f7-block-footer>
|
||||
<f7-list media-list>
|
||||
<f7-list-item media-item v-for="language in languages" :key="language.contentType"
|
||||
:title="language.name" :after="language.version" :footer="language.contentType" link="" @click="scriptLanguagePicked(language.contentType)">
|
||||
<span slot="media" class="item-initial">{{language.name[0]}}</span>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal margin-bottom"><small><strong>Note:</strong> Creating a new scripted module will <em>save the rule</em> before launching the script editor.</small></f7-block-footer>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'rules'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="rulesEventType === 'run'" name="rulesEventType" title="run these rule(s)" @click="updateRulesEventType('run')" />
|
||||
<f7-list-item radio :checked="rulesEventType === 'enable'" name="rulesEventType" title="enable or disable these rule(s)" @click="updateRulesEventType('enable')" />
|
||||
</f7-list>
|
||||
<config-sheet v-if="currentModuleType" :key="currentModule.id"
|
||||
:parameterGroups="[]"
|
||||
:parameters="currentModuleType.configDescriptions"
|
||||
:configuration="currentModule.configuration"
|
||||
@updated="dirty = true"
|
||||
/>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'media'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="mediaEventType === 'say'" name="MediaEventType" title="say something" @click="updateMediaEventType('say')" />
|
||||
<f7-list-item radio :checked="mediaEventType === 'play'" name="MediaEventType" title="play an audio file" @click="updateMediaEventType('play')" />
|
||||
</f7-list>
|
||||
<config-sheet v-if="currentModuleType" :key="currentModule.id"
|
||||
:parameterGroups="[]"
|
||||
:parameters="currentModuleType.configDescriptions"
|
||||
:configuration="currentModule.configuration"
|
||||
@updated="dirty = true"
|
||||
/>
|
||||
</f7-block>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.triggertype-big-button
|
||||
background var(--f7-card-bg-color)
|
||||
text-align center
|
||||
height 7.5rem
|
||||
.link
|
||||
color var(--f7-text-color)
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ModuleWizard from './module-wizard-mixin'
|
||||
import ItemPicker from '@/components/config/controls/item-picker.vue'
|
||||
import ConfigSheet from '@/components/config/config-sheet.vue'
|
||||
|
||||
export default {
|
||||
mixins: [ModuleWizard],
|
||||
props: ['currentModule', 'currentModuleType'],
|
||||
components: {
|
||||
ItemPicker,
|
||||
ConfigSheet
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
category: '',
|
||||
itemEventType: 'command',
|
||||
rulesEventType: 'cron',
|
||||
mediaEventType: 'say',
|
||||
languages: [],
|
||||
currentItem: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
commandSuggestions () {
|
||||
if (!this.currentItem || this.category !== 'item') return []
|
||||
let type = (this.currentItem.type === 'Group' && this.currentItem.groupType) ? this.currentItem.groupType : this.currentItem.type
|
||||
|
||||
if (this.currentItem.commandDescription && this.currentItem.commandDescription.commandOptions) {
|
||||
return this.currentItem.commandDescription.commandOptions
|
||||
}
|
||||
if (type === 'Switch') {
|
||||
return ['ON', 'OFF'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
if (type === 'Rollershutter') {
|
||||
return ['UP', 'DOWN', 'STOP'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
if (type === 'Contact') {
|
||||
return ['UP', 'DOWN', 'STOP'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
if (type === 'Color') {
|
||||
return ['ON', 'OFF'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chooseItemCategory () {
|
||||
this.openModelPicker()
|
||||
},
|
||||
chooseScriptCategory () {
|
||||
this.category = 'script'
|
||||
this.$emit('typeSelect', 'script.ScriptAction')
|
||||
this.$nextTick(() => {
|
||||
this.$set(this, 'languages', this.currentModuleType.configDescriptions
|
||||
.find((c) => c.name === 'type').options
|
||||
.map((l) => {
|
||||
return {
|
||||
contentType: l.value,
|
||||
name: l.label.split(' (')[0],
|
||||
version: l.label.split(' (')[1].replace(')', '')
|
||||
}
|
||||
}))
|
||||
})
|
||||
},
|
||||
chooseRulesCategory () {
|
||||
this.category = 'rules'
|
||||
this.updateRulesEventType('run')
|
||||
},
|
||||
chooseMediaCategory () {
|
||||
this.category = 'media'
|
||||
this.updateMediaEventType('say')
|
||||
},
|
||||
updateItemEventType (type) {
|
||||
this.itemEventType = type
|
||||
switch (type) {
|
||||
case 'command':
|
||||
this.$emit('typeSelect', 'core.ItemCommandAction')
|
||||
break
|
||||
}
|
||||
},
|
||||
updateRulesEventType (type) {
|
||||
this.rulesEventType = type
|
||||
switch (type) {
|
||||
case 'run':
|
||||
this.$emit('typeSelect', 'core.RunRuleAction')
|
||||
break
|
||||
case 'enable':
|
||||
this.$emit('typeSelect', 'core.RuleEnablementAction')
|
||||
break
|
||||
}
|
||||
},
|
||||
updateMediaEventType (type) {
|
||||
this.mediaEventType = type
|
||||
switch (type) {
|
||||
case 'say':
|
||||
this.$emit('typeSelect', 'media.SayAction')
|
||||
break
|
||||
case 'play':
|
||||
this.$emit('typeSelect', 'media.PlayAction')
|
||||
break
|
||||
}
|
||||
},
|
||||
updateColorCommand (evt) {
|
||||
this.$set(this.currentModule.configuration, 'command', evt.target.value)
|
||||
},
|
||||
commandToColor (evt) {
|
||||
if (!this.currentModule.configuration.command || this.currentModule.configuration.command.split(',').length !== 3) return null
|
||||
let color = this.currentModule.configuration.command.split(',')
|
||||
color[0] = parseInt(color[0])
|
||||
color[1] = color[1] / 100
|
||||
color[2] = color[2] / 100
|
||||
return { hsb: color }
|
||||
},
|
||||
colorToCommand (val) {
|
||||
let hsb = [...val.hsb]
|
||||
hsb[0] = Math.round(hsb[0]) % 360
|
||||
hsb[1] = Math.round(hsb[1] * 100)
|
||||
hsb[2] = Math.round(hsb[2] * 100)
|
||||
return hsb
|
||||
// this.$set(this.currentModule.configuration, 'command', hsb.join(','))
|
||||
},
|
||||
scriptLanguagePicked (value) {
|
||||
this.$emit('startScript', value)
|
||||
},
|
||||
itemPicked (value) {
|
||||
this.category = 'item'
|
||||
this.currentItem = value
|
||||
this.$set(this.currentModule.configuration, 'itemName', value.name)
|
||||
this.$emit('typeSelect', 'core.ItemCommandAction')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,228 @@
|
|||
<template>
|
||||
<f7-block v-if="!category">
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseItemCategory">
|
||||
<f7-icon size="35" f7="square_on_circle" class="margin" />
|
||||
Item<br />Condition
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseScriptCategory">
|
||||
<f7-icon size="35" f7="doc_plaintext" class="margin" />
|
||||
Script<br />Condition
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseTimeCategory">
|
||||
<f7-icon size="35" f7="clock" class="margin" />
|
||||
Time<br />Condition
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseEphemerisCategory">
|
||||
<f7-icon size="35" f7="calendar_today" class="margin" />
|
||||
Ephemeris<br />Schedule
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-list>
|
||||
<f7-list-button title="Show All" color="blue" @click="$emit('showAdvanced')"></f7-list-button>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'item'">
|
||||
<f7-list>
|
||||
<item-picker :value="currentModule.configuration.itemName" title="Item" @input="(val) => $set(currentModule.configuration, 'itemName', val)" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-item radio
|
||||
v-for="operator in operators" :key="operator.value"
|
||||
:title="operator.label"
|
||||
name="itemStateOperator"
|
||||
:checked="currentModule.configuration.operator === operator.value"
|
||||
@click="$set(currentModule.configuration, 'operator', operator.value)" />
|
||||
<f7-list-input
|
||||
label="State"
|
||||
name="itemState"
|
||||
type="text"
|
||||
:value="currentModule.configuration.state"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'state', evt.target.value)"
|
||||
/>
|
||||
</f7-list>
|
||||
<f7-list v-if="stateSuggestions.length">
|
||||
<f7-list-item radio :checked="currentModule.configuration.state === suggestion.value" v-for="suggestion in stateSuggestions" :key="suggestion.value"
|
||||
:title="suggestion.label" @click="$set(currentModule.configuration, 'state', suggestion.value)" />
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'script'">
|
||||
<f7-block-title class="padding-horizontal">A script evaluates to true</f7-block-title>
|
||||
<f7-list media-list>
|
||||
<f7-list-item media-item
|
||||
title="Design with Blockly"
|
||||
footer="A beginner-friendly way to build scripts visually by assembling blocks"
|
||||
link="" @click="scriptLanguagePicked('blockly')">
|
||||
<img src="res/img/blockly.svg" height="32" width="32" slot="media" />
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal margin-vertical">or choose the scripting language:</f7-block-footer>
|
||||
<f7-list media-list>
|
||||
<f7-list-item media-item v-for="language in languages" :key="language.contentType"
|
||||
:title="language.name" :after="language.version" :footer="language.contentType" link="" @click="scriptLanguagePicked(language.contentType)">
|
||||
<span slot="media" class="item-initial">{{language.name[0]}}</span>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal margin-bottom"><small><strong>Note:</strong> Creating a new scripted module will <em>save the rule</em> before launching the script editor.</small></f7-block-footer>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'time'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="timeEventType === 'dayOfWeek'" name="timeEventType" title="the current day of the week is" @click="updateTimeEventType('dayOfWeek')" />
|
||||
<f7-list-item radio :checked="timeEventType === 'timeOfDay'" name="timeEventType" title="inside a time range" @click="updateTimeEventType('timeOfDay')" />
|
||||
</f7-list>
|
||||
<config-sheet v-if="currentModuleType" :key="currentModule.id"
|
||||
:parameterGroups="[]"
|
||||
:parameters="currentModuleType.configDescriptions"
|
||||
:configuration="currentModule.configuration"
|
||||
@updated="dirty = true"
|
||||
/>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'ephemeris'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="ephemerisEventType === 'weekdays'" name="EphemerisEventType" title="it's a weekday" @click="updateEphemerisEventType('weekdays')" />
|
||||
<f7-list-item radio :checked="ephemerisEventType === 'weekends'" name="EphemerisEventType" title="it's the weekend" @click="updateEphemerisEventType('weekends')" />
|
||||
<f7-list-item radio :checked="ephemerisEventType === 'holidays'" name="EphemerisEventType" title="it's a holiday" @click="updateEphemerisEventType('holidays')" />
|
||||
<f7-list-item radio :checked="ephemerisEventType === 'dayset'" name="EphemerisEventType" title="today is in a specific dayset" @click="updateEphemerisEventType('dayset')" />
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal">Remember to configure Ephemeris in Settings before using these conditions.</f7-block-footer>
|
||||
<config-sheet v-if="currentModuleType" :key="currentModule.id"
|
||||
:parameterGroups="[]"
|
||||
:parameters="currentModuleType.configDescriptions"
|
||||
:configuration="currentModule.configuration"
|
||||
@updated="dirty = true"
|
||||
/>
|
||||
</f7-block>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.triggertype-big-button
|
||||
background var(--f7-card-bg-color)
|
||||
text-align center
|
||||
height 7.5rem
|
||||
.link
|
||||
color var(--f7-text-color)
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ModuleWizard from './module-wizard-mixin'
|
||||
import ItemPicker from '@/components/config/controls/item-picker.vue'
|
||||
import ConfigSheet from '@/components/config/config-sheet.vue'
|
||||
|
||||
export default {
|
||||
mixins: [ModuleWizard],
|
||||
props: ['currentModule', 'currentModuleType'],
|
||||
components: {
|
||||
ItemPicker,
|
||||
ConfigSheet
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
category: '',
|
||||
itemEventType: 'state',
|
||||
thingEventType: 'triggerChannelFired',
|
||||
timeEventType: 'cron',
|
||||
ephemerisEventType: 'weekdays',
|
||||
languages: [],
|
||||
operators: [
|
||||
{ value: '=', label: 'is equal to' },
|
||||
{ value: '!=', label: 'is different than' },
|
||||
{ value: '>', label: 'is greater than' },
|
||||
{ value: '>=', label: 'is greater or equal to' },
|
||||
{ value: '<', label: 'is less than' },
|
||||
{ value: '<=', label: 'is less or equal to' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chooseItemCategory () {
|
||||
this.openModelPicker()
|
||||
},
|
||||
chooseScriptCategory () {
|
||||
this.category = 'script'
|
||||
this.$emit('typeSelect', 'script.ScriptCondition')
|
||||
this.$nextTick(() => {
|
||||
this.$set(this, 'languages', this.currentModuleType.configDescriptions
|
||||
.find((c) => c.name === 'type').options
|
||||
.map((l) => {
|
||||
return {
|
||||
contentType: l.value,
|
||||
name: l.label.split(' (')[0],
|
||||
version: l.label.split(' (')[1].replace(')', '')
|
||||
}
|
||||
}))
|
||||
})
|
||||
},
|
||||
chooseTimeCategory () {
|
||||
this.category = 'time'
|
||||
this.updateTimeEventType('dayOfWeek')
|
||||
},
|
||||
chooseEphemerisCategory () {
|
||||
this.category = 'ephemeris'
|
||||
this.updateEphemerisEventType('weekdays')
|
||||
},
|
||||
updateItemEventType (type) {
|
||||
this.itemEventType = type
|
||||
switch (type) {
|
||||
case 'command':
|
||||
this.$emit('typeSelect', 'core.ItemCommandTrigger')
|
||||
break
|
||||
case 'updated':
|
||||
this.$emit('typeSelect', 'core.ItemStateUpdateTrigger')
|
||||
break
|
||||
case 'changed':
|
||||
this.$emit('typeSelect', 'core.ItemStateChangeTrigger')
|
||||
break
|
||||
}
|
||||
},
|
||||
updateTimeEventType (type) {
|
||||
this.timeEventType = type
|
||||
switch (type) {
|
||||
case 'dayOfWeek':
|
||||
this.$emit('typeSelect', 'timer.DayOfWeekCondition')
|
||||
break
|
||||
case 'timeOfDay':
|
||||
this.$emit('typeSelect', 'core.TimeOfDayCondition')
|
||||
break
|
||||
}
|
||||
},
|
||||
updateEphemerisEventType (type) {
|
||||
this.ephemerisEventType = type
|
||||
switch (type) {
|
||||
case 'weekdays':
|
||||
this.$emit('typeSelect', 'ephemeris.WeekdayCondition')
|
||||
break
|
||||
case 'weekends':
|
||||
this.$emit('typeSelect', 'ephemeris.WeekendCondition')
|
||||
break
|
||||
case 'holidays':
|
||||
this.$emit('typeSelect', 'ephemeris.HolidayCondition')
|
||||
break
|
||||
case 'dayset':
|
||||
this.$emit('typeSelect', 'ephemeris.DaysetCondition')
|
||||
break
|
||||
}
|
||||
},
|
||||
scriptLanguagePicked (value) {
|
||||
this.$emit('startScript', value)
|
||||
},
|
||||
itemPicked (value) {
|
||||
this.category = 'item'
|
||||
this.currentItem = value
|
||||
this.$set(this.currentModule.configuration, 'itemName', value.name)
|
||||
this.$set(this.currentModule.configuration, 'operator', '=')
|
||||
this.$emit('typeSelect', 'core.ItemStateCondition')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,74 @@
|
|||
import ModelPickerPopup from '@/components/model/model-picker-popup.vue'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
category: '',
|
||||
currentItem: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
commandSuggestions () {
|
||||
if (!this.currentItem || this.category !== 'item') return []
|
||||
let type = (this.currentItem.type === 'Group' && this.currentItem.groupType) ? this.currentItem.groupType : this.currentItem.type
|
||||
|
||||
if (this.currentItem.commandDescription && this.currentItem.commandDescription.commandOptions) {
|
||||
return this.currentItem.commandDescription.commandOptions
|
||||
}
|
||||
if (type === 'Switch') {
|
||||
return ['ON', 'OFF'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
if (type === 'Rollershutter') {
|
||||
return ['UP', 'DOWN', 'STOP'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
if (type === 'Color') {
|
||||
return ['ON', 'OFF'].map((c) => { return { command: c, label: c } })
|
||||
}
|
||||
|
||||
return ['ON', 'OFF'].map((c) => { return { command: c, label: c } })
|
||||
},
|
||||
stateSuggestions () {
|
||||
if (!this.currentItem || this.category !== 'item') return []
|
||||
let type = (this.currentItem.type === 'Group' && this.currentItem.groupType) ? this.currentItem.groupType : this.currentItem.type
|
||||
|
||||
if (this.currentItem.stateDescription && this.currentItem.stateDescription.options) {
|
||||
return this.currentItem.stateDescription.options
|
||||
}
|
||||
if (type === 'Switch') {
|
||||
return ['ON', 'OFF'].map((c) => { return { value: c, label: c } })
|
||||
}
|
||||
if (type === 'Rollershutter') {
|
||||
return ['UP', 'DOWN', 'STOP'].map((c) => { return { value: c, label: c } })
|
||||
}
|
||||
if (type === 'Contact') {
|
||||
return ['OPEN', 'CLOSED'].map((c) => { return { value: c, label: c } })
|
||||
}
|
||||
|
||||
return ['ON', 'OFF'].map((c) => { return { value: c, label: c } })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModelPicker () {
|
||||
const popup = {
|
||||
component: ModelPickerPopup
|
||||
}
|
||||
|
||||
this.$f7router.navigate({
|
||||
url: 'pick-from-model',
|
||||
route: {
|
||||
path: 'pick-from-model',
|
||||
popup
|
||||
}
|
||||
}, {
|
||||
props: {
|
||||
multiple: false
|
||||
}
|
||||
})
|
||||
|
||||
this.$f7.once('itemsPicked', this.itemPicked)
|
||||
this.$f7.once('modelPickerClosed', () => {
|
||||
this.$f7.off('itemsPicked', this.itemPicked)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
<template>
|
||||
<f7-block v-if="!category">
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseItemCategory">
|
||||
<f7-icon size="35" f7="square_on_circle" class="margin" />
|
||||
Item<br />Event
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseThingCategory">
|
||||
<f7-icon size="35" f7="lightbulb" class="margin" />
|
||||
Thing<br />Event
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-row class="margin-bottom">
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseTimeCategory">
|
||||
<f7-icon size="35" f7="clock" class="margin" />
|
||||
Time<br />Event
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
<f7-col class="elevation-2 elevation-hover-6 elevation-pressed-1 triggertype-big-button" width="50">
|
||||
<f7-link class="display-flex flex-direction-column no-ripple" no-ripple @click="chooseSystemCategory">
|
||||
<f7-icon size="35" f7="gear" class="margin" />
|
||||
System<br />Event
|
||||
</f7-link>
|
||||
</f7-col>
|
||||
</f7-row>
|
||||
<f7-list>
|
||||
<f7-list-button title="Show All" color="blue" @click="$emit('showAdvanced')"></f7-list-button>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'item'">
|
||||
<f7-list>
|
||||
<item-picker :required="true" :value="currentItem.name" title="Item" @input="(val) => $set(currentModule.configuration, 'itemName', val)" @itemSelected="(value) => { currentItem = value; updateItemEventType('command') }" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="itemEventType === 'command'" name="itemEventType" title="received a command" @click="updateItemEventType('command')" />
|
||||
<f7-list-item radio :checked="itemEventType === 'updated'" name="itemEventType" title="was updated" @click="updateItemEventType('updated')" />
|
||||
<f7-list-item radio :checked="itemEventType === 'changed'" name="itemEventType" title="changed" @click="updateItemEventType('changed')" />
|
||||
<f7-list-item radio v-if="currentItem && currentItem.type === 'Group'" :checked="itemEventType === 'memberCommand'" name="itemEventType" title="had a member receive a command" @click="updateItemEventType('memberCommand')" />
|
||||
<f7-list-item radio v-if="currentItem && currentItem.type === 'Group'" :checked="itemEventType === 'memberUpdated'" name="itemEventType" title="had a member update" @click="updateItemEventType('memberUpdated')" />
|
||||
<f7-list-item radio v-if="currentItem && currentItem.type === 'Group'" :checked="itemEventType === 'memberChanged'" name="itemEventType" title="had a member change" @click="updateItemEventType('memberChanged')" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-input
|
||||
v-if="itemEventType === 'command' || itemEventType === 'memberCommand'"
|
||||
label="Command"
|
||||
name="command"
|
||||
type="text"
|
||||
placeholder="Any"
|
||||
:value="currentModule.configuration.command"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'command', evt.target.value)"
|
||||
/>
|
||||
<f7-list-input
|
||||
v-if="itemEventType === 'updated' || itemEventType === 'memberUpdated'"
|
||||
label="to state"
|
||||
name="updatedState"
|
||||
type="text"
|
||||
placeholder="Any"
|
||||
:value="currentModule.configuration.state"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'state', evt.target.value)"
|
||||
/>
|
||||
<f7-list-input
|
||||
v-if="itemEventType === 'changed' || itemEventType === 'memberChanged'"
|
||||
label="from state"
|
||||
name="changedFromState"
|
||||
type="text"
|
||||
placeholder="Any"
|
||||
:value="currentModule.configuration.previousState"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'previousState', evt.target.value)"
|
||||
/>
|
||||
<f7-list-input
|
||||
v-if="itemEventType === 'changed' || itemEventType === 'memberChanged'"
|
||||
label="to state"
|
||||
name="changedToState"
|
||||
type="text"
|
||||
placeholder="Any"
|
||||
:value="currentModule.configuration.state"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'state', evt.target.value)"
|
||||
/>
|
||||
</f7-list>
|
||||
<f7-list v-if="(itemEventType === 'command' || itemEventType === 'memberCommand') && commandSuggestions.length">
|
||||
<f7-list-item radio :checked="currentModule.configuration.command === suggestion.command" v-for="suggestion in commandSuggestions" :key="suggestion.command"
|
||||
:title="suggestion.label" @click="$set(currentModule.configuration, 'command', suggestion.command)" />
|
||||
</f7-list>
|
||||
<f7-list v-else-if="stateSuggestions.length">
|
||||
<f7-list-item radio :checked="currentModule.configuration.state === suggestion.value" v-for="suggestion in stateSuggestions" :key="suggestion.value"
|
||||
:title="suggestion.label" @click="$set(currentModule.configuration, 'state', suggestion.value)" />
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'thing'">
|
||||
<f7-list>
|
||||
<thing-picker ref="thingPicker" :value="currentModule.configuration.thingUID" title="Thing" @input="(val) => $set(currentModule.configuration, 'thingUID', val)" :open-on-ready="true" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="thingEventType === 'triggerChannelFired'" name="thingEventType" title="a trigger channel fired" @click="updateThingEventType('triggerChannelFired')" />
|
||||
<f7-list-item radio v-if="currentModule.configuration.thingUID" :checked="thingEventType === 'statusUpdated'" name="thingEventType" title="status was updated" @click="updateThingEventType('statusUpdated')" />
|
||||
<f7-list-item radio v-if="currentModule.configuration.thingUID" :checked="thingEventType === 'statusChanged'" name="thingEventType" title="status changed" @click="updateThingEventType('statusChanged')" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-item
|
||||
v-if="thingEventType === 'statusUpdated'"
|
||||
title="to"
|
||||
smart-select :smart-select-params="{ view: $f7.view.main, openIn: 'popover' }">
|
||||
<select name="thingStatus" required @change="(evt) => $set(currentModule.configuration, 'status', evt.target.value)">
|
||||
<option v-for="status in [{ value: '', label: '' }, ...currentModuleType.configDescriptions.find((p) => p.name === 'status').options]"
|
||||
:value="status.value" :key="status.value"
|
||||
:selected="currentModule.configuration.status === status.value">
|
||||
{{status.label}}
|
||||
</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
<f7-list-item
|
||||
v-if="thingEventType === 'statusChanged'"
|
||||
title="from"
|
||||
smart-select :smart-select-params="{ view: $f7.view.main, openIn: 'popover' }">
|
||||
<select name="thingStatus" required @change="(evt) => $set(currentModule.configuration, 'previousStatus', evt.target.value)">
|
||||
<option v-for="status in [{ value: '', label: '' }, ...currentModuleType.configDescriptions.find((p) => p.name === 'previousStatus').options]"
|
||||
:value="status.value" :key="status.value"
|
||||
:selected="currentModule.configuration.previousStatus === status.value">
|
||||
{{status.label}}
|
||||
</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
<f7-list-item
|
||||
v-if="thingEventType === 'statusChanged'"
|
||||
title="to"
|
||||
smart-select :smart-select-params="{ view: $f7.view.main, openIn: 'popover' }">
|
||||
<select name="thingStatus" required @change="(evt) => $set(currentModule.configuration, 'status', evt.target.value)">
|
||||
<option v-for="status in [{ value: '', label: '' }, ...currentModuleType.configDescriptions.find((p) => p.name === 'status').options]"
|
||||
:value="status.value" :key="status.value"
|
||||
:selected="currentModule.configuration.status === status.value">
|
||||
{{status.label}}
|
||||
</option>
|
||||
</select>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<trigger-channel-picker v-if="thingEventType === 'triggerChannelFired'" :value="currentModule.configuration.channelUID" title="Channel" @input="(val) => $set(currentModule.configuration, 'channelUID', val)" :filter-thing="currentModule.configuration.thingUID" />
|
||||
</f7-list>
|
||||
<f7-list>
|
||||
<f7-list-input
|
||||
v-if="thingEventType === 'triggerChannelFired'"
|
||||
label="Event"
|
||||
name="triggerChannelEvent"
|
||||
type="text"
|
||||
placeholder="Any"
|
||||
:value="currentModule.configuration.event"
|
||||
@blur="(evt) => $set(currentModule.configuration, 'event', evt.target.value)"
|
||||
/>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'time'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="timeEventType === 'cron'" name="timeEventType" title="on a schedule (cron)" @click="updateTimeEventType('cron')" />
|
||||
<f7-list-item radio :checked="timeEventType === 'timeOfDay'" name="timeEventType" title="at a fixed time of the day" @click="updateTimeEventType('timeOfDay')" />
|
||||
</f7-list>
|
||||
<config-sheet v-if="currentModuleType" :key="currentSection + currentModule.id"
|
||||
:parameterGroups="[]"
|
||||
:parameters="currentModuleType.configDescriptions"
|
||||
:configuration="currentModule.configuration"
|
||||
@updated="dirty = true"
|
||||
/>
|
||||
</f7-block>
|
||||
<f7-block class="no-margin no-padding" v-else-if="category === 'system'">
|
||||
<f7-list>
|
||||
<f7-list-item radio :checked="systemEventType === 'start'" name="systemEventType" title="the system is being initialized" @click="updateSystemEventType('start')" />
|
||||
</f7-list>
|
||||
<f7-block-footer class="padding-horizontal margin-vertical">and this start level has been reached:</f7-block-footer>
|
||||
<f7-list v-if="systemEventType === 'start' && currentModule">
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 0" name="startLevel" title="00 - OSGi framework started" @click="$set(currentModule.configuration, 'startLevel', 0)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 10" name="startLevel" title="10 - OSGi bundles activated" @click="$set(currentModule.configuration, 'startLevel', 10)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 20" name="startLevel" title="20 - Entities (items, things...) loaded" @click="$set(currentModule.configuration, 'startLevel', 20)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 30" name="startLevel" title="30 - Items states restored from persistence" @click="$set(currentModule.configuration, 'startLevel', 30)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 40" name="startLevel" title="40 - Rules loaded" @click="$set(currentModule.configuration, 'startLevel', 40)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 50" name="startLevel" title="50 - Rule engine ready" @click="$set(currentModule.configuration, 'startLevel', 50)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 70" name="startLevel" title="70 - User interface running" @click="$set(currentModule.configuration, 'startLevel', 70)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 80" name="startLevel" title="80 - Things initialized" @click="$set(currentModule.configuration, 'startLevel', 90)" />
|
||||
<f7-list-item radio :checked="currentModule.configuration.startLevel === 100" name="startLevel" title="100 - Startup complete" @click="$set(currentModule.configuration, 'startLevel', 100)" />
|
||||
<f7-block-footer class="padding-horizontal"><small>Start levels below 40 are provided for completeness but will not make a difference since the rules engine is not initialized yet at these levels.</small></f7-block-footer>
|
||||
</f7-list>
|
||||
</f7-block>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.triggertype-big-button
|
||||
background var(--f7-card-bg-color)
|
||||
text-align center
|
||||
height 7.5rem
|
||||
.link
|
||||
color var(--f7-text-color)
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ModuleWizard from './module-wizard-mixin'
|
||||
import ItemPicker from '@/components/config/controls/item-picker.vue'
|
||||
import ThingPicker from '@/components/config/controls/thing-picker.vue'
|
||||
import TriggerChannelPicker from '@/components/config/controls/triggerchannel-picker.vue'
|
||||
import ConfigSheet from '@/components/config/config-sheet.vue'
|
||||
|
||||
export default {
|
||||
mixins: [ModuleWizard],
|
||||
props: ['currentModule', 'currentModuleType'],
|
||||
components: {
|
||||
ItemPicker,
|
||||
ThingPicker,
|
||||
TriggerChannelPicker,
|
||||
ConfigSheet
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
category: '',
|
||||
itemEventType: 'command',
|
||||
thingEventType: 'triggerChannelFired',
|
||||
timeEventType: 'cron',
|
||||
currentItem: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chooseItemCategory () {
|
||||
this.openModelPicker()
|
||||
},
|
||||
chooseThingCategory () {
|
||||
this.category = 'thing'
|
||||
this.updateThingEventType('triggerChannelFired')
|
||||
},
|
||||
chooseTimeCategory () {
|
||||
this.category = 'time'
|
||||
this.updateTimeEventType('cron')
|
||||
},
|
||||
chooseSystemCategory () {
|
||||
this.category = 'system'
|
||||
this.updateSystemEventType('start')
|
||||
},
|
||||
updateItemEventType (type) {
|
||||
this.itemEventType = type
|
||||
switch (type) {
|
||||
case 'command':
|
||||
this.$emit('typeSelect', 'core.ItemCommandTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { itemName: this.currentItem.name }))
|
||||
break
|
||||
case 'updated':
|
||||
this.$emit('typeSelect', 'core.ItemStateUpdateTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { itemName: this.currentItem.name }))
|
||||
break
|
||||
case 'changed':
|
||||
this.$emit('typeSelect', 'core.ItemStateChangeTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { itemName: this.currentItem.name }))
|
||||
break
|
||||
case 'memberCommand':
|
||||
this.$emit('typeSelect', 'core.GroupCommandTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { groupName: this.currentItem.name }))
|
||||
break
|
||||
case 'memberUpdated':
|
||||
this.$emit('typeSelect', 'core.GroupStateUpdateTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { groupName: this.currentItem.name }))
|
||||
break
|
||||
case 'memberChanged':
|
||||
this.$emit('typeSelect', 'core.GroupStateChangeTrigger')
|
||||
if (this.currentItem) this.$set(this.currentModule, 'configuration', Object.assign({}, { groupName: this.currentItem.name }))
|
||||
break
|
||||
}
|
||||
},
|
||||
updateThingEventType (type) {
|
||||
this.thingEventType = type
|
||||
switch (type) {
|
||||
case 'triggerChannelFired':
|
||||
this.$emit('typeSelect', 'core.ChannelEventTrigger', true)
|
||||
break
|
||||
case 'statusUpdated':
|
||||
this.$emit('typeSelect', 'core.ThingStatusUpdateTrigger', true)
|
||||
break
|
||||
case 'statusChanged':
|
||||
this.$emit('typeSelect', 'core.ThingStatusChangeTrigger', true)
|
||||
break
|
||||
}
|
||||
},
|
||||
updateTimeEventType (type) {
|
||||
this.timeEventType = type
|
||||
switch (type) {
|
||||
case 'cron':
|
||||
this.$emit('typeSelect', 'timer.GenericCronTrigger', true)
|
||||
break
|
||||
case 'timeOfDay':
|
||||
this.$emit('typeSelect', 'timer.TimeOfDayTrigger', true)
|
||||
break
|
||||
}
|
||||
},
|
||||
updateSystemEventType (type) {
|
||||
this.systemEventType = type
|
||||
switch (type) {
|
||||
case 'start':
|
||||
this.$emit('typeSelect', 'core.SystemStartlevelTrigger', true)
|
||||
this.$set(this.currentModule.configuration, 'startlevel', 20)
|
||||
break
|
||||
}
|
||||
},
|
||||
itemPicked (value) {
|
||||
this.category = 'item'
|
||||
this.currentItem = value
|
||||
this.$set(this.currentModule.configuration, 'itemName', value.name)
|
||||
this.updateItemEventType('command')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -41,16 +41,28 @@ export default {
|
|||
case 'core.GroupCommandTrigger':
|
||||
if (!config.groupName && !config.command) return moduleType.label
|
||||
if (!config.command) return 'When a member of ' + config.groupName + ' received a command'
|
||||
return 'When a member of ' + config.itemName + ' received command ' + config.command
|
||||
return 'When a member of ' + config.groupName + ' received command ' + config.command
|
||||
case 'core.GroupStateUpdateTrigger':
|
||||
if (!config.groupName) return moduleType.label
|
||||
return 'When ' + config.groupName + ' was updated' +
|
||||
return 'When a member of ' + config.groupName + ' was updated' +
|
||||
((config.state) ? ' to ' + config.state : '')
|
||||
case 'core.GroupStateChangeTrigger':
|
||||
if (!config.groupName) return moduleType.label
|
||||
return 'When a member of ' + config.groupName + ' changed' +
|
||||
((config.previousState) ? ' from ' + config.previousState : '') +
|
||||
((config.state) ? ' to ' + config.state : '')
|
||||
case 'core.ThingStatusUpdateTrigger':
|
||||
if (!config.thingUID) return moduleType.label
|
||||
return 'When ' + config.thingUID + ' status was updated' +
|
||||
((config.status) ? ' to ' + config.status : '')
|
||||
case 'core.ThingStatusChangeTrigger':
|
||||
if (!config.thingUID) return moduleType.label
|
||||
return 'When ' + config.thingUID + ' changed' +
|
||||
((config.previousStatus) ? ' from ' + config.previousStatus : '') +
|
||||
((config.status) ? ' to ' + config.status : '')
|
||||
case 'core.SystemStartlevelTrigger':
|
||||
if (config.startLevel === undefined) return moduleType.label
|
||||
return 'When the system has reached start level ' + config.startLevel
|
||||
// actions
|
||||
case 'core.ItemCommandAction':
|
||||
if (!config.itemName || !config.command) return moduleType.label
|
||||
|
@ -71,7 +83,6 @@ export default {
|
|||
case 'core.ItemStateCondition':
|
||||
if (!config.itemName || !config.operator || !config.state) return moduleType.label
|
||||
return 'If ' + config.itemName + ' ' + config.operator + ' ' + config.state
|
||||
|
||||
default:
|
||||
return moduleType.label
|
||||
}
|
||||
|
|
|
@ -91,10 +91,11 @@
|
|||
:title="mod.label || suggestedModuleTitle(mod, null, section)"
|
||||
:footer="mod.description || suggestedModuleDescription(mod, null, section)"
|
||||
v-for="mod in rule[section]" :key="mod.id"
|
||||
:link="isEditable && !showModuleControls" @click.native="(ev) => editModule(ev, section, mod)" swipeout>
|
||||
:link="isEditable && !showModuleControls"
|
||||
@click.native="(ev) => editModule(ev, section, mod)" swipeout>
|
||||
<f7-link slot="media" v-if="isEditable" icon-color="red" icon-aurora="f7:minus_circle_filled" icon-ios="f7:minus_circle_filled" icon-md="material:remove_circle_outline" @click="showSwipeout"></f7-link>
|
||||
<f7-link slot="after" v-if="!createMode && mod.type && mod.type.indexOf('script') === 0" icon-f7="pencil_ellipsis_rectangle" color="gray" @click.native="(ev) => editScriptDirect(ev, mod)" :tooltip="(isEditable) ? 'Edit script' : 'View script'"></f7-link>
|
||||
<f7-link slot="after" v-if="!createMode && mod.type === 'timer.GenericCronTrigger' && isEditable" icon-f7="calendar" color="gray" @click.native="(ev) => buildCronExpression(ev, mod)" tooltip="Build cron expression"></f7-link>
|
||||
<f7-link slot="after" v-if="mod.type && mod.type.indexOf('script') === 0" icon-f7="pencil_ellipsis_rectangle" color="gray" @click.native="(ev) => editModule(ev, section, mod, true)" :tooltip="'Edit module'"></f7-link>
|
||||
<f7-link slot="after" v-if="mod.type === 'timer.GenericCronTrigger' && isEditable" icon-f7="pencil_ellipsis_rectangle" color="gray" @click.native="(ev) => editModule(ev, section, mod, true)" tooltip="Edit module"></f7-link>
|
||||
<f7-swipeout-actions right v-if="isEditable">
|
||||
<f7-swipeout-button @click="(ev) => deleteModule(ev, section, mod)" style="background-color: var(--f7-swipeout-delete-button-bg-color)">Delete</f7-swipeout-button>
|
||||
</f7-swipeout-actions>
|
||||
|
@ -265,19 +266,19 @@ export default {
|
|||
})
|
||||
},
|
||||
save (stay) {
|
||||
if (!this.isEditable) return
|
||||
if (!this.isEditable) return Promise.reject()
|
||||
if (this.currentTab === 'code') {
|
||||
if (!this.fromYaml()) {
|
||||
return
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
if (!this.rule.uid) {
|
||||
this.$f7.dialog.alert('Please give an ID to the rule')
|
||||
return
|
||||
return Promise.reject()
|
||||
}
|
||||
if (!this.rule.name) {
|
||||
this.$f7.dialog.alert('Please give a name to the rule')
|
||||
return
|
||||
return Promise.reject()
|
||||
}
|
||||
const promise = (this.createMode)
|
||||
? this.$oh.api.postPlain('/rest/rules', JSON.stringify(this.rule), 'text/plain', 'application/json')
|
||||
|
@ -401,7 +402,7 @@ export default {
|
|||
this.$f7.swipeout.open(swipeoutElement)
|
||||
}
|
||||
},
|
||||
editModule (ev, section, mod) {
|
||||
editModule (ev, section, mod, force) {
|
||||
if (this.showModuleControls) return
|
||||
if (!this.isEditable) return
|
||||
let swipeoutElement = ev.target
|
||||
|
@ -414,6 +415,15 @@ export default {
|
|||
this.currentModule = Object.assign({}, mod)
|
||||
this.currentModuleType = this.moduleTypes[section].find((m) => m.uid === mod.type)
|
||||
|
||||
if (mod.type && mod.type.indexOf('script') === 0 && !force) {
|
||||
this.editScriptDirect(ev, mod)
|
||||
return
|
||||
}
|
||||
if (mod.type && mod.type === 'timer.GenericCronTrigger' && !force) {
|
||||
this.buildCronExpression(ev, mod)
|
||||
return
|
||||
}
|
||||
|
||||
const popup = {
|
||||
component: RuleModulePopup
|
||||
}
|
||||
|
@ -487,8 +497,10 @@ export default {
|
|||
})
|
||||
|
||||
this.$f7.once('ruleModuleConfigUpdate', this.saveModule)
|
||||
this.$f7.once('editNewScript', this.saveAndEditNewScript)
|
||||
this.$f7.once('ruleModuleConfigClosed', () => {
|
||||
this.$f7.off('ruleModuleConfigUpdate', this.saveModule)
|
||||
this.$f7.off('editNewScript', this.saveAndEditNewScript)
|
||||
this.moduleConfigClosed()
|
||||
})
|
||||
},
|
||||
|
@ -509,6 +521,12 @@ export default {
|
|||
this.$set(this.rule[this.currentSection], idx, updatedModule)
|
||||
}
|
||||
},
|
||||
saveAndEditNewScript (updatedModule) {
|
||||
this.saveModule(updatedModule)
|
||||
this.save().then(() => {
|
||||
this.$f7router.navigate('/settings/rules/' + this.rule.uid + '/script/' + updatedModule.id, { transition: this.$theme.aurora ? 'f7-cover-v' : '' })
|
||||
})
|
||||
},
|
||||
moduleConfigClosed () {
|
||||
this.currentModule = null
|
||||
this.currentModuleType = null
|
||||
|
@ -519,9 +537,9 @@ export default {
|
|||
this.currentModuleType = mod.type
|
||||
this.scriptCode = mod.configuration.script
|
||||
|
||||
const updatePromise = (this.rule.editable) ? this.save() : Promise.resolve()
|
||||
const updatePromise = (this.rule.editable || this.createMode) ? this.save() : Promise.resolve()
|
||||
updatePromise.then(() => {
|
||||
this.$f7router.navigate('script/' + mod.id, { transition: this.$theme.aurora ? 'f7-cover-v' : '' })
|
||||
this.$f7router.navigate('/settings/rules/' + this.rule.uid + '/script/' + mod.id, { transition: this.$theme.aurora ? 'f7-cover-v' : '' })
|
||||
})
|
||||
},
|
||||
buildCronExpression (ev, mod) {
|
||||
|
|
|
@ -22,23 +22,28 @@
|
|||
</f7-list-input>
|
||||
</f7-list>
|
||||
</f7-col>
|
||||
<f7-block-footer class="no-margin padding-left"><small>Tip: leave fields blank to set automatically to the suggested name and description. <f7-link @click="ruleModule.label = null; ruleModule.description = null">Clear</f7-link></small></f7-block-footer>
|
||||
<!-- <f7-block-footer class="no-margin padding-left"><small>Tip: leave fields blank to set automatically to the suggested name and description. <f7-link @click="ruleModule.label = null; ruleModule.description = null">Clear</f7-link></small></f7-block-footer> -->
|
||||
|
||||
<f7-block-title>Type of {{currentSection.replace(/.$/, '')}}</f7-block-title>
|
||||
<f7-list v-if="!currentRuleModuleType">
|
||||
<ul v-for="(mt, scope) in groupedModuleTypes(currentSection)" :key="scope">
|
||||
<f7-list-item divider :title="scope" />
|
||||
<f7-list-item radio v-for="moduleType in mt"
|
||||
:value="moduleType.uid"
|
||||
@change="setModuleType(moduleType)"
|
||||
:checked="ruleModule.type === moduleType.uid"
|
||||
:key="moduleType.uid" :title="moduleType.label" name="module-type"></f7-list-item>
|
||||
</ul>
|
||||
</f7-list>
|
||||
<f7-list v-else>
|
||||
<div v-if="ruleModule.new">
|
||||
<f7-block-title class="no-margin padding-horizontal margin-vertical" v-if="!advancedTypePicker" medium>{{sectionLabels[currentSection][0]}}</f7-block-title>
|
||||
<f7-list v-if="advancedTypePicker && !ruleModule.type">
|
||||
<ul v-for="(mt, scope) in groupedModuleTypes(currentSection)" :key="scope">
|
||||
<f7-list-item divider :title="scope" />
|
||||
<f7-list-item radio v-for="moduleType in mt"
|
||||
:value="moduleType.uid"
|
||||
@change="setModuleType(moduleType)"
|
||||
:checked="ruleModule.type === moduleType.uid"
|
||||
:key="moduleType.uid" :title="moduleType.label" name="module-type"></f7-list-item>
|
||||
</ul>
|
||||
</f7-list>
|
||||
<trigger-module-wizard v-else-if="!advancedTypePicker && currentSection === 'triggers'" :current-module="ruleModule" :current-module-type="currentRuleModuleType" @typeSelect="setModuleType" @showAdvanced="advancedTypePicker = true" />
|
||||
<condition-module-wizard v-else-if="!advancedTypePicker && currentSection === 'conditions'" :current-module="ruleModule" :current-module-type="currentRuleModuleType" @typeSelect="setModuleType" @showAdvanced="advancedTypePicker = true" @startScript="startScripting" />
|
||||
<action-module-wizard v-else-if="!advancedTypePicker && currentSection === 'actions'" :current-module="ruleModule" :current-module-type="currentRuleModuleType" @typeSelect="setModuleType" @showAdvanced="advancedTypePicker = true" @startScript="startScripting" />
|
||||
</div>
|
||||
<f7-list v-if="ruleModule.type && (!ruleModule.new || advancedTypePicker)">
|
||||
<f7-list-item :title="sectionLabels[currentSection][0]" ref="ruleModuleTypeSmartSelect" smart-select :smart-select-params="{ view: $f7.views.main, openIn: 'popup', closeOnSelect: true }">
|
||||
<select name="ruleModuleType"
|
||||
@change="setModuleType(moduleTypes[currentSection].find((t) => t.uid === $refs.ruleModuleTypeSmartSelect.f7SmartSelect.getValue()))">
|
||||
@change="setModuleType(moduleTypes[currentSection].find((t) => t.uid === $refs.ruleModuleTypeSmartSelect.f7SmartSelect.getValue()), true)">
|
||||
<optgroup v-for="(mt, scope) in groupedModuleTypes(currentSection)" :key="scope" :label="scope">
|
||||
<option v-for="moduleType in mt"
|
||||
:value="moduleType.uid" :key="moduleType.uid" :selected="currentRuleModuleType.uid === moduleType.uid">
|
||||
|
@ -48,8 +53,8 @@
|
|||
</select>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
<f7-block-title v-if="ruleModule && currentRuleModuleType" style="margin-bottom: calc(var(--f7-block-title-margin-bottom) - var(--f7-list-margin-vertical))">Configuration</f7-block-title>
|
||||
<f7-col v-if="ruleModule && currentRuleModuleType">
|
||||
<f7-block-title v-if="ruleModule && currentRuleModuleType && (!ruleModule.new || advancedTypePicker)" style="margin-bottom: calc(var(--f7-block-title-margin-bottom) - var(--f7-list-margin-vertical))">Configuration</f7-block-title>
|
||||
<f7-col v-if="ruleModule && currentRuleModuleType && (!ruleModule.new || advancedTypePicker)">
|
||||
<config-sheet :key="currentSection + ruleModule.id"
|
||||
ref="parameters"
|
||||
:parameterGroups="[]"
|
||||
|
@ -65,18 +70,24 @@
|
|||
|
||||
<script>
|
||||
import ConfigSheet from '@/components/config/config-sheet.vue'
|
||||
|
||||
import TriggerModuleWizard from '@/components/rule/trigger-module-wizard.vue'
|
||||
import ConditionModuleWizard from '@/components/rule/condition-module-wizard.vue'
|
||||
import ActionModuleWizard from '@/components/rule/action-module-wizard.vue'
|
||||
import ModuleDescriptionSuggestions from './module-description-suggestions'
|
||||
|
||||
export default {
|
||||
mixins: [ModuleDescriptionSuggestions],
|
||||
components: {
|
||||
TriggerModuleWizard,
|
||||
ConditionModuleWizard,
|
||||
ActionModuleWizard,
|
||||
ConfigSheet
|
||||
},
|
||||
props: ['rule', 'ruleModule', 'ruleModuleType', 'moduleTypes', 'currentSection'],
|
||||
data () {
|
||||
return {
|
||||
currentRuleModuleType: this.ruleModuleType,
|
||||
advancedTypePicker: false,
|
||||
sectionLabels: {
|
||||
triggers: ['When', 'Add Trigger'],
|
||||
actions: ['Then', 'Add Action'],
|
||||
|
@ -85,23 +96,34 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
setModuleType (moduleType) {
|
||||
setModuleType (val, clearConfig) {
|
||||
const moduleType = (typeof val === 'string') ? this.moduleTypes[this.currentSection].find((t) => t.uid === val) : val
|
||||
this.ruleModule.type = moduleType.uid
|
||||
this.$set(this, 'currentRuleModuleType', moduleType)
|
||||
this.$set(this.ruleModule, 'configuration', {})
|
||||
if (clearConfig) this.$set(this.ruleModule, 'configuration', {})
|
||||
this.ruleModule.label = this.ruleModule.description = ''
|
||||
},
|
||||
moduleConfigClosed () {
|
||||
this.$f7.emit('ruleModuleConfigClosed')
|
||||
},
|
||||
updateModuleConfig () {
|
||||
if (!this.$refs.parameters.isValid()) {
|
||||
if (this.$refs.parameters && !this.$refs.parameters.isValid()) {
|
||||
this.$f7.dialog.alert('Please review the configuration and correct validation errors')
|
||||
return
|
||||
}
|
||||
this.$f7.emit('ruleModuleConfigUpdate', this.ruleModule)
|
||||
this.$refs.modulePopup.close()
|
||||
},
|
||||
startScripting (language) {
|
||||
const contentType = (language === 'blockly') ? 'application/javascript' : language
|
||||
this.$set(this.ruleModule.configuration, 'type', contentType)
|
||||
if (language === 'blockly') {
|
||||
// initialize an empty blockly source
|
||||
this.$set(this.ruleModule.configuration, 'blockSource', '<xml xmlns="https://developers.google.com/blockly/xml"></xml>')
|
||||
}
|
||||
this.$f7.emit('editNewScript', this.ruleModule)
|
||||
this.$refs.modulePopup.close()
|
||||
},
|
||||
groupedModuleTypes (section) {
|
||||
const moduleTypes = this.moduleTypes[section].filter((t) => t.visibility === 'VISIBLE')
|
||||
let moduleTypesByScope = moduleTypes.reduce((prev, type, i, types) => {
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<f7-fab v-show="!newScript && isBlockly && blocklyCodePreview" position="right-bottom" slot="fixed" color="blue" @click="blocklyCodePreview = false">
|
||||
<f7-icon f7="ticket"></f7-icon>
|
||||
</f7-fab>
|
||||
<f7-fab v-show="!newScript && !script && mode === 'application/javascript' && !isBlockly" position="center-center" slot="fixed" color="blue" @click="convertToBlockly" text="Design with Blockly">
|
||||
<f7-fab v-show="!newScript && !script && mode === 'application/javascript' && !isBlockly" position="center-bottom" slot="fixed" color="blue" @click="convertToBlockly" text="Design with Blockly">
|
||||
<f7-icon f7="ticket_fill"></f7-icon>
|
||||
</f7-fab>
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="Layer_6"
|
||||
data-name="Layer 6"
|
||||
viewBox="0 0 192 192"
|
||||
version="1.1"
|
||||
sodipodi:docname="logo-only.svg"
|
||||
inkscape:version="0.92.2pre0 (973e216, 2017-07-25)"
|
||||
inkscape:export-filename="/usr/local/google/home/epastern/Documents/Blockly Logos/Square/logo-only.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<metadata
|
||||
id="metadata913">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>logo-only</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1379"
|
||||
id="namedview911"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="239.87642"
|
||||
inkscape:cy="59.742687"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g1013" />
|
||||
<defs
|
||||
id="defs902">
|
||||
<style
|
||||
id="style900">.cls-1{fill:#4285f4;}.cls-2{fill:#c8d1db;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title904">logo-only</title>
|
||||
<g
|
||||
id="g1013"
|
||||
transform="translate(23.500002,-7.9121105)"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<path
|
||||
id="path906"
|
||||
d="M 20.140625,32 C 13.433598,31.994468 7.9944684,37.433598 8,44.140625 V 148.85938 C 7.99447,155.56641 13.433598,161.00553 20.140625,161 h 4.726563 c 2.330826,8.74182 10.245751,14.82585 19.292968,14.83008 C 53.201562,175.81878 61.108176,169.73621 63.4375,161 h 4.841797 15.726562 c 4.418278,0 8,-3.58172 8,-8 V 40 l -8,-8 z"
|
||||
style="fill:#4285f4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccssccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path908"
|
||||
d="M 80.007812,31.994141 C 79.997147,49.696887 80,67.396525 80,85.109375 L 63.369141,75.710938 C 60.971784,74.358189 58.004891,76.087168 58,78.839844 v 40.621096 c 0.0049,2.75267 2.971786,4.48165 5.369141,3.1289 L 80,113.18945 v 37.5918 2.21875 8 h 8 1.425781 36.054689 c 6.36195,-2.6e-4 11.51927,-5.15758 11.51953,-11.51953 V 43.480469 C 136.97822,37.133775 131.8272,32.000222 125.48047,32 Z"
|
||||
style="fill:#c8d1db" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
Loading…
Reference in New Issue