[空调控制]自动选择空调执行参数
开启空调时自动识别当前室内温度室外温度差, 设置一个舒适的空调模式,15分钟检查一次判断是否继续调整,结合当前系统判断季节进一步锁死空调模式功能.
[
{
"id": "ab6f64c4dad8b73c",
"type": "server-state-changed",
"z": "8dde7d6f02804d4f",
"name": "客厅空调状态",
"server": "6d42cb4e16b94937",
"version": 6,
"outputs": 1,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"climate.777ca934"
],
"substring": [],
"regex": []
},
"outputInitially": true,
"stateType": "str",
"ifState": "",
"ifStateType": "str",
"ifStateOperator": "is",
"outputOnlyOnStateChange": true,
"for": "0",
"forType": "num",
"forUnits": "minutes",
"ignorePrevStateNull": false,
"ignorePrevStateUnknown": false,
"ignorePrevStateUnavailable": false,
"ignoreCurrentStateUnknown": false,
"ignoreCurrentStateUnavailable": false,
"outputProperties": [
{
"property": "ac_state",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "eventData"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"x": 130,
"y": 4080,
"wires": [
[
"9ef0f8d8c11f1a37"
]
]
},
{
"id": "9ef0f8d8c11f1a37",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "读取空调状态",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "climate.777ca934",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "ac_state",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "topic",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 320,
"y": 4080,
"wires": [
[
"25cc57eee2a7985c"
]
]
},
{
"id": "fb501e4eeb568778",
"type": "mqtt in",
"z": "8dde7d6f02804d4f",
"name": "",
"topic": "home/ac_comfort_cycle",
"qos": "2",
"datatype": "auto-detect",
"broker": "0fea454ccb5d8006",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 160,
"y": 4200,
"wires": [
[
"9ef0f8d8c11f1a37"
]
]
},
{
"id": "25cc57eee2a7985c",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "家里是否有人",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 2,
"halt_if": "on",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "input_boolean.person_maybe_left",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 520,
"y": 4080,
"wires": [
[
"ad19128ccc580383"
],
[]
]
},
{
"id": "ad19128ccc580383",
"type": "change",
"z": "8dde7d6f02804d4f",
"name": "配置实体传感器(按需修改室内传感器ID)",
"rules": [
{
"t": "set",
"p": "entity_id",
"pt": "msg",
"to": "topic",
"tot": "msg"
},
{
"t": "set",
"p": "indoor_sensor",
"pt": "msg",
"to": "sensor.xiaomi_cn_blt_3_1lgn2hjk8c402_mini_temperature_p_2_1001",
"tot": "str"
},
{
"t": "set",
"p": "outdoor_sensor",
"pt": "msg",
"to": "sensor.xiaomi_cn_blt_3_1lubuvk9t0k00_mini_temperature_p_2_1001",
"tot": "str"
},
{
"t": "set",
"p": "season_entity",
"pt": "msg",
"to": "input_select.season",
"tot": "str"
}
],
"x": 780,
"y": 4080,
"wires": [
[
"a759ef419ee93ce0"
]
]
},
{
"id": "a759ef419ee93ce0",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "读取空调状态",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "{{entity_id}}",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "ac_state",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 1040,
"y": 4080,
"wires": [
[
"9d242f88f0761cea"
]
]
},
{
"id": "9d242f88f0761cea",
"type": "function",
"z": "8dde7d6f02804d4f",
"name": "仅当空调非off/unavailable",
"func": "const s = (msg.ac_state || '').toLowerCase();\nif (s && s !== 'off' && s !== 'unavailable') { return msg; }\nreturn null;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 4180,
"wires": [
[
"ef6b35d44085a575"
]
]
},
{
"id": "ef6b35d44085a575",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "读取室内温度",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "{{indoor_sensor}}",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "indoor_temp",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 780,
"y": 4180,
"wires": [
[
"633a7c044b912f9c"
]
]
},
{
"id": "633a7c044b912f9c",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "读取室外温度",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "{{outdoor_sensor}}",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "outdoor_temp",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 960,
"y": 4180,
"wires": [
[
"b7b54536cbbf32e7"
]
]
},
{
"id": "b7b54536cbbf32e7",
"type": "api-current-state",
"z": "8dde7d6f02804d4f",
"name": "读取季节(可选)",
"server": "6d42cb4e16b94937",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "{{season_entity}}",
"state_type": "str",
"blockInputOverrides": true,
"outputProperties": [
{
"property": "season_state",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 1140,
"y": 4180,
"wires": [
[
"c2ad72ca104f21ae"
]
]
},
{
"id": "c2ad72ca104f21ae",
"type": "function",
"z": "8dde7d6f02804d4f",
"name": "舒适策略 + 季节约束",
"func": "const entity_id = msg.entity_id;\nconst ti = parseFloat(msg.indoor_temp);\nconst to = parseFloat(msg.outdoor_temp);\nlet season = (msg.season_state || '').toString().toLowerCase();\nif (!season) {\n node.warn('无法读取季节状态,使用默认值transition');\n season = 'transition'; // 默认过渡季\n}\n\nif (!entity_id || isNaN(ti) || isNaN(to)) {\n node.warn('缺少必要数据: entity_id/indoor_temp/outdoor_temp');\n return null;\n}\n\n// 动态舒适带\nlet comfortLow = 22;\nlet comfortHigh = 26;\nif (to >= 32) comfortHigh -= 1;\nif (to <= 10) comfortLow += 1;\n\nconst delta = ti - to;\nlet actions = [];\nconst set = (action, data) => actions.push({ action, data });\n\n// 先依据室内+室外生成建议动作\nif (ti >= comfortHigh + 2) {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'cool' });\n const target = (to >= 32 || delta <= 1) ? 25 : 26;\n const fan = (to >= 32 || delta <= 1) ? 'high' : 'medium';\n set('climate.set_temperature', { entity_id, temperature: target });\n set('climate.set_fan_mode', { entity_id, fan_mode: fan });\n} else if (ti >= comfortHigh) {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'cool' });\n const target = (to >= 30 || delta <= 1) ? 26 : 27;\n const fan = (to >= 30 || delta <= 1) ? 'medium' : 'low';\n set('climate.set_temperature', { entity_id, temperature: target });\n set('climate.set_fan_mode', { entity_id, fan_mode: fan });\n} else if (ti > comfortLow && ti < comfortHigh) {\n // 舒适区:按季节强制模式\n if (season === 'summer') {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'cool' });\n } else if (season === 'winter') {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'heat' });\n } // transition 不强制改模式\n set('climate.set_temperature', { entity_id, temperature: Math.round((comfortLow + comfortHigh) / 2) });\n set('climate.set_fan_mode', { entity_id, fan_mode: 'low' });\n} else if (ti <= comfortLow - 2) {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'heat' });\n const target = (to <= 10 || delta >= 1) ? 24 : 23;\n const fan = (to <= 10 || delta >= 3) ? 'high' : 'medium';\n set('climate.set_temperature', { entity_id, temperature: target });\n set('climate.set_fan_mode', { entity_id, fan_mode: fan });\n} else {\n set('climate.set_hvac_mode', { entity_id, hvac_mode: 'heat' });\n const target = (to <= 12 || delta >= 1) ? 23 : 22;\n const fan = (to <= 12 || delta >= 2) ? 'medium' : 'low';\n set('climate.set_temperature', { entity_id, temperature: target });\n set('climate.set_fan_mode', { entity_id, fan_mode: fan });\n}\n\n// 再应用季节白名单过滤\nconst allow = {\n summer: { heat: false, cool: true },\n winter: { heat: true, cool: false },\n transition: { heat: true, cool: true }\n}[season] || { heat: true, cool: true };\n\nactions = actions.filter(act => {\n if (act.action === 'climate.set_hvac_mode') {\n const mode = (act.data && act.data.hvac_mode) || '';\n if (mode === 'heat' && !allow.heat) return false;\n if (mode === 'cool' && !allow.cool) return false;\n }\n return true;\n});\n\nmsg.payload = actions;\nnode.warn(JSON.stringify({ entity_id, ti, to, season, comfortLow, comfortHigh, delta, actions }));\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 640,
"y": 4280,
"wires": [
[
"d6029c2e2779168a",
"9cda333e200fecbc"
]
]
},
{
"id": "d6029c2e2779168a",
"type": "debug",
"z": "8dde7d6f02804d4f",
"name": "预览动作",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 840,
"y": 4320,
"wires": []
},
{
"id": "9cda333e200fecbc",
"type": "split",
"z": "8dde7d6f02804d4f",
"name": "拆分每个指令",
"splt": "",
"spltType": "str",
"arraySplt": 1,
"arraySpltType": "len",
"stream": false,
"addname": "",
"property": "payload",
"x": 860,
"y": 4280,
"wires": [
[
"579bcff911d5b0b1"
]
]
},
{
"id": "579bcff911d5b0b1",
"type": "api-call-service",
"z": "8dde7d6f02804d4f",
"name": "统一调用服务",
"server": "6d42cb4e16b94937",
"version": 7,
"debugenabled": false,
"action": "",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [],
"labelId": [],
"data": "",
"dataType": "json",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"blockInputOverrides": false,
"domain": "",
"service": "",
"x": 1060,
"y": 4280,
"wires": [
[
"cd980dec595a9b1f"
]
]
},
{
"id": "cd980dec595a9b1f",
"type": "delay",
"z": "8dde7d6f02804d4f",
"name": "节流:15分钟仅1次",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "15",
"rateUnits": "second",
"randomFirst": "0",
"randomLast": "0",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 1170,
"y": 4340,
"wires": [
[
"ee417197a9fc4e15"
]
]
},
{
"id": "ee417197a9fc4e15",
"type": "mqtt out",
"z": "8dde7d6f02804d4f",
"name": "",
"topic": "home/ac_comfort_trigger",
"qos": "",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "0fea454ccb5d8006",
"x": 1170,
"y": 4400,
"wires": []
},
{
"id": "6d42cb4e16b94937",
"type": "server",
"name": "Home Assistant_main",
"version": 5,
"addon": true,
"rejectUnauthorizedCerts": true,
"ha_boolean": "y|yes|true|on|home|open",
"connectionDelay": true,
"cacheJson": true,
"heartbeat": true,
"heartbeatInterval": 30,
"areaSelector": "friendlyName",
"deviceSelector": "friendlyName",
"entitySelector": "friendlyName",
"statusSeparator": ": ",
"statusYear": "hidden",
"statusMonth": "short",
"statusDay": "numeric",
"statusHourCycle": "default",
"statusTimeFormat": "h:m",
"enableGlobalContextStore": true
},
{
"id": "0fea454ccb5d8006",
"type": "mqtt-broker",
"name": "",
"broker": "10.0.0.90",
"port": 1883,
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": 4,
"keepalive": 60,
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]