810 lines
23 KiB
JavaScript
810 lines
23 KiB
JavaScript
import { getCurrentLanguage } from './i18n'
|
|
|
|
function safeJsonParse(value, fallback = null) {
|
|
if (typeof value !== 'string') {
|
|
return value
|
|
}
|
|
|
|
const text = value.trim()
|
|
if (!text) {
|
|
return fallback
|
|
}
|
|
|
|
const looksLikeJson = (
|
|
(text.startsWith('{') && text.endsWith('}'))
|
|
|| (text.startsWith('[') && text.endsWith(']'))
|
|
)
|
|
|
|
if (!looksLikeJson) {
|
|
return fallback
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(text)
|
|
} catch (error) {
|
|
return fallback
|
|
}
|
|
}
|
|
|
|
function pickFirstFilledValue(...values) {
|
|
for (let index = 0; index < values.length; index += 1) {
|
|
const value = values[index]
|
|
if (value === undefined || value === null) {
|
|
continue
|
|
}
|
|
const text = String(value).trim()
|
|
if (text) {
|
|
return text
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
function getSmartDeviceText(key = '', params = {}) {
|
|
const isEn = getCurrentLanguage() === 'en'
|
|
const map = {
|
|
commonSwitch: isEn ? 'General Switch' : '通用开关',
|
|
slideControl: isEn ? 'Slider Control' : '滑动控制',
|
|
curtainButton: isEn ? 'Curtain Button' : '窗帘按钮',
|
|
unknownFunction: isEn ? 'Unknown Function' : '未知功能',
|
|
fullyOpen: isEn ? 'Fully Open' : '已全开',
|
|
fullyClosed: isEn ? 'Fully Closed' : '已全关',
|
|
paused: isEn ? 'Paused' : '已暂停',
|
|
openness: isEn ? `Open Level ${params.value}%` : `开合度 ${params.value}%`,
|
|
controllable: isEn ? 'Ready to Control' : '可控制',
|
|
currentValue: isEn ? `Current Value ${params.value}%` : `当前值 ${params.value}%`,
|
|
adjustable: isEn ? 'Adjustable' : '可调节',
|
|
enabled: isEn ? 'Enabled' : '已开启',
|
|
disabled: isEn ? 'Disabled' : '已关闭',
|
|
unnamedDevice: isEn ? 'Unnamed Device' : '未命名设备'
|
|
}
|
|
|
|
return map[key] || ''
|
|
}
|
|
|
|
function collectObjectArray(value, depth = 0) {
|
|
if (!value || depth > 6) {
|
|
return []
|
|
}
|
|
|
|
const normalizedValue = typeof value === 'string'
|
|
? (safeJsonParse(value, value))
|
|
: value
|
|
|
|
if (Array.isArray(normalizedValue)) {
|
|
if (normalizedValue.some(item => item && typeof item === 'object' && !Array.isArray(item))) {
|
|
return normalizedValue
|
|
}
|
|
|
|
for (let index = 0; index < normalizedValue.length; index += 1) {
|
|
const nestedList = collectObjectArray(normalizedValue[index], depth + 1)
|
|
if (nestedList.length) {
|
|
return nestedList
|
|
}
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
if (!normalizedValue || typeof normalizedValue !== 'object') {
|
|
return []
|
|
}
|
|
|
|
const candidateKeys = ['data', 'list', 'rows', 'items', 'records', 'result', 'payload', '$values']
|
|
for (let index = 0; index < candidateKeys.length; index += 1) {
|
|
const key = candidateKeys[index]
|
|
if (!Object.prototype.hasOwnProperty.call(normalizedValue, key)) {
|
|
continue
|
|
}
|
|
const nestedList = collectObjectArray(normalizedValue[key], depth + 1)
|
|
if (nestedList.length) {
|
|
return nestedList
|
|
}
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
function isSmartDeviceObject(value = {}) {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
return false
|
|
}
|
|
|
|
if (Array.isArray(value.functions)) {
|
|
return true
|
|
}
|
|
|
|
const deviceKeys = [
|
|
'deviceId',
|
|
'deviceNo',
|
|
'deviceName',
|
|
'platformDeviceId',
|
|
'deviceType',
|
|
'facilityCode',
|
|
'facilityType',
|
|
'currentStatus'
|
|
]
|
|
|
|
return deviceKeys.some(key => Object.prototype.hasOwnProperty.call(value, key))
|
|
}
|
|
|
|
function unwrapSmartDevicePayload(value, depth = 0) {
|
|
if (!value || depth > 6) {
|
|
return {}
|
|
}
|
|
|
|
const normalizedValue = typeof value === 'string'
|
|
? (safeJsonParse(value, value))
|
|
: value
|
|
|
|
if (!normalizedValue || typeof normalizedValue !== 'object') {
|
|
return {}
|
|
}
|
|
|
|
if (Array.isArray(normalizedValue)) {
|
|
for (let index = 0; index < normalizedValue.length; index += 1) {
|
|
const nestedDevice = unwrapSmartDevicePayload(normalizedValue[index], depth + 1)
|
|
if (Object.keys(nestedDevice).length) {
|
|
return nestedDevice
|
|
}
|
|
}
|
|
return {}
|
|
}
|
|
|
|
if (isSmartDeviceObject(normalizedValue)) {
|
|
return normalizedValue
|
|
}
|
|
|
|
const candidateKeys = ['data', 'result', 'payload', 'item', 'device', 'record']
|
|
for (let index = 0; index < candidateKeys.length; index += 1) {
|
|
const key = candidateKeys[index]
|
|
if (!Object.prototype.hasOwnProperty.call(normalizedValue, key)) {
|
|
continue
|
|
}
|
|
const nestedDevice = unwrapSmartDevicePayload(normalizedValue[key], depth + 1)
|
|
if (Object.keys(nestedDevice).length) {
|
|
return nestedDevice
|
|
}
|
|
}
|
|
|
|
return {}
|
|
}
|
|
|
|
function isCurtainDeviceLike(device = {}) {
|
|
const numericDeviceType = Number(device.deviceType)
|
|
if (numericDeviceType === 2) {
|
|
return true
|
|
}
|
|
|
|
const name = pickFirstFilledValue(device.deviceName, device.deviceNo).toLowerCase()
|
|
return name.includes('窗帘') || name.includes('curtain')
|
|
}
|
|
|
|
function normalizeFunctionCode(functionCode = '', device = {}) {
|
|
const normalizedCode = String(functionCode || '').trim().toLowerCase()
|
|
if (!normalizedCode) {
|
|
return ''
|
|
}
|
|
|
|
if (['light', 'switch', 'common:switch'].includes(normalizedCode)) {
|
|
return 'common:switch'
|
|
}
|
|
if (['slide', 'common:slide'].includes(normalizedCode)) {
|
|
return 'common:slide'
|
|
}
|
|
if (['curtain', 'curtain:button'].includes(normalizedCode)) {
|
|
return 'curtain:button'
|
|
}
|
|
if (normalizedCode === 'button') {
|
|
return isCurtainDeviceLike(device) ? 'curtain:button' : 'button'
|
|
}
|
|
|
|
return normalizedCode
|
|
}
|
|
|
|
function normalizeFunctionItem(item = {}, device = {}) {
|
|
const schema = item.commandSchema === undefined
|
|
? null
|
|
: (typeof item.commandSchema === 'string'
|
|
? (safeJsonParse(item.commandSchema, item.commandSchema))
|
|
: item.commandSchema)
|
|
const rawFunctionCode = pickFirstFilledValue(item.functionCode, item.controlType)
|
|
const normalizedCode = normalizeFunctionCode(rawFunctionCode, device)
|
|
|
|
return {
|
|
...item,
|
|
functionCode: pickFirstFilledValue(item.functionCode, item.controlType),
|
|
functionDes: pickFirstFilledValue(item.functionDes, item.description),
|
|
controlType: normalizedCode,
|
|
commandSchema: schema
|
|
}
|
|
}
|
|
|
|
function normalizeSchemaOption(option, index = 0) {
|
|
if (option && typeof option === 'object' && !Array.isArray(option)) {
|
|
const value = Object.prototype.hasOwnProperty.call(option, 'value')
|
|
? option.value
|
|
: (
|
|
Object.prototype.hasOwnProperty.call(option, 'code')
|
|
? option.code
|
|
: (Object.prototype.hasOwnProperty.call(option, 'key') ? option.key : index)
|
|
)
|
|
return {
|
|
label: pickFirstFilledValue(option.label, option.text, option.name, option.title, value),
|
|
value
|
|
}
|
|
}
|
|
|
|
return {
|
|
label: String(option),
|
|
value: option
|
|
}
|
|
}
|
|
|
|
function normalizeSchemaOptions(rawOptions) {
|
|
if (!Array.isArray(rawOptions)) {
|
|
return []
|
|
}
|
|
return rawOptions.map((item, index) => normalizeSchemaOption(item, index))
|
|
}
|
|
|
|
function isSchemaMetaKey(key = '') {
|
|
return [
|
|
'type',
|
|
'title',
|
|
'description',
|
|
'required',
|
|
'$schema',
|
|
'properties',
|
|
'fields',
|
|
'params',
|
|
'definitions'
|
|
].includes(String(key || '').trim())
|
|
}
|
|
|
|
function inferSchemaFieldType(fieldName = '', rawField) {
|
|
if (Array.isArray(rawField)) {
|
|
return 'enum'
|
|
}
|
|
|
|
if (typeof rawField === 'string') {
|
|
const normalizedType = rawField.trim().toLowerCase()
|
|
if (['boolean', 'bool'].includes(normalizedType)) {
|
|
return 'boolean'
|
|
}
|
|
if (['number', 'integer', 'int', 'float', 'double'].includes(normalizedType)) {
|
|
return 'number'
|
|
}
|
|
}
|
|
|
|
if (!rawField || typeof rawField !== 'object') {
|
|
return typeof rawField === 'boolean' ? 'boolean' : (typeof rawField === 'number' ? 'number' : 'string')
|
|
}
|
|
|
|
const explicitType = String(
|
|
rawField.type
|
|
|| rawField.valueType
|
|
|| rawField.inputType
|
|
|| rawField.dataType
|
|
|| ''
|
|
).trim().toLowerCase()
|
|
|
|
if (rawField.enum || rawField.options || rawField.oneOf || rawField.values) {
|
|
return 'enum'
|
|
}
|
|
if (['boolean', 'bool'].includes(explicitType)) {
|
|
return 'boolean'
|
|
}
|
|
if (['number', 'integer', 'int', 'float', 'double'].includes(explicitType)) {
|
|
return 'number'
|
|
}
|
|
if (['string', 'text'].includes(explicitType)) {
|
|
return 'string'
|
|
}
|
|
if (typeof rawField.default === 'boolean') {
|
|
return 'boolean'
|
|
}
|
|
if (typeof rawField.default === 'number') {
|
|
return 'number'
|
|
}
|
|
if (fieldName === 'value' && typeof rawField === 'object' && !Array.isArray(rawField)) {
|
|
return 'number'
|
|
}
|
|
return 'string'
|
|
}
|
|
|
|
function normalizeSchemaField(fieldName = '', rawField = {}) {
|
|
const normalizedType = inferSchemaFieldType(fieldName, rawField)
|
|
const fieldObject = rawField && typeof rawField === 'object' && !Array.isArray(rawField)
|
|
? rawField
|
|
: {}
|
|
const optionCandidates = fieldObject.enum || fieldObject.options || fieldObject.oneOf || fieldObject.values || rawField
|
|
|
|
return {
|
|
name: String(fieldName || '').trim(),
|
|
label: pickFirstFilledValue(
|
|
fieldObject.label,
|
|
fieldObject.title,
|
|
fieldObject.name,
|
|
fieldObject.description,
|
|
fieldName
|
|
),
|
|
type: normalizedType,
|
|
required: !!fieldObject.required,
|
|
min: Number.isFinite(Number(fieldObject.min))
|
|
? Number(fieldObject.min)
|
|
: (Number.isFinite(Number(fieldObject.minimum)) ? Number(fieldObject.minimum) : undefined),
|
|
max: Number.isFinite(Number(fieldObject.max))
|
|
? Number(fieldObject.max)
|
|
: (Number.isFinite(Number(fieldObject.maximum)) ? Number(fieldObject.maximum) : undefined),
|
|
step: Number.isFinite(Number(fieldObject.step))
|
|
? Number(fieldObject.step)
|
|
: (Number.isFinite(Number(fieldObject.multipleOf)) ? Number(fieldObject.multipleOf) : 1),
|
|
placeholder: pickFirstFilledValue(fieldObject.placeholder, fieldObject.hint, fieldObject.description),
|
|
defaultValue: Object.prototype.hasOwnProperty.call(fieldObject, 'default') ? fieldObject.default : undefined,
|
|
options: normalizeSchemaOptions(optionCandidates)
|
|
}
|
|
}
|
|
|
|
function parseCurrentStatus(value) {
|
|
if (value === undefined || value === null) {
|
|
return null
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
return value
|
|
}
|
|
|
|
const parsedJson = safeJsonParse(value, null)
|
|
if (parsedJson !== null) {
|
|
return parsedJson
|
|
}
|
|
|
|
const text = String(value).trim()
|
|
if (!text) {
|
|
return null
|
|
}
|
|
|
|
if (text === 'true' || text === 'false') {
|
|
return {
|
|
value: text === 'true'
|
|
}
|
|
}
|
|
|
|
const numericValue = Number(text)
|
|
if (Number.isFinite(numericValue) && String(numericValue) === text) {
|
|
return {
|
|
value: numericValue
|
|
}
|
|
}
|
|
|
|
return {
|
|
text
|
|
}
|
|
}
|
|
|
|
function resolveFunctionCurrentStatus(device = {}, preferredControlType = '') {
|
|
const functionList = Array.isArray(device.functions) ? device.functions : []
|
|
if (!functionList.length) {
|
|
return null
|
|
}
|
|
|
|
const preferredCategory = getControlTypeCategory(preferredControlType)
|
|
const preferredFunction = functionList.find((item) => {
|
|
const itemControlType = pickFirstFilledValue(item && item.controlType, item && item.functionCode)
|
|
return getControlTypeCategory(itemControlType) === preferredCategory && parseCurrentStatus(item && item.currentStatus)
|
|
})
|
|
if (preferredFunction) {
|
|
return preferredFunction.currentStatus
|
|
}
|
|
|
|
const fallbackFunction = functionList.find(item => parseCurrentStatus(item && item.currentStatus))
|
|
return fallbackFunction ? fallbackFunction.currentStatus : null
|
|
}
|
|
|
|
function resolveDeviceCurrentStatus(device = {}, preferredControlType = '') {
|
|
const deviceStatus = parseCurrentStatus(device.currentStatus)
|
|
if (deviceStatus) {
|
|
return device.currentStatus
|
|
}
|
|
return resolveFunctionCurrentStatus(device, preferredControlType)
|
|
}
|
|
|
|
const CONTROL_TYPE_CATEGORY_MAP = {
|
|
light: 'switch',
|
|
switch: 'switch',
|
|
'common:switch': 'switch',
|
|
slide: 'slide',
|
|
'common:slide': 'slide',
|
|
curtain: 'curtain',
|
|
'curtain:button': 'curtain'
|
|
}
|
|
|
|
export const SUPPORTED_DEVICE_CONTROL_TYPES = ['common:switch', 'common:slide', 'curtain:button', 'light', 'switch', 'curtain']
|
|
|
|
export function getControlTypeCategory(controlType = '') {
|
|
const normalizedType = String(controlType || '').trim().toLowerCase()
|
|
return CONTROL_TYPE_CATEGORY_MAP[normalizedType] || normalizedType
|
|
}
|
|
|
|
export function isSupportedDeviceControlType(controlType = '') {
|
|
const normalizedType = String(controlType || '').trim().toLowerCase()
|
|
return SUPPORTED_DEVICE_CONTROL_TYPES.includes(normalizedType) || ['switch', 'slide', 'curtain'].includes(getControlTypeCategory(normalizedType))
|
|
}
|
|
|
|
export function getDeviceControlLabel(controlType = '') {
|
|
const category = getControlTypeCategory(controlType)
|
|
if (category === 'switch') {
|
|
return getSmartDeviceText('commonSwitch')
|
|
}
|
|
if (category === 'slide') {
|
|
return getSmartDeviceText('slideControl')
|
|
}
|
|
if (category === 'curtain') {
|
|
return getSmartDeviceText('curtainButton')
|
|
}
|
|
return String(controlType || '').trim() || getSmartDeviceText('unknownFunction')
|
|
}
|
|
|
|
export function inferDeviceControlType(device = {}) {
|
|
const numericDeviceType = Number(device.deviceType)
|
|
if (numericDeviceType === 2) {
|
|
return 'curtain:button'
|
|
}
|
|
|
|
const functionList = Array.isArray(device.functions) ? device.functions : []
|
|
const normalizedFunctionTypes = functionList
|
|
.map(item => normalizeFunctionCode(
|
|
pickFirstFilledValue(item && item.functionCode, item && item.controlType),
|
|
device
|
|
))
|
|
.filter(Boolean)
|
|
|
|
if (normalizedFunctionTypes.some(item => getControlTypeCategory(item) === 'switch')) {
|
|
return 'common:switch'
|
|
}
|
|
if (normalizedFunctionTypes.some(item => getControlTypeCategory(item) === 'slide')) {
|
|
return 'common:slide'
|
|
}
|
|
if (normalizedFunctionTypes.some(item => getControlTypeCategory(item) === 'curtain')) {
|
|
return 'curtain:button'
|
|
}
|
|
|
|
if (numericDeviceType === 1 || numericDeviceType === 0) {
|
|
return 'common:switch'
|
|
}
|
|
|
|
const name = pickFirstFilledValue(device.deviceName, device.deviceNo).toLowerCase()
|
|
if (name.includes('窗帘') || name.includes('curtain')) {
|
|
return 'curtain:button'
|
|
}
|
|
if (name.includes('开关') || name.includes('switch')) {
|
|
return 'common:switch'
|
|
}
|
|
if (name.includes('亮度') || name.includes('滑块') || name.includes('slider') || name.includes('slide')) {
|
|
return 'common:slide'
|
|
}
|
|
|
|
return 'common:switch'
|
|
}
|
|
|
|
export function getDeviceIconClass(controlType = '') {
|
|
const category = getControlTypeCategory(controlType)
|
|
if (category === 'curtain') {
|
|
return 'tn-icon-hardware-fill'
|
|
}
|
|
if (category === 'slide') {
|
|
return 'tn-icon-more-horizontal'
|
|
}
|
|
if (category === 'switch') {
|
|
return 'tn-icon-power'
|
|
}
|
|
return 'tn-icon-light-fill'
|
|
}
|
|
|
|
export function buildDeviceStateText(device = {}) {
|
|
const controlType = inferDeviceControlType(device)
|
|
const category = getControlTypeCategory(controlType)
|
|
const status = parseCurrentStatus(resolveDeviceCurrentStatus(device, controlType))
|
|
|
|
if (category === 'curtain' || Number(device.deviceType) === 2) {
|
|
const action = pickFirstFilledValue(
|
|
status && status.command,
|
|
status && status.action,
|
|
status && status.state,
|
|
status && status.text
|
|
).toLowerCase()
|
|
const percentValue = Number(
|
|
status && (
|
|
status.value !== undefined
|
|
? status.value
|
|
: (status.percent !== undefined ? status.percent : status.position)
|
|
)
|
|
)
|
|
|
|
if (action === 'open') {
|
|
return getSmartDeviceText('fullyOpen')
|
|
}
|
|
if (action === 'close') {
|
|
return getSmartDeviceText('fullyClosed')
|
|
}
|
|
if (action === 'stop') {
|
|
return getSmartDeviceText('paused')
|
|
}
|
|
if (Number.isFinite(percentValue)) {
|
|
return getSmartDeviceText('openness', { value: percentValue })
|
|
}
|
|
return getSmartDeviceText('controllable')
|
|
}
|
|
|
|
if (category === 'slide') {
|
|
const slideValue = Number(
|
|
status && (
|
|
status.value !== undefined
|
|
? status.value
|
|
: (status.percent !== undefined ? status.percent : status.position)
|
|
)
|
|
)
|
|
if (Number.isFinite(slideValue)) {
|
|
return getSmartDeviceText('currentValue', { value: slideValue })
|
|
}
|
|
return getSmartDeviceText('adjustable')
|
|
}
|
|
|
|
const switchValue = status && status.value
|
|
if (typeof switchValue === 'boolean') {
|
|
return switchValue ? getSmartDeviceText('enabled') : getSmartDeviceText('disabled')
|
|
}
|
|
|
|
const switchCommand = String(
|
|
(status && (
|
|
status.command !== undefined
|
|
? status.command
|
|
: (status.action !== undefined ? status.action : status.text)
|
|
)) || ''
|
|
).trim().toLowerCase()
|
|
if (switchCommand === 'on') {
|
|
return getSmartDeviceText('enabled')
|
|
}
|
|
if (switchCommand === 'off') {
|
|
return getSmartDeviceText('disabled')
|
|
}
|
|
|
|
if (status && status.text) {
|
|
return String(status.text)
|
|
}
|
|
|
|
return getSmartDeviceText('controllable')
|
|
}
|
|
|
|
export function isDeviceActive(device = {}) {
|
|
const controlType = inferDeviceControlType(device)
|
|
const category = getControlTypeCategory(controlType)
|
|
const status = parseCurrentStatus(resolveDeviceCurrentStatus(device, controlType))
|
|
|
|
if (!status) {
|
|
return false
|
|
}
|
|
|
|
if (category === 'curtain' || Number(device.deviceType) === 2) {
|
|
const action = pickFirstFilledValue(
|
|
status.command,
|
|
status.action,
|
|
status.state,
|
|
status.text
|
|
).toLowerCase()
|
|
const value = Number(
|
|
status.value !== undefined
|
|
? status.value
|
|
: (status.percent !== undefined ? status.percent : status.position)
|
|
)
|
|
if (action === 'open') {
|
|
return true
|
|
}
|
|
if (action === 'close') {
|
|
return false
|
|
}
|
|
if (Number.isFinite(value)) {
|
|
return value > 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
if (category === 'slide') {
|
|
const value = Number(
|
|
status.value !== undefined
|
|
? status.value
|
|
: (status.percent !== undefined ? status.percent : status.position)
|
|
)
|
|
return Number.isFinite(value) ? value > 0 : false
|
|
}
|
|
|
|
const switchCommand = String(
|
|
(
|
|
status.command !== undefined
|
|
? status.command
|
|
: (status.action !== undefined ? status.action : status.text)
|
|
) || ''
|
|
).trim().toLowerCase()
|
|
if (switchCommand === 'on') {
|
|
return true
|
|
}
|
|
if (switchCommand === 'off') {
|
|
return false
|
|
}
|
|
|
|
return !!status.value
|
|
}
|
|
|
|
export function normalizeSmartDevice(item = {}) {
|
|
const sourceDevice = unwrapSmartDevicePayload(item)
|
|
const functionList = Array.isArray(sourceDevice.functions)
|
|
? sourceDevice.functions.filter(entry => entry && typeof entry === 'object').map(entry => normalizeFunctionItem(entry, sourceDevice))
|
|
: []
|
|
const normalizedDevice = {
|
|
...sourceDevice,
|
|
functions: functionList
|
|
}
|
|
const controlType = inferDeviceControlType(normalizedDevice)
|
|
const currentStatus = resolveDeviceCurrentStatus(normalizedDevice, controlType)
|
|
|
|
return {
|
|
...normalizedDevice,
|
|
currentStatus,
|
|
controlType,
|
|
iconClass: getDeviceIconClass(controlType),
|
|
active: isDeviceActive(normalizedDevice),
|
|
stateText: buildDeviceStateText(normalizedDevice),
|
|
roomName: pickFirstFilledValue(sourceDevice.roomName),
|
|
deviceName: pickFirstFilledValue(sourceDevice.deviceName, sourceDevice.deviceNo, sourceDevice.platformDeviceId, getSmartDeviceText('unnamedDevice'))
|
|
}
|
|
}
|
|
|
|
export function getDeviceFunctions(device = {}, includeFallback = true) {
|
|
const functionList = Array.isArray(device.functions)
|
|
? device.functions.filter(item => item && typeof item === 'object').map(item => normalizeFunctionItem(item, device))
|
|
: []
|
|
|
|
if (functionList.length) {
|
|
return functionList
|
|
}
|
|
|
|
if (!includeFallback) {
|
|
return []
|
|
}
|
|
|
|
const fallbackType = inferDeviceControlType(device)
|
|
return fallbackType
|
|
? [{
|
|
functionCode: fallbackType,
|
|
functionDes: getDeviceControlLabel(fallbackType),
|
|
controlType: fallbackType,
|
|
commandSchema: null,
|
|
isFallback: true
|
|
}]
|
|
: []
|
|
}
|
|
|
|
export function buildCommandSchemaFields(commandSchema = null) {
|
|
if (!commandSchema) {
|
|
return []
|
|
}
|
|
|
|
if (Array.isArray(commandSchema)) {
|
|
return commandSchema
|
|
.map((item, index) => normalizeSchemaField(
|
|
pickFirstFilledValue(item && item.name, item && item.key, `field${index + 1}`),
|
|
item
|
|
))
|
|
.filter(item => item.name)
|
|
}
|
|
|
|
if (typeof commandSchema !== 'object') {
|
|
return []
|
|
}
|
|
|
|
let schemaSource = null
|
|
if (commandSchema.properties && typeof commandSchema.properties === 'object') {
|
|
schemaSource = commandSchema.properties
|
|
} else if (commandSchema.fields && typeof commandSchema.fields === 'object') {
|
|
schemaSource = commandSchema.fields
|
|
} else if (commandSchema.params && typeof commandSchema.params === 'object') {
|
|
schemaSource = commandSchema.params
|
|
} else {
|
|
const candidateEntries = Object.keys(commandSchema)
|
|
.filter(key => !isSchemaMetaKey(key))
|
|
.filter(key => {
|
|
const value = commandSchema[key]
|
|
return value !== undefined && value !== null && typeof value !== 'function'
|
|
})
|
|
|
|
if (candidateEntries.length) {
|
|
schemaSource = candidateEntries.reduce((result, key) => {
|
|
result[key] = commandSchema[key]
|
|
return result
|
|
}, {})
|
|
}
|
|
}
|
|
|
|
if (!schemaSource || typeof schemaSource !== 'object') {
|
|
return []
|
|
}
|
|
|
|
return Object.keys(schemaSource)
|
|
.map(key => normalizeSchemaField(key, schemaSource[key]))
|
|
.filter(item => item.name)
|
|
}
|
|
|
|
export function normalizeSmartDeviceList(response) {
|
|
const payload = response && response.data !== undefined ? response.data : response
|
|
const deviceList = collectObjectArray(payload)
|
|
if (deviceList.length) {
|
|
return deviceList
|
|
.filter(item => item && typeof item === 'object' && !Array.isArray(item))
|
|
.map(item => normalizeSmartDevice(item))
|
|
}
|
|
|
|
const singleDevice = unwrapSmartDevicePayload(payload)
|
|
return Object.keys(singleDevice).length ? [normalizeSmartDevice(singleDevice)] : []
|
|
}
|
|
|
|
export function patchDeviceStatus(device = {}, command = {}, forcedControlType = '') {
|
|
const controlType = String(forcedControlType || '').trim().toLowerCase() || inferDeviceControlType(device)
|
|
const category = getControlTypeCategory(controlType)
|
|
|
|
if (category === 'curtain') {
|
|
const action = String(command.command || command.action || command.value || '').trim().toLowerCase()
|
|
if (!action) {
|
|
return normalizeSmartDevice(device)
|
|
}
|
|
|
|
return normalizeSmartDevice({
|
|
...device,
|
|
currentStatus: {
|
|
command: action
|
|
}
|
|
})
|
|
}
|
|
|
|
if (category === 'slide') {
|
|
const value = Number(
|
|
command.value !== undefined
|
|
? command.value
|
|
: (command.percent !== undefined ? command.percent : command.position)
|
|
)
|
|
return normalizeSmartDevice({
|
|
...device,
|
|
currentStatus: {
|
|
value: Number.isFinite(value) ? value : 0
|
|
}
|
|
})
|
|
}
|
|
|
|
const switchCommand = String(
|
|
(
|
|
command.command !== undefined
|
|
? command.command
|
|
: (
|
|
command.action !== undefined
|
|
? command.action
|
|
: (
|
|
command.value === true
|
|
? 'on'
|
|
: (command.value === false ? 'off' : command.value)
|
|
)
|
|
)
|
|
) || ''
|
|
).trim().toLowerCase()
|
|
|
|
if (switchCommand) {
|
|
return normalizeSmartDevice({
|
|
...device,
|
|
currentStatus: {
|
|
command: switchCommand
|
|
}
|
|
})
|
|
}
|
|
|
|
return normalizeSmartDevice(device)
|
|
}
|