הנחיות להוספת תמיכה במתאם Solo לחיבור באמצעות ZigBee2MQTT

כדי לחבר את המתאם Solo אל Home Assistant באמצעות ZigBee מומלץ להשתמש ב-ZigBee2MQTT.

בשלב זה עדיין אין תמיכה ב-ZHA.

להוספת תמיכה במתאם Solo, נא לפעול לפי השלבים הבאים:

  1. צרו את הקובץ egi_solo.js והדביקו לתוכו את הקוד תחת הספריה /homeassistant/zigbee2mqtt/external_converters. במידה והספריה external_converters לא קיימת בנתיב /homeassistant/zigbee2mqtt/ אז צריך ליצור אותה.

תוכן הקובץ

/homeassistant/zigbee2mqtt/external_converters/egi_solo.js

"use strict";

const exposes = require("zigbee-herdsman-converters/lib/exposes");
const tuya = require("zigbee-herdsman-converters/lib/tuya");
const e = exposes.presets;
const ea = exposes.access;

/** ---- Tiny OFF guard: DP1=0 => system_mode:'off'; ignore lone DP4 while OFF ---- */
const fzEGI = {
  power_off_wins_mode: {
    cluster: "manuSpecificTuya",
    type: ["commandDataReport", "commandDataResponse"],
    convert: (model, msg, publish, options, meta) => {
      const dps = Array.isArray(msg?.data?.dpValues) ? msg.data.dpValues : [];
      let sawDP1 = false;

      for (const dpv of dps) {
        if (dpv.dp === 1) {
          sawDP1 = true;
          const v = tuya.getDataValue ? tuya.getDataValue(dpv) : (
            Buffer.isBuffer(dpv.data) ? dpv.data[0] : (dpv.data?.data?.[0])
          );
          if (v === 0) return { system_mode: "off" }; // explicit OFF always wins
        }
      }
      // If only DP4 (mode) arrives and we *know* power is OFF, keep showing off
      if (!sawDP1 && dps.some((x) => x.dp === 4) && meta?.state?.state === "OFF") {
        return { system_mode: "off" };
      }
      return {};
    },
  },
};

/** ---- tz helpers: each handles SETs *and* no-op GETs for the same key(s) ---- */
const tzEGI = {
  system_mode: {
    key: ["system_mode"],
    // no-op GET to avoid "No converter available for 'get' 'system_mode'"
    convertGet: async (entity, key, meta) => {},
    // SET -> DP1 (power) + DP4 (mode)
    convertSet: async (entity, key, value, meta) => {
      const modeMap = { cool: 0, heat: 1, dry: 2, fan_only: 3 };
      if (value === "off") {
        await tuya.sendDataPointBool(entity, 1, false);           // DP1 power OFF
      } else {
        await tuya.sendDataPointBool(entity, 1, true);            // DP1 power ON
        await tuya.sendDataPointEnum(entity, 4, modeMap[value]);  // DP4 mode
      }
      return { state: { system_mode: value, state: value === "off" ? "OFF" : "ON" } };
    },
  },

  fan_mode: {
    key: ["fan_mode"],
    convertGet: async (entity, key, meta) => {},
    convertSet: async (entity, key, value, meta) => {
      // DP map: auto=0, low=1, medium=2, high=3
      const map = { low: 1, medium: 2, high: 3, auto: 0 };
      await tuya.sendDataPointEnum(entity, 5, map[value]);
      return { state: { fan_mode: value } };
    },
  },

  current_heating_setpoint: {
    key: ["current_heating_setpoint"],
    convertGet: async (entity, key, meta) => {},
    convertSet: async (entity, key, value, meta) => {
      const val = Math.round(Number(value));
      await tuya.sendDataPointValue(entity, 2, val); // DP2 numeric (4-byte)
      return { state: { current_heating_setpoint: val } };
    },
  },

  // Optional: silence GET for non-settable local_temperature
  local_temperature_get_only: {
    key: ["local_temperature"],
    convertGet: async (entity, key, meta) => {},
  },
};

module.exports = [
  {
    fingerprint: [
      { modelID: "TS0601", manufacturerName: "_TZE284_s7faf5of" },
      { modelID: "TS0601", manufacturerName: "_TZE200_rpk52nw5" },
      { modelID: "TS0603", manufacturerName: "_TZF200_s7faf5of" },
    ],
    model: "EGI_Solo_HVAC_Adapter",
    vendor: "EGI",
    description: "EGI Solo - HVAC Adapter (external converter)",

    // Generic Tuya datapoints first, then our minimal OFF guard
    fromZigbee: [tuya.fz.datapoints, fzEGI.power_off_wins_mode],

    // IMPORTANT: put our SET-capable converters *before* generic tz
    toZigbee: [
      tzEGI.system_mode,
      tzEGI.fan_mode,
      tzEGI.current_heating_setpoint,
      tzEGI.local_temperature_get_only, // GET only; harmless
      tuya.tz.datapoints,               // for anything else (e.g. set_as_slave)
    ],

    onEvent: tuya.onEventSetTime,
    configure: tuya.configureMagicPacket,

    exposes: [
      exposes
        .climate()
        .withSetpoint("current_heating_setpoint", 16, 32, 1)
        .withLocalTemperature()
        .withSystemMode(["off", "cool", "heat", "dry", "fan_only"])
        .withFanMode(["low", "medium", "high", "auto"]), // your requested order
      e.binary("set_as_slave", ea.STATE_SET, "ENABLE", "DISABLE")
        .withDescription("Set adapter as slave"),
    ],

    meta: {
      tuyaDatapoints: [
        [1, "state", tuya.valueConverterBasic.lookup({ ON: tuya.enum(1), OFF: tuya.enum(0) })],
        [2, "current_heating_setpoint", tuya.valueConverter.raw],
        [3, "local_temperature",        tuya.valueConverter.raw],
        [4, "system_mode",              tuya.valueConverterBasic.lookup({
          cool: tuya.enum(0), heat: tuya.enum(1), dry: tuya.enum(2), fan_only: tuya.enum(3),
        })],
        [5, "fan_mode",                 tuya.valueConverterBasic.lookup({
          auto: tuya.enum(0), low: tuya.enum(1), medium: tuya.enum(2), high: tuya.enum(3),
        })],
        [7, "set_as_slave",             tuya.valueConverterBasic.lookup({
          ENABLE: tuya.enum(1), DISABLE: tuya.enum(0),
        })],
      ],
    },
  },
];
  1. כדי שקובץ ה-Converter שהוכן בשלב הקודם יטען, הוסיפו את השורות הבאות לסוף הקובץ ההגדרות של Zig2MQTT שם הקובץ configuration.yaml והוא נמצא בנתיב /homeassistant/zigbee2mqtt/

השורות להוספה בסוף הקובץ

/homeassistant/zigbee2mqtt/configuration.yaml:

external_converters:
  - egi_solo.js
  1. כדי שהשינויים יכנסו לתוקף, צריך לאתחל את המערכת הום אסיסטנט באופן מלא. במידה ומשתמשים בדונגל ZigBee שמחבור לרשת ישירות, צריך לאתחל גם אותו.

זהו, הכל מוכן, רק נשאר להוסיף יישות (entity) מסוג מזגן (climate) לדשבורדים השונים, שם היישות זהה לשם שמוגדר ב-Zigbee2MQTT.