Виртуальное устройство + WB-LED Brightness = мерцания

Заметили некорректное поведение диммирования лент при определении виртуального устройства для WB-LED.

Задача:

  1. диммировать при долгом нажатии на выключатель (реализовано стандартными настройками wb-led)
  2. диммировать в интерфейсе через виртуальное устройство.

При включении - выключении проблем нет.
При диммировании через виртуальное устройство - проблем нет
При диммировании путем долгого нажатия выключателя - идет мерцание ленты и время диммирования кратно увеличивается.

Получается что проблема в работе вирутального устройства а точнее его синхронизации с реальным устройством.

Для устройст заданы алиасы:

global.__proto__.f2_led_Kabinet = "wb-led_196/Channel 3"
global.__proto__.f2_led_Kabinet_brightness = "wb-led_196/Channel 3 Brightness"
global.__proto__.f2_led_Vannaya = "wb-led_216/Channel 1"
global.__proto__.f2_led_Vannaya_brightness = "wb-led_216/Channel 1 Brightness"
global.__proto__.f2_balkon = "wb-mr6cu_184/K1";

Само виртуальное устройство:

var virtualDevName = "f2_led";
var virtualDevTitle = "_2 этаж: свет";

var config = [
    {
        dev: "f2_led_Kabinet",
        title: "Кабинет",
        brightness: true,
    },
    {
        dev: "f2_led_Vannaya",
        title: "Ванная",
        brightness: true,
    },
    {
        dev: "f2_balkon",
        title: "Балкон",
        brightness: false,
    }
];

var cells = {};
var virtualDevItems = [];
var realDevItems = [];
var order = 1;
for (var i = 0; i < config.length; i++) {
    var element = config[i];
    var cellName = "cell_{}".format(element.dev);
    cells[cellName] = {
        type: "switch",
        value: dev[global.__proto__[element.dev]],
        title: element.title,
        order: order,
    }
    order++;
    virtualDevItems.push("{}/{}".format(virtualDevName, cellName));
    realDevItems.push(global.__proto__[element.dev]);

    if (element.brightness) {
        var cellNameBrightness = "{}_{}".format(cellName, "brightness")
        cells[cellNameBrightness] = {
            type: "range",
            value: dev[global.__proto__["{}_{}".format(element.dev, "brightness")]],
            min: 0,
            max: 100,
            title: "{} {}".format(element.title, "Яркость"),
            order: order,
        }

        virtualDevItems.push("{}/{}".format(virtualDevName, cellNameBrightness));
        realDevItems.push(global.__proto__["{}_{}".format(element.dev, "brightness")]);

        order++;
    }
}


defineVirtualDevice(virtualDevName, {
    title: virtualDevTitle,
    cells: cells,
});

defineRule("{}/switch".format(virtualDevName), {
    whenChanged: virtualDevItems,
    then: function (newValue, devName, cellName) {
        var realDeviceName = cellName.replace("cell_", "");
        dev[global.__proto__[realDeviceName]] = newValue;
    }
});


defineRule("{}/sync".format(virtualDevName), {
    whenChanged: realDevItems,
    then: function (newValue, devName, cellName) {
        //log("devName: {}, cellName: {},  newValue: {}", devName, cellName, newValue);
        var realDevicePath = "{}/{}".format(devName, cellName);

        for (var i = 0; i < config.length; i++) {
            var element = config[i];
            var virtualCellName = null;

            switch (global.__proto__[element.dev]) {
                case realDevicePath: // Switch
                    virtualCellName = "{}/cell_{}".format(virtualDevName, element.dev);
                    break;
                case realDevicePath.replace(" Brightness", ""): // brightness
                    virtualCellName = "{}/cell_{}_brightness".format(virtualDevName, element.dev);
                    break;
            }

            if (virtualCellName && dev[virtualCellName] !== newValue) {
                dev[virtualCellName] = newValue;
            }
        }
    }
});

