Matter набирает популярность и есть инструменты разработчика для создания устройств Matter, но можно запустить Matter не разбираясь в коде, прошивка Tasmota позволяет относительно легко сделать Matter устройство.
Я не буду описывать как сделать Matter реле или PWM на стандартных GPIO ESP32 так как это описано в официальной документации Tasmota. Мы будем делать на 16 канальной микросхеме PWM PCA9685, в стандартной прошивке она отсутствует, я собирал её сам с включенными параметрами в файле my_user_config.h
#defineUSE_PCA9685_V2
#defineUSE_PCA9685_ADDR0x40
#defineUSE_PCA9685_FREQ1000Частота по умолчанию 50 хорошо подходит для сервоприводов, но нам нужно управлять светодиодами и установим частоту 1000 по умолчанию, через консоль или скрипты эту частоту можно поменять. Скомпилированную прошивку можно скачать тут:
Как использовать PCF8574 описано в официальной документации, согласно которой управлять выводами можно только через mqtt и из консоли, а если управлять можно из консоли, то значит и с помощью правил и скриптов, что мы и будем делать. Не забудьте в настройках указать
Первым делом включим Matter и добавим туда наши виртуальные светодиоды, для этого добавляем ендпоинты

Реализовать возможно только одноцветный, двухцветный (CT) и трехцветный (RGB)
Так как у нас Matter endpoint используют имена то надо чтобы в скрипте можно было обращаться по имени, для этого включим опцию SetOption83 1 выполнив в консоли:
SetOption83 1создадим файл с именем autoexec.be в меню Управление — Управление файловой системой, со следующим содержимым:
import matter
import persist
# ======= Единый конфиг =======
# tp: "dimmer", "ct", "rgb"
# channels: для dimmer - одно число, для ct - {warm, cold}, для rgb - {r, g, b}
var devices = [
{"name": "Led01", "tp": "dimmer", "channels": 0},
{"name": "Led02", "tp": "dimmer", "channels": 1},
{"name": "Led03", "tp": "dimmer", "channels": 2},
{"name": "Led04", "tp": "dimmer", "channels": 3},
{"name": "Led05", "tp": "dimmer", "channels": 4},
{"name": "Led06", "tp": "rgb", "channels": {"r": 5, "g": 6, "b": 7}},
{"name": "Led07", "tp": "ct", "channels": {"warm": 8, "cold": 9}},
# Чтобы добавить новое устройство - просто добавь строку:
# {"name": "Led08", "tp": "dimmer", "channels": 10},
# {"name": "Led09", "tp": "ct", "channels": {"warm": 11, "cold": 12}},
# {"name": "Led10", "tp": "rgb", "channels": {"r": 13, "g": 14, "b": 15}},
]
# ======= Инициализация persist =======
def init_persist()
if !persist.has("pwm_values") persist.pwm_values = {} end
if !persist.has("bri_values") persist.bri_values = {} end
if !persist.has("power_values") persist.power_values = {} end
if !persist.has("rgb_values") persist.rgb_values = {} end
if !persist.has("ct_values") persist.ct_values = {} end
for dev: devices
var n = dev["name"]
var tp = dev["tp"]
if !persist.pwm_values.contains(n) persist.pwm_values[n] = 4096 end
if !persist.bri_values.contains(n) persist.bri_values[n] = 254 end
if !persist.power_values.contains(n) persist.power_values[n] = 0 end
if tp == "rgb"
if !persist.rgb_values.contains(n) persist.rgb_values[n] = {"r": 4096, "g": 4096, "b": 4096} end
end
if tp == "ct"
if !persist.ct_values.contains(n) persist.ct_values[n] = 326 end # нейтральный белый
end
end
persist.save()
end
# ======= Вспомогательные функции =======
def bri_to_pwm(bri)
return int(bri * 4096 / 254)
end
# CT: 153=холодный, 500=тёплый
def ct_to_warm_cold(ct, pwm)
var warm = int(pwm * (ct - 153) / (500 - 153))
var cold = pwm - warm
return [warm, cold]
end
def pwm_off(channels, tp)
if tp == "dimmer"
tasmota.cmd("driver15 pwm," + str(channels) + ",0")
elif tp == "rgb"
tasmota.cmd("driver15 pwm," + str(channels["r"]) + ",0")
tasmota.cmd("driver15 pwm," + str(channels["g"]) + ",0")
tasmota.cmd("driver15 pwm," + str(channels["b"]) + ",0")
elif tp == "ct"
tasmota.cmd("driver15 pwm," + str(channels["warm"]) + ",0")
tasmota.cmd("driver15 pwm," + str(channels["cold"]) + ",0")
end
end
def pwm_on(name, channels, tp)
var pwm = persist.pwm_values[name]
if tp == "dimmer"
tasmota.cmd("driver15 pwm," + str(channels) + "," + str(pwm))
elif tp == "rgb"
var rgb = persist.rgb_values[name]
var scale = pwm / 4096.0
tasmota.cmd("driver15 pwm," + str(channels["r"]) + "," + str(int(rgb["r"] * scale)))
tasmota.cmd("driver15 pwm," + str(channels["g"]) + "," + str(int(rgb["g"] * scale)))
tasmota.cmd("driver15 pwm," + str(channels["b"]) + "," + str(int(rgb["b"] * scale)))
elif tp == "ct"
var wc = ct_to_warm_cold(persist.ct_values[name], pwm)
tasmota.cmd("driver15 pwm," + str(channels["warm"]) + "," + str(wc[0]))
tasmota.cmd("driver15 pwm," + str(channels["cold"]) + "," + str(wc[1]))
end
end
# ======= Регистрация правил =======
def handle_devices()
for dev: devices
var n = dev["name"]
var tp = dev["tp"]
var ch = dev["channels"]
# Power
tasmota.add_rule("mtrreceived#" + n + "#power",
def(value)
persist.power_values[n] = int(value)
persist.save()
if value == 1
pwm_on(n, ch, tp)
print(n + " Power ON")
else
pwm_off(ch, tp)
print(n + " Power OFF")
end
end
)
# Brightness
tasmota.add_rule("mtrreceived#" + n + "#bri",
def(brightness)
var pwm_value = bri_to_pwm(brightness)
persist.pwm_values[n] = pwm_value
persist.bri_values[n] = int(brightness)
persist.save()
if tp == "dimmer"
tasmota.cmd("driver15 pwm," + str(ch) + "," + str(pwm_value))
elif tp == "rgb"
var rgb = persist.rgb_values[n]
var scale = brightness / 254.0
tasmota.cmd("driver15 pwm," + str(ch["r"]) + "," + str(int(rgb["r"] * scale)))
tasmota.cmd("driver15 pwm," + str(ch["g"]) + "," + str(int(rgb["g"] * scale)))
tasmota.cmd("driver15 pwm," + str(ch["b"]) + "," + str(int(rgb["b"] * scale)))
elif tp == "ct"
var wc = ct_to_warm_cold(persist.ct_values[n], pwm_value)
tasmota.cmd("driver15 pwm," + str(ch["warm"]) + "," + str(wc[0]))
tasmota.cmd("driver15 pwm," + str(ch["cold"]) + "," + str(wc[1]))
end
print(n + " Bri: " + str(pwm_value))
end
)
# RGB цвет
if tp == "rgb"
tasmota.add_rule("mtrreceived#" + n + "#rgb",
def(rgb_str)
var r = int("0x" + rgb_str[0..1])
var g = int("0x" + rgb_str[2..3])
var b = int("0x" + rgb_str[4..5])
var r_pwm = int(r * 4096 / 255)
var g_pwm = int(g * 4096 / 255)
var b_pwm = int(b * 4096 / 255)
persist.rgb_values[n] = {"r": r_pwm, "g": g_pwm, "b": b_pwm}
persist.save()
var scale = persist.bri_values[n] / 254.0
tasmota.cmd("driver15 pwm," + str(ch["r"]) + "," + str(int(r_pwm * scale)))
tasmota.cmd("driver15 pwm," + str(ch["g"]) + "," + str(int(g_pwm * scale)))
tasmota.cmd("driver15 pwm," + str(ch["b"]) + "," + str(int(b_pwm * scale)))
print(n + " RGB: " + rgb_str)
end
)
end
# CT цветовая температура
if tp == "ct"
tasmota.add_rule("mtrreceived#" + n + "#ct",
def(ct)
persist.ct_values[n] = int(ct)
persist.save()
var wc = ct_to_warm_cold(int(ct), persist.pwm_values[n])
tasmota.cmd("driver15 pwm," + str(ch["warm"]) + "," + str(wc[0]))
tasmota.cmd("driver15 pwm," + str(ch["cold"]) + "," + str(wc[1]))
print(n + " CT: " + str(ct) + " warm:" + str(wc[0]) + " cold:" + str(wc[1]))
end
)
end
end
end
# ======= Восстановление состояния =======
def restore_state()
for dev: devices
var n = dev["name"]
var tp = dev["tp"]
var ch = dev["channels"]
var power = persist.power_values[n]
var bri = persist.bri_values[n]
if power == 1
pwm_on(n, ch, tp)
else
pwm_off(ch, tp)
end
var cmd = '{"Name":"' + n + '","Power":' + str(power) + ',"Bri":' + str(bri)
if tp == "ct"
cmd += ',"CT":' + str(persist.ct_values[n])
end
cmd += '}'
tasmota.cmd("MtrUpdate " + cmd)
print("Restore " + n + ": " + cmd)
end
end
init_persist()
tasmota.set_timer(0, handle_devices)
tasmota.set_timer(5000, restore_state)отредактируйте файл изменив выводы к которым у Вас подключены Led в массиве var devices, номера соответствуют номерам микросхемы PCA9685. Cохраняем файл, перезагружаем например командой в консоли
Restart 1Теперь можно добавить Matter устройство в Вашу систему умного дома.