# Руководство по написанию Control Scripts

Control Scripts — это PHP-классы автоматизации, живущие в `server/ControlScripts/Scopes/`. Все файлы в этой папке загружаются автоматически при старте сервера.

---

## Структура класса

```php
<?php

namespace ControlScripts\Scopes;

use \SHServ\Entities\Device;

class MyScope extends \SHServ\Middleware\ControlScripts 
              implements \SHServ\Implements\ControlScriptsInterface {

    // Объявить связи relay <-> button для синхронизации индикаторов
    public function register_sync_map(): void { }

    // Подписаться на события от устройств
    public function register_events_handlers(): void { }

    // Зарегистрировать action-скрипты (запуск вручную через UI/API)
    public function register_actions_scripts(): void { }

    // Зарегистрировать regular-скрипты (запуск по cron)
    public function register_regular_scripts(): void { }
}
```

---

## Action-скрипты

Запускаются вручную через `POST /api/v1/scripts/actions/run` или из UI.

```php
$this->add_action_script([
    "alias"       => "kitchen_light_toggle",   // уникальный alias
    "name"        => "Свет на кухне",
    "icon"        => '<i class="ph ph-lightbulb"></i>',  // Phosphor Icons
    "description" => "Включить/выключить основной свет",
    "author"      => "Eugene Sukhodolskiy"
], function($params) {
    $relay_api = $this->devices()->by_alias("kitchen_relay")->device_api();

    if ($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) {
        $relay_api->toggle_channel(0);
    }

    return ["result" => true];
});
```

Включение/выключение конкретного action-скрипта:
- `GET /api/v1/scripts/actions/alias/{alias}/enable`
- `GET /api/v1/scripts/actions/alias/{alias}/disable`

---

## Regular-скрипты

Запускаются периодически через cron: `GET /cron/regular-scripts`.

```php
$this->add_regular_script([
    "alias" => "check_door_sensor",
    "name"  => "Проверка датчика двери",
], function() {
    $sensor = $this->devices()->by_alias("door_sensor");
    // ...
});
```

Включение/выключение:
- `GET /api/v1/scripts/actions/regular/{alias}/enable`
- `GET /api/v1/scripts/actions/regular/{alias}/disable`

---

## Event-хендлеры

Подписка на события от устройств. Обработчик вызывается **после** ответа устройству (через `fastcgi_finish_request`), поэтому может занимать время.

```php
// Нажатие кнопки (канал 0) конкретного устройства
$this->add_event_handler("button@my_btns(0).press", function(Device $device, array $data) {
    $relay = $this->devices()->by_alias("my_relay");
    $relay->device_api()->toggle_channel(0);
});

// Приход устройства онлайн
$this->add_event_handler("button@my_btns.online", function(Device $device, array $data) {
    // синхронизировать индикаторы
    $this->helper()->sync_btn_channels($this->sync_map(), $device->alias);
});
```

### Паттерны имён событий

| Паттерн | Пример | Когда срабатывает |
|---------|---------|------------------|
| `{event_name}` | `button_press` | Любое устройство, любое событие с таким именем |
| `{type}.{event_name}` | `button.button_press` | Все устройства типа `button` |
| `{type}@{alias}.{event_name}` | `button@kitchen_btns.online` | Конкретное устройство |
| `{type}({ch}).{event_name}` | `button(2).button_press` | Все устройства типа, канал 2 |
| `{type}@{alias}({ch}).{event_name}` | `button@kitchen_btns(2).button_press` | Конкретное устройство, канал 2 |

### Известные event_name (от устройств)

| Устройство | event_name | Описание |
|-----------|-----------|---------|
| button | `press` | Нажатие кнопки |
| button | `online` | Устройство вышло в сеть |
| relay | `limit_switch_activated` | Сработал концевик |
| hatch | `limit_switch_activated` | Сработал концевик закрытия |
| hatch | `calibration_failed` | Не удалась калибровка |
| sensor | `presence_changed` | Изменение присутствия в помещении |

---

## Sync map

Описывает связи «какой канал реле ↔ какие каналы кнопок». Используется для автоматической синхронизации индикаторов кнопок с состоянием реле.

```php
public function register_sync_map(): void {
    $this->add_sync_connection([
        ["type" => "relay",  "alias" => "kitchen_relay", "channel" => 0],
        ["type" => "button", "alias" => "kitchen_btns",  "channel" => 1],
        ["type" => "button", "alias" => "hall_btns",     "channel" => 0],
    ]);
}
```

Первый элемент в массиве — обычно реле (источник состояния). Остальные — кнопки, чьи индикаторы синхронизируются.

### Хелперы синхронизации

```php
// При нажатии кнопки — переключить реле и синхронизировать все кнопки
$relay_api->toggle_channel($relay_channel);
$this->helper()->sync_relay_to_btns($this->sync_map(), $relay_alias);

// При появлении кнопки онлайн — синхронизировать её индикаторы с реле
$this->helper()->sync_btn_channels($this->sync_map(), $btn_alias);
```

---

## Доступные методы базового класса

```php
$this->devices()         // → Models\Devices (поиск по alias, id, hard_id)
$this->helper()          // → DeviceScriptsHelper (синхронизация)
$this->sync_map()        // → текущий sync_map_storage
$this->add_event_handler($name, $cb)
$this->add_action_script($attrs, $cb)
$this->add_regular_script($attrs, $cb)
$this->add_sync_connection($entries)
```

---

## Управление Scope через API

Включение/выключение целого Scope (всех его скриптов):
- `GET /api/v1/scripts/actions/scope/{name}/enable`
- `GET /api/v1/scripts/actions/scope/{name}/disable`

Scope отключается через БД — при следующем запуске сервера его скрипты не зарегистрируются.

---

## Общий trait Common

`server/ControlScripts/Common.php` — trait с готовыми хелперами для Scope-классов:

```php
use \ControlScripts\Common;

// Зарегистрировать глобальный sync_map (все реле и кнопки системы)
$this->register_global_device_sync_map();

// Установить индикаторы заглушённых каналов при появлении кнопки онлайн
$this->btn_on_online("my_btns", [/* muted channels */]);

// Установить обработчики нажатий для кнопок из sync_map
$this->set_btns_click_handlers("my_btns");
```
