# config.yaml
# конфигурационный файл контроллера

# =================================================
# глобальные настройки
controller:
  # путь к хранилищу результатов
  sqlite_databases: "/var/aqua_controller/core/db_controller.sqlite"
  # максимальный размер очереди
  queue_max_size: 100
  # дефолтное расписание.
  cron_default: "*/5 * * * *"  
  
# =================================================
# настройки логгирования
logging:
  # базовое имя файла логгирования с указанием полного пути
  filename: "/var/aqua_controller/controller.log"
  # уровень логгирования
  level: "debug"

# =================================================
# УСТРОЙСТВА
# =================================================
devices:
  # функциональное (логическое) устройство
  - name: "function"
    module: 
      path: "devices.function"
      class: "FUNCTION"

  # системное устройство Raspberry Pi 1 model b+. В блоке контроллера
  - name: "raspi"
    module: 
      path: "devices.raspi"
      class: "RASPI_adapter"

  # устройство воспроизведения звука.
  - name: "player"
    module: 
      path: "devices.player"
      class: "PLAYER_adapter"
    options:
      device: "default"

  # датчик температуры DS18B20 (общий для всех)
  - name: "ds18b20"
    module: 
      path: "devices.ds18b20"
      class: "DS18B20_adapter"

  # расширитель портов MCP23017. Блок контроллера
  - name: "mcp23017_controller"
    module: 
      path: "devices.mcp23017"
      class: "MCP23017_adapter"
    options:
      i2c_bus: 1
      i2c_address: 0x21  

  # мультиплексор TCA9548A. Крышка аквариума
  - name: "tca9548a_aquarium"
    module:
      path: "devices.tca9548a"
      class: "TCA9548A_adapter"
    options:      
      i2c_bus: 1
      i2c_address: 0x71

  # датчик влажности и температуры AM2320 (общий для всех).
  - name: "am2320"
    module: 
      path: "devices.am2320"
      class: "AM2320_adapter"
    options:
      i2c_bus: 1
      i2c_address: 0x5C  



  # ШИМ - модуль PCA9685. Блок контроллера.
  #- name: "pca9685_controller"
  #  module: 
  #    path: "devices.pca9685"
  #    class: "PCA9685_adapter"
  #  options:
  #    i2c_bus: 1
  #    i2c_address: 0x41  
  #    frequency: 100

  # ШИМ - модуль PCA9685. Крышка аквариума.
  #- name: "pca9685_aquarium"
  #  module: 
  #    path: "devices.pca9685"
  #    class: "PCA9685_adapter"
  #  options:
  #    i2c_bus: 1
  #    i2c_address: 0x40  
  #    frequency: 100



