米家/小爱同学通过 Home Assistant 控制第三方设备

米家/小爱同学通过 Home Assistant 控制第三方设备
Photo by Jakub Żerdzicki / Unsplash

目标

通过米家或小爱同学控制接入到Home Assistant的第三方设备,实现语音控制或自动化。

实现流程

小爱同学/手动触发 → 中枢网关 → 产生虚拟事件 → Xiaomi Home(Home Assistant) → NodeRed(解析虚拟事件) → 控制设备(如打开空调)

必备条件

  • 中枢网关
  • 小爱音箱(可选,用于语音控制)
  • Home Assistant
    • Xiaomi Home 插件
    • NodeRed (可选,用于复杂控制流程)
    • Midea AC LAN 插件(适用于美的或Colmo设备)

实例:通过小爱同学控制打开Colmo的中央空调

前置条件

  • 已经安装【必备条件】中的设备和插件,并且中枢网关、NodeRed、Colmo中央空调已经接入到 Home Assistant。

操作步骤

一、米家 - 产生虚拟事件

  1. 智能
  2. 右上角⊕
  3. 手动控制
  4. 输入个名称:【打开主卧空调】
  5. 将设备调整到:
    1. 添加执行动作
    2. 家居设备
    3. 选择 中枢网关
      1. 产生虚拟事件:【打开主卧空调】
  6. 保存
  7. 重新进入刚才创建的【打开主卧空调】
  8. 右上角 ⋮
  9. 小爱语音控制
    1. 在自定义指令中可以添加其他命令,比如【打开主卧中央空调】等

按照以上步骤,可以添加【关闭主卧空调】等操作。

二、NodeRed - 接收和处理虚拟事件

  1. 新建一个流程,命名为 【中央空调】
  2. 拖一个 home assistant 分类下的 events: state 到 画布
    1. Name:中枢网关-虚拟事件发生
    2. Entity:选择 【中枢网关 虚拟服务 虚拟事件发生】
    3. If State:is not 后面的文本框留空
    4. State Type:string
    5. 输出属性
      1. data: event data
      2. payload: 表达式 data.new_state.attributes.事件名称
  3. 拖一个 功能 下的 switch 节点到画布
    1. 名称:打开主卧空调
    2. 属性:payload
    3. 输入值匹配符号:==
    4. 匹配值,也就是虚拟事件名称:打开主卧空调
  4. 鼠标左键按住 中枢网关-虚拟事件发生 右边的连接点,并移动鼠标到 打开主卧空调 对象的左边连接点,完成连接
  5. 拖一个 home assistant 分类下的 action 到 画布
    1. Name:打开主卧空调
    2. Action:climate.turn_on
    3. Entity:选择 【主卧中央空调】
  6. 连接两个 打开主卧空调
  7. 根据以上流程可以添加 关闭主卧空调
  8. 后续还可以查询空调运行状态,并语音播报

完整的 Json 配置,仅供参考

