{
  "scenarioTemplate": {
    "name": "Внешний датчик температуры для термоголовок Aqara E1, SONOFF TRVZB, Danfoss 014G2463",
    "desc": "Сценарий настраивает термоголовку и привязывает к ней внешний датчик температуры. Поддерживает Aqara E1, SONOFF TRVZB, Danfoss 014G2463.\nСценарий автоматически определит тип термоголовки и применит соответствующую логику работы.\n\nВАЖНО: Сначала выберите внешний датчик температуры в настройках, затем активируйте сценарий переключателем 'Активно'.",
    "active": true,
    "onStart": true,
    "sync": false,
    "data": "let servicesList = [];\n\n// Выносим название и описание в переменные для использования в info и options\nlet scenarioName = {\n    ru: \"Внешний датчик температуры для термоголовок Aqara E1, SONOFF TRVZB, Danfoss 014G2463\",\n    en: \"External temperature sensor for thermostats Aqara E1, SONOFF TRVZB, Danfoss 014G2463\"\n};\n\nlet scenarioDescription = {\n    ru: \"Сценарий настраивает термоголовку и привязывает к ней внешний датчик температуры. Поддерживает Aqara E1, SONOFF TRVZB, Danfoss 014G2463.\\nСценарий автоматически определит тип термоголовки и применит соответствующую логику работы.\\n\\nВАЖНО: Сначала выберите внешний датчик температуры в настройках, затем активируйте сценарий переключателем 'Активно'.\",\n    en: \"Scenario configures the thermostat and binds an external temperature sensor to it. Supports Aqara E1, SONOFF TRVZB, Danfoss 014G2463.\\nThe scenario will automatically determine the thermostat type and apply the appropriate logic.\\n\\nIMPORTANT: First select the external temperature sensor in the settings, then activate the scenario with the 'Active' switch.\"\n};\n\ninfo = {\n    name: scenarioName.ru,\n    description: scenarioDescription.ru,\n    version: \"2.1\",\n    author: \"@BOOMikru\",\n    onStart: true,\n\n    sourceServices: [HS.Thermostat],\n    sourceCharacteristics: [HC.CurrentHeatingCoolingState],\n\n    options: {\n        desc: {\n            name: {en: \"\", ru: \"\"}, \n            desc: scenarioDescription,\n            type: \"String\",\n            value: \"\",\n            formType: \"status\"\n        },\n        sensor: {\n            name: {\n                en: \"Temperature sensor\",\n                ru: \"Датчик температуры\"\n            },\n            desc: {\n                en: \"External temperature sensor\",\n                ru: \"Внешний датчик температуры для привязки\"\n            },\n            type: \"String\",\n            value: \"\",\n            formType: \"list\",\n            values: servicesList\n        }\n    },\n\n    variables: {\n        lastTemp: undefined,\n        lastUpdateTime: undefined,\n        subscribed: false,\n        subscribe: undefined,\n    }\n}\n\n// Вывод в лог информационные сообщения о работе сценария\nlet debug = false\n\nfunction trigger(source, value, variables, options, context) {\n    try {\n        // Проверяем, запустился ли сценарий при старте хаба\n        let isHubStartup = context && context.toString().indexOf(\"HUB[OnStart]\")>=0;\n        \n        let acc = source.getAccessory()\n        let modelId = acc.getModelId()\n        let manufacturer = acc.getManufacturer()\n\n        // Проверка поддержки термостатов\n        let isSupported = false\n        let thermostatType = \"\"\n        \n        if (modelId == \"lumi.airrtc.agl001\" && manufacturer == \"Aqara\") {\n            isSupported = true\n            thermostatType = \"Aqara E1\"\n        } else if (modelId == \"TRVZB\" && manufacturer == \"SONOFF\") {\n            isSupported = true\n            thermostatType = \"SONOFF TRVZB\"\n        } else if (modelId == \"eTRV0101\" && manufacturer == \"Danfoss\") {\n            isSupported = true\n            thermostatType = \"Danfoss eTRV0101\"\n        }\n\n        if (!isSupported && !emulate) {\n            logError(`Поддерживаются только термостаты Aqara E1 (lumi.airrtc.agl001), SONOFF TRVZB, Danfoss 014G2463 (eTRV0101). Текущий: ${manufacturer} ${modelId}`, source, isHubStartup)\n            return;\n        }\n        \n        if (options.sensor === \"\") {\n            logError(\"Выберите внешний датчик. Если уже выбрали - активируйте сценарий заново\", source, isHubStartup)\n            return;\n        }\n        \n        let externalSwitch = undefined\n        let targetTemperature = undefined\n\n        // Поиск сервисов в зависимости от типа термостата\n        acc.getServices().forEach(function (service) {\n            if (service.getType() == HS.Switch) {\n                externalSwitch = service.getCharacteristic(HC.On)\n            } else if (service.getType() == HS.C_TemperatureControl) {\n                targetTemperature = service.getCharacteristic(HC.TargetTemperature)\n            } else if (emulate && service.getType() == HS.Thermostat) {\n                targetTemperature = service.getCharacteristic(HC.CurrentTemperature)\n            }\n        })\n\n        if (!externalSwitch && !emulate) {\n            logError(\"Не обнаружен переключатель [Внешний датчик температуры]\", source, isHubStartup)\n            return\n        }\n\n        if (!targetTemperature && !emulate) {\n            logError(\"Не обнаружен сервис [Температура внешнего датчика]\", source, isHubStartup)\n            return\n        }\n\n        if (!emulate) externalSwitch.setValue(true)\n        setValueFromSensor(source, variables, options, targetTemperature, isHubStartup)\n\n        if (!variables.subscribe || variables.subscribed != true) {\n            showSubscribeMessage(options.sensor, isHubStartup)\n            let subscribe = Hub.subscribeWithCondition(\"\", \"\", [HS.TemperatureSensor], [HC.CurrentTemperature], function (sensorSource, sensorValue) {\n                let service = sensorSource.getService()\n                let isSelected = service.getUUID() == options.sensor\n                if (isSelected && targetTemperature) {\n                    targetTemperature.setValue(sensorValue)\n                    if (variables.lastTemp != sensorValue) {\n                        logInfo(`Значение на термоголовку ${thermostatType} установлено: ${sensorValue}°C`, source, debug)\n                        variables.lastUpdateTime = Date.now();\n                        variables.lastTemp = sensorValue\n                    }\n                }\n            }, acc)\n            variables.subscribe = subscribe\n            variables.subscribed = true\n        }\n        if (!variables.midnightTask) {\n            variables.midnightTask = Cron.schedule(\"0 0 0 * * *\", function () {\n                setValueFromSensor(source, variables, options, targetTemperature, false)\n                logInfo(\"Полуночное обновление\", source, debug)\n            });\n        }\n    } catch (e) {\n        let isHubStartup = context && context.toString().includes(\"HUB[OnStart]\");\n        logError(`Ошибка установки внешнего датчика температуры: ${e.toString()}`, source, isHubStartup)\n    }\n}\n\nfunction setValueFromSensor(source, variables, options, targetTemperature, isHubStartup) {\n    try {\n        const cdata = options.sensor.split('.');\n        const aid = cdata[0];\n        const sid = cdata[1];\n        let sensorAccessory = Hub.getAccessory(aid)\n        if (!sensorAccessory) {\n            logError(`Не найден внешний датчик для термоголовки. ID: ${options.sensor}`, source, isHubStartup)\n            return\n        }\n        let sensorService = sensorAccessory.getService(sid)\n        if (sensorService) {\n\n            const status = sensorAccessory.getService(HS.AccessoryInformation).getCharacteristic(HC.C_Online).getValue() == true;\n            if (!status) {\n                logWarn(`Датчик ${getDeviceName(sensorService)} не в сети`, source)\n            }\n            let sensorValue = sensorService.getCharacteristic(HC.CurrentTemperature).getValue()\n            targetTemperature.setValue(sensorValue)\n            if (variables.lastTemp != sensorValue) {\n                logInfo(`Значение на термоголовку установлено: ${sensorValue}°C`, source, debug)\n                variables.lastTemp = sensorValue\n                variables.lastUpdateTime = Date.now();\n            }\n\n        } else {\n            logError(`Не найден внешний датчик для термоголовки. ID: ${options.sensor}`, source, isHubStartup)\n            return\n        }\n\n        const currentTime = Date.now();\n        if (variables.lastUpdateTime && (currentTime - variables.lastUpdateTime > oneDayMs)) {\n            logError(`Нет показаний от внешнего датчика (${getDeviceName(sensorService)}) в течении суток или более`, source, isHubStartup);\n            return;\n        }\n    } catch (e) {\n        logError(`Не удалось получить температуру с датчика ${options.sensor}: ${e.toString()}`, source, isHubStartup)\n    }\n}\n\nfunction showSubscribeMessage(sensor, isHubStartup) {\n    const cdata = sensor.split('.');\n    const aid = cdata[0];\n    const sid = cdata[1];\n    const acc = Hub.getAccessory(aid)\n    const service = acc.getService(sid)\n    const accName = service.getAccessory().getName()\n    const sName = service.getName()\n    if (isHubStartup) {\n        console.info(`Подключен внешний датчик: ${(accName == sName ? accName : accName + \" \" + sName)}`)\n    } else {\n        console.message(`Подключен внешний датчик: ${(accName == sName ? accName : accName + \" \" + sName)}`)\n    }\n}\n\nfunction getDeviceName(service) {\n    const acc = service.getAccessory();\n    const room = acc.getRoom().getName()\n    const accName = service.getAccessory().getName()\n    const sName = service.getName()\n    const name = room + \" -> \" + (accName == sName ? accName : accName + \" \" + sName) + \" (\" + service.getUUID() + \")\" + (!service.isVisible() ? \". Скрыт\" : \"\")\n    return name\n}\n\nfunction logInfo(text, source, show) {\n    if (show) console.info(getLogText(text, source));\n}\nfunction logWarn(text, source) {\n    console.warn(getLogText(text, source));\n}\nfunction logError(text, source, isHubStartup) {\n    if (isHubStartup) {\n        logWarn(text, source)\n    } else {\n        console.error(getLogText(text, source))\n    }\n}\nfunction getLogText(text, source) {\n    return `${text} | ${DEBUG_TITLE} ${getDeviceName(source.getService())}`\n}\n\nlet servicesListUnsort = [];\nHub.getAccessories().forEach(function (a) {\n    a.getServices().filter(function (s) { return s.getType() == HS.TemperatureSensor }).forEach(function (s) {\n        const c = s.getCharacteristic(HC.CurrentTemperature);\n        if (!c) return;\n        let displayname = getDeviceName(s)\n        servicesListUnsort.push({\n            name: { ru: displayname, en: displayname },\n            value: s.getUUID()\n        });\n    })\n});\n\nservicesList.push({ name: { ru: \"Не выбрано\", en: \"Not selected\", en: \"\" }, value: '' });\nservicesListUnsort.sort(function (a, b) { return a.name.ru.localeCompare(b.name.ru); }).forEach(function (s) { servicesList.push(s) })\n\n// Сутки\nconst oneDayMs = 23 * 59 * 60 * 1000\n// Константа для отладки\nconst DEBUG_TITLE = \"ВДТ: \";\n\n//Эмуляция работы на виртуальном термостате. Устанавливает текущую температуру\nlet emulate = false\n",
    "type": "LOGIC"
  }
}
