A comprehensive skill for building intelligent elderly care applications, based on the "Silver Guardian" (银发守护者) project.
This skill provides reusable modules, components, and best practices for developing applications that serve elderly users, with focus on:
Invoke this skill when:
fraudPreventionTips.js)// Daily tip rotation based on date
export function getTodayTip() {
const today = new Date()
const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24))
return fraudPreventionTips[dayOfYear % fraudPreventionTips.length]
}
// Voice播报
export function speakTip(tip) {
if ('speechSynthesis' in window) {
const text = `防诈骗提醒:${tip.title}。${tip.summary}`
const utterance = new SpeechSynthesisUtterance(text)
utterance.lang = 'zh-CN'
utterance.rate = 0.9
speechSynthesis.speak(utterance)
}
}
// Emergency help
export function emergencyHelp() {
if (confirm('您是否遇到了诈骗?\n点击"确定"拨打110\n点击"取消"拨打96110')) {
window.location.href = 'tel:110'
} else {
window.location.href = 'tel:96110'
}
}
mapService.js)class MapService {
constructor(apiKey) {
this.key = apiKey
this.currentLocation = null
}
// Get current location with fallback
async getCurrentLocation() {
return new Promise((resolve) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
this.currentLocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
}
resolve(this.currentLocation)
},
() => {
// Fallback to default location (Beijing)
this.currentLocation = { lat: 39.90923, lng: 116.397428 }
resolve(this.currentLocation)
}
)
} else {
this.currentLocation = { lat: 39.90923, lng: 116.397428 }
resolve(this.currentLocation)
}
})
}
// Search nearby facilities
async searchNearby(keyword, radius = 3000) {
const location = await this.getCurrentLocation()
const response = await fetch(
`https://restapi.amap.com/v3/place/around?key=${this.key}&location=${location.lng},${location.lat}&keywords=${encodeURIComponent(keyword)}&radius=${radius}&offset=20`
)
const data = await response.json()
return data.pois || []
}
}
// Anomaly detection
function checkHealthAnomaly(healthData) {
const anomalies = []
if (healthData.bp_systolic > 140 || healthData.bp_diastolic > 90) {
anomalies.push({
type: '血压偏高',
value: `${healthData.bp_systolic}/${healthData.bp_diastolic}`,
suggestion: '建议休息后再次测量,持续偏高请及时就医'
})
}
if (healthData.heart_rate > 100 || healthData.heart_rate < 60) {
anomalies.push({
type: '心率异常',
value: healthData.heart_rate,
suggestion: '心率不在正常范围,请关注身体状况'
})
}
return anomalies
}
// Parse relative time expressions
function parseTime(message) {
const patterns = [
{ regex: /(\d+)\s*分钟[之后]?/, unit: 'minute' },
{ regex: /(\d+)\s*小时[之后]?/, unit: 'hour' },
{ regex: /(\d+)[::](\d+)/, unit: 'time' },
{ regex: /早上\s*(\d+)/, unit: 'morning' },
{ regex: /晚上\s*(\d+)/, unit: 'evening' }
]
for (const pattern of patterns) {
const match = message.match(pattern.regex)
if (match) {
return { value: parseInt(match[1]), unit: pattern.unit }
}
}
return null
}
// Medicine management
class MedicineReminder {
constructor() {
this.medicines = []
this.reminders = []
}
// Add medicine
addMedicine(name, dosage, frequency, time) {
const medicine = {
id: Date.now(),
name,
dosage,
frequency,
time,
createdAt: new Date()
}
this.medicines.push(medicine)
this.scheduleReminder(medicine)
return medicine
}
// Schedule reminder
scheduleReminder(medicine) {
const [hour, minute] = medicine.time.split(':')
const now = new Date()
const reminderTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute)
if (reminderTime < now) {
reminderTime.setDate(reminderTime.getDate() + 1)
}
const delay = reminderTime - now
setTimeout(() => {
this.sendReminder(medicine)
// Reschedule for next day
this.scheduleReminder(medicine)
}, delay)
}
// Send reminder notification
sendReminder(medicine) {
if ('Notification' in window && Notification.permission === 'granted') {
new Notification('用药提醒', {
body: `该吃药了:${medicine.name} ${medicine.dosage}`,
icon: '/medicine-icon.png'
})
}
// Voice reminder
if ('speechSynthesis' in window) {
const text = `该吃药了:${medicine.name},剂量:${medicine.dosage}`
const utterance = new SpeechSynthesisUtterance(text)
utterance.lang = 'zh-CN'
utterance.rate = 0.9
speechSynthesis.speak(utterance)
}
}
// Mark as taken
markAsTaken(medicineId) {
const medicine = this.medicines.find(m => m.id === medicineId)
if (medicine) {
medicine.lastTaken = new Date()
return true
}
return false
}
}
// Family photo album with voice description
class FamilyAlbum {
constructor() {
this.photos = []
this.currentIndex = 0
this.autoPlayInterval = null
}
// Add photo
addPhoto(url, description, location, date) {
const photo = {
id: Date.now(),
url,
description,
location,
date: date || new Date()
}
this.photos.push(photo)
return photo
}
// Start auto slideshow with voice
startAutoPlay(interval = 5000) {
if (this.autoPlayInterval) {
clearInterval(this.autoPlayInterval)
}
this.autoPlayInterval = setInterval(() => {
this.currentIndex = (this.currentIndex + 1) % this.photos.length
const photo = this.photos[this.currentIndex]
// Voice description
if ('speechSynthesis' in window) {
const text = `这是${photo.location || '某地'}拍的照片,${photo.description || ''}`
const utterance = new SpeechSynthesisUtterance(text)
utterance.lang = 'zh-CN'
utterance.rate = 0.9
speechSynthesis.speak(utterance)
}
}, interval)
}
// Stop auto play
stopAutoPlay() {
if (this.autoPlayInterval) {
clearInterval(this.autoPlayInterval)
this.autoPlayInterval = null
}
}
}
// Bluetooth device manager
class BluetoothHealthManager {
constructor() {
this.device = null
this.server = null
this.characteristics = new Map()
}
// Scan and connect to device
async connect(deviceName) {
try {
this.device = await navigator.bluetooth.requestDevice({
filters: [
{ name: deviceName },
{ services: ['heart_rate', 'blood_pressure'] }
],
optionalServices: ['battery_service']
})
this.server = await this.device.gatt.connect()
return true
} catch (error) {
console.error('蓝牙连接失败:', error)
return false
}
}
// Read heart rate
async readHeartRate() {
try {
const service = await this.server.getPrimaryService('heart_rate')
const characteristic = await service.getCharacteristic('heart_rate_measurement')
const value = await characteristic.readValue()
return value.getUint8(1)
} catch (error) {
console.error('读取心率失败:', error)
return null
}
}
// Start monitoring
async startMonitoring(callback) {
try {
const service = await this.server.getPrimaryService('heart_rate')
const characteristic = await service.getCharacteristic('heart_rate_measurement')
await characteristic.startNotifications()
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const value = event.target.value.getUint8(1)
callback({ type: 'heartRate', value })
})
} catch (error) {
console.error('启动监测失败:', error)
}
}
// Disconnect
disconnect() {
if (this.device && this.device.gatt.connected) {
this.device.gatt.disconnect()
}
}
}
<template>
<div class="elderly-app">
<!-- Large font, high contrast -->
<div class="text-2xl font-bold text-gray-900">
{{ title }}
</div>
<!-- Voice button -->
<button @click="speak" class="voice-btn">
🔊 语音播报
</button>
<!-- Emergency button -->
<button @click="emergency" class="emergency-btn">
🆘 紧急求助
</button>
</div>
</template>
<style>
.elderly-app {
font-size: 18px;
line-height: 1.8;
}
.voice-btn {
background: #4CAF50;
color: white;
padding: 15px 30px;
border-radius: 10px;
font-size: 18px;
}
.emergency-btn {
background: #f44336;
color: white;
padding: 15px 30px;
border-radius: 10px;
font-size: 18px;
}
</style>
<!-- Large Button Component -->
<template>
<button
class="large-btn"
:style="{ fontSize: fontSize + 'px', padding: padding + 'px' }"
@click="handleClick"
>
<slot></slot>
</button>
</template>
<script>
export default {
props: {
fontSize: { type: Number, default: 20 },
padding: { type: Number, default: 15 }
},
methods: {
handleClick() {
// Voice feedback
if ('speechSynthesis' in window) {
const text = this.$slots.default()[0].children
const utterance = new SpeechSynthesisUtterance(text)
utterance.lang = 'zh-CN'
speechSynthesis.speak(utterance)
}
this.$emit('click')
}
}
}
</script>
<style scoped>
.large-btn {
min-width: 120px;
min-height: 60px;
border-radius: 12px;
border: 2px solid #333;
background: #fff;
color: #333;
cursor: pointer;
transition: all 0.3s;
}
.large-btn:hover {
background: #f0f0f0;
transform: scale(1.05);
}
.large-btn:active {
transform: scale(0.95);
}
</style>
npm install vue@3 express mysql2 tailwindcss
import MapService from './services/mapService.js'
const mapService = new MapService('YOUR_AMAP_KEY')
const places = await mapService.searchNearby('菜市场')
import { getTodayTip, speakTip } from './assets/fraudPreventionTips.js'
const tip = getTodayTip()
speakTip(tip)
import { checkHealthAnomaly } from './services/healthMonitor.js'
const anomalies = checkHealthAnomaly({
bp_systolic: 145,
bp_diastolic: 95,
heart_rate: 78
})
import MedicineReminder from './services/medicineReminder.js'
const reminder = new MedicineReminder()
reminder.addMedicine('降压药', '1片', 'daily', '08:00')
reminder.addMedicine('降糖药', '2片', 'daily', '12:00')
import FamilyAlbum from './services/familyAlbum.js'
const album = new FamilyAlbum()
album.addPhoto('/photos/family1.jpg', '全家福', '家里', '2024-01-01')
album.startAutoPlay(5000)
import BluetoothHealthManager from './services/bluetoothHealthManager.js'
const btManager = new BluetoothHealthManager()
await btManager.connect('小米手环')
btManager.startMonitoring((data) => {
console.log(`心率: ${data.value}`)
})
| Method | Parameters | Return | Description |
|---|---|---|---|
| -------- | ----------- | -------- | ------------- |
getCurrentLocation() | none | Promise | Get current GPS location |
searchNearby(keyword, radius) | string, number | Promise | Search nearby facilities |
| Method | Parameters | Return | Description |
|---|---|---|---|
| -------- | ----------- | -------- | ------------- |
addMedicine(name, dosage, frequency, time) | string, string, string, string | Object | Add new medicine |
markAsTaken(medicineId) | number | boolean | Mark medicine as taken |
| Method | Parameters | Return | Description |
|---|---|---|---|
| -------- | ----------- | -------- | ------------- |
addPhoto(url, description, location, date) | string, string, string, Date | Object | Add new photo |
startAutoPlay(interval) | number | void | Start auto slideshow |
stopAutoPlay() | none | void | Stop auto slideshow |
MIT License - Free for personal and commercial use.
Contributions welcome! Please submit issues and pull requests.
For questions and support, please open an issue on GitHub.
共 1 个版本