[
    {
        "id": "6013428aa8eaf5fc",
        "type": "subflow",
        "name": "播放语音通知",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 180,
                "y": 140,
                "wires": [
                    {
                        "id": "9294554b79e905c9"
                    }
                ]
            }
        ],
        "out": [],
        "env": [
            {
                "name": "message",
                "type": "str",
                "value": "这是需要播放的内容"
            }
        ],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "128ff6e18b8245ee",
        "type": "api-call-service",
        "z": "6013428aa8eaf5fc",
        "name": "小爱音箱提醒",
        "server": "0dc478d2cb4fca21",
        "version": 7,
        "debugenabled": true,
        "action": "notify.send_message",
        "floorId": [],
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "notify.xiaomi_cn_799921464_l06a_play_text_a_5_1",
            "notify.xiaomi_cn_862748684_lx06_play_text_a_5_1"
        ],
        "labelId": [],
        "data": "msg.payload",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "blockInputOverrides": true,
        "domain": "notify",
        "service": "send_message",
        "x": 440,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "9294554b79e905c9",
        "type": "function",
        "z": "6013428aa8eaf5fc",
        "name": "构造语音消息",
        "func": "const text = env.get(\"message\");\nconst output = {\n    message: JSON.stringify([text])\n};\nmsg.payload = output;\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 340,
        "y": 140,
        "wires": [
            [
                "128ff6e18b8245ee"
            ]
        ]
    },
    {
        "id": "9727f0c16587e4d0",
        "type": "tab",
        "label": "中央空调",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "b5353aca45a5e40d",
        "type": "switch",
        "z": "9727f0c16587e4d0",
        "name": "关闭主卧空调",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "关闭主卧空调",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 360,
        "y": 120,
        "wires": [
            [
                "c4b2ac72211b8615"
            ]
        ]
    },
    {
        "id": "64b1a65d439fbb29",
        "type": "server-state-changed",
        "z": "9727f0c16587e4d0",
        "name": "中枢网关-虚拟事件发生",
        "server": "0dc478d2cb4fca21",
        "version": 6,
        "outputs": 1,
        "exposeAsEntityConfig": "",
        "entities": {
            "entity": [
                "event.xiaomi_cn_1056524587_hub1_virtual_event_e_4_1"
            ],
            "substring": [],
            "regex": []
        },
        "outputInitially": true,
        "stateType": "str",
        "ifState": "",
        "ifStateType": "str",
        "ifStateOperator": "is_not",
        "outputOnlyOnStateChange": true,
        "for": "0",
        "forType": "num",
        "forUnits": "minutes",
        "ignorePrevStateNull": false,
        "ignorePrevStateUnknown": false,
        "ignorePrevStateUnavailable": false,
        "ignoreCurrentStateUnknown": false,
        "ignoreCurrentStateUnavailable": false,
        "outputProperties": [
            {
                "property": "data",
                "propertyType": "msg",
                "value": "",
                "valueType": "eventData"
            },
            {
                "property": "payload",
                "propertyType": "msg",
                "value": "data.new_state.attributes.事件名称",
                "valueType": "jsonata"
            }
        ],
        "x": 120,
        "y": 100,
        "wires": [
            [
                "b5353aca45a5e40d",
                "c1d7cd6f4099f5d4"
            ]
        ]
    },
    {
        "id": "09b655a64fccde0b",
        "type": "api-call-service",
        "z": "9727f0c16587e4d0",
        "name": "打开主卧空调",
        "server": "0dc478d2cb4fca21",
        "version": 7,
        "debugenabled": false,
        "action": "climate.turn_on",
        "floorId": [],
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "climate.211106237897739_climate"
        ],
        "labelId": [],
        "data": "",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "blockInputOverrides": false,
        "domain": "climate",
        "service": "turn_on",
        "x": 600,
        "y": 60,
        "wires": [
            [
                "cc54deeaf8d49bca"
            ]
        ]
    },
    {
        "id": "c4b2ac72211b8615",
        "type": "api-call-service",
        "z": "9727f0c16587e4d0",
        "name": "关闭主卧空调",
        "server": "0dc478d2cb4fca21",
        "version": 7,
        "debugenabled": false,
        "action": "climate.turn_off",
        "floorId": [],
        "areaId": [],
        "deviceId": [],
        "entityId": [
            "climate.211106237897739_climate"
        ],
        "labelId": [],
        "data": "",
        "dataType": "jsonata",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "blockInputOverrides": false,
        "domain": "climate",
        "service": "turn_off",
        "x": 600,
        "y": 120,
        "wires": [
            [
                "cc54deeaf8d49bca"
            ]
        ]
    },
    {
        "id": "cc54deeaf8d49bca",
        "type": "delay",
        "z": "9727f0c16587e4d0",
        "name": "",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 820,
        "y": 100,
        "wires": [
            [
                "03cf5f995d20ac27"
            ]
        ]
    },
    {
        "id": "03cf5f995d20ac27",
        "type": "api-current-state",
        "z": "9727f0c16587e4d0",
        "name": "主卧空调运行状态",
        "server": "0dc478d2cb4fca21",
        "version": 3,
        "outputs": 2,
        "halt_if": "off",
        "halt_if_type": "str",
        "halt_if_compare": "is_not",
        "entity_id": "climate.211106237897739_climate",
        "state_type": "str",
        "blockInputOverrides": false,
        "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": 1010,
        "y": 100,
        "wires": [
            [
                "426369e1861a8e85"
            ],
            [
                "4932358bb898d6b1"
            ]
        ]
    },
    {
        "id": "426369e1861a8e85",
        "type": "subflow:6013428aa8eaf5fc",
        "z": "9727f0c16587e4d0",
        "name": "",
        "env": [
            {
                "name": "message",
                "value": "主卧中央空调工作啦!",
                "type": "str"
            }
        ],
        "x": 1240,
        "y": 80,
        "wires": []
    },
    {
        "id": "4932358bb898d6b1",
        "type": "subflow:6013428aa8eaf5fc",
        "z": "9727f0c16587e4d0",
        "name": "",
        "env": [
            {
                "name": "message",
                "value": "主卧中央空调已经关闭啦!",
                "type": "str"
            }
        ],
        "x": 1240,
        "y": 120,
        "wires": []
    },
    {
        "id": "c1d7cd6f4099f5d4",
        "type": "switch",
        "z": "9727f0c16587e4d0",
        "name": "打开主卧空调",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "打开主卧空调",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 360,
        "y": 60,
        "wires": [
            [
                "09b655a64fccde0b"
            ]
        ]
    },
    {
        "id": "0dc478d2cb4fca21",
        "type": "server",
        "name": "Home Assistant",
        "version": 5,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": false,
        "cacheJson": true,
        "heartbeat": true,
        "heartbeatInterval": "30",
        "areaSelector": "friendlyName",
        "deviceSelector": "friendlyName",
        "entitySelector": "friendlyName",
        "statusSeparator": ": ",
        "statusYear": "hidden",
        "statusMonth": "2-digit",
        "statusDay": "2-digit",
        "statusHourCycle": "h23",
        "statusTimeFormat": "h:m",
        "enableGlobalContextStore": false
    }
]

Read more

挂载 Synology  Active Backup for Business 的备份镜像文件

挂载 Synology Active Backup for Business 的备份镜像文件

背景说明 群晖存储自带有个 Active Backup for Business [https://www.synology.com/zh-cn/knowledgebase/DSM/help/ActiveBackup/activebackup_business_overview] 看着可以选择单独的硬盘分区备份,以为和 Symantec Ghost 一样,恢复的时候可以选择某个分区直接还原。 然而,湾湾的开发人员的脑回路和正常人不太一样,经过尝试和咨询客服,想要使用 Active Backup for Business 恢复向导 [https://www.synology.com/zh-cn/knowledgebase/DSM/help/ActiveBackup/activebackup_business_recovery_wizard] 对镜像进行恢复,有以下限制和需要满足的条件 * 不能单独选择某个分区恢复,只能全盘恢复,

By Null