# =============================================
# ПРАВИЛА
# =============================================
# Формируем общие правила получения информации с сенсоров/датчиков и обработки полученных результатов 
#
# ВАЖНО: Правила выполняются ровно в том порядке, в котором они указаны в текущем конфигурационном файле
# Если Вы предполагаете использовать в каком либо из правил показания одного из датчиков, то его 
# сначала необходимо получить в одном из правил, расположенном выше текущего
#
# rules_new:
#   - name: str               - Наименование правила. Параметр обязательный. Используется в качестве описателя правила.
#                               Кроме того, наименование правила используется в логгировании дейстий контроллера,
#                               будь то просто событие информационного характера, или ошибка и т.п.
#     code: str               - Код результа. Параметр не обязательный. Используется лишь в том случае, когда действия правила
#                               (actions) подразумевают под собой получение данных (method: "get"). В этом случае в хранилище
#                               попадет последнее полученное методом get значение с указанием кода и времени его получения  
#                               Если параметр не указан, то даже в случае получения действиями данных, значение не будет сохранено 
#                               в хранилище данных. Если параметр не сохранен в хранилище, то использование его значений невозможно.
#     condition: str          - Условие, при выполнении которого выполняются действия для правила. Параметр не обязательный.
#                               Если параметр не указан, то правило выполняется без проверки условий, т.е. безакцептно
#                               ВАЖНО! Условие может содержать расчетные значения вида {S|T->param[nn]} (описание ниже по тектсу), 
#                               математические выражения и логические операции сравнения. Предварительно расчетные значения заменяются 
#                               на соотвествующие им числовые значения, а затем происходят математические преобразования и логическое сравнение.
#     cron: str               - Расписание стандартного cron - вида * * * * *. Параметр не обязательный.
#                               Если параметр не указан, то по умолчанию будет применено расписание вида * * * * *, т.е.
#                               правило будет выполняться ежеминутно, при соблюдении условий condition (если таковое присутствует)
#     actions: list           - массив выполняемых задач (действий)
#       - device: str         - Наименование устройства (в соответсвии с сегментом devices). Параметр обязательный.
#         method: "get|set"   - Тип метода, который будет использован при обращении к устройству device
#         ** kwargs: str|int  - Перечень параметров, который будет передан get|set - методу. Параметр не обязательный.
#
#         Следует учесть что:
#           1. Преред выполнением задач массива actions выполняются проверка, все-ли устройства, указанные в массиве, доступны.
#           Если какие-либо из устройств всего массива не доступны, то выполнение всего массива прерывается
#           2. Задачи массива actions выполняются ровно в том порядке, в котором они указаны в массива actions
#           Если задача массива отработала корректно, то выполняем следующую задачу
#           Если задача массива отработала не корректно, то останавливаем весь процесс выполнения всего массива
#           3. Все ключи секции actions, кроме [device, method], могут содержать расчетные значения вида {S|T:param},  
#           математические выражения и логические операции преобразования. 
#           Предварительно расчетные значения заменяются на соотвествующие им числовые значения, а затем происходят математические и 
#           логические преобразования. 
#           Необходимо также отметить, что для запуска математических и логических преобразований, требуется задать лидирующим знак равенства
#           (например: "= {S->am2320_up_internal_temperature} - {S->am2320_up_external_temperature[0]}"). Если в указанном примере не указать 
#           лидирующим символом знак равенства, то расчетные параметры будут заменена на соотвествующие им числовые значения, и в переменную 
#           будет передана строковая величина, без математического и логического преобразования (т.е. "23.50 - 20.67")
#           Если все-таки требуется в качестве значения переменной передать строку в которой стоит лидирующим 
#           знак равенства (например: "= тут какое-то описание ="), то перед лидирующим знаком равенства требуется поставить знак 
#           апострофа ('). В этом случае расчетные значения, если они присутствуют, будут заменены на соотвествующие им числовые, но 
#           математическое и логическое преобразование будет игнорировано.
#
#         Расчетные параметры вида {S|T->param[nn]}, с использованием математических выражений, и в случае условий - логических операций.
#         Это использование параметров вида {S|T->param[nn]}, где:
#           - {T->time}           - это обозначение для значения времени, вида HH:MM:SS (или короткий вариант HH:MM).
#           - {T->current_time}   - это текущее время, в момент выполнения расчета.
#                                   Т.е. в математическое выражение или сравнение может быть подставлено время (заботливо переведенное в секунды)
#                                   Например: {T->current_time} - {T->08:00:00} даст Вам разницу в секундах между текущим временем и 08:00 утра.
#                                   А если в условии указать например строку вида: {T->08:00:00} <= {T->current_time} < {T->10:00:00}, то мы получим 
#                                   ИСТИНУ, если текущее время лежит в диапазоне между 08:00 и 10:00 утра, либо
#                                   ЛОЖЬ, если текущее время вне укуазанного диапазона.  
#           - {S->code[nn]}       - это обозначение для значения показаний датчиков и прочего, хранящегося в хранилище с кодом "code".
#                                   Т.е., если в  хранилище нет указанного значения (причем последнего полученного), то условие будет считаться не 
#                                   выполненным, а математическое выражение не консистентным 
#                                   Следует обратить внимание что допускается извлечение данных, заданной глубины. Например, выражение {S->code[1]} 
#                                   означает, что нам необходимо предпоследнее значение. Если не указывать глубину извлекаемого значения или указать 
#                                   "[0]", то система вернет из хранилища последнее значение 
#           - {D->param}          - это обозначение для значений даты. Поддерживаются следующие параметры:
#                                   {D->day}        - текущий день месяца (1 - 31)
#                                   {D->month}      - текущий месяц (1 - 12)
#                                   {D->year}       - текущий год (например: 2024)
#                                   {D->weekday}    - текущий день недели (1 - 7, где 1 = понедельник, 7 = воскресенье)
#         Параметр глубины [nn]   - это числовое выражение, позволяющее извлекать не последнее имеющееся в хранилище значение. Параметр глубины доступен
#                                   только для значений типа {S->}. Если Вы попытаетесь указать глубину для значения типа {Т->}, то параметр глубины 
#                                   будет принят к обработке, но будет проигнорирован
#                                   
#         Выражения, используемые расчетные параметры, допускают:
#           - логические операции: and, or, not
#           - операторы сравнения: ==, !=, >, <, >=, <=
#           - группировка: () для приоритета операций
#           - математические операции:
#             - арифметические: +, -, *, /, // (целочисленное деление), % (остаток от деления)
#             - степень: **
#             - унарные операции: +x, -x
#           - литералы и структуры данных:
#             - списки: [элемент1, элемент2, ..., элементN]
#             - числа: целые (42), вещественные (3.14)
#             - строки: 'текст' или "текст"
#             - логические значения: True, False
#           - математические функции: 
#             - round(x) - округление
#             - int(x) - преобразование в целое
#             - abs(x) - модуль числа
#             - math.* - все функции модуля math
#
#   Примеры использования оператора in и списков:
#     - "{D->weekday} in [1, 2, 3, 4, 5]"                                           # Проверка рабочих дней (пн - пт)
#     - "{D->weekday} in [6, 7]"                                                    # Проверка выходных дней
#     - "{D->day} in [1, 15, 31]"                                                   # Проверка конкретных дней месяца
#     - "{D->month} in [10, 11, 12, 1, 2, 3]"                                       # Проверка месяцев отопительного сезона
#     - "{S->temperature[0]} in [18, 19, 20, 21, 22]"                               # Проверка конкретных значений температуры
#     - "({D->weekday} in [1, 2, 3, 4, 5]) and ({T->current_time} > {T->08:00:00})" # Комбинированное условие
#
# Некоторые формулы для расчета рассвета и заката.
# Рассвет:
# - линейное возрастание
#     round((Vb + (Ve - Vb) * (({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода}))), 2)
# - параболическое возрастание: быстро в начале, медленно в конце
#     round((V_b + (V_e - V_b) * (1 - (1 - (({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода}))) ** 2)), 2)
# - параболическое возрастание: медленно в начале, быстро в конце
#     round((V_b + (V_e - V_b) * ((({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода})) ** 2)), 2)
# Закат:
# - линейное убывание
#     round((Vb - (Vb - Ve) * (({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода}))), 2)
# - параболическое убывание: быстро в начале, медленно в конце
#     round((Vb - (Vb - Ve) * ((({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода})) ** 2)), 2)
# - параболическое убывание: медленно в начале, быстро в конце
#     round((Vb - (Vb - Ve) * math.sqrt(({T:current_time} - {T:начало периода}) / ({T:конец периода} - {T:начало периода}))), 2)

