Files
Evergreen-Health-Mobile/src/components/devices/FragranceCard.vue
T
2026-11-13 14:03:38 +08:00

168 lines
7.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { reactive, toRefs, watchEffect } from 'vue'
import { Button as VanButton, Slider as VanSlider } from 'vant'
import type { HomeAssistantDiscoveredDevice } from '../../services/health'
import { useDeviceControlContext } from '../../devices/controlContext'
import { useDeviceCardState } from '../../devices/useDeviceCardState'
import { defaults, labels, sync } from '../../devices/fragrance/parameters'
import type { ParameterSnapshot } from '../../devices/parameterState'
const props = defineProps<{ device: HomeAssistantDiscoveredDevice }>()
const { device: fragranceDevice } = toRefs(props)
const { haStatus, operationBusy, switchingDeviceId, isDeviceTurnedOn, toggleDevice,
applyParam: setParameter, settingParam } = useDeviceControlContext()
const localParams = reactive({ ...defaults })
const parameterKey = (key: string) => props.device.moduleKey === 'wake-fragrance' ? `wake_${key}` : key
const card = useDeviceCardState(() => props.device)
const state = {
...card,
available: (key: string) => card.available(parameterKey(key)),
readable: (key: string) => card.readable(parameterKey(key)),
display: (key: string, value: unknown, suffix = '') => card.display(parameterKey(key), value, suffix),
}
function syncReadings() {
const controls: ParameterSnapshot = {}
for (const key of Object.keys(labels)) {
const reading = haStatus.value?.controlParameters?.[parameterKey(key)]
if (reading && state.available(key)) controls[key] = reading
}
Object.assign(localParams, defaults)
sync(localParams, controls)
}
watchEffect(syncReadings)
async function applyParam(key: string, value: unknown) {
await setParameter(props.device.deviceId, parameterKey(key), value)
syncReadings()
}
</script>
<template>
<section class="device-card" v-if="fragranceDevice">
<div class="device-head">
<div class="dev-info">
<span class="category-tag">香氛控制</span>
<h3>{{ fragranceDevice.name }}</h3>
<span class="status-dot" :class="{ online: fragranceDevice.online }">
{{ fragranceDevice.online ? '设备在线' : '设备离线' }}
</span>
</div>
<VanButton
size="small"
round
:type="isDeviceTurnedOn(fragranceDevice) ? 'primary' : 'default'"
:color="isDeviceTurnedOn(fragranceDevice) ? '#0c5045' : ''"
:loading="switchingDeviceId === fragranceDevice.deviceId"
:disabled="operationBusy || !state.powerAvailable()"
@click="toggleDevice(fragranceDevice)"
>
{{ state.powerLabel('释放中', '已关闭') }}
</VanButton>
</div>
<small class="p-tip">上方“释放中/已关闭”只表示香薰主开关;感应夜灯在下方单独显示和控制。</small>
<div class="params-body">
<div class="param-row">
<div class="param-header">
<span class="p-title">单次出香时长</span>
<strong class="p-val">{{ state.display('fragrance_duration', localParams.fragranceDuration, ' 秒') }}</strong>
</div>
<VanSlider
v-model="localParams.fragranceDuration"
:min="2"
:max="6"
:step="1"
:disabled="operationBusy || !state.available('fragrance_duration')"
@change="applyParam('fragrance_duration', localParams.fragranceDuration)"
/>
<div class="range-labels"><span>2 秒</span><span>4 秒</span><span>6 秒</span></div>
</div>
<div class="param-row">
<div class="param-header">
<span class="p-title">自动出香间隔</span>
<strong class="p-val">{{ state.display('fragrance_interval', localParams.fragranceInterval, ' 分钟') }}</strong>
</div>
<VanSlider
v-model="localParams.fragranceInterval"
:min="10"
:max="20"
:step="5"
:disabled="operationBusy || !state.available('fragrance_interval')"
@change="applyParam('fragrance_interval', localParams.fragranceInterval)"
/>
<div class="range-labels"><span>10 分钟</span><span>15 分钟</span><span>20 分钟</span></div>
</div>
<div class="param-row inline-row">
<div>
<div class="p-title">香薰氛围灯</div>
<small class="p-tip">与香薰释放独立控制</small>
</div>
<button
class="mini-toggle"
:class="{ active: state.readable('fragrance_light') && localParams.fragranceLight }"
:disabled="operationBusy || !state.available('fragrance_light')"
@click="applyParam('fragrance_light', !localParams.fragranceLight)"
>{{ state.display('fragrance_light', localParams.fragranceLight ? '已开启' : '已关闭') }}</button>
</div>
<div class="param-row inline-row">
<div>
<div class="p-title">人来增香</div>
<small class="p-tip">检测到有人活动时自动增香</small>
</div>
<button
class="mini-toggle"
:class="{ active: state.readable('fragrance_auto_release') && localParams.fragranceAutoRelease }"
:disabled="operationBusy || !state.available('fragrance_auto_release')"
@click="applyParam('fragrance_auto_release', !localParams.fragranceAutoRelease)"
>{{ state.display('fragrance_auto_release', localParams.fragranceAutoRelease ? '已开启' : '已关闭') }}</button>
</div>
<div class="param-row inline-row">
<div>
<div class="p-title">感应夜灯</div>
<small class="p-tip">有人移动时自动开启柔光</small>
</div>
<button
class="mini-toggle"
:class="{ active: state.readable('fragrance_auto_light') && localParams.fragranceAutoLight }"
:disabled="operationBusy || !state.available('fragrance_auto_light')"
@click="applyParam('fragrance_auto_light', !localParams.fragranceAutoLight)"
>{{ state.display('fragrance_auto_light', localParams.fragranceAutoLight ? '已开启' : '已关闭') }}</button>
</div>
<div class="param-row inline-row">
<div>
<div class="p-title">无人时自动关灯</div>
<small class="p-tip">关闭时氛围灯保持常亮</small>
</div>
<button
class="mini-toggle"
:class="{ active: state.readable('fragrance_light_auto_off') && localParams.fragranceLightAutoOff }"
:disabled="operationBusy || !state.available('fragrance_light_auto_off')"
@click="applyParam('fragrance_light_auto_off', !localParams.fragranceLightAutoOff)"
>{{ state.display('fragrance_light_auto_off', localParams.fragranceLightAutoOff ? '已开启' : '已关闭') }}</button>
</div>
<div class="param-row inline-row">
<div>
<div class="p-title">立即出香</div>
<small class="p-tip">{{ state.available('fragrance_quick_release') ? '手动触发一次当前时长的香氛释放' : '快速出香能力当前不可用' }}</small>
</div>
<VanButton
size="small"
round
plain
color="#0c5045"
:loading="settingParam === `${fragranceDevice.deviceId}_${parameterKey('fragrance_quick_release')}`"
:disabled="operationBusy || !state.available('fragrance_quick_release')"
@click="applyParam('fragrance_quick_release', true)"
>{{ state.available('fragrance_quick_release') ? '快速出香' : '暂不可用' }}</VanButton>
</div>
</div>
</section>
</template>