1. 项目概述与核心价值家里装了太阳能光伏板的朋友估计都琢磨过同一个问题白天发的电用不完眼睁睁看着它卖回给电网价格可能只有自用电价的一半甚至更低晚上没太阳了又要从电网买高价电来用。这种“发电时用不完用电时不够发”的错配让光伏系统的经济性大打折扣。我自己家装了8千瓦的光伏系统后就一直在想能不能让那些“可调节”的大功率电器——比如电热水器、充电桩、蓄热电暖器——只在光伏发电有富余的时候工作实现真正的“自发自用余电不上网”这就是今天要聊的“基于太阳能发电的智能家居能耗控制”方案的核心思路。它不是什么高深莫测的黑科技而是一套非常务实、可落地的自动化系统。其核心逻辑很简单实时比较“光伏发电功率”和“家庭实时用电功率”当发电有足够富余时自动开启预设的大功率电器消耗掉这部分多余的电能当发电不足或家庭用电激增时则自动关闭该电器避免从电网购电。我选择的硬件核心是Shelly EM这款智能电能监测模块。它自带两个电流互感器CT钳可以同时、独立地测量两路电路的实时功率完美契合我们“一路测全家用电一路测光伏发电”的需求。软件逻辑部分则用Node.js写一个轻量级的后台服务定期从 Shelly EM 读取数据进行计算判断并通过网络控制一个智能开关如 Shelly 1来操作电器。这套方案特别适合有一定动手能力、希望最大化光伏自发自用率、降低电费的家庭用户或极客。它不依赖复杂的家庭自动化中枢如 Home Assistant直接通过简单的脚本实现核心逻辑稳定且响应迅速。接下来我将从硬件接线、数据获取、逻辑编写到部署优化的全流程拆解每一个步骤和背后的原理。2. 系统架构与硬件选型解析2.1 整体工作流程设计在动手之前我们需要把整个系统的工作流想清楚。一个健壮的自动化系统其流程必须是清晰且闭环的。数据采集Shelly EM 通过两个CT钳分别钳在家庭总进线和光伏逆变器输出线上以每秒数次的频率测量实时功率单位瓦特并通过Wi-Fi将数据上传至本地网络或Shelly云。数据获取与计算Node.js 服务以固定时间间隔例如每60秒通过HTTP请求从 Shelly EM 的API接口获取最新的功率数据。计算逻辑为可用富余功率 光伏发电功率 - 家庭总用电功率。逻辑判断与执行如果可用富余功率 目标电器额定功率 安全阈值则向智能开关发送“开启”指令。如果可用富余功率 0即家庭用电已超过光伏发电开始从电网取电则向智能开关发送“关闭”指令。如果富余功率介于0和目标电器功率之间则保持开关现有状态不变。设备控制智能开关如 Shelly 1接收到指令后控制其继电器吸合或断开从而接通或切断目标电器的电源。这个流程的核心在于“可用富余功率”的计算。这里有一个关键点Shelly EM 测量功率时是有方向的。通常我们将“用电”定义为正功率“发电”定义为负功率。所以如果光伏板正在发电其测量值会是一个负数。在我们的计算中需要将其转换为正数来表示“发电量”。2.2 硬件清单与选型考量核心硬件Shelly EM功能双通道电能监测支持实时功率、电压、电流、电量统计Wi-Fi连接提供本地和云端API。选型理由相比单通道的电能监测模块双通道的Shelly EM可以同时独立监测两路电流无需额外设备接线和逻辑都更简洁。其API文档完善社区支持好是完成此项目的性价比之选。关键配件务必确认包装内包含两个电流互感器CT钳。根据你家总进线和逆变器输出线的电流大小选择合适量程家庭用户一般选择50A或100A的规格足够。我的进线是63A空开使用50A的CT钳完全没问题但要注意留有余量。执行硬件Wi-Fi智能开关选项1推荐Shelly 1或Shelly 1PM。1PM自带功率测量可以额外验证被控电器的实际功耗但本项目不是必须。选择Shelly系列的好处是生态统一控制API类似。选项2其他支持HTTP/API控制的智能插座或开关如Sonoff Basic需刷Tasmota固件、TP-Link Kasa系列等。你需要事先查明其远程控制的URL地址。安全警告被控电器功率必须严格小于智能开关继电器的额定功率Shelly 1是16A。控制电热水器、电暖器等大功率设备前务必核对电器铭牌上的额定电流和功率。辅助硬件网络稳定的2.4GHz Wi-Fi网络。Shelly设备对5GHz支持不佳务必确保安装位置2.4GHz信号强度良好。供电为Shelly EM和Shelly 1提供220V电源。通常可以从被控电器所在的插座或配电箱取电。安装工具螺丝刀、电工胶布、可能需要的线缆等。注意电气安全是第一位的所有接线操作必须在断开总闸或相应回路空开的前提下进行。如果你对家庭电路不熟悉强烈建议请专业电工协助完成硬件安装部分。3. 硬件安装与Shelly EM配置详解3.1 电路接线与CT钳安装这是整个项目最具实操性也最需要谨慎的一步。错误的接线可能导致数据错误甚至设备损坏。第一步断电与准备关闭家庭配电箱的总开关用验电笔确认无电后再操作。规划好Shelly EM的安装位置最好靠近配电箱方便取电和布线。第二步为Shelly EM供电找到一条稳定的220V电源线可以从配电箱的一个空闲空开下引或从附近一个常通电的插座引将其火线L接入Shelly EM的L端子零线N接入N端子。这一步是给设备本身供电。第三步安装CT钳1监测家庭总用电目标线路找到从电网接入你家电表再进入你家配电箱总开关的那根火线。通常是一根较粗的导线。安装方法将第一个CT钳对应Shelly EM的P1通道套在这根火线上。CT钳上通常有一个箭头标识箭头方向应指向用电负载即指向你家的配电箱。这是保证功率测量符号正确的关键。接线将CT钳的引出线两根细线连接到Shelly EM上标有P1和P1-的端子。通常不分正负但如果后续数据符号不对可以交换这两根线。第四步安装CT钳2监测光伏发电目标线路找到从光伏逆变器交流输出端接入你家配电箱的那根火线。安装方法将第二个CT钳对应P2通道套在这根线上。此处的箭头方向应指向电网方向即从逆变器指向配电箱/电网。这是因为从逆变器的角度看它是在向电网/家庭负载“输出”电能。接线将CT钳引出线接入Shelly EM的P2和P2-端子。重要概念澄清有朋友问CT钳是夹在直流侧太阳能板到逆变器还是交流侧逆变器输出。必须夹在交流侧因为Shelly EM只能测量交流电参数。我们监测的是逆变器之后、送入家庭电网的“可用交流电能”。第五步安装执器Shelly 1将Shelly 1串联到你要控制的电器如电暖器的供电回路中。即墙壁插座的火线 - Shelly 1的L端子供电入Shelly 1的O端子开关出 - 电暖器插头的火线零线直接贯通。为Shelly 1本身供电的N端子也需要接上零线。确保电暖器本身的物理开关处于打开状态其通断将由Shelly 1的继电器控制。3.2 Shelly设备网络配置与数据验证所有硬件接好后闭合总开关上电。手机App配置下载“Shelly”官方App。按照提示将Shelly EM和Shelly 1分别接入你的Wi-Fi网络。建议为它们设置静态IP或在路由器中做DHCP保留方便Node.js服务稳定访问。验证数据方向打开Shelly App查看Shelly EM的设备页面。你应该能看到两个通道的实时功率。理想情况当家里有负载用电光伏不发电时比如晚上P1家庭用电显示为正功率如500WP2光伏显示为0或接近0。当光伏发电且发电量大于家庭用电时P1可能显示一个较小的正数家庭基础用电而P2应显示为一个负数如-3000W。这个负数正是我们需要的“光伏发电功率”。如果符号相反如果P2显示为正数说明CT钳2的箭头方向反了或者接线反了。最安全的校正方法是在断电后将CT钳2的物理方向旋转180度即箭头指向反方向这比交换接线端子更可靠。记录关键参数在Shelly App中进入Shelly EM的设备设置找到“设备信息”记录下设备ID一串字母数字组合如AABBCCDDEEFF。设备通道通常是0。云端访问密钥在App的“用户设置”里可以生成一个“API密钥”Auth Key。同时注意你的服务器地址如果你使用Shelly云服务通常是https://shelly-XX-eu.shelly.cloudXX代表地区编号。更推荐使用本地局域网API地址为http://[Shelly-EM的本地IP]这样速度更快不依赖外网。4. Node.js控制逻辑实现与代码精讲硬件就绪后我们开始编写“大脑”——Node.js脚本。我将用一个增强版的脚本来演示它包含错误处理、状态防抖和本地API调用。4.1 项目初始化与依赖首先确保你的电脑或服务器比如树莓派上安装了Node.js。新建一个项目目录并初始化mkdir solar-auto-control cd solar-auto-control npm init -y我们主要使用node-fetch库来发起HTTP请求。安装它npm install node-fetch4.2 核心控制脚本详解创建一个名为solar-controller.js的文件。下面我逐段解释代码// 引入 fetch 库用于调用API import fetch from node-fetch; // 配置区域 // 方式一使用Shelly本地API推荐速度快不依赖互联网 const SHELLY_EM_IP 192.168.1.100; // 替换为你的Shelly EM本地IP const SHELLY_EM_URL http://${SHELLY_EM_IP}/rpc; // 方式二使用Shelly云端API备用需要外网 // const SHELLY_CLOUD_SERVER https://shelly-XX-eu.shelly.cloud; // const SHELLY_AUTH_KEY 你的云端AuthKey; const SHELLY_EM_ID 你的设备ID; // 从App中获取 const SHELLY_EM_CHANNEL 0; // 通常是0 // 智能开关的控制URL以Shelly 1为例 const SWITCH_ON_URL http://192.168.1.101/relay/0?turnon; // 替换为你的Shelly 1 IP const SWITCH_OFF_URL http://192.168.1.101/relay/0?turnoff; // 被控电器的额定功率瓦特务必填写准确并考虑启动电流 const DEVICE_RATED_POWER 2000; // 例如2000W 的电暖器 // 动作迟滞阈值防止在临界点频繁开关 const HYSTERESIS_THRESHOLD 100; // 单位瓦特 // 检查间隔毫秒 const CHECK_INTERVAL_MS 60 * 1000; // 60秒 // 全局状态记录用于防抖和日志 let lastSwitchState unknown; // on, off, unknown // 配置结束 配置要点解析本地API vs 云端API优先使用本地IP访问。你可以在路由器管理页面或Shelly App中查看设备的IP地址。本地访问延迟极低毫秒级且断网也能工作。设备功率DEVICE_RATED_POWER务必填写电器铭牌上的额定功率。对于电热类设备运行功率通常很稳定。对于电机类如空调压缩机启动功率可能数倍于额定功率本项目逻辑可能不适用需特别谨慎。迟滞阈值HYSTERESIS_THRESHOLD非常重要。假设没有迟滞当富余功率在2000W上下轻微波动时开关会在一分钟内反复动作。设置一个100W的迟滞意味着开启条件变为富余功率 (2000 100)W关闭条件变为富余功率 0W。这样就形成了一个“缓冲区”避免了频繁开关保护电器和继电器。/** * 控制智能开关的函数 * param {string} action - on 或 off */ async function controlSwitch(action) { const url action on ? SWITCH_ON_URL : SWITCH_OFF_URL; try { const response await fetch(url); const text await response.text(); console.log([${new Date().toLocaleTimeString()}] 开关 ${action} 指令已发送。响应: ${text}); lastSwitchState action; } catch (error) { console.error([${new Date().toLocaleTimeString()}] 控制开关时出错:, error.message); } } /** * 从Shelly EM获取功率数据使用本地RPC API */ async function getPowerDataFromLocal() { try { // Shelly EM的RPC调用格式 const requestBody { id: 1, method: EM.GetStatus, params: { id: SHELLY_EM_CHANNEL } }; const response await fetch(SHELLY_EM_URL, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const data await response.json(); // Shelly EM返回的数据结构 const emeters data.result.emeters; // 通道0总用电通常为正 const homeConsumption emeters[0].power; // 单位瓦特 // 通道1光伏发电发电时为负值 const solarProduction -emeters[1].power; // 转换为正数表示发电量 return { home: homeConsumption, solar: solarProduction, success: true }; } catch (error) { console.error([${new Date().toLocaleTimeString()}] 从Shelly EM获取数据失败:, error.message); return { home: 0, solar: 0, success: false }; } } /** * 核心决策逻辑 */ async function checkAndControl() { console.log(\n[${new Date().toLocaleTimeString()}] 开始执行检查...); const powerData await getPowerDataFromLocal(); if (!powerData.success) { console.log( 数据获取失败本次跳过。); return; } const { home, solar } powerData; const availableSurplus solar - home; // 可用富余功率 console.log( 家庭实时用电: ${home.toFixed(1)} W); console.log( 光伏实时发电: ${solar.toFixed(1)} W); console.log( 可用富余功率: ${availableSurplus.toFixed(1)} W); console.log( 电器额定功率: ${DEVICE_RATED_POWER} W (迟滞阈值: ${HYSTERESIS_THRESHOLD} W)); console.log( 开关上次状态: ${lastSwitchState}); // 决策逻辑 if (availableSurplus 0) { // 情况1用电大于发电正在从电网买电 console.log( 决策: 可用功率为负正在使用电网电。); if (lastSwitchState ! off) { console.log( 执行: 关闭电器。); await controlSwitch(off); } else { console.log( 执行: 状态未变已关闭无需操作。); } } else if (availableSurplus (DEVICE_RATED_POWER HYSTERESIS_THRESHOLD)) { // 情况2富余功率足够大考虑了迟滞可以开启电器 console.log( 决策: 富余功率超过阈值可以开启电器。); if (lastSwitchState ! on) { console.log( 执行: 开启电器。); await controlSwitch(on); } else { console.log( 执行: 状态未变已开启无需操作。); } } else { // 情况3富余功率为正但不足以开启电器或处于迟滞区间保持现状 console.log( 决策: 富余功率不足或处于稳定区间保持当前状态。); } } /** * 主函数设置定时器 */ function main() { console.log( 太阳能智能控制器已启动 ); console.log(配置检查间隔 ${CHECK_INTERVAL_MS / 1000} 秒电器功率 ${DEVICE_RATED_POWER}W); // 立即执行一次 checkAndControl(); // 设置定时循环 setInterval(checkAndControl, CHECK_INTERVAL_MS); } // 启动程序 main();4.3 脚本运行与后台常驻保存好脚本后在终端运行node solar-controller.js你应该能看到控制台每60秒输出一次当前的功率数据和决策日志。测试时你可以用手遮挡部分光伏板模拟发电量下降或者打开一个大型电器模拟用电上升观察脚本是否能正确发出开关指令。要让这个脚本在服务器如树莓派上24小时不间断运行有几种方法使用screen或tmux在终端会话中启动脚本然后断开连接脚本会在后台运行。使用pm2进程管理器推荐npm install -g pm2 pm2 start solar-controller.js --name solar-control pm2 save pm2 startup # 设置开机自启根据提示操作pm2可以管理进程状态、查看日志、监控资源是生产环境更可靠的选择。创建系统服务在树莓派上可以创建一个systemd服务单元文件实现更专业的开机自启和守护。5. 高级优化与常见问题排查基础功能实现后我们可以从稳定性、准确性和用户体验方面进行优化。5.1 逻辑优化策略多时段策略电暖器在深夜开启可能毫无意义即使有光伏余电但家庭已入睡无需供暖。可以在脚本中集成时间判断例如只在上午8点到晚上10点之间执行自动化逻辑。const currentHour new Date().getHours(); const ENABLE_HOUR_START 8; const ENABLE_HOUR_END 22; if (currentHour ENABLE_HOUR_START || currentHour ENABLE_HOUR_END) { console.log( 非启用时段强制关闭电器。); await controlSwitch(off); return; }平滑滤波与数据有效性检查Shelly EM的瞬时功率可能有小幅跳动。可以改为读取过去1分钟的平均功率或者连续读取3次数据取中位数避免单次波动误触发。async function getStablePowerData(sampleCount 3, intervalMs 2000) { let readings []; for (let i 0; i sampleCount; i) { const data await getPowerDataFromLocal(); if (data.success) { readings.push(data); } if (i sampleCount - 1) await delay(intervalMs); } // 计算中位数或平均值 const avgHome readings.reduce((sum, d) sum d.home, 0) / readings.length; const avgSolar readings.reduce((sum, d) sum d.solar, 0) / readings.length; return { home: avgHome, solar: avgSolar, success: readings.length 0 }; }状态持久化与容错脚本重启后lastSwitchState会丢失。可以每次操作后将状态写入一个本地文件或小型数据库如SQLite。重启时先读取文件并向智能开关发送一次状态查询请求同步实际状态避免误操作。5.2 常见问题与排查清单在部署过程中你可能会遇到以下问题。这里提供一个排查思路问题现象可能原因排查步骤Node.js脚本无法获取数据1. IP地址错误2. 网络不通3. Shelly EM未启用本地RPC1. 在浏览器访问http://[Shelly-EM-IP]/status看能否返回JSON数据。2. 在Shelly App中确认设备在线。3. 确保Shelly固件已更新本地HTTP/RPC访问已开启默认是开启的。功率数据符号错误如光伏显示为正CT钳安装方向错误1. 在光伏发电时检查Shelly App中P2通道数值。应为负数。2. 如果为正数断电后将CT钳2的物理方向旋转180度重装。开关不动作或动作相反1. 控制URL错误2. 继电器逻辑相反1. 手动在浏览器访问SWITCH_ON_URL和SWITCH_OFF_URL测试开关是否能正常响应。2. 检查Shelly 1的设置确认继电器模式是“开关”模式而非“瞬间”模式。电器频繁开关1. 功率阈值设置不当2. 功率数据波动大1. 增大HYSTERESIS_THRESHOLD迟滞阈值如从100W调到200W。2. 实现上述的“平滑滤波”逻辑使用平均功率而非瞬时功率做判断。夜间或阴天电器误开启逻辑缺陷未考虑发电量为零的情况在决策逻辑中增加条件if (solarProduction 50) { // 假设小于50W视为无有效发电直接关闭 }。脚本运行一段时间后停止1. 未捕获的异常导致进程退出2. 内存泄漏少见1. 用pm2管理它会自动重启崩溃的进程。2. 在setInterval和fetch外围添加try...catch确保单个循环错误不会导致整个脚本停止。5.3 方案扩展思路这个基础框架可以衍生出很多有趣的玩法多设备优先级控制如果你有电热水器、电动汽车充电桩、游泳池水泵等多个可调负载可以给它们设定不同的功率等级和优先级。当富余功率达到2000W时开启热水器达到6000W时再开启充电桩。集成到家庭自动化平台将Shelly EM的数据通过MQTT协议接入Home Assistant或Node-RED。在这些可视化平台上你可以更灵活地设计自动化流程创建漂亮的能源仪表盘并与其他智能设备联动如晴天自动打开窗帘。增加储能电池逻辑如果你家有储能电池逻辑可以更复杂优先用光伏给电池充电电池充满后再用富余电力启动电器在电价高峰时段优先使用电池供电并关闭非必要大功率电器。数据记录与分析让Node.js脚本将每次读取的功率数据和开关动作记录到数据库如InfluxDB然后用Grafana绘制成图表。你可以清晰地看到光伏发电曲线、家庭用电曲线以及自动化控制的效果为后续优化提供数据支持。这个项目的魅力在于它用相对低的成本和一定的动手能力解决了家庭能源管理中的一个真问题。看到电暖器在阳光明媚的午后自动启动消耗着“免费”的太阳能而在阴天或夜晚自动关闭那种“物尽其用”的满足感是单纯购买成品设备无法比拟的。
基于光伏发电的智能家居能耗控制:Shelly EM与Node.js实现
1. 项目概述与核心价值家里装了太阳能光伏板的朋友估计都琢磨过同一个问题白天发的电用不完眼睁睁看着它卖回给电网价格可能只有自用电价的一半甚至更低晚上没太阳了又要从电网买高价电来用。这种“发电时用不完用电时不够发”的错配让光伏系统的经济性大打折扣。我自己家装了8千瓦的光伏系统后就一直在想能不能让那些“可调节”的大功率电器——比如电热水器、充电桩、蓄热电暖器——只在光伏发电有富余的时候工作实现真正的“自发自用余电不上网”这就是今天要聊的“基于太阳能发电的智能家居能耗控制”方案的核心思路。它不是什么高深莫测的黑科技而是一套非常务实、可落地的自动化系统。其核心逻辑很简单实时比较“光伏发电功率”和“家庭实时用电功率”当发电有足够富余时自动开启预设的大功率电器消耗掉这部分多余的电能当发电不足或家庭用电激增时则自动关闭该电器避免从电网购电。我选择的硬件核心是Shelly EM这款智能电能监测模块。它自带两个电流互感器CT钳可以同时、独立地测量两路电路的实时功率完美契合我们“一路测全家用电一路测光伏发电”的需求。软件逻辑部分则用Node.js写一个轻量级的后台服务定期从 Shelly EM 读取数据进行计算判断并通过网络控制一个智能开关如 Shelly 1来操作电器。这套方案特别适合有一定动手能力、希望最大化光伏自发自用率、降低电费的家庭用户或极客。它不依赖复杂的家庭自动化中枢如 Home Assistant直接通过简单的脚本实现核心逻辑稳定且响应迅速。接下来我将从硬件接线、数据获取、逻辑编写到部署优化的全流程拆解每一个步骤和背后的原理。2. 系统架构与硬件选型解析2.1 整体工作流程设计在动手之前我们需要把整个系统的工作流想清楚。一个健壮的自动化系统其流程必须是清晰且闭环的。数据采集Shelly EM 通过两个CT钳分别钳在家庭总进线和光伏逆变器输出线上以每秒数次的频率测量实时功率单位瓦特并通过Wi-Fi将数据上传至本地网络或Shelly云。数据获取与计算Node.js 服务以固定时间间隔例如每60秒通过HTTP请求从 Shelly EM 的API接口获取最新的功率数据。计算逻辑为可用富余功率 光伏发电功率 - 家庭总用电功率。逻辑判断与执行如果可用富余功率 目标电器额定功率 安全阈值则向智能开关发送“开启”指令。如果可用富余功率 0即家庭用电已超过光伏发电开始从电网取电则向智能开关发送“关闭”指令。如果富余功率介于0和目标电器功率之间则保持开关现有状态不变。设备控制智能开关如 Shelly 1接收到指令后控制其继电器吸合或断开从而接通或切断目标电器的电源。这个流程的核心在于“可用富余功率”的计算。这里有一个关键点Shelly EM 测量功率时是有方向的。通常我们将“用电”定义为正功率“发电”定义为负功率。所以如果光伏板正在发电其测量值会是一个负数。在我们的计算中需要将其转换为正数来表示“发电量”。2.2 硬件清单与选型考量核心硬件Shelly EM功能双通道电能监测支持实时功率、电压、电流、电量统计Wi-Fi连接提供本地和云端API。选型理由相比单通道的电能监测模块双通道的Shelly EM可以同时独立监测两路电流无需额外设备接线和逻辑都更简洁。其API文档完善社区支持好是完成此项目的性价比之选。关键配件务必确认包装内包含两个电流互感器CT钳。根据你家总进线和逆变器输出线的电流大小选择合适量程家庭用户一般选择50A或100A的规格足够。我的进线是63A空开使用50A的CT钳完全没问题但要注意留有余量。执行硬件Wi-Fi智能开关选项1推荐Shelly 1或Shelly 1PM。1PM自带功率测量可以额外验证被控电器的实际功耗但本项目不是必须。选择Shelly系列的好处是生态统一控制API类似。选项2其他支持HTTP/API控制的智能插座或开关如Sonoff Basic需刷Tasmota固件、TP-Link Kasa系列等。你需要事先查明其远程控制的URL地址。安全警告被控电器功率必须严格小于智能开关继电器的额定功率Shelly 1是16A。控制电热水器、电暖器等大功率设备前务必核对电器铭牌上的额定电流和功率。辅助硬件网络稳定的2.4GHz Wi-Fi网络。Shelly设备对5GHz支持不佳务必确保安装位置2.4GHz信号强度良好。供电为Shelly EM和Shelly 1提供220V电源。通常可以从被控电器所在的插座或配电箱取电。安装工具螺丝刀、电工胶布、可能需要的线缆等。注意电气安全是第一位的所有接线操作必须在断开总闸或相应回路空开的前提下进行。如果你对家庭电路不熟悉强烈建议请专业电工协助完成硬件安装部分。3. 硬件安装与Shelly EM配置详解3.1 电路接线与CT钳安装这是整个项目最具实操性也最需要谨慎的一步。错误的接线可能导致数据错误甚至设备损坏。第一步断电与准备关闭家庭配电箱的总开关用验电笔确认无电后再操作。规划好Shelly EM的安装位置最好靠近配电箱方便取电和布线。第二步为Shelly EM供电找到一条稳定的220V电源线可以从配电箱的一个空闲空开下引或从附近一个常通电的插座引将其火线L接入Shelly EM的L端子零线N接入N端子。这一步是给设备本身供电。第三步安装CT钳1监测家庭总用电目标线路找到从电网接入你家电表再进入你家配电箱总开关的那根火线。通常是一根较粗的导线。安装方法将第一个CT钳对应Shelly EM的P1通道套在这根火线上。CT钳上通常有一个箭头标识箭头方向应指向用电负载即指向你家的配电箱。这是保证功率测量符号正确的关键。接线将CT钳的引出线两根细线连接到Shelly EM上标有P1和P1-的端子。通常不分正负但如果后续数据符号不对可以交换这两根线。第四步安装CT钳2监测光伏发电目标线路找到从光伏逆变器交流输出端接入你家配电箱的那根火线。安装方法将第二个CT钳对应P2通道套在这根线上。此处的箭头方向应指向电网方向即从逆变器指向配电箱/电网。这是因为从逆变器的角度看它是在向电网/家庭负载“输出”电能。接线将CT钳引出线接入Shelly EM的P2和P2-端子。重要概念澄清有朋友问CT钳是夹在直流侧太阳能板到逆变器还是交流侧逆变器输出。必须夹在交流侧因为Shelly EM只能测量交流电参数。我们监测的是逆变器之后、送入家庭电网的“可用交流电能”。第五步安装执器Shelly 1将Shelly 1串联到你要控制的电器如电暖器的供电回路中。即墙壁插座的火线 - Shelly 1的L端子供电入Shelly 1的O端子开关出 - 电暖器插头的火线零线直接贯通。为Shelly 1本身供电的N端子也需要接上零线。确保电暖器本身的物理开关处于打开状态其通断将由Shelly 1的继电器控制。3.2 Shelly设备网络配置与数据验证所有硬件接好后闭合总开关上电。手机App配置下载“Shelly”官方App。按照提示将Shelly EM和Shelly 1分别接入你的Wi-Fi网络。建议为它们设置静态IP或在路由器中做DHCP保留方便Node.js服务稳定访问。验证数据方向打开Shelly App查看Shelly EM的设备页面。你应该能看到两个通道的实时功率。理想情况当家里有负载用电光伏不发电时比如晚上P1家庭用电显示为正功率如500WP2光伏显示为0或接近0。当光伏发电且发电量大于家庭用电时P1可能显示一个较小的正数家庭基础用电而P2应显示为一个负数如-3000W。这个负数正是我们需要的“光伏发电功率”。如果符号相反如果P2显示为正数说明CT钳2的箭头方向反了或者接线反了。最安全的校正方法是在断电后将CT钳2的物理方向旋转180度即箭头指向反方向这比交换接线端子更可靠。记录关键参数在Shelly App中进入Shelly EM的设备设置找到“设备信息”记录下设备ID一串字母数字组合如AABBCCDDEEFF。设备通道通常是0。云端访问密钥在App的“用户设置”里可以生成一个“API密钥”Auth Key。同时注意你的服务器地址如果你使用Shelly云服务通常是https://shelly-XX-eu.shelly.cloudXX代表地区编号。更推荐使用本地局域网API地址为http://[Shelly-EM的本地IP]这样速度更快不依赖外网。4. Node.js控制逻辑实现与代码精讲硬件就绪后我们开始编写“大脑”——Node.js脚本。我将用一个增强版的脚本来演示它包含错误处理、状态防抖和本地API调用。4.1 项目初始化与依赖首先确保你的电脑或服务器比如树莓派上安装了Node.js。新建一个项目目录并初始化mkdir solar-auto-control cd solar-auto-control npm init -y我们主要使用node-fetch库来发起HTTP请求。安装它npm install node-fetch4.2 核心控制脚本详解创建一个名为solar-controller.js的文件。下面我逐段解释代码// 引入 fetch 库用于调用API import fetch from node-fetch; // 配置区域 // 方式一使用Shelly本地API推荐速度快不依赖互联网 const SHELLY_EM_IP 192.168.1.100; // 替换为你的Shelly EM本地IP const SHELLY_EM_URL http://${SHELLY_EM_IP}/rpc; // 方式二使用Shelly云端API备用需要外网 // const SHELLY_CLOUD_SERVER https://shelly-XX-eu.shelly.cloud; // const SHELLY_AUTH_KEY 你的云端AuthKey; const SHELLY_EM_ID 你的设备ID; // 从App中获取 const SHELLY_EM_CHANNEL 0; // 通常是0 // 智能开关的控制URL以Shelly 1为例 const SWITCH_ON_URL http://192.168.1.101/relay/0?turnon; // 替换为你的Shelly 1 IP const SWITCH_OFF_URL http://192.168.1.101/relay/0?turnoff; // 被控电器的额定功率瓦特务必填写准确并考虑启动电流 const DEVICE_RATED_POWER 2000; // 例如2000W 的电暖器 // 动作迟滞阈值防止在临界点频繁开关 const HYSTERESIS_THRESHOLD 100; // 单位瓦特 // 检查间隔毫秒 const CHECK_INTERVAL_MS 60 * 1000; // 60秒 // 全局状态记录用于防抖和日志 let lastSwitchState unknown; // on, off, unknown // 配置结束 配置要点解析本地API vs 云端API优先使用本地IP访问。你可以在路由器管理页面或Shelly App中查看设备的IP地址。本地访问延迟极低毫秒级且断网也能工作。设备功率DEVICE_RATED_POWER务必填写电器铭牌上的额定功率。对于电热类设备运行功率通常很稳定。对于电机类如空调压缩机启动功率可能数倍于额定功率本项目逻辑可能不适用需特别谨慎。迟滞阈值HYSTERESIS_THRESHOLD非常重要。假设没有迟滞当富余功率在2000W上下轻微波动时开关会在一分钟内反复动作。设置一个100W的迟滞意味着开启条件变为富余功率 (2000 100)W关闭条件变为富余功率 0W。这样就形成了一个“缓冲区”避免了频繁开关保护电器和继电器。/** * 控制智能开关的函数 * param {string} action - on 或 off */ async function controlSwitch(action) { const url action on ? SWITCH_ON_URL : SWITCH_OFF_URL; try { const response await fetch(url); const text await response.text(); console.log([${new Date().toLocaleTimeString()}] 开关 ${action} 指令已发送。响应: ${text}); lastSwitchState action; } catch (error) { console.error([${new Date().toLocaleTimeString()}] 控制开关时出错:, error.message); } } /** * 从Shelly EM获取功率数据使用本地RPC API */ async function getPowerDataFromLocal() { try { // Shelly EM的RPC调用格式 const requestBody { id: 1, method: EM.GetStatus, params: { id: SHELLY_EM_CHANNEL } }; const response await fetch(SHELLY_EM_URL, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(requestBody) }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const data await response.json(); // Shelly EM返回的数据结构 const emeters data.result.emeters; // 通道0总用电通常为正 const homeConsumption emeters[0].power; // 单位瓦特 // 通道1光伏发电发电时为负值 const solarProduction -emeters[1].power; // 转换为正数表示发电量 return { home: homeConsumption, solar: solarProduction, success: true }; } catch (error) { console.error([${new Date().toLocaleTimeString()}] 从Shelly EM获取数据失败:, error.message); return { home: 0, solar: 0, success: false }; } } /** * 核心决策逻辑 */ async function checkAndControl() { console.log(\n[${new Date().toLocaleTimeString()}] 开始执行检查...); const powerData await getPowerDataFromLocal(); if (!powerData.success) { console.log( 数据获取失败本次跳过。); return; } const { home, solar } powerData; const availableSurplus solar - home; // 可用富余功率 console.log( 家庭实时用电: ${home.toFixed(1)} W); console.log( 光伏实时发电: ${solar.toFixed(1)} W); console.log( 可用富余功率: ${availableSurplus.toFixed(1)} W); console.log( 电器额定功率: ${DEVICE_RATED_POWER} W (迟滞阈值: ${HYSTERESIS_THRESHOLD} W)); console.log( 开关上次状态: ${lastSwitchState}); // 决策逻辑 if (availableSurplus 0) { // 情况1用电大于发电正在从电网买电 console.log( 决策: 可用功率为负正在使用电网电。); if (lastSwitchState ! off) { console.log( 执行: 关闭电器。); await controlSwitch(off); } else { console.log( 执行: 状态未变已关闭无需操作。); } } else if (availableSurplus (DEVICE_RATED_POWER HYSTERESIS_THRESHOLD)) { // 情况2富余功率足够大考虑了迟滞可以开启电器 console.log( 决策: 富余功率超过阈值可以开启电器。); if (lastSwitchState ! on) { console.log( 执行: 开启电器。); await controlSwitch(on); } else { console.log( 执行: 状态未变已开启无需操作。); } } else { // 情况3富余功率为正但不足以开启电器或处于迟滞区间保持现状 console.log( 决策: 富余功率不足或处于稳定区间保持当前状态。); } } /** * 主函数设置定时器 */ function main() { console.log( 太阳能智能控制器已启动 ); console.log(配置检查间隔 ${CHECK_INTERVAL_MS / 1000} 秒电器功率 ${DEVICE_RATED_POWER}W); // 立即执行一次 checkAndControl(); // 设置定时循环 setInterval(checkAndControl, CHECK_INTERVAL_MS); } // 启动程序 main();4.3 脚本运行与后台常驻保存好脚本后在终端运行node solar-controller.js你应该能看到控制台每60秒输出一次当前的功率数据和决策日志。测试时你可以用手遮挡部分光伏板模拟发电量下降或者打开一个大型电器模拟用电上升观察脚本是否能正确发出开关指令。要让这个脚本在服务器如树莓派上24小时不间断运行有几种方法使用screen或tmux在终端会话中启动脚本然后断开连接脚本会在后台运行。使用pm2进程管理器推荐npm install -g pm2 pm2 start solar-controller.js --name solar-control pm2 save pm2 startup # 设置开机自启根据提示操作pm2可以管理进程状态、查看日志、监控资源是生产环境更可靠的选择。创建系统服务在树莓派上可以创建一个systemd服务单元文件实现更专业的开机自启和守护。5. 高级优化与常见问题排查基础功能实现后我们可以从稳定性、准确性和用户体验方面进行优化。5.1 逻辑优化策略多时段策略电暖器在深夜开启可能毫无意义即使有光伏余电但家庭已入睡无需供暖。可以在脚本中集成时间判断例如只在上午8点到晚上10点之间执行自动化逻辑。const currentHour new Date().getHours(); const ENABLE_HOUR_START 8; const ENABLE_HOUR_END 22; if (currentHour ENABLE_HOUR_START || currentHour ENABLE_HOUR_END) { console.log( 非启用时段强制关闭电器。); await controlSwitch(off); return; }平滑滤波与数据有效性检查Shelly EM的瞬时功率可能有小幅跳动。可以改为读取过去1分钟的平均功率或者连续读取3次数据取中位数避免单次波动误触发。async function getStablePowerData(sampleCount 3, intervalMs 2000) { let readings []; for (let i 0; i sampleCount; i) { const data await getPowerDataFromLocal(); if (data.success) { readings.push(data); } if (i sampleCount - 1) await delay(intervalMs); } // 计算中位数或平均值 const avgHome readings.reduce((sum, d) sum d.home, 0) / readings.length; const avgSolar readings.reduce((sum, d) sum d.solar, 0) / readings.length; return { home: avgHome, solar: avgSolar, success: readings.length 0 }; }状态持久化与容错脚本重启后lastSwitchState会丢失。可以每次操作后将状态写入一个本地文件或小型数据库如SQLite。重启时先读取文件并向智能开关发送一次状态查询请求同步实际状态避免误操作。5.2 常见问题与排查清单在部署过程中你可能会遇到以下问题。这里提供一个排查思路问题现象可能原因排查步骤Node.js脚本无法获取数据1. IP地址错误2. 网络不通3. Shelly EM未启用本地RPC1. 在浏览器访问http://[Shelly-EM-IP]/status看能否返回JSON数据。2. 在Shelly App中确认设备在线。3. 确保Shelly固件已更新本地HTTP/RPC访问已开启默认是开启的。功率数据符号错误如光伏显示为正CT钳安装方向错误1. 在光伏发电时检查Shelly App中P2通道数值。应为负数。2. 如果为正数断电后将CT钳2的物理方向旋转180度重装。开关不动作或动作相反1. 控制URL错误2. 继电器逻辑相反1. 手动在浏览器访问SWITCH_ON_URL和SWITCH_OFF_URL测试开关是否能正常响应。2. 检查Shelly 1的设置确认继电器模式是“开关”模式而非“瞬间”模式。电器频繁开关1. 功率阈值设置不当2. 功率数据波动大1. 增大HYSTERESIS_THRESHOLD迟滞阈值如从100W调到200W。2. 实现上述的“平滑滤波”逻辑使用平均功率而非瞬时功率做判断。夜间或阴天电器误开启逻辑缺陷未考虑发电量为零的情况在决策逻辑中增加条件if (solarProduction 50) { // 假设小于50W视为无有效发电直接关闭 }。脚本运行一段时间后停止1. 未捕获的异常导致进程退出2. 内存泄漏少见1. 用pm2管理它会自动重启崩溃的进程。2. 在setInterval和fetch外围添加try...catch确保单个循环错误不会导致整个脚本停止。5.3 方案扩展思路这个基础框架可以衍生出很多有趣的玩法多设备优先级控制如果你有电热水器、电动汽车充电桩、游泳池水泵等多个可调负载可以给它们设定不同的功率等级和优先级。当富余功率达到2000W时开启热水器达到6000W时再开启充电桩。集成到家庭自动化平台将Shelly EM的数据通过MQTT协议接入Home Assistant或Node-RED。在这些可视化平台上你可以更灵活地设计自动化流程创建漂亮的能源仪表盘并与其他智能设备联动如晴天自动打开窗帘。增加储能电池逻辑如果你家有储能电池逻辑可以更复杂优先用光伏给电池充电电池充满后再用富余电力启动电器在电价高峰时段优先使用电池供电并关闭非必要大功率电器。数据记录与分析让Node.js脚本将每次读取的功率数据和开关动作记录到数据库如InfluxDB然后用Grafana绘制成图表。你可以清晰地看到光伏发电曲线、家庭用电曲线以及自动化控制的效果为后续优化提供数据支持。这个项目的魅力在于它用相对低的成本和一定的动手能力解决了家庭能源管理中的一个真问题。看到电暖器在阳光明媚的午后自动启动消耗着“免费”的太阳能而在阴天或夜晚自动关闭那种“物尽其用”的满足感是单纯购买成品设备无法比拟的。