rules:
  # =============================================
  # ПОЛУЧЕНИЕ ДАННЫХ С ДАТЧИКОВ
  # =============================================
  - name: "Блок контроллера. Raspberry Pi 1 model b+. Процессор. Температура."
    code: "raspi_cpu_temperature"
    cron: "* * * * *"
    actions:
      - device: "raspi"
        method: "get"
        parametr: "cpu_temperature" 

  - name: "Блок контроллера. Блоки питания. Температура."
    code: "controller_power_temperature"
    cron: "* * * * *"
    actions:
      - device: "ds18b20"
        method: "get"
        parametr: "temperature"
        device_uid: "28-24020002e306"

  - name: "Блок контроллера. Датчик протечки. Нижний уровень."
    code: "controller_raindrop_level_down"
    cron: "* * * * *"
    actions: 
      # установка канала на вход
      - device: "mcp23017_controller"
        method: "set"
        mode: "input"
        channel: 1
      # чтение состояния канала  
      - device: "mcp23017_controller"
        method: "get"
        channel: 1

  - name: "Блок контроллера. Датчик протечки. Верхний уровень."
    code: "controller_raindrop_level_up"
    cron: "* * * * *"
    actions: 
      # установка канала на вход
      - device: "mcp23017_controller"
        method: "set"
        mode: "input"
        channel: 2
      # чтение состояния канала  
      - device: "mcp23017_controller"
        method: "get"
        channel: 2

  - name: "Крышка аквариума. Подкрышечное пространство. Температура."
    code: "aquarium_internal_temperature"
    cron: "* * * * *"
    actions:
      - device: "tca9548a_aquarium"
        method: "set"
        channel: 0
      - device: "am2320"
        method: "get"
        state: "temperature" 

  - name: "Крышка аквариума. Подкрышечное пространство. Влажность."
    code: "aquarium_internal_humidity"
    cron: "* * * * *"
    actions:
      - device: "tca9548a_aquarium"
        method: "set"
        channel: 0
      - device: "am2320"
        method: "get"
        state: "humidity" 

  - name: "Крышка аквариума. Отсек оборудования. Температура."
    code: "aquarium_external_temperature"
    cron: "* * * * *"
    actions:
      - device: "tca9548a_aquarium"
        method: "set"
        channel: 1
      - device: "am2320"
        method: "get"
        state: "temperature" 

  - name: "Крышка аквариума. Отсек оборудования. Влажность."
    code: "aquarium_external_humidity"
    cron: "* * * * *"
    actions:
      - device: "tca9548a_aquarium"
        method: "set"
        channel: 1
      - device: "am2320"
        method: "get"
        state: "humidity" 

  # =============================================
  # УПРАВЛЕНИЕ УСТРОЙСТВАМИ
  # =============================================
  - name: "Контроллер. Блок розеток. Фильтр. Режим работы: круглосуточно, без остановок"
    code: "controller_filter"
    cron: "*/5 * * * *"
    actions: 
      - device: "mcp23017_controller"
        method: "set"
        mode: "output"
        channel: 5
        state: 1
      - device: "mcp23017_controller"
        method: "get"
        channel: 5

  # =============================================
  # МОНИТОРИНГ
  # =============================================
  # Здесь будет блок правил, обрабатывающих текущие и предыдущие значения
  # и реакция на разницу этих данных


  #- name: "Рассвет. Плавный розжиг LED (08:00:00 -> 10:00:00) - медленно в начале, быстро в конце"
  #  code: "aquarium_led_15"
  #  condition: "{T:08:00:00} <= {T:current_time} and {T:current_time} < {T:10:00:00}"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_aquarium"
  #      method: "set"
  #      channel: 15
  #      duration: 10
  #      steps: 10
  #      state: "round((0 + (100 - 0) * ((({T:current_time} - {T:08:00:00}) / ({T:10:00:00} - {T:08:00:00})) ** 2)), 2)"
  #    - device: "pca9685_aquarium"
  #      method: "get"
  #      channel: 15
  #      state: "channel"

  #- name: "День. Ровное свечение LED (10:00:00 -> 21:00:00)"
  #  code: "aquarium_led_15"
  #  condition: "{T:10:00:00} <= {T:current_time} and {T:current_time} < {T:21:00:00}"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_aquarium"
  #      method: "set"
  #      channel: 15
  #      state: 100
  #    - device: "pca9685_aquarium"
  #      method: "get"
  #      channel: 15
  #      state: "channel"
  
  #- name: "Закат. Плавное угасание LED (21:00:00 -> 22:00:00) - быстро в начале, медленно в конце"
  #  code: "aquarium_led_15"
  #  condition: "{T:21:00:00} <= {T:current_time} and {T:current_time} < {T:22:00:00}"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_aquarium"
  #      method: "set"
  #      channel: 15
  #      duration: 10
  #      steps: 10
  #      state: "round((100 - (100 - 0) * ((({T:current_time} - {T:21:00:00}) / ({T:22:00:00} - {T:21:00:00})) ** 2)), 2)"
  #    - device: "pca9685_aquarium"
  #      method: "get"
  #      channel: 15
  #      state: "channel"

  #- name: "Ночь. Нет свечения LED (22:00:00 -> 08:00:00)"
  #  code: "aquarium_led_15"
  #  condition: "({T:22:00:00} <= {T:current_time} and {T:current_time} <= {T:23:59:59}) or ({T:00:00:00} <= {T:current_time} and {T:current_time} < {T:08:00:00})"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_aquarium"
  #      method: "set"
  #      channel: 15
  #      state: 0
  #    - device: "pca9685_aquarium"
  #      method: "get"
  #      channel: 15
  #      state: "channel"

  #- name: "Фильтр. Режим работы: круглосуточно, без остановок"
  #  code: "controller_filter"
  #  cron: "*/5 * * * *"
  #  actions: 
  #    - device: "mcp23017_controller"
  #      method: "set"
  #      channel: 0
  #      state: 1
  #    - device: "mcp23017_controller"
  #      method: "get"
  #      channel: 0
  #      state: "channel"

  # =============================================
  # ОБСЛУЖИВАНИЕ СИСТЕМЫ
  # =============================================
  #- name: "Зачистка данных, старше 30 дней"
  #  code: "cleanup_result_30day"
  #  cron: "0 0 * * *"
  #  actions: 
  #    - device: "function"
  #      method: "get"
  #      state: "get_count_result"
  #      sensors: []
  #      days: 30
  #    - device: "function"
  #      method: "set"
  #      state: "cleanup_result"
  #      sensors: []
  #      days: 30
  #    - device: "function"
  #      method: "set"
  #      state: "vacuum_db"




  #- name: "Выключение вентилятора"
  #  condition: "{S:am2320_up_internal_temperature} - {S:am2320_up_external_temperature} < 5"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_up"
  #      method: "set"
  #      channel: 0
  #      state: 258

  #- name: "Включение вентилятора (просто один из вариантов)" 
  #  condition: "{S:am2320_up_internal_temperature} - {S:am2320_up_external_temperature} >= 5"
  #  cron: "* * * * *"
  #  actions: 
  #    - device: "pca9685_up"
  #      method: "set"
  #      channel: 0
  #      state: "round(({S:am2320_up_internal_temperature} - {S:am2320_up_external_temperature}) * 100)"



