My heart beats for U —— 心率同步 Grafana 展示
整了个小活:把苹果健康的心率定时同步到服务器上,并由 Grafana 绘制展示,效果大概如下:
可以点击博客右上角的 ♥️ 标志查看,因为套了 Cloudflare Tunnel,国内访问速度不佳,尽量挂🪜访问
大概的思路就是使用这个 app Health Auto Export - JSON+CSV 的 Restful API 的功能,设置定时将心率数据发送到部署的 http 接口以写入 InfluxDB,再由 Grafana 连接 InfluxDB 绘制看板
Health Auto Export - JSON+CSV 这个 app 如果想要定时同步,需要开通 Premium,Lifetime 美区是 24.99 USD,有点小贵,但似乎也没有什么平替方案
订阅后新建一个 Automation:
- Automation Type 为
REST API
- URL 就是你下面要部署的服务的地址,API 路径为
/push/heart_rate
- Data Type 选择
Health Metrics
- Select Health Metrics 勾选
Heart Rate
- Export Format 选择 JSON
- Sync Cadence 可以选择 1 分钟也可以选择 5 分钟,Apple Watch 并不会一直监测心率
勾上 Enable 即可,为了保证在程序退出后还能同步,可以添加一个桌面小组件
接着就是部署服务开放 Restful API 端口,接收数据并写入 InfluxDB 了。部署 InfluxDB 可以自行 Google 不再赘述,注意服务用的是 InfluxDB 2
服务的源码在 reekystive/healthkit-collector,是个 node 项目,可以直接用 pnpm 启动监听 3000 端口。我写了个 Dockerfile 将其打包成 docker 镜像,部署在家里的服务器上
go
FROM node:20-alpine AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.14.2 --activate
# Set working directory
WORKDIR /app
# Copy package.json and pnpm-lock.yaml
COPY package.json pnpm-lock.yaml* ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build the application
RUN pnpm build
# Stage 2: Production stage
FROM node:20-alpine AS production
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.14.2 --activate
# Set working directory
WORKDIR /app
# Copy package.json and pnpm-lock.yaml
COPY package.json pnpm-lock.yaml* ./
# Install production dependencies only
RUN pnpm install --prod --frozen-lockfile
# Copy built application from builder stage
COPY --from=builder /app/dist ./dist
# Set environment variables
# These are default values that can be overridden when running the container
ENV NODE_ENV=production
ENV PORT=3000
# Expose the port your app runs on (using the PORT environment variable)
EXPOSE ${PORT}
# Command to run the application
CMD ["node", "dist/index.js"]
启动时需要设置四个连接 InfluxDB 使用的环境变量
INFLUXDB_TOKEN='your_influxdb_token'
INFLUXDB_URL='your_influxdb_url'
INFLUXDB_ORG='your_influxdb_org'
INFLUXDB_BUCKET='your_influxdb_bucket'
部署完成后可以尝试进行一次同步,服务会输出写入 DB 成功的 log
最后就是部署个 Grafana 绘制仪表盘了,添加好 Data Source,新建仪表盘,查询语句如下
from(bucket: "bpm")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "heart_rate")
|> filter(fn: (r) => r["_field"] == "avg" or r["_field"] == "max" or r["_field"] == "min")
Enjoy!