chore: initial import
This commit is contained in:
809
utils/smart-device.js
Normal file
809
utils/smart-device.js
Normal file
@@ -0,0 +1,809 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user