168 lines
7.8 KiB
Vue
168 lines
7.8 KiB
Vue
<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>
|