浇水提醒刚弹出又消失,植物状态别只存一个百分比

室内植物实验室的琴叶榕显示 74%,龟背竹显示 68%。如果页面只保存一个"湿度百分比",就会遇到很奇怪的提醒:传感器一瞬间低于阈值,浇水按钮出现;下一次采样回弹 2%,提醒又消失,用户根本不知道该不该处理。

我把植物状态拆成原始读数、平滑读数、提醒阶段和最后处理时间。百分比仍然用于卡片展示,但是否提醒由独立的规则控制。

一次测量不是植物状态

ts 复制代码
export interface MoistureSample {
  plantId: string
  capturedAt: number
  value: number
}

export type CarePhase = 'healthy' | 'watching' | 'waterSoon' | 'watering'

export interface PlantState {
  id: string
  name: string
  zoneId: string
  moisture: number
  rawMoisture: number
  phase: CarePhase
  lastWateredAt: number
  lastSampleAt: number
}

rawMoisture 用于追查异常,moisture 是页面显示的平滑值。用户点击浇水后进入 watering,不能因为下一条旧样本到达又立即变回缺水。

先过滤突刺,再判断区间

单点抖动不值得触发通知。我使用最近五次读数的中位数,避免一个异常高值或低值把整条趋势拉偏。

ts 复制代码
function median(values: number[]): number {
  const sorted = values.slice().sort((a: number, b: number) => a - b)
  const middle = Math.floor(sorted.length / 2)
  return sorted.length % 2 === 0 ?
    (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle]
}

export function smooth(samples: MoistureSample[], plantId: string): number | undefined {
  const recent = samples.filter((item: MoistureSample) => item.plantId === plantId)
    .sort((a: MoistureSample, b: MoistureSample) => b.capturedAt - a.capturedAt)
    .slice(0, 5)
    .map((item: MoistureSample) => item.value)
  return recent.length === 0 ? undefined : median(recent)
}

琴叶榕、凤尾蕨和豆瓣绿的阈值不同,因此阈值不写死在页面组件里。

ts 复制代码
export interface CareRule {
  plantId: string
  watchBelow: number
  waterBelow: number
  recoverAbove: number
}

export function nextPhase(
  phase: CarePhase,
  moisture: number,
  rule: CareRule
): CarePhase {
  if (phase === 'watering') return phase
  if (moisture <= rule.waterBelow) return 'waterSoon'
  if (moisture <= rule.watchBelow) return 'watching'
  if (moisture >= rule.recoverAbove) return 'healthy'
  return phase
}

recoverAbove 高于 waterBelow,形成一个恢复区间。湿度在 43% 和 45% 附近来回时,提醒不会每采一次就翻转。

浇水动作先进入处理中

按钮点击不能直接把湿度改成 100%。真正的传感器可能还没反馈,或者用户只浇了一小部分水。点击后记录动作时间并进入 watering,等待连续样本确认。

ts 复制代码
export function startWatering(state: PlantState, now: number): PlantState {
  if (state.phase !== 'waterSoon') return state
  return { ...state, phase: 'watering', lastWateredAt: now }
}

export function applySample(
  state: PlantState,
  samples: MoistureSample[],
  rule: CareRule,
  next: MoistureSample
): PlantState {
  const value = smooth([...samples, next], state.id)
  if (value === undefined) return state
  const withinWait = state.phase === 'watering' &&
    next.capturedAt - state.lastWateredAt < 15 * 60 * 1000
  const phase = withinWait && value < rule.recoverAbove ? 'watering' :
    nextPhase(state.phase, value, rule)
  return { ...state, rawMoisture: next.value, moisture: value,
    phase, lastSampleAt: next.capturedAt }
}

浇水后十五分钟内,卡片显示"正在吸收水分"。这段时间不重复弹提醒,读数达到恢复线后再回到健康状态。

平板列表按植物 ID 选择

阳台、书房、客厅的筛选会改变植物卡片顺序。选择状态保存为 selectedPlantId,而不是"第几张卡片"。

ts 复制代码
ForEach(this.visiblePlants, (plant: PlantState) => {
  this.plantTile(plant)
    .border({ width: this.selectedPlantId === plant.id ? 2 : 0,
      color: '#68A87E' })
    .onClick(() => { this.selectedPlantId = plant.id })
}, (plant: PlantState) => plant.id)

趋势图读取 moisture,详情页可以保留 rawMoisture 与采样时间,用户看到异常时才展开查看。系统能力接入后,传感器适配器只要不断产生 MoistureSample;区间判断、浇水确认和多区域选择仍保持同一套状态链路。

相关推荐
ö Constancy几秒前
一台电脑的硬件组成与作用:按功能分类详解(Windows 与 Linux 视角)
linux·windows·电脑
SomeOtherTime5 分钟前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
w3296362715 分钟前
飞书接单到月度结算:我用一个 DSH 插件跑通鞋类采购 WMS,附 22 项验收
pdf·node.js·飞书·后端开发·ai agent
qq_4069810310 分钟前
c++ lamda(三)Lambda表达式
java·前端·c++
zhanlang021713 分钟前
HTML 零基础入门到企业实战【第五章 5.4】内嵌网页 iframe 标签:嵌入网页、视频、地图实战
前端·html·音视频
FYKJ_201018 分钟前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计
总要学点什么28 分钟前
第106篇:三维特效-流动线效果——让一颗光点沿整条线一路滚到头
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
2分钟速写快排8 小时前
孔乙己的茴字有多少种写法?|AI 对话实时通信的三种实现
前端·javascript·github
Penge66610 小时前
JavaScript:BOM 详解
前端
Penge66610 小时前
Vite vs Webpack:前端构建工具原理与实战
前端