Проблема в defineRule(“{}/sync”.format(virtualDevName)
Когда мы диммируем через физическую кнопку в правило прилетает значение с небольшим опозданием которое в свою очередь вызывает мерцание ленты из-за dev[virtualCellName] = newValue;.

Подскажите как это обходить? Только таймерами с присваением значения яркости в вирутуальное устройство через N секунд после начала диммирования? Не хочется городить костыли для такой простой задачи.

Как-то у вас тут всё сложно и не понятно что за чем :sweat_smile:

Как вариант попробуйте создать вирт. устройство с нужными вам контроллами и привязать их к реальным устройствам с помощью такого скрипта.

Возможно решит вашу задачу :upside_down_face:

2 лайка

Добрый день!
Для диагностики проблемы пришлите, пожалуйста, архив с диагностической информацией контроллера во время выполнения правила. Создание архива описано в документации.

Добрый день!

Не получили от вас обратной связи. Вопрос еще актуален?

Добрый день, вопрос актуален, но еще не добрался до объекта чтобы проверить и сделать дамп.

Тогда оставим тему открытой. Ждем от вас архив.

Добрый день. Диагностический архив создать не получается.

root@wirenboard-AWJY4GZR:~# wb-diag-collect diag
Start data collecting
Traceback (most recent call last):
  File "/usr/lib/python3.9/asyncio/subprocess.py", line 135, in wait
    return await self._transport._wait()
  File "/usr/lib/python3.9/asyncio/base_subprocess.py", line 235, in _wait
    return await waiter
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.9/asyncio/tasks.py", line 492, in wait_for
    fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/bin/wb-diag-collect", line 10, in <module>
    sys.exit(main())
  File "/usr/share/wb-diag-collect/wb/diag/diag_collect.py", line 71, in main
    asyncio.get_event_loop().run_until_complete(
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/usr/share/wb-diag-collect/wb/diag/collector.py", line 33, in collect
    await self.copy_journalctl(
  File "/usr/share/wb-diag-collect/wb/diag/collector.py", line 167, in copy_journalctl
    await asyncio.wait_for(proc.wait(), timeout=timeout)
  File "/usr/lib/python3.9/asyncio/tasks.py", line 494, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError
  • Обновите ПО контроллера.

  • Снимите архив через web-интерфейс.

Рекомендую использовать стандартный способ диммирования по длинному нажатию.

Обновил ПО,

  • Снимите архив через web-интерфейс.

Лоадер крутится но ничего не происходит.

В логах:

22-07-2025 15:06:39.615 [wb-diag-collect]	rm: cannot remove '/var/www/diag/*.zip': No such file or directory
22-07-2025 15:06:39.590 [wb-diag-collect]	asyncio.exceptions.TimeoutError
22-07-2025 15:06:39.590 [wb-diag-collect]	    raise exceptions.TimeoutError() from exc
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/lib/python3.9/asyncio/tasks.py", line 494, in wait_for
22-07-2025 15:06:39.590 [wb-diag-collect]	    await asyncio.wait_for(proc.wait(), timeout=timeout)
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/share/wb-diag-collect/wb/diag/collector.py", line 121, in execute_commands
22-07-2025 15:06:39.590 [wb-diag-collect]	    await self.execute_commands(tmpdir, options["commands"], options["timeout"])
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/share/wb-diag-collect/wb/diag/collector.py", line 32, in collect
22-07-2025 15:06:39.590 [wb-diag-collect]	    path = await wb_archive_collector.collect(self.options, "/var/www/diag/", "diag_output")
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/share/wb-diag-collect/wb/diag/rpc_server.py", line 117, in diag
22-07-2025 15:06:39.590 [wb-diag-collect]	Traceback (most recent call last):
22-07-2025 15:06:39.590 [wb-diag-collect]	The above exception was the direct cause of the following exception:
22-07-2025 15:06:39.590 [wb-diag-collect]	asyncio.exceptions.CancelledError
22-07-2025 15:06:39.590 [wb-diag-collect]	    fut.result()
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/lib/python3.9/asyncio/tasks.py", line 492, in wait_for
22-07-2025 15:06:39.590 [wb-diag-collect]	Traceback (most recent call last):
22-07-2025 15:06:39.590 [wb-diag-collect]	During handling of the above exception, another exception occurred:
22-07-2025 15:06:39.590 [wb-diag-collect]	asyncio.exceptions.CancelledError
22-07-2025 15:06:39.590 [wb-diag-collect]	    return await waiter
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/lib/python3.9/asyncio/base_subprocess.py", line 235, in _wait
22-07-2025 15:06:39.590 [wb-diag-collect]	    return await self._transport._wait()
22-07-2025 15:06:39.590 [wb-diag-collect]	  File "/usr/lib/python3.9/asyncio/subprocess.py", line 135, in wait
22-07-2025 15:06:39.590 [wb-diag-collect]	Traceback (most recent call last):
22-07-2025 15:06:39.590 [wb-diag-collect]	future: <Task finished name='Collect diagnostics (may be long running)' coro=<AsyncMQTTRPCServer.diag() done, defined at /usr/share/wb-diag-collect/wb/diag/rpc_server.py:106> exception=TimeoutError()>
22-07-2025 15:06:39.590 [wb-diag-collect]	Task exception was never retrieved
22-07-2025 15:06:35.445 [mosquitto]	1753185995: Client auto-3D4A4D46-C6B4-48B6-C4E6-579B43AC33FB disconnected.
22-07-2025 15:06:35.445 [mosquitto]	1753185995: New client connected from /var/run/mosquitto/mosquitto.sock:0 as auto-3D4A4D46-C6B4-48B6-C4E6-579B43AC33FB (p2, c1, k60).
22-07-2025 15:06:35.442 [mosquitto]	1753185995: New connection from /var/run/mosquitto/mosquitto.sock:0 on port 0.
22-07-2025 15:06:29.860 [mosquitto]	1753185989: New client connected from ::ffff:127.0.0.1:41746 as wb-mqtt-homeui-XeheRQIxHh (p2, c1, k60, u'xxx').

Рекомендую использовать стандартный способ диммирования по длинному нажатию.

Именно так и настроено.

  • Сообщите модель контроллера, его серийный номер и текущий релиз.

  • Проверьте использование ресурсов, например, с помощью top или htop, чтобы удостовериться, что система не перегружена. Отключите все правила, которые могут занимать ресурсы.

  • Убедитесь, что на контроллере достаточно свободного места на диске df -h.

Пожалуйста, покажите вывод данных команд.

  • Сообщите модель контроллера, его серийный номер и текущий релиз.

Номер партии: 7.2.1B 577
Версия контроллера: 7.2.1
Дата производства: 2022-02-10 11:44:13+00:00
Название релиза: wb-2504
Тип релиза: stable
Серийный номер: AWJY4GZR

Проверьте использование ресурсов, например, с помощью top или htop , чтобы удостовериться, что система не перегружена. Отключите все правила, которые могут занимать ресурсы

Убедитесь, что на контроллере достаточно свободного места на диске df -h

Благодарю за информацию.
Понадобится ещё время, чтобы разобраться.

Для сбора диагностической информации попробуйте увеличить таймаут. Например:

wb-diag-collect -t 20 diag

Увеличение таймаута помогло создать архив:

приложен диагностический архив, доступен только сотрудникам поддержки
(630,7 КБ)

Есть какое-то продвижение по вопросу?

Добрый день!
Пока не удалось разобраться - у меня ваш скрипт не управляет WB-LED, ищу причину.

Если найду способ как диммировать в интерфейсе через виртуальное устройство другим способом — вы готовы его рассмотреть?

Да, готовы рассмотреть любые способы чтобы можно было димировать с физической кнопки и с виртуального устройства.

1 лайк

Добрый день!

Перепробовал множество вариантов и пришел к такому, который приемлемо работает:


global.__proto__.f2_led_Kabinet = "wb-led_196/Channel 3"
global.__proto__.f2_led_Kabinet_brightness = "wb-led_196/Channel 3 Brightness"
global.__proto__.f2_led_Vannaya = "wb-led_216/Channel 1"
global.__proto__.f2_led_Vannaya_brightness = "wb-led_216/Channel 1 Brightness"
global.__proto__.f2_balkon = "wb-mr6cu_184/K1";

var virtualDevName = "f2_led";
var virtualDevTitle = "_2 этаж: свет";

var config = [
    { dev: "f2_led_Kabinet", title: "Кабинет", brightness: true },
    { dev: "f2_led_Vannaya", title: "Ванная", brightness: true },
    { dev: "f2_balkon", title: "Балкон", brightness: false },
];

var cells = {};
var virtualDevItems = [];
var realDevItems = [];
var order = 1;

for (var i = 0; i < config.length; i++) {
    var element = config[i];
    var cellName = "cell_" + element.dev;
    cells[cellName] = {
        type: "switch",
        value: dev[global.__proto__[element.dev]],
        title: element.title,
        order: order,
    };
    order++;
    virtualDevItems.push(virtualDevName + "/" + cellName);
    realDevItems.push(global.__proto__[element.dev]);

    if (element.brightness) {
        var cellNameBrightness = cellName + "_brightness";
        cells[cellNameBrightness] = {
            type: "range",
            value: dev[global.__proto__[element.dev + "_brightness"]],
            min: 0,
            max: 100,
            title: element.title + " Яркость",
            order: order,
        };
        order++;
        virtualDevItems.push(virtualDevName + "/" + cellNameBrightness);
        realDevItems.push(global.__proto__[element.dev + "_brightness"]);
    }
}

defineVirtualDevice(virtualDevName, {
    title: virtualDevTitle,
    cells: cells,
});


defineRule(virtualDevName + "/switch", {
    whenChanged: virtualDevItems,
    then: function (newValue, devName, cellName) {
        var realDeviceName = cellName.replace("cell_", "");
        var realPath = global.__proto__[realDeviceName];

       
        if (dev[realPath] !== newValue) {
            dev[realPath] = newValue;
        }
    }
});


defineRule(virtualDevName + "/sync", {
    whenChanged: realDevItems,
    then: function (newValue, devName, cellName) {
        var realDevicePath = devName + "/" + cellName;

        for (var i = 0; i < config.length; i++) {
            var element = config[i];

            var virtCellSwitch = virtualDevName + "/cell_" + element.dev;
            var virtCellBrightness = virtualDevName + "/cell_" + element.dev + "_brightness";

            var realPathSwitch = global.__proto__[element.dev];
            var realPathBrightness = global.__proto__[element.dev + "_brightness"];

            var virtTarget = null;

            if (realPathSwitch === realDevicePath) {
                virtTarget = virtCellSwitch;
            } else if (realPathBrightness === realDevicePath) {
                virtTarget = virtCellBrightness;
            }

            if (virtTarget !== null) {
                var current = dev[virtTarget];

                if (typeof current === "number" && typeof newValue === "number") {
                    if (Math.abs(current - newValue) >= 1) {
                        dev[virtTarget] = newValue;
                    }
                } else if (current !== newValue) {
                    dev[virtTarget] = newValue;
                }
            }
        }
    }
});

Спасибо. Ваш вариант скрипта работает без проблем.

По сути все что нужно это проверять текущее значение реального устройства перед записью в него и если текущее значение не отличается от записываемого то не записывать.

Я, честно говоря, ожидал что подобные проверки есть внутри движка и если прилетает тоже самое значение то нотификации не тригерятся.
Буду знать, спасибо за помощь!

1 лайк

Интересна эта проверка.
Зачем проверять на number и далее вычитать?
Почему не сработает просто? Типы то одинаковые.
if (current !== newValue)