文章目录
-
- 在当今万物互联的时代,物联网设备正以前所未有的速度渗透到我们生活的方方面面。从智能家居的温度传感器到工业环境的监控设备,海量数据正源源不断地产生。然而,这些数据的价值往往受限于展示和分析的平台。与此同时,WordPress作为全球最流行的内容管理系统,已经超越了简单的博客平台,发展成为功能强大的网站构建工具。本教程将深入探讨如何将这两者结合——通过WordPress的代码二次开发,连接智能硬件并创建专业的物联网设备数据面板,同时实现多种常用互联网小工具功能。
-
- 在开始集成物联网设备之前,我们需要建立一个适合开发的WordPress环境。建议使用本地开发环境如XAMPP、MAMP或Local by Flywheel,这些工具可以快速搭建包含Apache、MySQL和PHP的完整环境。 对于物联网开发,我们需要确保WordPress安装满足以下条件: WordPress 5.0及以上版本 PHP 7.4及以上(建议8.0+以获得更好性能) 启用REST API功能 安装并激活调试插件,如Query Monitor和Debug Bar
- 物联网设备与WordPress的通信通常通过以下几种方式实现: HTTP/HTTPS请求:设备直接向WordPress REST API端点发送数据 MQTT协议:轻量级的发布/订阅消息传输协议,适合低带宽环境 WebSocket连接:实现实时双向通信 第三方物联网平台中转:通过如ThingsBoard、AWS IoT等平台收集数据,再同步到WordPress 在本教程中,我们将主要使用HTTP REST API方式,因为它与WordPress的集成最为直接,且大多数智能硬件都支持HTTP客户端库。
- Postman或Insomnia:用于测试API端点 Arduino IDE或PlatformIO:用于嵌入式设备编程 Node-RED(可选):可视化物联网编程工具,可用于数据预处理 Chart.js或D3.js:用于数据可视化 Vue.js或React(可选):用于构建交互式前端面板
-
- WordPress REST API提供了一套标准的、易于使用的接口,允许外部应用与WordPress进行数据交互。默认情况下,WordPress已经为文章、页面、用户等核心内容类型提供了API端点。 对于物联网数据,我们需要创建自定义端点来接收和存储设备数据。这可以通过两种方式实现: 使用register_rest_route函数创建全新端点 扩展现有端点(如文章类型)来存储设备数据
- 下面是一个完整的插件示例,用于创建接收物联网设备数据的API端点: <?php /** * Plugin Name: IoT Device Data Manager * Description: 接收和展示物联网设备数据的WordPress插件 * Version: 1.0.0 * Author: Your Name */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class IoT_Device_Manager { private $table_name; public function __construct() { global $wpdb; $this->table_name = $wpdb->prefix . 'iot_device_data'; // 注册激活钩子 register_activation_hook(__FILE__, array($this, 'create_database_table')); // 注册REST API路由 add_action('rest_api_init', array($this, 'register_rest_routes')); // 注册管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 注册短代码 add_shortcode('iot_dashboard', array($this, 'iot_dashboard_shortcode')); } // 创建数据库表 public function create_database_table() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} ( id mediumint(9) NOT NULL AUTO_INCREMENT, device_id varchar(100) NOT NULL, device_type varchar(50) NOT NULL, temperature decimal(5,2), humidity decimal(5,2), pressure decimal(7,2), pm25 int(11), voltage decimal(6,3), status varchar(20) DEFAULT 'active', created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX device_index (device_id), INDEX time_index (created_at) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } // 注册REST API路由 public function register_rest_routes() { // 接收设备数据的端点 register_rest_route('iot/v1', '/data', array( 'methods' => 'POST', 'callback' => array($this, 'handle_device_data'), 'permission_callback' => array($this, 'verify_device_token'), 'args' => array( 'device_id' => array( 'required' => true, 'validate_callback' => function($param) { return is_string($param) && strlen($param) <= 100; } ), 'temperature' => array( 'required' => false, 'validate_callback' => function($param) { return is_numeric($param) && $param >= -50 && $param <= 100; } ), 'humidity' => array( 'required' => false, 'validate_callback' => function($param) { return is_numeric($param) && $param >= 0 && $param <= 100; } ), // 其他参数... ) )); // 获取设备数据的端点 register_rest_route('iot/v1', '/data/(?P<device_id>[a-zA-Z0-9_-]+)', array( 'methods' => 'GET', 'callback' => array($this, 'get_device_data'), 'permission_callback' => function() { return current_user_can('edit_posts'); } )); } // 处理设备数据 public function handle_device_data($request) { global $wpdb; $parameters = $request->get_params(); // 验证必要参数 if (empty($parameters['device_id'])) { return new WP_Error('missing_device_id', '设备ID是必需的', array('status' => 400)); } // 准备插入数据 $data = array( 'device_id' => sanitize_text_field($parameters['device_id']), 'device_type' => sanitize_text_field($parameters['device_type'] ?? 'unknown'), 'temperature' => isset($parameters['temperature']) ? floatval($parameters['temperature']) : null, 'humidity' => isset($parameters['humidity']) ? floatval($parameters['humidity']) : null, 'pressure' => isset($parameters['pressure']) ? floatval($parameters['pressure']) : null, 'pm25' => isset($parameters['pm25']) ? intval($parameters['pm25']) : null, 'voltage' => isset($parameters['voltage']) ? floatval($parameters['voltage']) : null, 'status' => 'active' ); // 插入数据库 $result = $wpdb->insert($this->table_name, $data); if ($result === false) { return new WP_Error('db_insert_error', '数据插入失败', array('status' => 500)); } // 触发数据接收动作,供其他插件使用 do_action('iot_device_data_received', $data, $wpdb->insert_id); return rest_ensure_response(array( 'success' => true, 'message' => '数据接收成功', 'data_id' => $wpdb->insert_id, 'timestamp' => current_time('mysql') )); } // 验证设备令牌 public function verify_device_token($request) { // 从请求头获取API密钥 $api_key = $request->get_header('X-IoT-API-Key'); if (empty($api_key)) { return false; } // 验证API密钥(这里应该从数据库或配置中获取有效密钥) $valid_keys = get_option('iot_valid_api_keys', array()); return in_array($api_key, $valid_keys); } // 获取设备数据 public function get_device_data($request) { global $wpdb; $device_id = $request->get_param('device_id'); $limit = $request->get_param('limit') ? intval($request->get_param('limit')) : 100; $query = $wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE device_id = %s ORDER BY created_at DESC LIMIT %d", $device_id, $limit ); $results = $wpdb->get_results($query, ARRAY_A); if (empty($results)) { return rest_ensure_response(array( 'success' => false, 'message' => '未找到设备数据' )); } return rest_ensure_response(array( 'success' => true, 'data' => $results, 'count' => count($results) )); } // 添加管理菜单 public function add_admin_menu() { add_menu_page( '物联网设备管理', 'IoT设备', 'manage_options', 'iot-device-manager', array($this, 'display_admin_page'), 'dashicons-admin-generic', 30 ); add_submenu_page( 'iot-device-manager', '设备数据', '数据查看', 'manage_options', 'iot-device-data', array($this, 'display_device_data_page') ); } // 显示管理页面 public function display_admin_page() { ?> <div class="wrap"> <h1>物联网设备管理</h1> <div class="card"> <h2>API端点信息</h2> <p><strong>数据提交URL:</strong> <?php echo rest_url('iot/v1/data'); ?></p> <p><strong>请求方法:</strong> POST</p> <p><strong>内容类型:</strong> application/json</p> <h3>请求示例</h3> <pre> { "device_id": "sensor_001", "device_type": "environment", "temperature": 23.5, "humidity": 65.2, "pressure": 1013.25, "pm25": 35 } </pre> </div> </div> <?php } // 短代码显示仪表板 public function iot_dashboard_shortcode($atts) { $atts = shortcode_atts(array( 'device_id' => '', 'title' => '物联网设备数据面板', 'show_charts' => 'true' ), $atts, 'iot_dashboard'); // 生成唯一ID用于JavaScript $dashboard_id = 'iot-dashboard-' . uniqid(); ob_start(); ?> <div id="<?php echo esc_attr($dashboard_id); ?>" class="iot-dashboard"> <h3><?php echo esc_html($atts['title']); ?></h3> <div class="iot-stats-grid"> <div class="iot-stat-card"> <div class="stat-label">当前温度</div> <div class="stat-value temperature">--</div> <div class="stat-unit">°C</div> </div> <div class="iot-stat-card"> <div class="stat-label">当前湿度</div> <div class="stat-value humidity">--</div> <div class="stat-unit">%</div> </div> <div class="iot-stat-card"> <div class="stat-label">空气质量</div> <div class="stat-value pm25">--</div> <div class="stat-unit">PM2.5</div> </div> <div class="iot-stat-card"> <div class="stat-label">设备状态</div> <div class="stat-value status">--</div> <div class="stat-unit"></div> </div> </div> <?php if ($atts['show_charts'] === 'true') : ?> <div class="iot-charts-container"> <div class="chart-container"> <canvas id="<?php echo esc_attr($dashboard_id); ?>-temp-chart"></canvas> </div> <div class="chart-container"> <canvas id="<?php echo esc_attr($dashboard_id); ?>-humidity-chart"></canvas> </div> </div> <?php endif; ?> <div class="iot-data-table"> <table> <thead> <tr> <th>时间</th> <th>温度(°C)</th> <th>湿度(%)</th> <th>气压(hPa)</th> <th>PM2.5</th> </tr> </thead> <tbody> <!-- 数据将通过JavaScript动态加载 --> </tbody> </table> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // 初始化物联网仪表板 initIoTDashboard('<?php echo esc_js($dashboard_id); ?>', '<?php echo esc_js($atts['device_id']); ?>'); }); </script> <?php return ob_get_clean(); } } // 初始化插件 new IoT_Device_Manager(); // 添加前端样式和脚本 add_action('wp_enqueue_scripts', function() { if (has_shortcode(get_post()->post_content, 'iot_dashboard')) { // 引入Chart.js用于数据可视化 wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), '3.7.0', true); // 引入自定义脚本 wp_enqueue_script('iot-dashboard', plugin_dir_url(__FILE__) . 'js/iot-dashboard.js', array('jquery', 'chart-js'), '1.0.0', true); // 传递AJAX URL到前端 wp_localize_script('iot-dashboard', 'iot_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'rest_url' => rest_url('iot/v1/data/'), 'nonce' => wp_create_nonce('iot_nonce') )); // 添加样式 wp_enqueue_style('iot-dashboard-style', plugin_dir_url(__FILE__) . 'css/iot-dashboard.css'); } }); // 添加AJAX处理 add_action('wp_ajax_get_iot_data', 'handle_iot_data_ajax'); add_action('wp_ajax_nopriv_get_iot_data', 'handle_iot_data_ajax'); function handle_iot_data_ajax() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'iot_nonce')) { wp_die('权限验证失败'); } global $wpdb; $table_name = $wpdb->prefix . 'iot_device_data'; $device_id = sanitize_text_field($_POST['device_id']); $limit = intval($_POST['limit'] ?? 50); $data = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$table_name} WHERE device_id = %s ORDER BY created_at DESC LIMIT %d", $device_id, $limit ), ARRAY_A); wp_send_json_success($data); }
- // iot-dashboard.js function initIoTDashboard(dashboardId, deviceId) { const dashboard = document.getElementById(dashboardId); if (!dashboard) return; // 初始化图表 let tempChart = null; let humidityChart = null; // 获取数据并更新仪表板 function updateDashboard() { if (!deviceId) { console.error('设备ID未指定'); return; } // 使用REST API获取数据 fetch(`${iot_ajax.rest_url}${deviceId}?limit=50`) .then(response => response.json()) .then(data => { if (data.success && data.data.length > 0) { updateStats(data.data[0]); // 更新最新数据 updateCharts(data.data); // 更新图表 updateTable(data.data); // 更新表格 } }) .catch(error => console.error('获取数据失败:', error)); } // 更新统计数据 function updateStats(latestData) { const tempElement = dashboard.querySelector('.temperature'); const humidityElement = dashboard.querySelector('.humidity'); const pm25Element = dashboard.querySelector('.pm25'); const statusElement = dashboard.querySelector('.status'); if (tempElement && latestData.temperature !== null) { tempElement.textContent = latestData.temperature.toFixed(1); // 根据温度设置颜色 tempElement.style.color = getTemperatureColor(latestData.temperature); } if (humidityElement && latestData.humidity !== null) { humidityElement.textContent = latestData.humidity.toFixed(1); } if (pm25Element && latestData.pm25 !== null) { pm25Element.textContent = latestData.pm25; // 根据PM2.5设置颜色 pm25Element.style.color = getPM25Color(latestData.pm25); } statusElement && latestData.status) { statusElement.textContent = latestData.status; statusElement.className = `status status-${latestData.status}`; } } // 更新图表 function updateCharts(data) { // 反转数据,使时间从旧到新 const reversedData = [...data].reverse(); // 准备图表数据 const labels = reversedData.map(item => { const date = new Date(item.created_at); return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`; }); const temperatures = reversedData.map(item => item.temperature); const humidities = reversedData.map(item => item.humidity); // 温度图表 const tempCanvas = document.getElementById(`${dashboardId}-temp-chart`); if (tempCanvas) { if (tempChart) { tempChart.destroy(); } tempChart = new Chart(tempCanvas.getContext('2d'), { type: 'line', data: { labels: labels, datasets: [{ label: '温度 (°C)', data: temperatures, borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: '温度变化趋势' } }, scales: { y: { beginAtZero: false, title: { display: true, text: '温度 (°C)' } } } } }); } // 湿度图表 const humidityCanvas = document.getElementById(`${dashboardId}-humidity-chart`); if (humidityCanvas) { if (humidityChart) { humidityChart.destroy(); } humidityChart = new Chart(humidityCanvas.getContext('2d'), { type: 'line', data: { labels: labels, datasets: [{ label: '湿度 (%)', data: humidities, borderColor: 'rgb(54, 162, 235)', backgroundColor: 'rgba(54, 162, 235, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, plugins: { legend: { position: 'top', }, title: { display: true, text: '湿度变化趋势' } }, scales: { y: { beginAtZero: false, min: 0, max: 100, title: { display: true, text: '湿度 (%)' } } } } }); } } // 更新数据表格 function updateTable(data) { const tbody = dashboard.querySelector('.iot-data-table tbody'); if (!tbody) return; tbody.innerHTML = ''; data.forEach(item => { const row = document.createElement('tr'); const date = new Date(item.created_at); const timeString = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; row.innerHTML = ` <td>${timeString}</td> <td>${item.temperature !== null ? item.temperature.toFixed(1) : '--'}</td> <td>${item.humidity !== null ? item.humidity.toFixed(1) : '--'}</td> <td>${item.pressure !== null ? item.pressure.toFixed(1) : '--'}</td> <td>${item.pm25 !== null ? item.pm25 : '--'}</td> `; tbody.appendChild(row); }); } // 辅助函数:根据温度获取颜色 function getTemperatureColor(temp) { if (temp < 10) return '#3498db'; // 冷 - 蓝色 if (temp < 20) return '#2ecc71'; // 凉爽 - 绿色 if (temp < 28) return '#f1c40f'; // 舒适 - 黄色 if (temp < 35) return '#e67e22'; // 温暖 - 橙色 return '#e74c3c'; // 热 - 红色 } // 辅助函数:根据PM2.5获取颜色 function getPM25Color(pm25) { if (pm25 <= 35) return '#2ecc71'; // 优 - 绿色 if (pm25 <= 75) return '#f1c40f'; // 良 - 黄色 if (pm25 <= 115) return '#e67e22'; // 轻度污染 - 橙色 if (pm25 <= 150) return '#e74c3c'; // 中度污染 - 红色 return '#8e44ad'; // 重度污染 - 紫色 } // 初始加载数据 updateDashboard(); // 设置定时刷新(每30秒) setInterval(updateDashboard, 30000); // 添加手动刷新按钮 const refreshButton = document.createElement('button'); refreshButton.textContent = '刷新数据'; refreshButton.className = 'iot-refresh-btn'; refreshButton.onclick = updateDashboard; const header = dashboard.querySelector('h3'); if (header) { header.appendChild(refreshButton); } } #### 2.4 前端CSS样式 (iot-dashboard.css) / iot-dashboard.css /.iot-dashboard { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; padding: 20px; background: #f8f9fa; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .iot-dashboard h3 { margin-top: 0; color: #2c3e50; display: flex; justify-content: space-between; align-items: center; } .iot-stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; } .iot-stat-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); text-align: center; transition: transform 0.3s ease; } .iot-stat-card:hover { transform: translateY(-5px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .stat-label { font-size: 14px; color: #7f8c8d; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 1px; } .stat-value { font-size: 36px; font-weight: bold; color: #2c3e50; margin: 10px 0; transition: color 0.3s ease; } .stat-unit { font-size: 14px; color: #95a5a6; } .iot-charts-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin: 30px 0; } .chart-container { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); } .iot-data-table { margin-top: 30px; overflow-x: auto; } .iot-data-table table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 5px rgba(0,0,0,0.05); } .iot-data-table th { background: #34495e; color: white; padding: 15px; text-align: left; font-weight: 600; } .iot-data-table td { padding: 12px 15px; border-bottom: 1px solid #ecf0f1; } .iot-data-table tr:hover { background: #f8f9fa; } .iot-refresh-btn { background: #3498db; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background 0.3s ease; } .iot-refresh-btn:hover { background: #2980b9; } / 状态指示器 /.status-active { color: #27ae60; } .status-inactive { color: #e74c3c; } .status-warning { color: #f39c12; } / 响应式设计 /@media (max-width: 768px) { .iot-stats-grid { grid-template-columns: repeat(2, 1fr); } .iot-charts-container { grid-template-columns: 1fr; } } @media (max-width: 480px) { .iot-stats-grid { grid-template-columns: 1fr; } .iot-dashboard { padding: 10px; } } ### 第三部分:智能硬件端代码实现 #### 3.1 Arduino ESP8266/ESP32示例代码 // iot_sensor_to_wordpress.ino
在当今万物互联的时代,物联网设备正以前所未有的速度渗透到我们生活的方方面面。从智能家居的温度传感器到工业环境的监控设备,海量数据正源源不断地产生。然而,这些数据的价值往往受限于展示和分析的平台。与此同时,WordPress作为全球最流行的内容管理系统,已经超越了简单的博客平台,发展成为功能强大的网站构建工具。本教程将深入探讨如何将这两者结合——通过WordPress的代码二次开发,连接智能硬件并创建专业的物联网设备数据面板,同时实现多种常用互联网小工具功能。
在开始集成物联网设备之前,我们需要建立一个适合开发的WordPress环境。建议使用本地开发环境如XAMPP、MAMP或Local by Flywheel,这些工具可以快速搭建包含Apache、MySQL和PHP的完整环境。
对于物联网开发,我们需要确保WordPress安装满足以下条件:
- WordPress 5.0及以上版本
- PHP 7.4及以上(建议8.0+以获得更好性能)
- 启用REST API功能
- 安装并激活调试插件,如Query Monitor和Debug Bar
物联网设备与WordPress的通信通常通过以下几种方式实现:
- HTTP/HTTPS请求:设备直接向WordPress REST API端点发送数据
- MQTT协议:轻量级的发布/订阅消息传输协议,适合低带宽环境
- WebSocket连接:实现实时双向通信
- 第三方物联网平台中转:通过如ThingsBoard、AWS IoT等平台收集数据,再同步到WordPress
在本教程中,我们将主要使用HTTP REST API方式,因为它与WordPress的集成最为直接,且大多数智能硬件都支持HTTP客户端库。
- Postman或Insomnia:用于测试API端点
- Arduino IDE或PlatformIO:用于嵌入式设备编程
- Node-RED(可选):可视化物联网编程工具,可用于数据预处理
- Chart.js或D3.js:用于数据可视化
- Vue.js或React(可选):用于构建交互式前端面板
WordPress REST API提供了一套标准的、易于使用的接口,允许外部应用与WordPress进行数据交互。默认情况下,WordPress已经为文章、页面、用户等核心内容类型提供了API端点。
对于物联网数据,我们需要创建自定义端点来接收和存储设备数据。这可以通过两种方式实现:
- 使用
register_rest_route函数创建全新端点 - 扩展现有端点(如文章类型)来存储设备数据
下面是一个完整的插件示例,用于创建接收物联网设备数据的API端点:
<?php
/**
* Plugin Name: IoT Device Data Manager
* Description: 接收和展示物联网设备数据的WordPress插件
* Version: 1.0.0
* Author: Your Name
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class IoT_Device_Manager {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'iot_device_data';
// 注册激活钩子
register_activation_hook(__FILE__, array($this, 'create_database_table'));
// 注册REST API路由
add_action('rest_api_init', array($this, 'register_rest_routes'));
// 注册管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 注册短代码
add_shortcode('iot_dashboard', array($this, 'iot_dashboard_shortcode'));
}
// 创建数据库表
public function create_database_table() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
id mediumint(9) NOT NULL AUTO_INCREMENT,
device_id varchar(100) NOT NULL,
device_type varchar(50) NOT NULL,
temperature decimal(5,2),
humidity decimal(5,2),
pressure decimal(7,2),
pm25 int(11),
voltage decimal(6,3),
status varchar(20) DEFAULT 'active',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX device_index (device_id),
INDEX time_index (created_at)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
// 注册REST API路由
public function register_rest_routes() {
// 接收设备数据的端点
register_rest_route('iot/v1', '/data', array(
'methods' => 'POST',
'callback' => array($this, 'handle_device_data'),
'permission_callback' => array($this, 'verify_device_token'),
'args' => array(
'device_id' => array(
'required' => true,
'validate_callback' => function($param) {
return is_string($param) && strlen($param) <= 100;
}
),
'temperature' => array(
'required' => false,
'validate_callback' => function($param) {
return is_numeric($param) && $param >= -50 && $param <= 100;
}
),
'humidity' => array(
'required' => false,
'validate_callback' => function($param) {
return is_numeric($param) && $param >= 0 && $param <= 100;
}
),
// 其他参数...
)
));
// 获取设备数据的端点
register_rest_route('iot/v1', '/data/(?P<device_id>[a-zA-Z0-9_-]+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_device_data'),
'permission_callback' => function() {
return current_user_can('edit_posts');
}
));
}
// 处理设备数据
public function handle_device_data($request) {
global $wpdb;
$parameters = $request->get_params();
// 验证必要参数
if (empty($parameters['device_id'])) {
return new WP_Error('missing_device_id', '设备ID是必需的', array('status' => 400));
}
// 准备插入数据
$data = array(
'device_id' => sanitize_text_field($parameters['device_id']),
'device_type' => sanitize_text_field($parameters['device_type'] ?? 'unknown'),
'temperature' => isset($parameters['temperature']) ? floatval($parameters['temperature']) : null,
'humidity' => isset($parameters['humidity']) ? floatval($parameters['humidity']) : null,
'pressure' => isset($parameters['pressure']) ? floatval($parameters['pressure']) : null,
'pm25' => isset($parameters['pm25']) ? intval($parameters['pm25']) : null,
'voltage' => isset($parameters['voltage']) ? floatval($parameters['voltage']) : null,
'status' => 'active'
);
// 插入数据库
$result = $wpdb->insert($this->table_name, $data);
if ($result === false) {
return new WP_Error('db_insert_error', '数据插入失败', array('status' => 500));
}
// 触发数据接收动作,供其他插件使用
do_action('iot_device_data_received', $data, $wpdb->insert_id);
return rest_ensure_response(array(
'success' => true,
'message' => '数据接收成功',
'data_id' => $wpdb->insert_id,
'timestamp' => current_time('mysql')
));
}
// 验证设备令牌
public function verify_device_token($request) {
// 从请求头获取API密钥
$api_key = $request->get_header('X-IoT-API-Key');
if (empty($api_key)) {
return false;
}
// 验证API密钥(这里应该从数据库或配置中获取有效密钥)
$valid_keys = get_option('iot_valid_api_keys', array());
return in_array($api_key, $valid_keys);
}
// 获取设备数据
public function get_device_data($request) {
global $wpdb;
$device_id = $request->get_param('device_id');
$limit = $request->get_param('limit') ? intval($request->get_param('limit')) : 100;
$query = $wpdb->prepare(
"SELECT * FROM {$this->table_name} WHERE device_id = %s ORDER BY created_at DESC LIMIT %d",
$device_id,
$limit
);
$results = $wpdb->get_results($query, ARRAY_A);
if (empty($results)) {
return rest_ensure_response(array(
'success' => false,
'message' => '未找到设备数据'
));
}
return rest_ensure_response(array(
'success' => true,
'data' => $results,
'count' => count($results)
));
}
// 添加管理菜单
public function add_admin_menu() {
add_menu_page(
'物联网设备管理',
'IoT设备',
'manage_options',
'iot-device-manager',
array($this, 'display_admin_page'),
'dashicons-admin-generic',
30
);
add_submenu_page(
'iot-device-manager',
'设备数据',
'数据查看',
'manage_options',
'iot-device-data',
array($this, 'display_device_data_page')
);
}
// 显示管理页面
public function display_admin_page() {
?>
<div class="wrap">
<h1>物联网设备管理</h1>
<div class="card">
<h2>API端点信息</h2>
<p><strong>数据提交URL:</strong> <?php echo rest_url('iot/v1/data'); ?></p>
<p><strong>请求方法:</strong> POST</p>
<p><strong>内容类型:</strong> application/json</p>
<h3>请求示例</h3>
<pre>
{
"device_id": "sensor_001",
"device_type": "environment",
"temperature": 23.5,
"humidity": 65.2,
"pressure": 1013.25,
"pm25": 35
}
</pre>
</div>
</div>
<?php
}
// 短代码显示仪表板
public function iot_dashboard_shortcode($atts) {
$atts = shortcode_atts(array(
'device_id' => '',
'title' => '物联网设备数据面板',
'show_charts' => 'true'
), $atts, 'iot_dashboard');
// 生成唯一ID用于JavaScript
$dashboard_id = 'iot-dashboard-' . uniqid();
ob_start();
?>
<div id="<?php echo esc_attr($dashboard_id); ?>" class="iot-dashboard">
<h3><?php echo esc_html($atts['title']); ?></h3>
<div class="iot-stats-grid">
<div class="iot-stat-card">
<div class="stat-label">当前温度</div>
<div class="stat-value temperature">--</div>
<div class="stat-unit">°C</div>
</div>
<div class="iot-stat-card">
<div class="stat-label">当前湿度</div>
<div class="stat-value humidity">--</div>
<div class="stat-unit">%</div>
</div>
<div class="iot-stat-card">
<div class="stat-label">空气质量</div>
<div class="stat-value pm25">--</div>
<div class="stat-unit">PM2.5</div>
</div>
<div class="iot-stat-card">
<div class="stat-label">设备状态</div>
<div class="stat-value status">--</div>
<div class="stat-unit"></div>
</div>
</div>
<?php if ($atts['show_charts'] === 'true') : ?>
<div class="iot-charts-container">
<div class="chart-container">
<canvas id="<?php echo esc_attr($dashboard_id); ?>-temp-chart"></canvas>
</div>
<div class="chart-container">
<canvas id="<?php echo esc_attr($dashboard_id); ?>-humidity-chart"></canvas>
</div>
</div>
<?php endif; ?>
<div class="iot-data-table">
<table>
<thead>
<tr>
<th>时间</th>
<th>温度(°C)</th>
<th>湿度(%)</th>
<th>气压(hPa)</th>
<th>PM2.5</th>
</tr>
</thead>
<tbody>
<!-- 数据将通过JavaScript动态加载 -->
</tbody>
</table>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 初始化物联网仪表板
initIoTDashboard('<?php echo esc_js($dashboard_id); ?>', '<?php echo esc_js($atts['device_id']); ?>');
});
</script>
<?php
return ob_get_clean();
}
}
// 初始化插件
new IoT_Device_Manager();
// 添加前端样式和脚本
add_action('wp_enqueue_scripts', function() {
if (has_shortcode(get_post()->post_content, 'iot_dashboard')) {
// 引入Chart.js用于数据可视化
wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), '3.7.0', true);
// 引入自定义脚本
wp_enqueue_script('iot-dashboard', plugin_dir_url(__FILE__) . 'js/iot-dashboard.js', array('jquery', 'chart-js'), '1.0.0', true);
// 传递AJAX URL到前端
wp_localize_script('iot-dashboard', 'iot_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'rest_url' => rest_url('iot/v1/data/'),
'nonce' => wp_create_nonce('iot_nonce')
));
// 添加样式
wp_enqueue_style('iot-dashboard-style', plugin_dir_url(__FILE__) . 'css/iot-dashboard.css');
}
});
// 添加AJAX处理
add_action('wp_ajax_get_iot_data', 'handle_iot_data_ajax');
add_action('wp_ajax_nopriv_get_iot_data', 'handle_iot_data_ajax');
function handle_iot_data_ajax() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'iot_nonce')) {
wp_die('权限验证失败');
}
global $wpdb;
$table_name = $wpdb->prefix . 'iot_device_data';
$device_id = sanitize_text_field($_POST['device_id']);
$limit = intval($_POST['limit'] ?? 50);
$data = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$table_name} WHERE device_id = %s ORDER BY created_at DESC LIMIT %d",
$device_id,
$limit
), ARRAY_A);
wp_send_json_success($data);
}
// iot-dashboard.js
function initIoTDashboard(dashboardId, deviceId) {
const dashboard = document.getElementById(dashboardId);
if (!dashboard) return;
// 初始化图表
let tempChart = null;
let humidityChart = null;
// 获取数据并更新仪表板
function updateDashboard() {
if (!deviceId) {
console.error('设备ID未指定');
return;
}
// 使用REST API获取数据
fetch(`${iot_ajax.rest_url}${deviceId}?limit=50`)
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
updateStats(data.data[0]); // 更新最新数据
updateCharts(data.data); // 更新图表
updateTable(data.data); // 更新表格
}
})
.catch(error => console.error('获取数据失败:', error));
}
// 更新统计数据
function updateStats(latestData) {
const tempElement = dashboard.querySelector('.temperature');
const humidityElement = dashboard.querySelector('.humidity');
const pm25Element = dashboard.querySelector('.pm25');
const statusElement = dashboard.querySelector('.status');
if (tempElement && latestData.temperature !== null) {
tempElement.textContent = latestData.temperature.toFixed(1);
// 根据温度设置颜色
tempElement.style.color = getTemperatureColor(latestData.temperature);
}
if (humidityElement && latestData.humidity !== null) {
humidityElement.textContent = latestData.humidity.toFixed(1);
}
if (pm25Element && latestData.pm25 !== null) {
pm25Element.textContent = latestData.pm25;
// 根据PM2.5设置颜色
pm25Element.style.color = getPM25Color(latestData.pm25);
}
// iot-dashboard.js
function initIoTDashboard(dashboardId, deviceId) {
const dashboard = document.getElementById(dashboardId);
if (!dashboard) return;
// 初始化图表
let tempChart = null;
let humidityChart = null;
// 获取数据并更新仪表板
function updateDashboard() {
if (!deviceId) {
console.error('设备ID未指定');
return;
}
// 使用REST API获取数据
fetch(`${iot_ajax.rest_url}${deviceId}?limit=50`)
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
updateStats(data.data[0]); // 更新最新数据
updateCharts(data.data); // 更新图表
updateTable(data.data); // 更新表格
}
})
.catch(error => console.error('获取数据失败:', error));
}
// 更新统计数据
function updateStats(latestData) {
const tempElement = dashboard.querySelector('.temperature');
const humidityElement = dashboard.querySelector('.humidity');
const pm25Element = dashboard.querySelector('.pm25');
const statusElement = dashboard.querySelector('.status');
if (tempElement && latestData.temperature !== null) {
tempElement.textContent = latestData.temperature.toFixed(1);
// 根据温度设置颜色
tempElement.style.color = getTemperatureColor(latestData.temperature);
}
if (humidityElement && latestData.humidity !== null) {
humidityElement.textContent = latestData.humidity.toFixed(1);
}
if (pm25Element && latestData.pm25 !== null) {
pm25Element.textContent = latestData.pm25;
// 根据PM2.5设置颜色
pm25Element.style.color = getPM25Color(latestData.pm25);
}
statusElement && latestData.status) {
statusElement.textContent = latestData.status;
statusElement.className = `status status-${latestData.status}`;
}
}
// 更新图表
function updateCharts(data) {
// 反转数据,使时间从旧到新
const reversedData = [...data].reverse();
// 准备图表数据
const labels = reversedData.map(item => {
const date = new Date(item.created_at);
return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
});
const temperatures = reversedData.map(item => item.temperature);
const humidities = reversedData.map(item => item.humidity);
// 温度图表
const tempCanvas = document.getElementById(`${dashboardId}-temp-chart`);
if (tempCanvas) {
if (tempChart) {
tempChart.destroy();
}
tempChart = new Chart(tempCanvas.getContext('2d'), {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '温度 (°C)',
data: temperatures,
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '温度变化趋势'
}
},
scales: {
y: {
beginAtZero: false,
title: {
display: true,
text: '温度 (°C)'
}
}
}
}
});
}
// 湿度图表
const humidityCanvas = document.getElementById(`${dashboardId}-humidity-chart`);
if (humidityCanvas) {
if (humidityChart) {
humidityChart.destroy();
}
humidityChart = new Chart(humidityCanvas.getContext('2d'), {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '湿度 (%)',
data: humidities,
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '湿度变化趋势'
}
},
scales: {
y: {
beginAtZero: false,
min: 0,
max: 100,
title: {
display: true,
text: '湿度 (%)'
}
}
}
}
});
}
}
// 更新数据表格
function updateTable(data) {
const tbody = dashboard.querySelector('.iot-data-table tbody');
if (!tbody) return;
tbody.innerHTML = '';
data.forEach(item => {
const row = document.createElement('tr');
const date = new Date(item.created_at);
const timeString = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
row.innerHTML = `
<td>${timeString}</td>
<td>${item.temperature !== null ? item.temperature.toFixed(1) : '--'}</td>
<td>${item.humidity !== null ? item.humidity.toFixed(1) : '--'}</td>
<td>${item.pressure !== null ? item.pressure.toFixed(1) : '--'}</td>
<td>${item.pm25 !== null ? item.pm25 : '--'}</td>
`;
tbody.appendChild(row);
});
}
// 辅助函数:根据温度获取颜色
function getTemperatureColor(temp) {
if (temp < 10) return '#3498db'; // 冷 - 蓝色
if (temp < 20) return '#2ecc71'; // 凉爽 - 绿色
if (temp < 28) return '#f1c40f'; // 舒适 - 黄色
if (temp < 35) return '#e67e22'; // 温暖 - 橙色
return '#e74c3c'; // 热 - 红色
}
// 辅助函数:根据PM2.5获取颜色
function getPM25Color(pm25) {
if (pm25 <= 35) return '#2ecc71'; // 优 - 绿色
if (pm25 <= 75) return '#f1c40f'; // 良 - 黄色
if (pm25 <= 115) return '#e67e22'; // 轻度污染 - 橙色
if (pm25 <= 150) return '#e74c3c'; // 中度污染 - 红色
return '#8e44ad'; // 重度污染 - 紫色
}
// 初始加载数据
updateDashboard();
// 设置定时刷新(每30秒)
setInterval(updateDashboard, 30000);
// 添加手动刷新按钮
const refreshButton = document.createElement('button');
refreshButton.textContent = '刷新数据';
refreshButton.className = 'iot-refresh-btn';
refreshButton.onclick = updateDashboard;
const header = dashboard.querySelector('h3');
if (header) {
header.appendChild(refreshButton);
}
}
#### 2.4 前端CSS样式 (iot-dashboard.css)
/ iot-dashboard.css /
.iot-dashboard {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.iot-dashboard h3 {
margin-top: 0;
color: #2c3e50;
display: flex;
justify-content: space-between;
align-items: center;
}
.iot-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.iot-stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
text-align: center;
transition: transform 0.3s ease;
}
.iot-stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.stat-label {
font-size: 14px;
color: #7f8c8d;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.stat-value {
font-size: 36px;
font-weight: bold;
color: #2c3e50;
margin: 10px 0;
transition: color 0.3s ease;
}
.stat-unit {
font-size: 14px;
color: #95a5a6;
}
.iot-charts-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: 30px 0;
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.iot-data-table {
margin-top: 30px;
overflow-x: auto;
}
.iot-data-table table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.iot-data-table th {
background: #34495e;
color: white;
padding: 15px;
text-align: left;
font-weight: 600;
}
.iot-data-table td {
padding: 12px 15px;
border-bottom: 1px solid #ecf0f1;
}
.iot-data-table tr:hover {
background: #f8f9fa;
}
.iot-refresh-btn {
background: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
}
.iot-refresh-btn:hover {
background: #2980b9;
}
/ 状态指示器 /
.status-active {
color: #27ae60;
}
.status-inactive {
color: #e74c3c;
}
.status-warning {
color: #f39c12;
}
/ 响应式设计 /
@media (max-width: 768px) {
.iot-stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.iot-charts-container {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.iot-stats-grid {
grid-template-columns: 1fr;
}
.iot-dashboard {
padding: 10px;
}
}
### 第三部分:智能硬件端代码实现
#### 3.1 Arduino ESP8266/ESP32示例代码
// iot_sensor_to_wordpress.ino
// #include <WiFi.h> // 对于ESP32
// WiFi配置
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
// WordPress站点配置
const char* wordpress_host = "你的网站域名";
const int wordpress_port = 443; // HTTPS端口
const char* api_endpoint = "/wp-json/iot/v1/data";
const char* api_key = "你的API密钥";
// 传感器配置
DHT dht(DHTPIN, DHTTYPE);
// 设备ID(每个设备唯一)
const String device_id = "sensor_001";
const String device_type = "environment";
// 定时器
unsigned long previousMillis = 0;
const long interval = 30000; // 30秒发送一次数据
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("物联网设备启动中...");
// 初始化传感器
dht.begin();
// 连接WiFi
connectToWiFi();
Serial.println("设备准备就绪");
}
void loop() {
unsigned long currentMillis = millis();
// 定时发送数据
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// 读取传感器数据
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// 检查读取是否成功
if (isnan(temperature) || isnan(humidity)) {
Serial.println("读取传感器数据失败");
return;
}
// 发送数据到WordPress
sendToWordPress(temperature, humidity);
}
// 其他任务...
delay(1000);
}
void connectToWiFi() {
Serial.print("连接WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("nWiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("nWiFi连接失败");
}
}
void sendToWordPress(float temperature, float humidity) {
Serial.println("准备发送数据到WordPress...");
// 创建JSON数据
DynamicJsonDocument doc(256);
doc["device_id"] = device_id;
doc["device_type"] = device_type;
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["voltage"] = getBatteryVoltage(); // 假设有电池电压读取函数
String jsonData;
serializeJson(doc, jsonData);
// 使用WiFiClientSecure进行HTTPS连接
WiFiClientSecure client;
client.setInsecure(); // 跳过证书验证(生产环境应使用证书验证)
if (!client.connect(wordpress_host, wordpress_port)) {
Serial.println("连接WordPress失败");
return;
}
// 构建HTTP请求
String request = String("POST ") + api_endpoint + " HTTP/1.1rn" +
"Host: " + wordpress_host + "rn" +
"User-Agent: IoT-Device/1.0rn" +
"X-IoT-API-Key: " + api_key + "rn" +
"Content-Type: application/jsonrn" +
"Content-Length: " + jsonData.length() + "rn" +
"Connection: closernrn" +
jsonData;
// 发送请求
client.print(request);
// 等待响应
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println("请求超时");
client.stop();
return;
}
}
// 读取响应
while (client.available()) {
String line = client.readStringUntil('r');
Serial.print(line);
}
Serial.println("n数据发送完成");
client.stop();
}
float getBatteryVoltage() {
// 模拟读取电池电压(实际实现取决于硬件)
return 3.7; // 示例值
}
#### 3.2 Raspberry Pi Python示例代码
import requests
import json
import time
import logging
from datetime import datetime
import board
import adafruit_dht # 对于DHT传感器
import psutil # 用于系统监控
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class WordPressIoTClient:
def __init__(self, config):
self.config = config
self.device_id = config['device_id']
self.api_url = f"https://{config['wordpress_host']}/wp-json/iot/v1/data"
self.headers = {
'X-IoT-API-Key': config['api_key'],
'Content-Type': 'application/json'
}
# 初始化传感器
self.init_sensors()
def init_sensors(self):
"""初始化传感器"""
try:
# DHT22温湿度传感器
self.dht_device = adafruit_dht.DHT22(board.D4)
logger.info("DHT22传感器初始化成功")
except Exception as e:
logger.error(f"传感器初始化失败: {e}")
self.dht_device = None
def read_sensor_data(self):
"""读取传感器数据"""
data = {
'device_id': self.device_id,
'device_type': 'raspberry_pi',
'timestamp': datetime.now().isoformat()
}
# 读取温湿度
if self.dht_device:
try:
temperature = self.dht_device.temperature
humidity = self.dht_device.humidity
if temperature is not None and humidity is not None:
data['temperature'] = round(temperature, 2)
data['humidity'] = round(humidity, 2)
else:
logger.warning("读取传感器数据失败")
except RuntimeError as e:
logger.error(f"读取传感器错误: {e}")
# 读取系统信息
data['cpu_usage'] = psutil.cpu_percent()
data['memory_usage'] = psutil.virtual_memory().percent
data['disk_usage'] = psutil.disk_usage('/').percent
# 读取GPIO状态(示例)
data['gpio_status'] = self.read_gpio_status()
return data
def read_gpio_status(self):
"""读取GPIO状态"""
# 这里可以添加实际的GPIO读取逻辑
return {
'pin_17': 1,
'pin_18': 0,
'pin_27': 1
}
def send_data(self, data):
"""发送数据到WordPress"""
try:
response = requests.post(
self.api_url,
headers=self.headers,
json=data,
timeout=10
)
if response.status_code == 200:
logger.info(f"数据发送成功: {response.json()}")
return True
else:
logger.error(f"数据发送失败: {response.status_code} - {response.text}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"请求异常: {e}")
return False
def run(self, interval=30):
"""主循环"""
logger


