代码服务器湿度监测集成:环境传感器与数据可视化全指南
代码服务器湿度监测集成:环境传感器与数据可视化全指南
【免费下载链接】code-server VS Code in the browser 项目地址: https://gitcode.com/gh_mirrors/co/code-server
引言:解决开发环境湿度监控痛点
你是否曾因服务器机房湿度异常导致硬件故障而焦头烂额?作为开发者,我们往往忽视环境因素对开发环境稳定性的影响。本文将展示如何利用 code-server (VS Code in the browser) 构建一个功能完备的湿度监测系统,让你能够实时监控开发环境的湿度变化,并通过直观的数据可视化界面及时发现潜在问题。
读完本文后,你将能够:
- 理解 code-server 的扩展机制和 API 接口
- 搭建基于 WebSocket 的实时数据传输通道
- 开发自定义 code-server 扩展以集成环境传感器
- 实现湿度数据的实时可视化展示
- 配置异常湿度警报系统
技术架构概览
系统组件关系图
数据流程时序图
环境准备与依赖安装
硬件需求
- 运行 code-server 的服务器(推荐配置:2核CPU,2GB内存)
- DHT11/DHT22 湿度传感器模块
- Arduino 或 Raspberry Pi 开发板
- 面包板和杜邦线(用于连接传感器)
软件依赖
| 依赖项 | 版本要求 | 用途 |
|---|---|---|
| Node.js | ≥ 14.x | 运行 code-server 和扩展 |
| npm | ≥ 6.x | 包管理 |
| code-server | ≥ 4.0.0 | 基础编辑器环境 |
| Johnny-Five | ≥ 2.0.0 | 硬件交互库 |
| Chart.js | ≥ 3.0.0 | 数据可视化 |
| Express | ≥ 4.0.0 | HTTP 服务器(code-server 内置) |
安装步骤
- 确保 code-server 已正确安装并运行
# 克隆代码仓库
git clone https://gitcode.com/gh_mirrors/co/code-server.git
cd code-server
# 安装依赖
npm install
# 构建项目
npm run build
# 启动 code-server
npm run start
- 安装传感器开发依赖
# 创建扩展开发目录
mkdir -p ~/.local/share/code-server/extensions/humidity-monitor
# 初始化扩展项目
cd ~/.local/share/code-server/extensions/humidity-monitor
npm init -y
# 安装必要依赖
npm install johnny-five chart.js @types/node --save
code-server 扩展开发基础
扩展项目结构
humidity-monitor/
├── package.json # 扩展元数据
├── src/
│ ├── extension.ts # 扩展入口点
│ ├── sensor/
│ │ ├── sensor.ts # 传感器交互逻辑
│ │ └── reader.ts # 数据读取实现
│ ├── webview/
│ │ ├── index.html # 可视化界面
│ │ ├── app.ts # 前端逻辑
│ │ └── style.css # 界面样式
│ └── utils/
│ ├── alert.ts # 警报系统
│ └── logger.ts # 日志工具
├── tsconfig.json # TypeScript 配置
└── README.md # 扩展说明文档
扩展清单文件 (package.json)
{
"name": "humidity-monitor",
"displayName": "Humidity Monitor",
"version": "1.0.0",
"engines": {
"vscode": "^1.60.0"
},
"main": "./out/extension.js",
"categories": [
"Other"
],
"activationEvents": [
"onCommand:humidity-monitor.start",
"onStartupFinished"
],
"contributes": {
"commands": [{
"command": "humidity-monitor.start",
"title": "Humidity Monitor: Start Monitoring"
}],
"configuration": {
"title": "Humidity Monitor",
"properties": {
"humidity-monitor.sensorType": {
"type": "string",
"default": "DHT22",
"description": "Type of humidity sensor (DHT11/DHT22)"
},
"humidity-monitor.checkInterval": {
"type": "number",
"default": 5000,
"description": "Sensor check interval in milliseconds"
},
"humidity-monitor.warningThreshold": {
"type": "number",
"default": 70,
"description": "Humidity warning threshold percentage"
},
"humidity-monitor.dangerThreshold": {
"type": "number",
"default": 85,
"description": "Humidity danger threshold percentage"
}
}
}
}
}
WebSocket 实时通信通道实现
code-server 内置了 WebSocket 路由系统,我们可以利用它来构建实时数据传输通道。下面是实现 WebSocket 服务端的关键代码:
WebSocket 服务端实现 (server-side)
// src/node/routes/humidityWebSocket.ts
import { WebSocketRouter, WebSocketHandler } from '../wsRouter';
import { HumiditySensor } from '../../common/sensor';
export function setupHumidityWebSocket(router: WebSocketRouter) {
const sensor = new HumiditySensor();
let intervalId: NodeJS.Timeout;
const handler: WebSocketHandler = (ws, req) => {
console.log('Client connected to humidity WebSocket');
// 开始定期读取湿度数据
intervalId = setInterval(async () => {
try {
const humidity = await sensor.read();
ws.send(JSON.stringify({
timestamp: new Date().toISOString(),
humidity: humidity,
unit: '%'
}));
} catch (error) {
console.error('Error reading humidity:', error);
ws.send(JSON.stringify({
error: 'Failed to read humidity data',
timestamp: new Date().toISOString()
}));
}
}, 5000); // 每5秒读取一次
// 处理客户端断开连接
ws.on('close', () => {
console.log('Client disconnected from humidity WebSocket');
clearInterval(intervalId);
});
// 处理客户端消息
ws.on('message', (message) => {
console.log('Received message:', message.toString());
// 可以处理客户端发送的配置更新等消息
});
};
// 注册WebSocket路由
router.ws('/humidity-data', handler);
return {
// 提供清理函数
dispose: () => {
if (intervalId) {
clearInterval(intervalId);
}
}
};
}
修改 code-server 主应用以注册 WebSocket 路由
// src/node/app.ts
import { setupHumidityWebSocket } from './routes/humidityWebSocket';
// ... 现有代码 ...
export async function createApp(args: CliArgs): Promise {
// ... 现有代码 ...
// 设置WebSocket路由
const wsRouter = new WebSocketRouter()
handleUpgrade(wsRouter, server)
// 注册湿度监测WebSocket
const humidityWebSocket = setupHumidityWebSocket(wsRouter);
// ... 现有代码 ...
return {
// ... 现有代码 ...
dispose: async () => {
// ... 现有清理代码 ...
humidityWebSocket.dispose();
await Promise.all([
// ... 现有Promise ...
])
}
}
}
客户端 WebSocket 连接 (client-side)
// webview/src/wsClient.ts
export class HumidityWebSocketClient {
private ws: WebSocket | null = null;
private callbacks: ((data: any) => void)[] = [];
constructor(private url: string) {}
connect(): Promise {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected to humidity WebSocket server');
resolve();
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.callbacks.forEach(callback => callback(data));
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
this.ws.onclose = () => {
console.log('WebSocket connection closed');
// 自动重连逻辑
setTimeout(() => this.connect(), 3000);
};
});
}
onDataReceived(callback: (data: any) => void): void {
this.callbacks.push(callback);
}
disconnect(): void {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
湿度传感器模块开发
传感器硬件连接
对于 Raspberry Pi,我们需要通过 GPIO 接口连接 DHT22 传感器:
传感器读取代码实现
// src/common/sensor.ts
import { Gpio } from 'onoff';
import { promisify } from 'util';
export class HumiditySensor {
private sensor: any; // 实际项目中应使用具体类型
private pin: number;
private type: string;
constructor(type: string = 'DHT22', pin: number = 4) {
this.type = type;
this.pin = pin;
this.initializeSensor();
}
private initializeSensor(): void {
try {
// 根据传感器类型和引脚初始化
const sensorModule = require('node-dht-sensor');
if (!sensorModule.initialize(this.type === 'DHT11' ? 11 : 22, this.pin)) {
console.error('Failed to initialize sensor');
throw new Error('Sensor initialization failed');
}
this.sensor = sensorModule;
} catch (error) {
console.error('Error initializing sensor:', error);
throw error;
}
}
async read(): Promise {
if (!this.sensor) {
this.initializeSensor();
}
return new Promise((resolve, reject) => {
this.sensor.read((err: Error, temperature: number, humidity: number) => {
if (err) {
reject(err);
return;
}
if (humidity < 0 || humidity > 100) {
reject(new Error(`Invalid humidity value: ${humidity}`));
return;
}
resolve(humidity);
});
});
}
}
自定义 code-server 扩展开发
扩展激活与传感器初始化
// src/extension.ts
import * as vscode from 'vscode';
import { HumidityMonitor } from './humidityMonitor';
import { HumidityViewProvider } from './humidityViewProvider';
export function activate(context: vscode.ExtensionContext) {
console.log('"humidity-monitor" extension is now active!');
// 获取配置
const config = vscode.workspace.getConfiguration('humidity-monitor');
// 创建视图提供器
const viewProvider = new HumidityViewProvider(context.extensionUri);
// 注册Webview面板
const disposable = vscode.window.registerWebviewViewProvider(
'humidityMonitor.view',
viewProvider
);
// 创建湿度监测器实例
const monitor = new HumidityMonitor(
config.get('sensorType') || 'DHT22',
config.get('checkInterval') || 5000,
config.get('warningThreshold') || 70,
config.get('dangerThreshold') || 85
);
// 注册命令
const startCommand = vscode.commands.registerCommand('humidity-monitor.start', () => {
monitor.startMonitoring();
vscode.commands.executeCommand('humidityMonitor.view.focus');
// 向视图提供器传递数据更新回调
monitor.onDataUpdate((data) => {
viewProvider.updateData(data);
});
// 警报回调
monitor.onAlert((level, message) => {
vscode.window.showErrorMessage(`Humidity Alert: ${message}`);
// 在状态栏显示警报
const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
statusBarItem.text = `$(warning) Humidity ${level}: ${message}`;
statusBarItem.show();
// 5秒后隐藏状态栏警报
setTimeout(() => statusBarItem.dispose(), 5000);
});
});
// 停止监测命令
const stopCommand = vscode.commands.registerCommand('humidity-monitor.stop', () => {
monitor.stopMonitoring();
vscode.window.showInformationMessage('Humidity monitoring stopped');
});
context.subscriptions.push(
disposable,
startCommand,
stopCommand,
// 当扩展停用时停止监测
new vscode.Disposable(() => monitor.stopMonitoring())
);
}
export function deactivate() {}
湿度监测管理器实现
// src/humidityMonitor.ts
import { HumiditySensor } from './sensor';
export enum AlertLevel {
INFO = 'info',
WARNING = 'warning',
DANGER = 'danger'
}
export class HumidityMonitor {
private sensor: HumiditySensor;
private interval: number;
private warningThreshold: number;
private dangerThreshold: number;
private monitoringInterval: NodeJS.Timeout | null = null;
private dataUpdateCallbacks: ((data: any) => void)[] = [];
private alertCallbacks: ((level: AlertLevel, message: string) => void)[] = [];
private historicalData: Array<{ timestamp: Date, humidity: number }> = [];
constructor(
sensorType: string,
interval: number,
warningThreshold: number,
dangerThreshold: number
) {
this.sensor = new HumiditySensor(sensorType);
this.interval = interval;
this.warningThreshold = warningThreshold;
this.dangerThreshold = dangerThreshold;
}
startMonitoring(): void {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
}
this.monitoringInterval = setInterval(async () => {
await this.takeReading();
}, this.interval);
// 立即读取一次
this.takeReading();
}
stopMonitoring(): void {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
this.monitoringInterval = null;
}
}
private async takeReading(): Promise {
try {
const humidity = await this.sensor.read();
const timestamp = new Date();
// 存储历史数据(保留最近100个数据点)
this.historicalData.push({ timestamp, humidity });
if (this.historicalData.length > 100) {
this.historicalData.shift();
}
// 触发数据更新回调
this.triggerDataUpdate({
current: humidity,
timestamp,
historical: this.historicalData
});
// 检查阈值并可能触发警报
this.checkThresholds(humidity);
} catch (error) {
console.error('Error taking humidity reading:', error);
}
}
private checkThresholds(humidity: number): void {
if (humidity >= this.dangerThreshold) {
this.triggerAlert(
AlertLevel.DANGER,
`Extreme humidity detected: ${humidity}% (Threshold: ${this.dangerThreshold}%)`
);
} else if (humidity >= this.warningThreshold) {
this.triggerAlert(
AlertLevel.WARNING,
`High humidity detected: ${humidity}% (Threshold: ${this.warningThreshold}%)`
);
}
}
onDataUpdate(callback: (data: any) => void): void {
this.dataUpdateCallbacks.push(callback);
}
onAlert(callback: (level: AlertLevel, message: string) => void): void {
this.alertCallbacks.push(callback);
}
private triggerDataUpdate(data: any): void {
this.dataUpdateCallbacks.forEach(callback => callback(data));
}
private triggerAlert(level: AlertLevel, message: string): void {
this.alertCallbacks.forEach(callback => callback(level, message));
}
}
数据可视化界面开发
Webview 视图提供器实现
// src/humidityViewProvider.ts
import * as vscode from 'vscode';
export class HumidityViewProvider implements vscode.WebviewViewProvider {
public static readonly viewType = 'humidityMonitor.view';
private _view?: vscode.WebviewView;
private _data: any = { current: null, historical: [] };
constructor(private readonly _extensionUri: vscode.Uri) {}
public resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken
) {
this._view = webviewView;
webviewView.webview.options = {
// 允许访问扩展资源
localResourceRoots: [this._extensionUri],
// 启用脚本
enableScripts: true
};
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
// 处理来自webview的消息
webviewView.webview.onDidReceiveMessage(data => {
switch (data.type) {
case 'ready':
// Webview已准备好,发送当前数据
this._view?.webview.postMessage({
type: 'update',
data: this._data
});
break;
}
});
}
public updateData(data: any): void {
this._data = data;
// 将数据发送到webview
this._view?.webview.postMessage({
type: 'update',
data: data
});
}
private _getHtmlForWebview(webview: vscode.Webview): string {
// 获取资源URI
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(
this._extensionUri, 'webview', 'dist', 'style.css'
));
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(
this._extensionUri, 'webview', 'dist', 'main.js'
));
const chartJsUri = webview.asWebviewUri(vscode.Uri.joinPath(
this._extensionUri, 'node_modules', 'chart.js', 'dist', 'chart.umd.min.js'
));
// 页面HTML
return `
Humidity Monitor
湿度监测仪表盘
当前湿度:
--
%
等待监测开始...
阈值设置
%
%
`;
}
}
前端可视化实现
// webview/src/main.ts
document.addEventListener('DOMContentLoaded', () => {
// 从VS Code获取webview API
const vscode = acquireVsCodeApi();
// 初始化图表
const ctx = document.getElementById('humidity-chart') as HTMLCanvasElement;
let humidityChart: Chart;
// 初始化Chart.js图表
function initChart() {
humidityChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: '湿度 (%)',
data: [],
borderColor: '#3498db',
backgroundColor: 'rgba(52, 152, 219, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: '时间'
},
ticks: {
maxTicksLimit: 10
}
},
y: {
min: 0,
max: 100,
title: {
display: true,
text: '湿度 (%)'
}
}
},
plugins: {
legend: {
position: 'top',
},
tooltip: {
mode: 'index',
intersect: false,
callbacks: {
title: function(context) {
const date = new Date(context[0].label);
return date.toLocaleTimeString();
}
}
}
}
}
});
}
// 更新图表数据
function updateChart(data: any) {
if (!humidityChart) {
initChart();
}
// 更新当前湿度显示
const currentHumidityEl = document.getElementById('current-humidity');
if (currentHumidityEl && data.current !== null) {
currentHumidityEl.textContent = data.current.toFixed(1);
// 根据湿度值更改显示颜色
if (data.current >= 85) {
currentHumidityEl.classList.add('danger');
currentHumidityEl.classList.remove('warning', 'normal');
} else if (data.current >= 70) {
currentHumidityEl.classList.add('warning');
currentHumidityEl.classList.remove('danger', 'normal');
} else {
currentHumidityEl.classList.add('normal');
currentHumidityEl.classList.remove('danger', 'warning');
}
}
// 更新状态指示器
const statusIndicator = document.getElementById('status-indicator');
if (statusIndicator) {
const dot = statusIndicator.querySelector('.dot');
const text = statusIndicator.querySelector('.text');
if (dot && text) {
dot.classList.add('active');
text.textContent = '正在监测...';
}
}
// 更新历史数据图表
if (data.historical && data.historical.length > 0) {
humidityChart.data.labels = data.historical.map((entry: any) => {
return new Date(entry.timestamp).toISOString();
});
humidityChart.data.datasets[0].data = data.historical.map((entry: any) => entry.humidity);
humidityChart.update();
}
}
// 监听来自扩展的消息
window.addEventListener('message', event => {
const message = event.data;
switch (message.type) {
case 'update':
updateChart(message.data);
break;
}
});
// 保存阈值设置
document.getElementById('save-thresholds')?.addEventListener('click', () => {
const warningThreshold = (document.getElementById('warning-threshold') as HTMLInputElement).value;
const dangerThreshold = (document.getElementById('danger-threshold') as HTMLInputElement).value;
vscode.postMessage({
type: 'saveThresholds',
warningThreshold: parseInt(warningThreshold),
dangerThreshold: parseInt(dangerThreshold)
});
alert('阈值设置已保存');
});
// 通知扩展webview已准备就绪
vscode.postMessage({ type: 'ready' });
});
扩展打包与安装
扩展打包配置
// package.json 添加打包脚本
{
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint . --ext .ts,.tsx",
"test": "node ./out/test/runTest.js",
"package": "vsce package"
},
"devDependencies": {
"@types/node": "^14.14.37",
"@types/vscode": "^1.60.0",
"@typescript-eslint/eslint-plugin": "^4.21.0",
"@typescript-eslint/parser": "^4.21.0",
"eslint": "^7.23.0",
"typescript": "^4.2.3",
"@vscode/vsce": "^1.95.0"
}
}
打包与安装命令
# 编译TypeScript代码
npm run compile
# 打包扩展为vsix文件
npm run package
# 安装扩展到code-server
code-server --install-extension humidity-monitor-1.0.0.vsix
系统测试与优化
功能测试清单
| 测试项 | 测试方法 | 预期结果 |
|---|---|---|
| 传感器连接 | 启动监测后观察数据是否变化 | 湿度值应在合理范围内波动 |
| 实时数据传输 | 观察图表更新频率 | 图表应每5秒更新一次数据 |
| 阈值警报 | 调整阈值至当前湿度以上 | 应触发相应级别的警报 |
| 数据可视化 | 持续监测30分钟 | 图表应平稳显示历史数据,无明显延迟 |
| WebSocket重连 | 重启code-server | 客户端应自动重连并恢复数据接收 |
性能优化建议
-
数据采样优化
- 根据实际需求调整采样频率,非关键场景可降低至10-30秒/次
- 实现自适应采样算法,湿度变化剧烈时提高频率,稳定时降低频率
-
前端性能优化
- 限制历史数据点数量,建议保留最近100个数据点
- 使用Web Workers处理数据转换,避免阻塞UI线程
- 实现图表数据缓存机制,减少重复渲染
-
网络传输优化
- 对连续小幅波动的湿度数据进行节流处理
- 考虑使用压缩算法减少WebSocket传输数据量
部署与使用指南
部署架构图
启动与使用步骤
- 连接湿度传感器到开发板并确保正常工作
- 启动code-server并加载湿度监测扩展
- 通过命令面板运行"Humidity Monitor: Start Monitoring"
- 在侧边栏打开湿度监测仪表盘查看实时数据
- 根据实际环境调整湿度阈值设置
常见问题解决
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 无数据显示 | 传感器未正确连接 | 检查传感器接线,重启监测 |
| 数据异常波动 | 传感器附近有水源或气流 | 调整传感器位置,远离干扰源 |
| 扩展无法激活 | code-server版本不兼容 | 更新code-server至4.0.0以上版本 |
| WebSocket连接失败 | 网络配置问题 | 检查防火墙设置,确保WebSocket端口开放 |
结论与未来扩展
通过本文介绍的方法,我们成功构建了一个集成在code-server中的湿度监测系统,实现了环境湿度的实时监测、数据可视化和异常警报功能。该系统不仅展示了code-server的扩展能力,也为开发其他类型的环境监测系统提供了参考架构。
未来功能扩展方向
- 多传感器支持:集成温度、气压等更多环境参数监测
- 数据持久化:添加数据库支持以存储历史数据,实现趋势分析
- 远程控制:增加通过code-server控制环境调节设备的能力
- 多用户支持:实现基于角色的访问控制,支持团队共享监测数据
- AI预测:利用机器学习算法预测环境变化趋势
总结
本项目展示了如何利用code-server的可扩展性和Web技术栈构建专业的环境监测系统。通过WebSocket实现的实时数据传输确保了监测的及时性,而自定义扩展机制则提供了与开发环境的无缝集成。这种方法不仅适用于湿度监测,还可推广到各种需要实时数据采集和分析的场景中,为开发者创造更加智能和舒适的开发环境。
【免费下载链接】code-server VS Code in the browser 项目地址: https://gitcode.com/gh_mirrors/co/code-server







