Update alexa integration with new metadata syntax (#1145)
Signed-off-by: jsetton <jeremy.setton@gmail.com>pull/1206/head
parent
9aeb301771
commit
1671c1aaaf
|
@ -1,271 +0,0 @@
|
||||||
|
|
||||||
const categories = [
|
|
||||||
'ACTIVITY_TRIGGER',
|
|
||||||
'CAMERA',
|
|
||||||
'COMPUTER',
|
|
||||||
'CONTACT_SENSOR',
|
|
||||||
'DOOR',
|
|
||||||
'DOORBELL',
|
|
||||||
'EXTERIOR_BLIND',
|
|
||||||
'FAN',
|
|
||||||
'GAME_CONSOLE',
|
|
||||||
'GARAGE_DOOR',
|
|
||||||
'INTERIOR_BLIND',
|
|
||||||
'LAPTOP',
|
|
||||||
'LIGHT',
|
|
||||||
'MICROWAVE',
|
|
||||||
'MOBILE_PHONE',
|
|
||||||
'MOTION_SENSOR',
|
|
||||||
'MUSIC_SYSTEM',
|
|
||||||
'NETWORK_HARDWARE',
|
|
||||||
'OTHER',
|
|
||||||
'OVEN',
|
|
||||||
'PHONE',
|
|
||||||
'SCENE_TRIGGER',
|
|
||||||
'SCREEN',
|
|
||||||
'SECURITY_PANEL',
|
|
||||||
'SMARTLOCK',
|
|
||||||
'SMARTPLUG',
|
|
||||||
'SPEAKER',
|
|
||||||
'STREAMING_DEVICE',
|
|
||||||
'SWITCH',
|
|
||||||
'TABLET',
|
|
||||||
'TEMPERATURE_SENSOR',
|
|
||||||
'THERMOSTAT',
|
|
||||||
'TV',
|
|
||||||
'WEARABLE'
|
|
||||||
]
|
|
||||||
|
|
||||||
// Group endpoints are generated from the display categories. Example from the docs: SECURITY_PANEL => Endpoint.SecurityPanel.
|
|
||||||
const groupEndpoints = categories
|
|
||||||
.map(category => {
|
|
||||||
const convertedChars = []
|
|
||||||
let capitalizeNext = false
|
|
||||||
for (let i = 0; i < category.length; i++) {
|
|
||||||
const currentChar = category.charAt(i)
|
|
||||||
if (i === 0) {
|
|
||||||
convertedChars.push(currentChar.toUpperCase())
|
|
||||||
} else if (currentChar === '_') {
|
|
||||||
capitalizeNext = true
|
|
||||||
} else if (capitalizeNext) {
|
|
||||||
convertedChars.push(currentChar.toUpperCase())
|
|
||||||
capitalizeNext = false
|
|
||||||
} else {
|
|
||||||
convertedChars.push(currentChar.toLocaleLowerCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'Endpoint.' + convertedChars.join('')
|
|
||||||
}).reduce((endpoints, endpointName) => {
|
|
||||||
endpoints[endpointName] = []
|
|
||||||
return endpoints
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const labels = {
|
|
||||||
'Switchable': [],
|
|
||||||
'Lighting': [],
|
|
||||||
'Blind': [],
|
|
||||||
'Door': [],
|
|
||||||
'Lock': [],
|
|
||||||
'Outlet': [],
|
|
||||||
'CurrentHumidity': [],
|
|
||||||
'CurrentTemperature': [],
|
|
||||||
'TargetTemperature': [],
|
|
||||||
'LowerTemperature': [],
|
|
||||||
'UpperTemperature': [],
|
|
||||||
'HeatingCoolingMode': [],
|
|
||||||
'ColorTemperature': [],
|
|
||||||
'Activity': [],
|
|
||||||
'Scene': [],
|
|
||||||
'EntertainmentChannel': [],
|
|
||||||
'EntertainmentInput': [],
|
|
||||||
'EqualizerBass': [],
|
|
||||||
'EqualizerMidrange': [],
|
|
||||||
'EqualizerTreble': [],
|
|
||||||
'EqualizerMode': [],
|
|
||||||
'MediaPlayer': [],
|
|
||||||
'SpeakerMute': [],
|
|
||||||
'SpeakerVolume': [],
|
|
||||||
'ContactSensor': [],
|
|
||||||
'MotionSensor': [],
|
|
||||||
'SecurityAlarmMode': [],
|
|
||||||
'BurglaryAlarm': [],
|
|
||||||
'FireAlarm': [],
|
|
||||||
'CarbonMonoxideAlarm': [],
|
|
||||||
'WaterAlarm': [],
|
|
||||||
'ModeComponent': [],
|
|
||||||
'RangeComponent': [],
|
|
||||||
'ToggleComponent': []
|
|
||||||
}
|
|
||||||
|
|
||||||
const p = (type, name, label, description, options, advanced) => {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
label,
|
|
||||||
description,
|
|
||||||
advanced,
|
|
||||||
limitToOptions: !!options,
|
|
||||||
options: (!options) ? undefined : options.split(',').map((o) => {
|
|
||||||
const parts = o.split('=')
|
|
||||||
return { value: parts[0], label: parts[1] || parts[0] }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const categoryParameter = p('TEXT', 'category', 'Category', 'Override the default category for the class', categories.join(','), true)
|
|
||||||
const scaleParameter = p('TEXT', 'scale', 'Scale', 'Temperature Unit', 'Celsius,Fahrenheit')
|
|
||||||
const comfortRangeParameter = p('TEXT', 'comfortRange', 'Comfort Range', 'Number to define the comfort range, defaults: 2°F or 1°C')
|
|
||||||
const setpointRangeParameter = p('TEXT', 'setpointRange', 'Setpoint Range', 'Format: <code>minRange:maxRange</code>')
|
|
||||||
const rangeParameter = p('TEXT', 'range', 'Range', 'Format: <code>minRange:maxRange</code>')
|
|
||||||
const volumeIncrementParameter = p('INTEGER', 'increment', 'Increment')
|
|
||||||
const friendlyNamesParameter = p('TEXT', 'friendlyNames', 'Friendly Names', 'each name formatted as <code>@assetIdOrName</code>, defaults to item label name')
|
|
||||||
const nonControllableParameter = p('BOOLEAN', 'nonControllable', 'Non Controllable')
|
|
||||||
const languageParameter = p('TEXT', 'language', 'Language', 'defaults to your server regional settings, if defined, otherwise en', 'de,en,es,fr,hi,it,ja,pt')
|
|
||||||
const actionMappingsParameter = p('TEXT', 'actionMappings', 'Action Mappings')
|
|
||||||
const stateMappingsParameter = p('TEXT', 'stateMappings', 'State Mappings')
|
|
||||||
|
|
||||||
const capabilities = {
|
|
||||||
'PowerController.powerState': [],
|
|
||||||
'BrightnessController.brightness': [],
|
|
||||||
'PowerLevelController.powerLevel': [],
|
|
||||||
'PercentageController.percentage': [],
|
|
||||||
'ThermostatController.targetSetpoint': [
|
|
||||||
scaleParameter,
|
|
||||||
setpointRangeParameter
|
|
||||||
],
|
|
||||||
'ThermostatController.upperSetpoint': [
|
|
||||||
scaleParameter,
|
|
||||||
comfortRangeParameter,
|
|
||||||
setpointRangeParameter
|
|
||||||
],
|
|
||||||
'ThermostatController.lowerSetpoint': [
|
|
||||||
scaleParameter,
|
|
||||||
comfortRangeParameter,
|
|
||||||
setpointRangeParameter
|
|
||||||
],
|
|
||||||
'ThermostatController.thermostatMode': [
|
|
||||||
p('TEXT', 'OFF', 'OFF State'),
|
|
||||||
p('TEXT', 'HEAT', 'HEAT State'),
|
|
||||||
p('TEXT', 'COOL', 'COOL State'),
|
|
||||||
p('TEXT', 'ECO', 'ECO State'),
|
|
||||||
p('TEXT', 'AUTO', 'AUTO State'),
|
|
||||||
p('TEXT', 'binding', 'Binding', 'Auto-configure modes for binding', 'daikin,max,nest,zwave'),
|
|
||||||
p('TEXT', 'supportedModes', 'Supported modes'),
|
|
||||||
p('BOOLEAN', 'supportsSetpointMode', 'Supports Setpoint Mode', '', null, true)
|
|
||||||
],
|
|
||||||
'TemperatureSensor.temperature': [scaleParameter],
|
|
||||||
'LockController.lockState': [
|
|
||||||
p('TEXT', 'LOCKED', 'Locked State'),
|
|
||||||
p('TEXT', 'UNLOCKED', 'Unlocked State'),
|
|
||||||
p('TEXT', 'JAMMED', 'Jammed State')
|
|
||||||
],
|
|
||||||
'ColorController.color': [],
|
|
||||||
'ColorTemperatureController.colorTemperatureInKelvin': [
|
|
||||||
p('INTEGER', 'increment', 'Increment'),
|
|
||||||
rangeParameter,
|
|
||||||
p('TEXT', 'binding', 'Binding', 'Auto-configure range for binding', 'hue,lifx,milight,tradfri,yeelight')
|
|
||||||
],
|
|
||||||
'SceneController.scene': [
|
|
||||||
p('TEXT', 'supportsDeactivation', 'Supports deactivation')
|
|
||||||
],
|
|
||||||
'ChannelController.channel': [
|
|
||||||
// User should switch to YAML for this one
|
|
||||||
],
|
|
||||||
'InputController.input': [
|
|
||||||
p('TEXT', 'supportedInputs', 'required list of supported input values (e.g. "HMDI1,TV,XBOX")')
|
|
||||||
],
|
|
||||||
'Speaker.volume': [
|
|
||||||
volumeIncrementParameter
|
|
||||||
],
|
|
||||||
'Speaker.muted': [],
|
|
||||||
'StepSpeaker.volume': [
|
|
||||||
volumeIncrementParameter
|
|
||||||
],
|
|
||||||
'StepSpeaker.muted': [],
|
|
||||||
'PlaybackController.playback': [],
|
|
||||||
'EqualizerController.bands:bass': [
|
|
||||||
rangeParameter
|
|
||||||
],
|
|
||||||
'EqualizerController.bands:midrange': [
|
|
||||||
rangeParameter
|
|
||||||
],
|
|
||||||
'EqualizerController.bands:treble': [
|
|
||||||
rangeParameter
|
|
||||||
],
|
|
||||||
'EqualizerController.modes': [
|
|
||||||
p('TEXT', 'MOVIE', 'MOVIE State'),
|
|
||||||
p('TEXT', 'MUSIC', 'MUSIC State'),
|
|
||||||
p('TEXT', 'NIGHT', 'NIGHT State'),
|
|
||||||
p('TEXT', 'SPORT', 'SPORT State'),
|
|
||||||
p('TEXT', 'TV', 'TV State'),
|
|
||||||
p('TEXT', 'supportedModes', 'Supported modes')
|
|
||||||
],
|
|
||||||
|
|
||||||
// TODO the rest
|
|
||||||
'ContactSensor.detectionState': [],
|
|
||||||
'MotionSensor.detectionState': [],
|
|
||||||
'SecurityPanelController.armState': [
|
|
||||||
p('TEXT', 'DISARMED', 'DISARMED State'),
|
|
||||||
p('TEXT', 'ARMED_STAY', 'ARMED_STAY State'),
|
|
||||||
p('TEXT', 'ARMED_AWAY', 'ARMED_AWAY State'),
|
|
||||||
p('TEXT', 'ARMED_NIGHT', 'ARMED_NIGHT State'),
|
|
||||||
p('TEXT', 'AUTHORIZATION_REQUIRED', 'AUTHORIZATION_REQUIRED State'),
|
|
||||||
p('TEXT', 'UNAUTHORIZED', 'UNAUTHORIZED State'),
|
|
||||||
p('TEXT', 'NOT_READY', 'NOT_READY State'),
|
|
||||||
p('TEXT', 'UNCLEARED_ALARM', 'UNCLEARED_ALARM State'),
|
|
||||||
p('TEXT', 'UNCLEARED_TROUBLE', 'UNCLEARED_TROUBLE State'),
|
|
||||||
p('TEXT', 'BYPASS_NEEDED', 'BYPASS_NEEDED State'),
|
|
||||||
p('TEXT', 'supportedArmStates', 'Supported arm states'),
|
|
||||||
p('BOOLEAN', 'supportsPinCodes', 'Supports pin codes'),
|
|
||||||
p('INTEGER', 'exitDelay', 'Exit Delay')
|
|
||||||
],
|
|
||||||
'SecurityPanelController.burglaryAlarm': [],
|
|
||||||
'SecurityPanelController.fireAlarm': [],
|
|
||||||
'SecurityPanelController.carbonMonoxideAlarm': [],
|
|
||||||
'SecurityPanelController.waterAlarm': [],
|
|
||||||
'ModeController.mode': [
|
|
||||||
friendlyNamesParameter,
|
|
||||||
nonControllableParameter,
|
|
||||||
p('TEXT', 'supportedModes', 'Supported Modes'),
|
|
||||||
p('BOOLEAN', 'ordered', 'Ordered'),
|
|
||||||
languageParameter,
|
|
||||||
actionMappingsParameter,
|
|
||||||
stateMappingsParameter
|
|
||||||
],
|
|
||||||
'RangeController.rangeValue': [
|
|
||||||
friendlyNamesParameter,
|
|
||||||
nonControllableParameter,
|
|
||||||
p('TEXT', 'supportedRange', 'Supported Range'),
|
|
||||||
p('TEXT', 'presets', 'Presets'),
|
|
||||||
p('TEXT', 'unitOfMeasure', 'Unit of Measure'),
|
|
||||||
languageParameter,
|
|
||||||
actionMappingsParameter,
|
|
||||||
stateMappingsParameter
|
|
||||||
],
|
|
||||||
'ToggleController.toggleState': [
|
|
||||||
friendlyNamesParameter,
|
|
||||||
nonControllableParameter,
|
|
||||||
languageParameter,
|
|
||||||
actionMappingsParameter,
|
|
||||||
stateMappingsParameter
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
let classes = {}
|
|
||||||
|
|
||||||
for (let l in labels) {
|
|
||||||
labels[l].unshift(categoryParameter)
|
|
||||||
classes['label:' + l] = labels[l]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let l in groupEndpoints) {
|
|
||||||
groupEndpoints[l].unshift(categoryParameter)
|
|
||||||
classes['endpoint:' + l] = groupEndpoints[l]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let c in capabilities) {
|
|
||||||
capabilities[c].unshift(categoryParameter)
|
|
||||||
classes[c] = capabilities[c]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default classes
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
export const ARM_STATES = ['DISARMED', 'ARMED_STAY', 'ARMED_AWAY', 'ARMED_NIGHT']
|
||||||
|
|
||||||
|
export const EQUALIZER_MODES = ['MOVIE', 'MUSIC', 'NIGHT', 'SPORT', 'TV']
|
||||||
|
|
||||||
|
export const FAN_DIRECTIONS = ['FORWARD', 'REVERSE']
|
||||||
|
|
||||||
|
export const FAN_SPEEDS = ['OFF', 'LOW', 'MEDIUM', 'HIGH']
|
||||||
|
|
||||||
|
export const LANGUAGES = {
|
||||||
|
en: 'English',
|
||||||
|
fr: 'French',
|
||||||
|
de: 'German',
|
||||||
|
hi: 'Hindi',
|
||||||
|
it: 'Italian',
|
||||||
|
ja: 'Japanese',
|
||||||
|
pt: 'Portuguese',
|
||||||
|
es: 'Spanish'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LOCK_STATES = ['LOCKED', 'UNLOCKED', 'JAMMED']
|
||||||
|
|
||||||
|
export const OPEN_STATES = ['CLOSED', 'OPEN']
|
||||||
|
|
||||||
|
export const PLAYBACK_OPERATIONS = ['Play', 'Pause', 'Next', 'Previous', 'FastForward', 'Rewind']
|
||||||
|
|
||||||
|
export const TEMPERATURE_SCALES = ['CELSIUS', 'FAHRENHEIT']
|
||||||
|
|
||||||
|
export const THERMOSTAT_MODES = ['OFF', 'HEAT', 'COOL', 'ECO', 'AUTO']
|
||||||
|
|
||||||
|
export const THERMOSTAT_FAN_MODES = ['AUTO', 'ON']
|
||||||
|
|
||||||
|
export const VACUUM_MODES = ['CLEAN', 'DOCK', 'SPOT', 'PAUSE', 'STOP']
|
||||||
|
|
||||||
|
export const UNITS_OF_MEASURE = {
|
||||||
|
'Angle.Degrees': '°',
|
||||||
|
'Angle.Radians': 'rad',
|
||||||
|
'Percent': '%',
|
||||||
|
'Distance.Yards': 'yd',
|
||||||
|
'Distance.Inches': 'in',
|
||||||
|
'Distance.Meters': 'm',
|
||||||
|
'Distance.Feet': 'ft',
|
||||||
|
'Distance.Miles': 'mi',
|
||||||
|
'Distance.Kilometers': 'km',
|
||||||
|
'Mass.Kilograms': 'kg',
|
||||||
|
'Mass.Grams': 'g',
|
||||||
|
'Weight.Pounds': 'lb',
|
||||||
|
'Weight.Ounces': 'oz',
|
||||||
|
'Temperature.Degrees': '°',
|
||||||
|
'Temperature.Celsius': '°C',
|
||||||
|
'Temperature.Fahrenheit': '°F',
|
||||||
|
'Temperature.Kelvin': 'K',
|
||||||
|
'Volume.Gallons': 'gal',
|
||||||
|
'Volume.Pints': 'pt',
|
||||||
|
'Volume.Quarts': 'qt',
|
||||||
|
'Volume.Liters': 'l',
|
||||||
|
'Volume.CubicMeters': 'm3',
|
||||||
|
'Volume.CubicFeet': 'ft3'
|
||||||
|
}
|
|
@ -0,0 +1,411 @@
|
||||||
|
import p from './parameters.js'
|
||||||
|
import {
|
||||||
|
ARM_STATES,
|
||||||
|
EQUALIZER_MODES,
|
||||||
|
FAN_DIRECTIONS,
|
||||||
|
FAN_SPEEDS,
|
||||||
|
LOCK_STATES,
|
||||||
|
OPEN_STATES,
|
||||||
|
THERMOSTAT_MODES,
|
||||||
|
THERMOSTAT_FAN_MODES,
|
||||||
|
VACUUM_MODES
|
||||||
|
} from './constants.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Camera Attributes
|
||||||
|
CameraStream: {
|
||||||
|
itemTypes: ['String'],
|
||||||
|
parameters: () => [p.proxyBaseUrl(), p.resolution(), p.basicAuth('username'), p.basicAuth('password')]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cover Attributes
|
||||||
|
OpenState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
TargetOpenState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
requires: ['CurrentOpenState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
CurrentOpenState: {
|
||||||
|
itemTypes: ['Contact', 'Number', 'String', 'Switch'],
|
||||||
|
requires: ['TargetOpenState'],
|
||||||
|
parameters: (item) =>
|
||||||
|
item.type === 'Contact' || item.type === 'Switch'
|
||||||
|
? [p.inverted()]
|
||||||
|
: OPEN_STATES.map((state) => p.valueMapping(state))
|
||||||
|
},
|
||||||
|
ObstacleAlert: {
|
||||||
|
itemTypes: ['Contact ', 'Switch'],
|
||||||
|
requires: ['OpenState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
PositionState: {
|
||||||
|
itemTypes: ['Dimmer', 'Rollershutter'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.inverted(item.type === 'Rollershutter'),
|
||||||
|
p.presets(item.stateDescription, '20=Morning,60=Afternoon,80=Evening:@Setting.Night', true),
|
||||||
|
p.language(item.settings && item.settings.regional.language)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
TiltAngle: {
|
||||||
|
itemTypes: ['Dimmer', 'Number', 'Number:Angle', 'Rollershutter'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.inverted(item.type === 'Rollershutter'),
|
||||||
|
p.presets(item.stateDescription, '20=Morning,60=Afternoon,80=Evening:@Setting.Night', true),
|
||||||
|
p.language(item.settings && item.settings.regional.language)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Entertainment Attributes
|
||||||
|
Channel: {
|
||||||
|
itemTypes: ['Number', 'String'],
|
||||||
|
parameters: () => [p.channelMappings(), p.retrievable()]
|
||||||
|
},
|
||||||
|
Input: {
|
||||||
|
itemTypes: ['Number', 'String'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.supportedInputs(item.stateDescription, item.type === 'String' ? 'HDMI1=Cable,HDMI2=Kodi' : '1=Cable,2=Kodi'),
|
||||||
|
p.language(item.settings && item.settings.regional.language),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
VolumeLevel: {
|
||||||
|
itemTypes: ['Dimmer', 'Number'],
|
||||||
|
parameters: (item) => [p.increment(10), ...(item.type === 'Number' ? [p.stepSpeaker()] : [])]
|
||||||
|
},
|
||||||
|
MuteState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.inverted(), p.stepSpeaker()]
|
||||||
|
},
|
||||||
|
EqualizerBass: {
|
||||||
|
itemTypes: ['Dimmer', 'Number'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.equalizerRange(item.type === 'Dimmer' ? '0:100' : '-10:10'),
|
||||||
|
p.equalizerDefaultLevel(item.type === 'Dimmer' ? 50 : 0),
|
||||||
|
p.increment(item.type === 'Dimmer' ? 'INCREASE/DECREASE' : 1),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
EqualizerMidrange: {
|
||||||
|
itemTypes: ['Dimmer', 'Number'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.equalizerRange(item.type === 'Dimmer' ? '0:100' : '-10:10'),
|
||||||
|
p.equalizerDefaultLevel(item.type === 'Dimmer' ? 50 : 0),
|
||||||
|
p.increment(item.type === 'Dimmer' ? 'INCREASE/DECREASE' : 1),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
EqualizerTreble: {
|
||||||
|
itemTypes: ['Dimmer', 'Number'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.equalizerRange(item.type === 'Dimmer' ? '0:100' : '-10:10'),
|
||||||
|
p.equalizerDefaultLevel(item.type === 'Dimmer' ? 50 : 0),
|
||||||
|
p.increment(item.type === 'Dimmer' ? 'INCREASE/DECREASE' : 1),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
EqualizerMode: {
|
||||||
|
itemTypes: ['Number', 'String'],
|
||||||
|
parameters: () => [
|
||||||
|
...EQUALIZER_MODES.map((mode) => p.valueMapping(mode)),
|
||||||
|
p.supportedEqualizerModes(),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Playback: {
|
||||||
|
itemTypes: ['Player'],
|
||||||
|
parameters: () => [p.supportedOperations()]
|
||||||
|
},
|
||||||
|
PlaybackStop: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
requires: ['Playback'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fan Attributes
|
||||||
|
FanDirection: {
|
||||||
|
itemTypes: ['String', 'Switch'],
|
||||||
|
parameters: (item) => [
|
||||||
|
...(item.type === 'Switch' ? [p.inverted()] : FAN_DIRECTIONS.map((direction) => p.valueMapping(direction))),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
FanOscillate: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.inverted(), p.retrievable()]
|
||||||
|
},
|
||||||
|
FanSpeed: {
|
||||||
|
itemTypes: ['Dimmer', 'Number', 'String'],
|
||||||
|
parameters: (item) => [
|
||||||
|
...(item.type === 'Dimmer'
|
||||||
|
? [p.inverted()]
|
||||||
|
: item.type === 'Number'
|
||||||
|
? [p.speedLevels()]
|
||||||
|
: FAN_SPEEDS.map((speed) => p.valueMapping(speed))),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Light Attributes
|
||||||
|
Brightness: {
|
||||||
|
itemTypes: ['Color', 'Dimmer'],
|
||||||
|
parameters: () => [p.retrievable()]
|
||||||
|
},
|
||||||
|
Color: {
|
||||||
|
itemTypes: ['Color'],
|
||||||
|
parameters: () => [p.retrievable()]
|
||||||
|
},
|
||||||
|
ColorTemperature: {
|
||||||
|
itemTypes: ['Dimmer', 'Number'],
|
||||||
|
parameters: (item) => [
|
||||||
|
...(item.type === 'Dimmer' ? [p.colorTemperatureBinding()] : []),
|
||||||
|
p.colorTemperatureRange(),
|
||||||
|
p.increment(item.type === 'Dimmer' ? 'INCREASE/DECREASE' : 500),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Networking Attributes
|
||||||
|
NetworkAccess: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.inverted(), p.retrievable()],
|
||||||
|
visible: (item) => item.groups
|
||||||
|
.map((group) => group.metadata.alexa.config || {})
|
||||||
|
.some((config) => !!config.macAddress)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Scene Attributes
|
||||||
|
Scene: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.supportsDeactivation()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Security Attributes
|
||||||
|
LockState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
parameters: () => [p.inverted(), p.retrievable()]
|
||||||
|
},
|
||||||
|
TargetLockState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
requires: ['CurrentLockState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
CurrentLockState: {
|
||||||
|
itemTypes: ['Contact', 'Number', 'String', 'Switch'],
|
||||||
|
requires: ['TargetLockState'],
|
||||||
|
parameters: (item) =>
|
||||||
|
item.type === 'Contact' || item.type === 'Switch'
|
||||||
|
? [p.inverted()]
|
||||||
|
: LOCK_STATES.map((state) => p.valueMapping(state))
|
||||||
|
},
|
||||||
|
ArmState: {
|
||||||
|
itemTypes: ['Number', 'String', 'Switch'],
|
||||||
|
parameters: () => [
|
||||||
|
...ARM_STATES.map((state) => p.valueMapping(state)),
|
||||||
|
p.supportedArmStates(),
|
||||||
|
p.pinCodes(),
|
||||||
|
p.exitDelay(),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
BurglaryAlarm: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
CarbonMonoxideAlarm: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
FireAlarm: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
WaterAlarm: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
AlarmAlert: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
requires: ['ArmState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
ReadyAlert: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
requires: ['ArmState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
TroubleAlert: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
requires: ['ArmState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
ZonesAlert: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
requires: ['ArmState'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sensor Attributes
|
||||||
|
BatteryLevel: {
|
||||||
|
itemTypes: ['Dimmer', 'Number', 'Number:Dimensionless']
|
||||||
|
},
|
||||||
|
CurrentHumidity: {
|
||||||
|
itemTypes: ['Dimmer', 'Number', 'Number:Dimensionless']
|
||||||
|
},
|
||||||
|
CurrentTemperature: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
parameters: (item) => [p.scale(item)]
|
||||||
|
},
|
||||||
|
ContactDetectionState: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
MotionDetectionState: {
|
||||||
|
itemTypes: ['Contact', 'Switch'],
|
||||||
|
parameters: () => [p.inverted()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Switchable Attributes
|
||||||
|
PowerState: {
|
||||||
|
itemTypes: ['Color', 'Dimmer', 'Number', 'String', 'Switch'],
|
||||||
|
parameters: (item) => [
|
||||||
|
...(item.type === 'Number' || item.type === 'String'
|
||||||
|
? [p.valueMapping('OFF', true), p.valueMapping('ON', true)]
|
||||||
|
: []),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
PowerLevel: {
|
||||||
|
itemTypes: ['Dimmer'],
|
||||||
|
parameters: () => [p.retrievable()]
|
||||||
|
},
|
||||||
|
Percentage: {
|
||||||
|
itemTypes: ['Dimmer', 'Rollershutter'],
|
||||||
|
parameters: (item) => [p.inverted(item.type === 'Rollershutter'), p.retrievable()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Thermostat Attributes
|
||||||
|
TargetTemperature: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
parameters: (item) => [p.scale(item), p.setpointRange(item), p.retrievable()]
|
||||||
|
},
|
||||||
|
CoolingSetpoint: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
requires: ['HeatingSetpoint'],
|
||||||
|
parameters: (item) => [p.scale(item), p.comfortRange(item), p.setpointRange(item), p.retrievable()]
|
||||||
|
},
|
||||||
|
HeatingSetpoint: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
requires: ['CoolingSetpoint'],
|
||||||
|
parameters: (item) => [p.scale(item), p.comfortRange(item), p.setpointRange(item), p.retrievable()]
|
||||||
|
},
|
||||||
|
EcoCoolingSetpoint: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
requires: ['EcoHeatingSetpoint'],
|
||||||
|
parameters: (item) => [p.scale(item), p.comfortRange(item), p.setpointRange(item), p.retrievable()]
|
||||||
|
},
|
||||||
|
EcoHeatingSetpoint: {
|
||||||
|
itemTypes: ['Number', 'Number:Temperature'],
|
||||||
|
requires: ['EcoCoolingSetpoint'],
|
||||||
|
parameters: (item) => [p.scale(item), p.comfortRange(item), p.setpointRange(item), p.retrievable()]
|
||||||
|
},
|
||||||
|
HeatingCoolingMode: {
|
||||||
|
itemTypes: ['Number', 'String', 'Switch'],
|
||||||
|
parameters: () => [
|
||||||
|
...THERMOSTAT_MODES.map((mode) => p.thermostatModeMapping(mode)),
|
||||||
|
p.thermostatModeBinding(),
|
||||||
|
p.supportedThermostatModes(),
|
||||||
|
p.supportsSetpointMode(),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
ThermostatHold: {
|
||||||
|
itemTypes: ['Number', 'String', 'Switch'],
|
||||||
|
requires: ['HeatingCoolingMode'],
|
||||||
|
parameters: (item) => [p.valueMapping('RESUME')]
|
||||||
|
},
|
||||||
|
ThermostatFan: {
|
||||||
|
itemTypes: ['String', 'Switch'],
|
||||||
|
parameters: (item) => [
|
||||||
|
...(item.type === 'Switch' ? [p.inverted()] : THERMOSTAT_FAN_MODES.map((mode) => p.valueMapping(mode))),
|
||||||
|
p.retrievable()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Vacuum Attributes
|
||||||
|
VacuumMode: {
|
||||||
|
itemTypes: ['Number', 'String'],
|
||||||
|
parameters: () => [...VACUUM_MODES.map((mode) => p.valueMapping(mode)), p.retrievable()]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Generic Attributes
|
||||||
|
Mode: {
|
||||||
|
itemTypes: ['Number', 'String', 'Switch'],
|
||||||
|
supports: ['multiInstance'],
|
||||||
|
parameters: (item, config) => [
|
||||||
|
p.capabilityNames(
|
||||||
|
item.groups.length ? item.label : '@Setting.Mode',
|
||||||
|
'Wash Temperature,@Setting.WaterTemperature'
|
||||||
|
),
|
||||||
|
p.nonControllable(item.stateDescription),
|
||||||
|
p.retrievable(),
|
||||||
|
p.supportedModes(item.stateDescription),
|
||||||
|
p.ordered(),
|
||||||
|
p.language(item.settings && item.settings.regional.language),
|
||||||
|
p.actionMappings(
|
||||||
|
{ set: 'mode', ...(config.ordered && { adjust: '(±deltaValue)' }) },
|
||||||
|
'Close=Down,Open=Up,Lower=Down,Raise=Up'
|
||||||
|
),
|
||||||
|
p.stateMappings(['mode'], 'Closed=Down,Open=Up')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
RangeValue: {
|
||||||
|
itemTypes: [
|
||||||
|
'Dimmer',
|
||||||
|
'Number',
|
||||||
|
'Number:Angle',
|
||||||
|
'Number:Dimensionless',
|
||||||
|
'Number:Length',
|
||||||
|
'Number:Mass',
|
||||||
|
'Number:Temperature',
|
||||||
|
'Number:Volume',
|
||||||
|
'Rollershutter'
|
||||||
|
],
|
||||||
|
supports: ['multiInstance'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.capabilityNames(item.groups.length ? item.label : '@Setting.RangeValue', '@Setting.FanSpeed,Speed'),
|
||||||
|
p.nonControllable(item.stateDescription),
|
||||||
|
p.retrievable(),
|
||||||
|
p.inverted(item.type === 'Rollershutter'),
|
||||||
|
...(item.type === 'Dimmer'
|
||||||
|
? [p.supportedCommands(['ON', 'OFF', 'INCREASE', 'DECREASE'], 'INCREASE=@Value.Up,DECREASE=@Value.Down')]
|
||||||
|
: item.type === 'Rollershutter'
|
||||||
|
? [p.supportedCommands(['UP', 'DOWN', 'MOVE', 'STOP'], 'UP=@Value.Open,DOWN=@Value.Close,STOP=@Value.Stop')]
|
||||||
|
: []),
|
||||||
|
p.supportedRange(
|
||||||
|
item.stateDescription,
|
||||||
|
item.type === 'Dimmer' || item.type === 'Rollershutter' ? '0:100:1' : '0:10:1'
|
||||||
|
),
|
||||||
|
p.presets(item.stateDescription, '1=@Value.Low:Lowest,10=@Value.High:Highest'),
|
||||||
|
p.unitOfMeasure(item),
|
||||||
|
p.language(item.settings && item.settings.regional.language),
|
||||||
|
p.actionMappings({ set: 'value', adjust: '(±deltaValue)' }, 'Close=0,Open=100,Lower=(-10),Raise=(+10)'),
|
||||||
|
p.stateMappings({ default: 'value', range: 'minValue:maxValue' }, 'Closed=0,Open=1:100')
|
||||||
|
]
|
||||||
|
},
|
||||||
|
ToggleState: {
|
||||||
|
itemTypes: ['Switch'],
|
||||||
|
supports: ['multiInstance'],
|
||||||
|
parameters: (item) => [
|
||||||
|
p.capabilityNames(item.groups.length ? item.label : '@Setting.ToggleState', '@Setting.Oscillate,Rotate'),
|
||||||
|
p.nonControllable(item.stateDescription),
|
||||||
|
p.retrievable(),
|
||||||
|
p.inverted(),
|
||||||
|
p.language(item.settings && item.settings.regional.language),
|
||||||
|
p.actionMappings(['ON', 'OFF'], 'Close=OFF,Open=ON'),
|
||||||
|
p.stateMappings(['ON', 'OFF'], 'Closed=OFF,Open=ON')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,317 @@
|
||||||
|
import attributes from './deviceattributes.js'
|
||||||
|
import p from './parameters.js'
|
||||||
|
|
||||||
|
const genericAttributes = ['Mode', 'RangeValue', 'ToggleState']
|
||||||
|
const genericDeviceAttributes = ['PowerState', ...genericAttributes]
|
||||||
|
const networkDeviceAttributes = ['NetworkAccess', ...genericDeviceAttributes]
|
||||||
|
const mobileDeviceAttributes = ['BatteryLevel', ...networkDeviceAttributes]
|
||||||
|
const sensorAttributes = ['BatteryLevel', ...genericAttributes]
|
||||||
|
|
||||||
|
const cameraAttributes = ['CameraStream', 'BatteryLevel', ...genericDeviceAttributes]
|
||||||
|
const doorAttributes = ['OpenState', 'TargetOpenState', 'CurrentOpenState', ...genericAttributes]
|
||||||
|
const blindAttributes = ['PositionState', 'TiltAngle', ...doorAttributes]
|
||||||
|
const fanAttributes = ['FanDirection', 'FanOscillate', 'FanSpeed', ...genericDeviceAttributes]
|
||||||
|
const lightAttributes = ['Brightness', 'Color', 'ColorTemperature', ...genericDeviceAttributes]
|
||||||
|
const switchAttributes = ['PowerLevel', 'Percentage', ...genericDeviceAttributes]
|
||||||
|
|
||||||
|
const entertainmentAttributes = [
|
||||||
|
'VolumeLevel',
|
||||||
|
'MuteState',
|
||||||
|
'Channel',
|
||||||
|
'Input',
|
||||||
|
'Playback',
|
||||||
|
'PlaybackStop',
|
||||||
|
'EqualizerBass',
|
||||||
|
'EqualizerMidrange',
|
||||||
|
'EqualizerTreble',
|
||||||
|
'EqualizerMode',
|
||||||
|
...genericDeviceAttributes
|
||||||
|
]
|
||||||
|
const securityAttributes = [
|
||||||
|
'ArmState',
|
||||||
|
'BurglaryAlarm',
|
||||||
|
'CarbonMonoxideAlarm',
|
||||||
|
'FireAlarm',
|
||||||
|
'WaterAlarm',
|
||||||
|
'AlarmAlert',
|
||||||
|
'ReadyAlert',
|
||||||
|
'TroubleAlert',
|
||||||
|
'ZonesAlert',
|
||||||
|
...genericAttributes
|
||||||
|
]
|
||||||
|
const thermostatAttributes = [
|
||||||
|
'TargetTemperature',
|
||||||
|
'CoolingSetpoint',
|
||||||
|
'HeatingSetpoint',
|
||||||
|
'EcoCoolingSetpoint',
|
||||||
|
'EcoHeatingSetpoint',
|
||||||
|
'HeatingCoolingMode',
|
||||||
|
'ThermostatHold',
|
||||||
|
'ThermostatFan',
|
||||||
|
'CurrentTemperature',
|
||||||
|
'CurrentHumidity',
|
||||||
|
'BatteryLevel',
|
||||||
|
...genericAttributes
|
||||||
|
]
|
||||||
|
|
||||||
|
const blindParameters = (item) => {
|
||||||
|
const attributes = ['PositionState', 'TiltAngle']
|
||||||
|
const metadata = item.members.map((mbr) => mbr.metadata && mbr.metadata.alexa.value).join(',')
|
||||||
|
return attributes.every((attr) => metadata.includes(attr)) ? [p.primaryControl()] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkParameters = (item) => {
|
||||||
|
const deviceTypes = ['NetworkHardware', 'Router']
|
||||||
|
const connection = item.groups.find((g) => deviceTypes.includes(g.metadata.alexa.value))
|
||||||
|
return connection ? [p.connectedTo(connection.label || connection.name), p.hostname(), p.macAddress()] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultParameters = (item) => {
|
||||||
|
const itemType = item.groupType || item.type
|
||||||
|
return itemType === 'Group' || !item.groups.length
|
||||||
|
? [p.deviceName(item.label), p.deviceDescription(`${itemType} ${item.name}`)]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Activity: {
|
||||||
|
defaultAttributes: ['Scene'],
|
||||||
|
supportsGroup: false
|
||||||
|
},
|
||||||
|
AirConditioner: {
|
||||||
|
defaultAttributes: ['HeatingCoolingMode'],
|
||||||
|
supportedAttributes: ['HeatingCoolingMode', 'TargetTemperature', 'CurrentTemperature', ...fanAttributes]
|
||||||
|
},
|
||||||
|
AirFreshener: {
|
||||||
|
defaultAttributes: ['FanSpeed'],
|
||||||
|
supportedAttributes: fanAttributes
|
||||||
|
},
|
||||||
|
AirPurifier: {
|
||||||
|
defaultAttributes: ['FanSpeed'],
|
||||||
|
supportedAttributes: fanAttributes
|
||||||
|
},
|
||||||
|
Automobile: {
|
||||||
|
supportedAttributes: [
|
||||||
|
'BatteryLevel',
|
||||||
|
'FanSpeed',
|
||||||
|
'LockState',
|
||||||
|
'PowerState',
|
||||||
|
'CurrentTemperature',
|
||||||
|
...genericAttributes
|
||||||
|
]
|
||||||
|
},
|
||||||
|
AutomobileAccessory: {
|
||||||
|
supportedAttributes: ['BatteryLevel', 'CameraStream', 'FanSpeed', 'PowerState', ...genericAttributes]
|
||||||
|
},
|
||||||
|
Awning: {
|
||||||
|
defaultAttributes: ['PositionState', 'OpenState'],
|
||||||
|
supportedAttributes: blindAttributes,
|
||||||
|
groupParameters: blindParameters
|
||||||
|
},
|
||||||
|
Blind: {
|
||||||
|
defaultAttributes: ['PositionState', 'OpenState'],
|
||||||
|
supportedAttributes: blindAttributes,
|
||||||
|
groupParameters: blindParameters
|
||||||
|
},
|
||||||
|
BluetoothSpeaker: {
|
||||||
|
defaultAttributes: ['VolumeLevel'],
|
||||||
|
supportedAttributes: ['BatteryLevel', ...entertainmentAttributes]
|
||||||
|
},
|
||||||
|
Camera: {
|
||||||
|
defaultAttributes: ['CameraStream'],
|
||||||
|
supportedAttributes: cameraAttributes
|
||||||
|
},
|
||||||
|
ChristmasTree: {
|
||||||
|
defaultAttributes: ['PowerState', 'Brightness', 'Color'],
|
||||||
|
supportedAttributes: lightAttributes
|
||||||
|
},
|
||||||
|
CoffeeMaker: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Computer: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: networkDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
ContactSensor: {
|
||||||
|
defaultAttributes: ['ContactDetectionState'],
|
||||||
|
supportedAttributes: ['ContactDetectionState', ...sensorAttributes]
|
||||||
|
},
|
||||||
|
Curtain: {
|
||||||
|
defaultAttributes: ['PositionState', 'OpenState'],
|
||||||
|
supportedAttributes: blindAttributes,
|
||||||
|
groupParameters: blindParameters
|
||||||
|
},
|
||||||
|
Dishwasher: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Door: {
|
||||||
|
defaultAttributes: ['OpenState'],
|
||||||
|
supportedAttributes: doorAttributes
|
||||||
|
},
|
||||||
|
Doorbell: {
|
||||||
|
defaultAttributes: ['CameraStream'],
|
||||||
|
supportedAttributes: cameraAttributes
|
||||||
|
},
|
||||||
|
Dryer: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Fan: {
|
||||||
|
defaultAttributes: ['FanSpeed'],
|
||||||
|
supportedAttributes: fanAttributes
|
||||||
|
},
|
||||||
|
GameConsole: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: networkDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
GarageDoor: {
|
||||||
|
defaultAttributes: ['OpenState'],
|
||||||
|
supportedAttributes: ['ObstacleAlert', ...doorAttributes]
|
||||||
|
},
|
||||||
|
Headphones: {
|
||||||
|
defaultAttributes: ['VolumeLevel'],
|
||||||
|
supportedAttributes: ['BatteryLevel', ...entertainmentAttributes]
|
||||||
|
},
|
||||||
|
Hub: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Laptop: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: mobileDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
Light: {
|
||||||
|
defaultAttributes: ['PowerState', 'Brightness', 'Color'],
|
||||||
|
supportedAttributes: lightAttributes
|
||||||
|
},
|
||||||
|
Lock: {
|
||||||
|
defaultAttributes: ['LockState'],
|
||||||
|
supportedAttributes: ['LockState', 'TargetLockState', 'CurrentLockState', 'BatteryLevel', ...genericAttributes]
|
||||||
|
},
|
||||||
|
Microwave: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
MobilePhone: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: mobileDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
MotionSensor: {
|
||||||
|
defaultAttributes: ['MotionDetectionState'],
|
||||||
|
supportedAttributes: ['MotionDetectionState', ...sensorAttributes]
|
||||||
|
},
|
||||||
|
MusicSystem: {
|
||||||
|
defaultAttributes: ['Playback'],
|
||||||
|
supportedAttributes: entertainmentAttributes
|
||||||
|
},
|
||||||
|
NetworkHardware: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Outlet: {
|
||||||
|
defaultAttributes: ['PowerState', 'PowerLevel', 'Percentage'],
|
||||||
|
supportedAttributes: switchAttributes
|
||||||
|
},
|
||||||
|
Oven: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Phone: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Printer: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Router: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Scene: {
|
||||||
|
defaultAttributes: ['Scene'],
|
||||||
|
supportsGroup: false
|
||||||
|
},
|
||||||
|
Screen: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: entertainmentAttributes
|
||||||
|
},
|
||||||
|
SecurityPanel: {
|
||||||
|
defaultAttributes: ['ArmState'],
|
||||||
|
supportedAttributes: securityAttributes
|
||||||
|
},
|
||||||
|
SecuritySystem: {
|
||||||
|
defaultAttributes: ['ArmState'],
|
||||||
|
supportedAttributes: securityAttributes
|
||||||
|
},
|
||||||
|
Shade: {
|
||||||
|
defaultAttributes: ['PositionState', 'OpenState'],
|
||||||
|
supportedAttributes: blindAttributes,
|
||||||
|
groupParameters: blindParameters
|
||||||
|
},
|
||||||
|
Shutter: {
|
||||||
|
defaultAttributes: ['PositionState', 'OpenState'],
|
||||||
|
supportedAttributes: blindAttributes,
|
||||||
|
groupParameters: blindParameters
|
||||||
|
},
|
||||||
|
SlowCooker: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
Speaker: {
|
||||||
|
defaultAttributes: ['VolumeLevel'],
|
||||||
|
supportedAttributes: entertainmentAttributes
|
||||||
|
},
|
||||||
|
StreamingDevice: {
|
||||||
|
defaultAttributes: ['Playback'],
|
||||||
|
supportedAttributes: entertainmentAttributes
|
||||||
|
},
|
||||||
|
Switch: {
|
||||||
|
defaultAttributes: ['PowerState', 'PowerLevel', 'Percentage'],
|
||||||
|
supportedAttributes: switchAttributes
|
||||||
|
},
|
||||||
|
Tablet: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: mobileDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
Television: {
|
||||||
|
defaultAttributes: ['Channel'],
|
||||||
|
supportedAttributes: entertainmentAttributes
|
||||||
|
},
|
||||||
|
TemperatureSensor: {
|
||||||
|
defaultAttributes: ['CurrentTemperature'],
|
||||||
|
supportedAttributes: ['CurrentTemperature', ...sensorAttributes]
|
||||||
|
},
|
||||||
|
Thermostat: {
|
||||||
|
defaultAttributes: ['HeatingCoolingMode'],
|
||||||
|
supportedAttributes: thermostatAttributes,
|
||||||
|
groupParameters: (item) => [p.scale(item, true)]
|
||||||
|
},
|
||||||
|
VacuumCleaner: {
|
||||||
|
defaultAttributes: ['VacuumMode'],
|
||||||
|
supportedAttributes: ['VacuumMode', 'FanSpeed', 'BatteryLevel', ...genericDeviceAttributes]
|
||||||
|
},
|
||||||
|
Washer: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: genericDeviceAttributes
|
||||||
|
},
|
||||||
|
WaterHeater: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: ['TargetTemperature', 'CurrentTemperature', ...genericDeviceAttributes]
|
||||||
|
},
|
||||||
|
Wearable: {
|
||||||
|
defaultAttributes: ['PowerState'],
|
||||||
|
supportedAttributes: mobileDeviceAttributes,
|
||||||
|
groupParameters: networkParameters
|
||||||
|
},
|
||||||
|
Other: {
|
||||||
|
supportedAttributes: Object.keys(attributes).filter((attr) => attr !== 'NetworkAccess' && attr !== 'Scene')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { UNITS_OF_MEASURE } from './constants.js'
|
||||||
|
|
||||||
|
export const camelCase = (string, pascalCase = false) =>
|
||||||
|
string
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/[ _-]/)
|
||||||
|
.map((word, index) => (!index && !pascalCase ? word : word.charAt(0).toUpperCase() + word.slice(1)))
|
||||||
|
.join('')
|
||||||
|
|
||||||
|
export const titleCase = (string) =>
|
||||||
|
string
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/[ _-]/)
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ')
|
||||||
|
|
||||||
|
export const docLink = (title, anchor) => {
|
||||||
|
const link = `%DOC_URL%#${anchor || title.replace(/[. ]/g, '-').toLowerCase()}`
|
||||||
|
return `<a class="external text-color-blue" target="_blank" href="${link}">${title}</a>`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getGroupParameter = (parameter, groups) => {
|
||||||
|
for (const group of groups) {
|
||||||
|
const config = group.metadata.alexa.config || {}
|
||||||
|
if (parameter in config) return config[parameter]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getOptions = (options, preserve = false) =>
|
||||||
|
Array.isArray(options)
|
||||||
|
? options.map((value) => ({ value, label: preserve ? value : titleCase(value) }))
|
||||||
|
: Object.keys(options).map((value) => ({ value, label: options[value] }))
|
||||||
|
|
||||||
|
export const getSemanticFormat = (type, format) =>
|
||||||
|
Object.keys(format).reduce(
|
||||||
|
(value, key, index, array) =>
|
||||||
|
`${value}${index ? (index === array.length - 1 ? ' or ' : ', ') : ''}` +
|
||||||
|
`<code>${Array.isArray(format) || key === 'default' ? type : camelCase(`${key}_${type}`)}=${format[key]}</code>`,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getTemperatureScale = (item) => {
|
||||||
|
const itemType = item.groupType || item.type
|
||||||
|
const state = (item.state !== 'NULL' && item.state !== 'UNDEF' && item.state) || ''
|
||||||
|
const statePresentation = (item.stateDescription && item.stateDescription.pattern) || ''
|
||||||
|
const format = (itemType === 'Number:Temperature' && state) || statePresentation
|
||||||
|
if (format.endsWith('°C')) return 'CELSIUS'
|
||||||
|
if (format.endsWith('°F')) return 'FAHRENHEIT'
|
||||||
|
const { measurementSystem } = (item.settings && item.settings.regional) || {}
|
||||||
|
if (measurementSystem === 'SI') return 'CELSIUS'
|
||||||
|
if (measurementSystem === 'US') return 'FAHRENHEIT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUnitOfMeasure = (item) => {
|
||||||
|
const itemType = item.groupType || item.type
|
||||||
|
const state = (item.state !== 'NULL' && item.state !== 'UNDEF' && item.state) || ''
|
||||||
|
const statePresentation = (item.stateDescription && item.stateDescription.pattern) || ''
|
||||||
|
const format =
|
||||||
|
((itemType === 'Dimmer' || itemType === 'Rollershutter') && '%') ||
|
||||||
|
(itemType.startsWith('Number:') && state) ||
|
||||||
|
statePresentation
|
||||||
|
return Object.keys(UNITS_OF_MEASURE).find((id) => format.endsWith(UNITS_OF_MEASURE[id]))
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import deviceAttributes from './deviceattributes.js'
|
||||||
|
import deviceTypes, { defaultParameters } from './devicetypes.js'
|
||||||
|
|
||||||
|
const classes = {}
|
||||||
|
|
||||||
|
for (const type of Object.keys(deviceTypes)) {
|
||||||
|
const { defaultAttributes = [], supportedAttributes = [], supportsGroup = true } = deviceTypes[type]
|
||||||
|
classes[type] = {}
|
||||||
|
|
||||||
|
if (supportsGroup) {
|
||||||
|
const { groupParameters = [] } = deviceTypes[type]
|
||||||
|
classes[type]['Group'] = { parameters: [defaultParameters].concat(groupParameters) }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attribute of defaultAttributes) {
|
||||||
|
const { itemTypes = [], parameters } = deviceAttributes[attribute]
|
||||||
|
for (const itemType of itemTypes) {
|
||||||
|
if (!classes[type][itemType]) classes[type][itemType] = { parameters: [defaultParameters] }
|
||||||
|
if (parameters) classes[type][itemType].parameters.push(parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attribute of supportedAttributes) {
|
||||||
|
const { itemTypes = [], parameters = [], ...properties } = deviceAttributes[attribute]
|
||||||
|
classes[`${type}.${attribute}`] = {}
|
||||||
|
for (const itemType of itemTypes) {
|
||||||
|
classes[`${type}.${attribute}`][itemType] = { parameters: [defaultParameters].concat(parameters), ...properties }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default classes
|
|
@ -0,0 +1,440 @@
|
||||||
|
import {
|
||||||
|
ARM_STATES,
|
||||||
|
EQUALIZER_MODES,
|
||||||
|
LANGUAGES,
|
||||||
|
PLAYBACK_OPERATIONS,
|
||||||
|
TEMPERATURE_SCALES,
|
||||||
|
THERMOSTAT_MODES,
|
||||||
|
UNITS_OF_MEASURE
|
||||||
|
} from './constants.js'
|
||||||
|
import {
|
||||||
|
docLink,
|
||||||
|
getGroupParameter,
|
||||||
|
getOptions,
|
||||||
|
getSemanticFormat,
|
||||||
|
getTemperatureScale,
|
||||||
|
getUnitOfMeasure,
|
||||||
|
titleCase
|
||||||
|
} from './helpers.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
actionMappings: (format, placeholder) => ({
|
||||||
|
name: 'actionMappings',
|
||||||
|
label: 'Action Mappings',
|
||||||
|
description: `Each mapping formatted as ${getSemanticFormat('action', format)} (${docLink('Semantic Extensions')})`,
|
||||||
|
type: 'TEXT',
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true
|
||||||
|
}),
|
||||||
|
basicAuth: (setting) => ({
|
||||||
|
name: setting,
|
||||||
|
label: `Basic Authentication ${titleCase(setting)}`,
|
||||||
|
type: 'TEXT'
|
||||||
|
}),
|
||||||
|
capabilityNames: (defaultValue, placeholder) => ({
|
||||||
|
name: 'capabilityNames',
|
||||||
|
label: 'Capability Names',
|
||||||
|
description: `Each name formatted as <code>@assetIdOrName</code> (${docLink('Asset Catalog')})`,
|
||||||
|
type: 'TEXT',
|
||||||
|
default: [defaultValue],
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true,
|
||||||
|
required: !defaultValue
|
||||||
|
}),
|
||||||
|
channelMappings: () => ({
|
||||||
|
name: 'channelMappings',
|
||||||
|
label: 'Channel Mappings',
|
||||||
|
description: 'Each mapping formatted as <code>channelName=channelNumber<code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
placeholder: 'CBS=2\nNBC=4\nABC=7\nPBS=13',
|
||||||
|
multiple: true
|
||||||
|
}),
|
||||||
|
colorTemperatureBinding: () => ({
|
||||||
|
name: 'binding',
|
||||||
|
label: 'Binding/Device Type',
|
||||||
|
description: 'Range binding presets',
|
||||||
|
type: 'TEXT',
|
||||||
|
options: getOptions({
|
||||||
|
'lifx:color': 'LIFX (Color)',
|
||||||
|
'lifx:white': 'LIFX (White)',
|
||||||
|
'milight:color': 'Milight/Easybulb/Limitless (Color)',
|
||||||
|
'milight:white': 'Milight/Easybulb/Limitless (White)',
|
||||||
|
'hue:color': 'Philips Hue (Color)',
|
||||||
|
'hue:white': 'Philips Hue (White)',
|
||||||
|
'tplinksmarthome:color': 'TP-Link Smart Home (Color)',
|
||||||
|
'tplinksmarthome:white': 'TP-Link Smart Home (White)',
|
||||||
|
'tradfri:color': 'TRÅDFRI (Color)',
|
||||||
|
'tradfri:white': 'TRÅDFRI (White)',
|
||||||
|
'yeelight:color': 'Yeelight (Color)',
|
||||||
|
'yeelight:white': 'Yeelight (White)'
|
||||||
|
}),
|
||||||
|
limitToOptions: true,
|
||||||
|
visible: (_, config) => config.range === '1000:10000'
|
||||||
|
}),
|
||||||
|
colorTemperatureRange: () => ({
|
||||||
|
name: 'range',
|
||||||
|
label: 'Temperature Range in Kelvin',
|
||||||
|
description: 'Formatted as <code>minRange:maxRange</code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: '1000:10000',
|
||||||
|
pattern: '[0-9]+:[0-9]+',
|
||||||
|
visible: (_, config) => !config.binding
|
||||||
|
}),
|
||||||
|
comfortRange: (item) => ({
|
||||||
|
name: 'comfortRange',
|
||||||
|
label: 'Comfort Range',
|
||||||
|
type: 'INTEGER',
|
||||||
|
min: 1,
|
||||||
|
default: (config) => {
|
||||||
|
const scale = config.scale || getGroupParameter('scale', item.groups) || getTemperatureScale(item)
|
||||||
|
if (scale === 'CELSIUS') return 1
|
||||||
|
if (scale === 'FAHRENHEIT') return 2
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
connectedTo: (value) => ({
|
||||||
|
name: 'connectedTo',
|
||||||
|
label: 'Connected To',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: value,
|
||||||
|
readOnly: true
|
||||||
|
}),
|
||||||
|
deviceDescription: (defaultValue) => ({
|
||||||
|
name: 'description',
|
||||||
|
label: 'Device Description',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: defaultValue,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
deviceName: (defaultValue) => ({
|
||||||
|
name: 'name',
|
||||||
|
label: 'Device Name',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: defaultValue,
|
||||||
|
advanced: !!defaultValue,
|
||||||
|
required: !defaultValue
|
||||||
|
}),
|
||||||
|
equalizerDefaultLevel: (defaultValue) => ({
|
||||||
|
name: 'defaultLevel',
|
||||||
|
label: 'Default Level',
|
||||||
|
description: 'Defaults to equalizer range midpoint',
|
||||||
|
type: 'INTEGER',
|
||||||
|
default: (config) => {
|
||||||
|
if (!config.range) return defaultValue
|
||||||
|
const range = config.range.split(':').map((n) => parseInt(n))
|
||||||
|
if (range[0] < range[1]) return Math.round((range[0] + range[1]) / 2)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
equalizerRange: (defaultValue) => ({
|
||||||
|
name: 'range',
|
||||||
|
label: 'Equalizer Range',
|
||||||
|
description: 'Formatted as <code>minRange:maxRange</code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: defaultValue,
|
||||||
|
pattern: '[+-]?[0-9]+:[+-]?[0-9]+'
|
||||||
|
}),
|
||||||
|
exitDelay: () => ({
|
||||||
|
name: 'exitDelay',
|
||||||
|
label: 'Exit Delay in Seconds',
|
||||||
|
type: 'INTEGER',
|
||||||
|
min: 0,
|
||||||
|
max: 255,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
hostname: () => ({
|
||||||
|
name: 'hostname',
|
||||||
|
label: 'Hostname',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: 'N/A',
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
increment: (defaultValue) => ({
|
||||||
|
name: 'increment',
|
||||||
|
label: 'Default Increment',
|
||||||
|
...(isNaN(defaultValue) && { description: `Defaults to ${defaultValue}` }),
|
||||||
|
type: 'INTEGER',
|
||||||
|
min: 1,
|
||||||
|
...(!isNaN(defaultValue) && { default: defaultValue })
|
||||||
|
}),
|
||||||
|
inverted: (defaultValue = false) => ({
|
||||||
|
name: 'inverted',
|
||||||
|
label: 'Inverted',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: defaultValue
|
||||||
|
}),
|
||||||
|
language: (defaultValue) => ({
|
||||||
|
name: 'language',
|
||||||
|
label: 'Language',
|
||||||
|
description: 'Language for text-based names',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: LANGUAGES[defaultValue] ? defaultValue : 'en',
|
||||||
|
options: getOptions(LANGUAGES),
|
||||||
|
limitToOptions: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
macAddress: () => ({
|
||||||
|
name: 'macAddress',
|
||||||
|
label: 'MAC Address',
|
||||||
|
description: 'Formatted as EUI-48 or EUI-64 address with colon or dash separators',
|
||||||
|
type: 'TEXT',
|
||||||
|
pattern: '([0-9a-fA-F]{2}(-|:)){7}[0-9a-fA-F]{2}$|^([0-9a-fA-F]{2}(-|:)){5}[0-9a-fA-F]{2}'
|
||||||
|
}),
|
||||||
|
nonControllable: (stateDescription) => ({
|
||||||
|
name: 'nonControllable',
|
||||||
|
label: 'Non-Controllable',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: (stateDescription && stateDescription.readOnly) === true,
|
||||||
|
visible: (_, config) => !!config.retrievable
|
||||||
|
}),
|
||||||
|
ordered: () => ({
|
||||||
|
name: 'ordered',
|
||||||
|
label: 'Ordered',
|
||||||
|
description: 'If modes can be adjusted incrementally',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: false
|
||||||
|
}),
|
||||||
|
pinCodes: () => ({
|
||||||
|
name: 'pinCodes',
|
||||||
|
label: 'Pin Codes',
|
||||||
|
description: 'Each code formatted as 4-digit pin',
|
||||||
|
type: 'TEXT',
|
||||||
|
placeholder: '1234\n9876',
|
||||||
|
multiple: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
presets: (stateDescription, placeholder, advanced = false) => ({
|
||||||
|
name: 'presets',
|
||||||
|
label: 'Presets',
|
||||||
|
description:
|
||||||
|
'Each preset formatted as <code>presetValue=@assetIdOrName1:@assetIdOrName2:...</code>' +
|
||||||
|
` (${docLink('Asset Catalog')})`,
|
||||||
|
type: 'TEXT',
|
||||||
|
default:
|
||||||
|
stateDescription &&
|
||||||
|
stateDescription.options &&
|
||||||
|
stateDescription.options
|
||||||
|
.filter((option) => !isNaN(option.value))
|
||||||
|
.map((option) => `${option.value}=${option.label}`),
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true,
|
||||||
|
advanced
|
||||||
|
}),
|
||||||
|
primaryControl: () => ({
|
||||||
|
name: 'primaryControl',
|
||||||
|
label: 'Primary Control',
|
||||||
|
description: 'Primary control for open/close/stop utterances',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: 'position',
|
||||||
|
options: getOptions({ position: 'Position', tilt: 'Tilt' }),
|
||||||
|
limitToOptions: true
|
||||||
|
}),
|
||||||
|
proxyBaseUrl: () => ({
|
||||||
|
name: 'proxyBaseUrl',
|
||||||
|
label: 'Proxy Base URL',
|
||||||
|
type: 'TEXT',
|
||||||
|
required: true,
|
||||||
|
pattern: 'https://.+'
|
||||||
|
}),
|
||||||
|
resolution: () => ({
|
||||||
|
name: 'resolution',
|
||||||
|
label: 'Resolution',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: '1080p',
|
||||||
|
options: getOptions(['480p', '720p', '1080p']),
|
||||||
|
limitToOptions: true
|
||||||
|
}),
|
||||||
|
retrievable: () => ({
|
||||||
|
name: 'retrievable',
|
||||||
|
label: 'State Retrievable',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: true,
|
||||||
|
advanced: true,
|
||||||
|
visible: (_, config) => !config.nonControllable
|
||||||
|
}),
|
||||||
|
scale: (item, advanced = false) => ({
|
||||||
|
name: 'scale',
|
||||||
|
label: 'Scale',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: getGroupParameter('scale', item.groups) || getTemperatureScale(item),
|
||||||
|
options: getOptions(TEMPERATURE_SCALES),
|
||||||
|
limitToOptions: true,
|
||||||
|
advanced
|
||||||
|
}),
|
||||||
|
setpointRange: (item) => ({
|
||||||
|
name: 'setpointRange',
|
||||||
|
label: 'Setpoint Range',
|
||||||
|
description: 'Formatted as <code>minRange:maxRange</code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: (config) => {
|
||||||
|
const scale = config.scale || getGroupParameter('scale', item.groups) || getTemperatureScale(item)
|
||||||
|
if (scale === 'CELSIUS') return '4:32'
|
||||||
|
if (scale === 'FAHRENHEIT') return '40:90'
|
||||||
|
},
|
||||||
|
pattern: '[+-]?[0-9]+:[+-]?[0-9]+'
|
||||||
|
}),
|
||||||
|
speedLevels: () => ({
|
||||||
|
name: 'speedLevels',
|
||||||
|
label: 'Speed Levels',
|
||||||
|
type: 'INTEGER',
|
||||||
|
min: 2,
|
||||||
|
default: 3
|
||||||
|
}),
|
||||||
|
stateMappings: (format, placeholder) => ({
|
||||||
|
name: 'stateMappings',
|
||||||
|
label: 'State Mappings',
|
||||||
|
description: `Each mapping formatted as ${getSemanticFormat('state', format)} (${docLink('Semantic Extensions')})`,
|
||||||
|
type: 'TEXT',
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true
|
||||||
|
}),
|
||||||
|
stepSpeaker: () => ({
|
||||||
|
name: 'stepSpeaker',
|
||||||
|
label: 'Control Speaker in Discrete Steps',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: false,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
supportedArmStates: () => ({
|
||||||
|
name: 'supportedArmStates',
|
||||||
|
label: 'Supported Arm States',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: (config) => ARM_STATES.filter((state) => !!config[state]),
|
||||||
|
options: getOptions(ARM_STATES),
|
||||||
|
limitToOptions: true,
|
||||||
|
multiple: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
supportedCommands: (commands, placeholder) => ({
|
||||||
|
name: 'supportedCommands',
|
||||||
|
label: 'Supported Commands',
|
||||||
|
description:
|
||||||
|
'Each command formatted as <code>command</code> or <code>command=@assetIdOrName1:...</code>' +
|
||||||
|
` (${docLink('Asset Catalog')})<br />Supported commands are ${commands.join(', ')}`,
|
||||||
|
type: 'TEXT',
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true
|
||||||
|
}),
|
||||||
|
supportedEqualizerModes: () => ({
|
||||||
|
name: 'supportedModes',
|
||||||
|
label: 'Supported Modes',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: (config) => EQUALIZER_MODES.filter((mode) => !!config[mode]),
|
||||||
|
options: getOptions(EQUALIZER_MODES),
|
||||||
|
limitToOptions: true,
|
||||||
|
multiple: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
supportedInputs: (stateDescription, placeholder) => ({
|
||||||
|
name: 'supportedInputs',
|
||||||
|
label: 'Supported Inputs',
|
||||||
|
description: 'Each input formatted as <code>inputValue=inputName1:inputName2:...</code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
default:
|
||||||
|
stateDescription &&
|
||||||
|
stateDescription.options &&
|
||||||
|
stateDescription.options.map((option) => `${option.value}=${option.label}`),
|
||||||
|
placeholder: placeholder.replace(/,/g, '\n'),
|
||||||
|
multiple: true,
|
||||||
|
required: !stateDescription || !stateDescription.options || !stateDescription.options.length
|
||||||
|
}),
|
||||||
|
supportedModes: (stateDescription) => ({
|
||||||
|
name: 'supportedModes',
|
||||||
|
label: 'Supported Modes',
|
||||||
|
description:
|
||||||
|
`Each mode formatted as <code>mode=@assetIdOrName1:@assetIdOrName2:...</code> (${docLink('Asset Catalog')})`,
|
||||||
|
type: 'TEXT',
|
||||||
|
default:
|
||||||
|
stateDescription &&
|
||||||
|
stateDescription.options &&
|
||||||
|
stateDescription.options.map((option) => `${option.value}=${option.label}`),
|
||||||
|
placeholder: 'Normal=Normal:Cottons\nWhites=Whites',
|
||||||
|
multiple: true,
|
||||||
|
required: !stateDescription || !stateDescription.options || !stateDescription.options.length
|
||||||
|
}),
|
||||||
|
supportedOperations: () => ({
|
||||||
|
name: 'supportedOperations',
|
||||||
|
label: 'Supported Operations',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: PLAYBACK_OPERATIONS,
|
||||||
|
options: getOptions(PLAYBACK_OPERATIONS, true),
|
||||||
|
limitToOptions: true,
|
||||||
|
multiple: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
supportedRange: (stateDescription, defaultValue) => ({
|
||||||
|
name: 'supportedRange',
|
||||||
|
label: 'Supported Range',
|
||||||
|
description: 'Formatted as <code>minValue:maxValue:precision</code>',
|
||||||
|
type: 'TEXT',
|
||||||
|
default:
|
||||||
|
stateDescription &&
|
||||||
|
!isNaN(stateDescription.minimum) &&
|
||||||
|
!isNaN(stateDescription.maximum) &&
|
||||||
|
!isNaN(stateDescription.step)
|
||||||
|
? `${stateDescription.minimum}:${stateDescription.maximum}:${stateDescription.step}`
|
||||||
|
: defaultValue,
|
||||||
|
pattern: '[+-]?[0-9]+:[+-]?[0-9]+:[0-9]+'
|
||||||
|
}),
|
||||||
|
supportedThermostatModes: () => ({
|
||||||
|
name: 'supportedModes',
|
||||||
|
label: 'Supported Modes',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: (config) => THERMOSTAT_MODES.filter((mode) => !!config[mode]),
|
||||||
|
options: getOptions(THERMOSTAT_MODES),
|
||||||
|
limitToOptions: true,
|
||||||
|
multiple: true,
|
||||||
|
advanced: true,
|
||||||
|
visible: (_, config) => !config.binding
|
||||||
|
}),
|
||||||
|
supportsDeactivation: () => ({
|
||||||
|
name: 'supportsDeactivation',
|
||||||
|
label: 'Supports Deactivation',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: true
|
||||||
|
}),
|
||||||
|
supportsSetpointMode: () => ({
|
||||||
|
name: 'supportsSetpointMode',
|
||||||
|
label: 'Supports Setpoint Mode-aware Feature',
|
||||||
|
description: 'In most cases, this feature should remain enabled',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
default: true,
|
||||||
|
advanced: true
|
||||||
|
}),
|
||||||
|
thermostatModeBinding: () => ({
|
||||||
|
name: 'binding',
|
||||||
|
label: 'Thermostat Binding',
|
||||||
|
type: 'TEXT',
|
||||||
|
options: getOptions({
|
||||||
|
broadlinkthermostat: 'Broadlink Thermostat',
|
||||||
|
daikin: 'Daikin',
|
||||||
|
ecobee: 'ecobee',
|
||||||
|
insteon: 'Insteon',
|
||||||
|
max: 'MAX!',
|
||||||
|
nest: 'Nest',
|
||||||
|
radiothermostat: 'RadioThermostat',
|
||||||
|
venstarthermostat: 'Venstar Thermostat',
|
||||||
|
zwave: 'Z-Wave'
|
||||||
|
}),
|
||||||
|
limitToOptions: true,
|
||||||
|
visible: (_, config) => THERMOSTAT_MODES.every((mode) => !config[mode]) && !config.supportedModes.length
|
||||||
|
}),
|
||||||
|
thermostatModeMapping: (mode) => ({
|
||||||
|
name: mode,
|
||||||
|
label: `${titleCase(mode)} Mapping`,
|
||||||
|
type: 'TEXT',
|
||||||
|
visible: (_, config) => !config.binding
|
||||||
|
}),
|
||||||
|
valueMapping: (value, required = false) => ({
|
||||||
|
name: value,
|
||||||
|
label: `${titleCase(value)} Mapping`,
|
||||||
|
type: 'TEXT',
|
||||||
|
required
|
||||||
|
}),
|
||||||
|
unitOfMeasure: (item) => ({
|
||||||
|
name: 'unitOfMeasure',
|
||||||
|
label: 'Unit of Measure',
|
||||||
|
type: 'TEXT',
|
||||||
|
default: getUnitOfMeasure(item),
|
||||||
|
options: getOptions(Object.keys(UNITS_OF_MEASURE), true),
|
||||||
|
limitToOptions: true
|
||||||
|
})
|
||||||
|
}
|
|
@ -83,9 +83,11 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
configurationWithDefaults () {
|
configurationWithDefaults () {
|
||||||
let conf = Object.assign({}, this.configuration)
|
const conf = Object.assign({}, this.configuration)
|
||||||
this.parameters.forEach((p) => {
|
this.parameters.forEach((p) => {
|
||||||
if (conf[p.name] === undefined && p.default !== undefined) conf[p.name] = p.default
|
if (conf[p.name] === undefined && p.default !== undefined) {
|
||||||
|
conf[p.name] = typeof p.default === 'function' ? p.default(this.configuration) : p.default
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
:name="configDescription.name"
|
:name="configDescription.name"
|
||||||
:value="formattedValue"
|
:value="formattedValue"
|
||||||
:autocomplete="autoCompleteOptions ? 'off' : ''"
|
:autocomplete="autoCompleteOptions ? 'off' : ''"
|
||||||
|
:placeholder="configDescription.placeholder"
|
||||||
:pattern="configDescription.pattern"
|
:pattern="configDescription.pattern"
|
||||||
:required="configDescription.required" validate
|
:required="configDescription.required" validate
|
||||||
:clear-button="!configDescription.required && configDescription.context !== 'password'"
|
:clear-button="!configDescription.required && configDescription.context !== 'password'"
|
||||||
|
|
|
@ -1,39 +1,75 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-if="ready">
|
||||||
<div style="text-align:right" class="padding-right" v-if="itemType !== 'Group'">
|
<div style="text-align:right" class="padding-right" v-if="itemType !== 'Group'">
|
||||||
<label @click="toggleMultiple" style="cursor:pointer">Multiple</label> <f7-checkbox :checked="multiple" @change="toggleMultiple" />
|
<label @click="toggleMultiple" style="cursor:pointer">Multiple</label>
|
||||||
|
<f7-checkbox :checked="multiple" @change="toggleMultiple" />
|
||||||
</div>
|
</div>
|
||||||
<f7-list>
|
<f7-list>
|
||||||
<f7-list-item :key="classSelectKey"
|
<f7-list-item
|
||||||
:title="(multiple) ? 'Alexa Classes' : 'Alexa Class'" smart-select :smart-select-params="{ openIn: 'popup', searchbar: true, closeOnSelect: !multiple, scrollToSelectedItem: true }" ref="classes">
|
:key="classSelectKey"
|
||||||
<select name="parameters" @change="updateClasses" :multiple="multiple">
|
:title="'Alexa Device Type' + (itemType !== 'Group' ? (!multiple ? '/Attribute' : '/Attributes') : '')"
|
||||||
|
smart-select
|
||||||
|
:smart-select-params="{ openIn: 'popup', searchbar: true, closeOnSelect: !multiple, scrollToSelectedItem: true }"
|
||||||
|
ref="classes">
|
||||||
|
<select v-if="itemType === 'Group'" name="classes" @change="updateClasses">
|
||||||
|
<option value="" />
|
||||||
|
<option v-for="cl in orderedClasses" :value="cl" :key="cl" :selected="isSelected(cl)">
|
||||||
|
{{ cl }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-else name="classes" @change="updateClasses" :multiple="multiple">
|
||||||
<option v-if="!multiple" value="" />
|
<option v-if="!multiple" value="" />
|
||||||
<optgroup label="Labels" v-if="!multiple && itemType !== 'Group'">
|
<optgroup label="Default Attributes" v-if="!multiple">
|
||||||
<option v-for="cl in orderedClasses.filter((c) => c.indexOf('label:') === 0)"
|
<option v-for="cl in defaultClasses" :value="cl" :key="cl" :selected="isSelected(cl)">
|
||||||
:value="cl.replace('label:', '')" :key="cl"
|
{{ cl }}
|
||||||
:selected="isSelected(cl.replace('label:', ''))">
|
|
||||||
{{ cl.replace('label:', '') }}
|
|
||||||
</option>
|
</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Capabilities">
|
<optgroup label="Specific Attributes">
|
||||||
<option v-for="cl in orderedClasses.filter((c) => c.indexOf('label:') !== 0 && c.indexOf('endpoint:') === (itemType === 'Group'? 0 : -1))"
|
<option v-for="cl in specificClasses" :value="cl" :key="cl" :selected="isSelected(cl)" :disabled="isDefined(cl)">
|
||||||
:value="cl.replace('endpoint:', '')" :key="cl"
|
{{ cl }}
|
||||||
:selected="isSelected(cl.replace('endpoint:', ''))">
|
</option>
|
||||||
{{ cl.replace('endpoint:', '') }}
|
</optgroup>
|
||||||
|
<optgroup label="Generic Attributes" v-if="!multiple">
|
||||||
|
<option v-for="cl in genericClasses" :value="cl" :key="cl" :selected="isSelected(cl)">
|
||||||
|
{{ cl }}
|
||||||
</option>
|
</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
|
<f7-block-footer class="padding-left no-padding no-margin" v-if="isPartOfGroupEndpoint">
|
||||||
|
<small v-html="`Part of group endpoint${item.groups.length > 1 ? 's' : ''}: ${groupLinks}`" />
|
||||||
|
</f7-block-footer>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
<div>
|
<div>
|
||||||
<config-sheet :parameterGroups="[]" :parameters="parameters" :configuration="metadata.config" />
|
<config-sheet :parameterGroups="[]" :parameters="parameters" :configuration="metadata.config" />
|
||||||
</div>
|
</div>
|
||||||
|
<f7-block class="padding-top no-padding no-margin" v-if="itemType === 'Group' && classes.length">
|
||||||
|
<f7-block-title class="padding-left">
|
||||||
|
Group Endpoint Capabilities
|
||||||
|
</f7-block-title>
|
||||||
|
<f7-list>
|
||||||
|
<f7-list-item
|
||||||
|
v-for="cap in groupCapabilities"
|
||||||
|
:title="cap.name + (cap.isIgnored ? ' (Ignored)' : '')"
|
||||||
|
:after="cap.item"
|
||||||
|
:key="`${cap.name}:${cap.item}`"
|
||||||
|
:disabled="cap.isIgnored"
|
||||||
|
:link="`/settings/items/${cap.item}/metadata/alexa`" />
|
||||||
|
</f7-list>
|
||||||
|
<f7-block-footer class="padding-left" v-if="!groupCapabilities.length">
|
||||||
|
No direct group members of {{ item.name }} configured for Alexa
|
||||||
|
</f7-block-footer>
|
||||||
|
</f7-block>
|
||||||
<p class="padding">
|
<p class="padding">
|
||||||
<f7-link color="blue" external target="_blank" href="https://www.openhab.org/link/alexa">
|
<f7-link color="blue" external target="_blank" :href="docLink">
|
||||||
Alexa Integration Documentation
|
Alexa Integration Documentation
|
||||||
</f7-link>
|
</f7-link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="text-align-center">
|
||||||
|
<f7-preloader />
|
||||||
|
<div>Loading...</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -47,43 +83,130 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
itemType: this.item.type,
|
|
||||||
classesDefs: Object.keys(AlexaDefinitions),
|
classesDefs: Object.keys(AlexaDefinitions),
|
||||||
multiple: this.item.type !== 'Group' && !!this.metadata.value && this.metadata.value.indexOf(',') > 0,
|
itemType: this.item.groupType || this.item.type,
|
||||||
classSelectKey: this.$f7.utils.id()
|
multiple: !!this.metadata.value && this.metadata.value.indexOf(',') > 0,
|
||||||
|
classSelectKey: this.$f7.utils.id(),
|
||||||
|
docUrl:
|
||||||
|
`https://${this.$store.state.runtimeInfo.buildString === 'Release Build' ? 'www' : 'next'}.openhab.org` +
|
||||||
|
'/link/alexa',
|
||||||
|
ready: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
Promise.all([
|
||||||
|
this.$oh.api.get('/rest/services/org.openhab.i18n/config'),
|
||||||
|
...this.item.groupNames.map((groupName) => this.$oh.api.get(`/rest/items/${groupName}?metadata=alexa`))
|
||||||
|
]).then(([regional, ...groups]) => {
|
||||||
|
this.item.groups = groups
|
||||||
|
.map((g) => ({ ...g, members: g.members.filter((mbr) => mbr.name !== this.item.name && mbr.metadata) }))
|
||||||
|
.filter((g) => g.metadata && !g.groupType)
|
||||||
|
this.item.settings = { regional }
|
||||||
|
this.ready = true
|
||||||
|
})
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
classes () {
|
classes () {
|
||||||
if (!this.multiple) return this.metadata.value
|
return this.metadata.value ? this.metadata.value.split(',') : []
|
||||||
return (this.metadata.value) ? this.metadata.value.split(',') : []
|
|
||||||
},
|
},
|
||||||
orderedClasses () {
|
orderedClasses () {
|
||||||
return [...this.classesDefs].sort((a, b) => {
|
return [...this.classesDefs]
|
||||||
return a.localeCompare(b)
|
.filter((cl) => this.isVisible(cl) && this.supportsGroupType(cl) && !this.requiresGroupAttributes(cl))
|
||||||
})
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
},
|
||||||
|
defaultClasses () {
|
||||||
|
return this.orderedClasses.filter((cl) => cl.split('.').length === 1)
|
||||||
|
},
|
||||||
|
genericClasses () {
|
||||||
|
return this.orderedClasses.filter((cl) => cl.split('.').length === 2 && this.supportsMultiInstance(cl))
|
||||||
|
},
|
||||||
|
specificClasses () {
|
||||||
|
return this.orderedClasses.filter((cl) => cl.split('.').length === 2 && !this.supportsMultiInstance(cl))
|
||||||
},
|
},
|
||||||
parameters () {
|
parameters () {
|
||||||
if (!this.classes) return []
|
return this.classes.reduce((parameters, cl) => {
|
||||||
if (!this.multiple) {
|
const { parameters: params = [] } = this.getDefinition(cl)
|
||||||
return AlexaDefinitions['label:' + this.classes] || AlexaDefinitions['endpoint:' + this.classes] || [...AlexaDefinitions[this.classes]]
|
for (const p of params.map((p) => p(this.item, this.metadata.config)).flat()) {
|
||||||
|
if (p.description) p.description = p.description.replace('%DOC_URL%', this.docUrl)
|
||||||
|
if (!parameters.find((e) => e.name === p.name)) parameters.push(p)
|
||||||
}
|
}
|
||||||
const params = []
|
return parameters
|
||||||
this.classes.forEach((c) => {
|
}, [])
|
||||||
for (const p of AlexaDefinitions[c]) {
|
},
|
||||||
if (!params.find(p2 => p2.name === p.name)) params.push(p)
|
groupCapabilities () {
|
||||||
|
return this.item.members
|
||||||
|
.filter((mbr) => mbr.metadata && (mbr.groupType || mbr.type) !== 'Group')
|
||||||
|
.reduce((caps, mbr, idx, arr) => caps.concat(
|
||||||
|
mbr.metadata.alexa.value.split(',').map((cl) => ({
|
||||||
|
name: cl.split('.').pop().trim() || 'N/A',
|
||||||
|
item: mbr.name,
|
||||||
|
isIgnored:
|
||||||
|
!this.isSupportedGroupAttribute(cl) ||
|
||||||
|
!this.hasRequiredGroupAttributes(cl, mbr, arr) ||
|
||||||
|
(!this.supportsMultiInstance(cl) &&
|
||||||
|
arr.findIndex((mbr) => mbr.metadata.alexa.value.split(',').includes(cl)) !== idx)
|
||||||
|
}))
|
||||||
|
), [])
|
||||||
|
},
|
||||||
|
groupLinks () {
|
||||||
|
return this.item.groups
|
||||||
|
.map((g) => `<a class="text-color-blue" href="/settings/items/${g.name}/metadata/alexa">${g.label || g.name}</a>`)
|
||||||
|
.join(', ')
|
||||||
|
},
|
||||||
|
isPartOfGroupEndpoint () {
|
||||||
|
return this.itemType !== 'Group' && this.item.groups.length > 0
|
||||||
|
},
|
||||||
|
docLink () {
|
||||||
|
if (this.itemType === 'Group') {
|
||||||
|
return `${this.docUrl}#group-endpoint`
|
||||||
|
} else if (this.classes.length === 0 || !this.classesDefs.includes(this.classes[0])) {
|
||||||
|
return `${this.docUrl}#${this.isPartOfGroupEndpoint ? 'group-endpoint' : 'single-endpoint'}`
|
||||||
|
} else if (this.classes[0].indexOf('.') >= 0) {
|
||||||
|
return `${this.docUrl}#${this.classes[0].split('.')[1].toLowerCase()}`
|
||||||
|
} else if (this.classes[0] === 'Scene' || this.classes[0] === 'Activity') {
|
||||||
|
return `${this.docUrl}#${this.classes[0].toLowerCase()}`
|
||||||
|
} else {
|
||||||
|
return `${this.docUrl}#device-types`
|
||||||
}
|
}
|
||||||
})
|
|
||||||
return params
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isSelected (cl) {
|
isSelected (cl) {
|
||||||
return (this.multiple) ? this.classes.indexOf(cl) >= 0 : this.classes === cl
|
return this.classes.indexOf(cl) >= 0
|
||||||
|
},
|
||||||
|
isDefined (cl) {
|
||||||
|
return this.item.groups.some((g) => g.members.some((mbr) => mbr.metadata.alexa.value.split(',').includes(cl)))
|
||||||
|
},
|
||||||
|
isSupportedGroupAttribute (cl) {
|
||||||
|
return this.metadata.value === cl.split('.')[0] && this.classesDefs.includes(cl)
|
||||||
|
},
|
||||||
|
isVisible (cl) {
|
||||||
|
const { visible = () => true } = this.getDefinition(cl)
|
||||||
|
return !!AlexaDefinitions[cl] && !!AlexaDefinitions[cl][this.itemType] && visible(this.item)
|
||||||
|
},
|
||||||
|
getDefinition (cl, item) {
|
||||||
|
const itemType = item ? item.groupType || item.type : this.itemType
|
||||||
|
return (AlexaDefinitions[cl] && AlexaDefinitions[cl][itemType]) || {}
|
||||||
|
},
|
||||||
|
hasRequiredGroupAttributes (cl, item, items) {
|
||||||
|
const { requires = [] } = this.getDefinition(cl, item)
|
||||||
|
const type = cl.split('.')[0]
|
||||||
|
return requires.every((attr) => items.find((i) => i.metadata.alexa.value.split(',').includes(`${type}.${attr}`)))
|
||||||
|
},
|
||||||
|
requiresGroupAttributes (cl) {
|
||||||
|
const { requires = [] } = this.getDefinition(cl)
|
||||||
|
return !this.isPartOfGroupEndpoint && requires.length > 0
|
||||||
|
},
|
||||||
|
supportsGroupType (cl) {
|
||||||
|
return !this.isPartOfGroupEndpoint || this.item.groups.some((g) => cl.startsWith(`${g.metadata.alexa.value}.`))
|
||||||
|
},
|
||||||
|
supportsMultiInstance (cl) {
|
||||||
|
const { supports = [] } = this.getDefinition(cl)
|
||||||
|
return supports.includes('multiInstance')
|
||||||
},
|
},
|
||||||
toggleMultiple () {
|
toggleMultiple () {
|
||||||
this.multiple = !this.multiple
|
this.multiple = !this.multiple
|
||||||
this.metadata.value = ''
|
if (this.metadata.value.indexOf(',') > 0) this.metadata.value = ''
|
||||||
this.classSelectKey = this.$f7.utils.id()
|
this.classSelectKey = this.$f7.utils.id()
|
||||||
},
|
},
|
||||||
updateClasses () {
|
updateClasses () {
|
||||||
|
|
|
@ -139,6 +139,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
onPageBeforeIn () {
|
onPageBeforeIn () {
|
||||||
this.generic = MetadataNamespaces.map((n) => n.name).indexOf(this.namespace) < 0
|
this.generic = MetadataNamespaces.map((n) => n.name).indexOf(this.namespace) < 0
|
||||||
|
this.ready = false
|
||||||
},
|
},
|
||||||
onPageAfterIn () {
|
onPageAfterIn () {
|
||||||
this.$oh.api.get(`/rest/items/${this.itemName}?metadata=${this.namespace}`).then((data) => {
|
this.$oh.api.get(`/rest/items/${this.itemName}?metadata=${this.namespace}`).then((data) => {
|
||||||
|
|
Loading…
Reference in New Issue