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

室内植物实验室的琴叶榕显示 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;区间判断、浇水确认和多区域选择仍保持同一套状态链路。

相关推荐
sunshine22 girl1 小时前
Java学习六 Java常见API-2--String-3-字符串的截取替换和其他方法
java·开发语言·学习
lsc_blog1 小时前
远程和居家办公的经历,技术简历上怎么摆
面试·职场和发展·重构·pdf·求职招聘
LOVE️YOU1 小时前
Python 函数名、函数对象与“把函数作为参数传递”
开发语言·python
码艺-Alimjan2 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
2601_957883842 小时前
2026年9月:雷神笔记本售后相关资讯
python·电脑
sunshine22 girl2 小时前
Java学习六 Java常见API-1-Radom随机数
java·开发语言·学习
可乐鸡翅yeah_2 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒2 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
潼心1412o2 小时前
C++初阶(长期更新)第6讲:类和对象(下)
开发语言·c++·算法