콘텐츠로 이동

Functions

페이지: 설정 -> 기능

지원되는 기능의 전체 목록은 지원되는 기능을 참조하세요.

기능 컨트롤러는 종종 입력 및 출력을 사용하는 작업을 수행합니다.

참고

"마지막"은 기능이 데이터베이스에서 마지막(최신) 측정값만 가져온다는 것을 의미합니다. "과거"는 기능이 설정된 "최대 연령(초)"까지 현재부터 모든 측정값을 가져온다는 것을 의미합니다(예: 측정값이 10초마다 수집되고 최대 연령이 60초로 설정된 경우, 평균적으로 기능이 작동하는 데 6개의 측정값이 반환됩니다).

목록에서 함수 찾기~

페이지 상단의 드롭다운은 함수를 제어·자동화, 트리거, 사용자 함수로 묶어 보여줍니다. 검색창은 표시 이름과 모듈 이름 양쪽으로 걸립니다.

사용자 정의 기능~

AoT에는 사용자 생성 기능을 AoT 시스템에서 사용할 수 있도록 하는 사용자 정의 기능 가져오기 시스템이 있습니다. 사용자 정의 기능은 [톱니바퀴 아이콘] -> 구성 -> 사용자 정의 기능 페이지에서 업로드할 수 있습니다. 가져온 후에는 설정 -> 기능 페이지에서 사용할 수 있습니다.

작동하는 기능 모듈을 개발한 경우, 새 GitHub 이슈 생성 또는 풀 리퀘스트를 고려해 보세요. 해당 모듈이 기본 제공 세트에 포함될 수 있습니다.

적절한 포맷팅 예제를 보려면 디렉토리 AoT/aot/functions에 있는 기본 제공 모듈을 열어보세요.

또한, 디렉토리 AoT/aot/functions/examples에는 사용자 정의 기능 예제가 포함되어 있습니다.

기본 제공 세트에 포함되지 않은 사용자 정의 모듈에 전념하는 또 다른 GitHub 저장소는 aot-inc/AoT-custom에서 확인할 수 있습니다.

새로운 측정값/단위가 필요한 기능의 경우, [톱니바퀴 아이콘] -> 구성 -> 측정값 페이지에서 추가할 수 있습니다.

PID 컨트롤러~

비례-적분-미분(PID) 컨트롤러는 시스템 제어를 위해 산업 전반에서 사용되는 제어 루프 피드백 메커니즘입니다. 이는 온도와 같은 측정 가능한 조건을 효율적으로 원하는 상태로 가져가고, 오버슈트와 진동을 최소화하며 그 상태를 유지합니다. 잘 조정된 PID 컨트롤러는 설정값에 빠르게 도달하고, 오버슈트를 최소화하며, 설정값을 적은 진동으로 유지합니다.

PID 설정은 PID가 활성화된 동안 변경할 수 있으며, 새로운 설정은 즉시 적용됩니다. 컨트롤러가 일시 중지된 동안 설정이 변경되면, 컨트롤러가 작동을 재개할 때 변경된 값이 사용됩니다.

PID 컨트롤러 옵션~

설정 설명
활성화/비활성화 특정 PID 컨트롤러를 켜거나 끕니다.
일시 중지 일시 중지 상태에서는 제어 변수가 업데이트되지 않으며 PID가 관련 출력을 켜지 않습니다. 현재 PID 출력 값을 잃지 않고 설정을 변경할 수 있습니다.
유지 유지 상태에서는 제어 변수가 업데이트되지 않지만 PID가 관련 출력을 켭니다. 현재 PID 출력 값을 잃지 않고 설정을 변경할 수 있습니다.
재개 유지 또는 일시 중지 상태에서 PID 컨트롤러를 재개합니다.
방향 조절하려는 방향입니다. 예를 들어 온도를 올리기만 하면 된다면 "상승"으로 설정하고, 상승 및 하강 모두 필요하다면 "양방향"으로 설정합니다.
주기 PID가 측정을 수행하고 업데이트되며 출력을 조정하는 간격입니다.
시작 오프셋 (초) 첫 계산/측정을 시도하기 전에 대기할 시간입니다.
최대 연령 센서 측정값의 최대 허용 연령(초)입니다. 측정값이 이 연령보다 오래되었으면 측정값이 무시되고 PID는 출력을 작동시키지 않습니다. 이는 PID가 최신 측정값만 사용하도록 보장하는 안전 장치입니다.
설정값 환경을 조절하려는 특정 값입니다. 예를 들어 습도를 60%로 조절하려면 60을 입력합니다.
밴드 (+/- 설정값) 히스테리시스 옵션입니다. 0이 아닌 값으로 설정하면 설정값이 밴드로 변환됩니다. 밴드의 최대값은 설정값+밴드, 최소값은 설정값-밴드입니다. 상승 시 밴드 최대값을 초과하면 PID는 대기하고, 조건이 밴드 최소값 아래로 떨어지면 조절을 재개합니다. 하강 시 밴드 최소값 아래로 떨어지면 PID는 대기하고, 조건이 밴드 최대값 위로 올라가면 조절을 재개합니다. 양방향으로 설정하면 밴드의 외부 최소 및 최대값에서만 조절이 이루어지며 밴드 내에서는 중지됩니다. 히스테리시스를 비활성화하려면 0으로 설정합니다.
하강을 음수로 저장 체크하면 모든 출력 변수(PID 및 출력 지속 시간/듀티 사이클)가 측정값 데이터베이스에 음수 값으로 저장됩니다. 이는 PID가 현재 상승 중인지 하강 중인지 그래프에 표시하는 데 유용합니다. 모든 값을 양수로 저장하려면 비활성화합니다.
KP 게인 비례 계수(0 이상). 오류의 현재 값을 고려합니다. 예를 들어 오류가 크고 양수일 경우 제어 출력도 크고 양수가 됩니다.
KI 게인 적분 계수(0 이상). 오류의 과거 값을 고려합니다. 예를 들어 현재 출력이 충분히 강하지 않으면 오류의 적분이 시간이 지남에 따라 누적되고 컨트롤러는 더 강한 동작을 적용합니다.
KD 게인 미분 계수(0 이상). 현재 변화율을 기반으로 오류의 예측된 미래 값을 고려합니다.
적분기 최소값 Ki_total을 계산할 때 허용되는 적분기의 최소값입니다. (Ki_total = Ki * 적분기; PID 출력 = Kp_total + Ki_total + Kd_total)
적분기 최대값 Ki_total을 계산할 때 허용되는 적분기의 최대값입니다. (Ki_total = Ki * 적분기; PID 출력 = Kp_total + Ki_total + Kd_total)
출력 (상승/하강) 특정 환경 조건을 상승 또는 하강시키는 출력입니다. 예를 들어 온도를 상승시키는 경우 히팅 패드나 코일일 수 있습니다.
최소 켜짐 지속 시간, 듀티 사이클 또는 양 (상승/하강) PID 출력이 Output (하강)을 켜기 전에 도달해야 하는 최소값입니다. PID 출력이 이 값보다 작으면 지속 시간 출력은 켜지지 않으며 PWM 출력은 Always Min이 활성화되지 않는 한 꺼집니다.
최대 켜짐 지속 시간, 듀티 사이클 또는 양 (상승/하강) Output (상승)에 설정할 수 있는 최대 지속 시간, 볼륨 또는 듀티 사이클입니다. PID 출력이 이 값을 초과하면 여기 설정된 최대값이 사용됩니다.
최소 꺼짐 지속 시간 (상승/하강) 켜짐/꺼짐(지속 시간) 출력의 경우, 출력이 다시 켜지기 전에 꺼져 있어야 하는 최소 시간입니다. 이는 빠른 전원 사이클링으로 인해 손상될 수 있는 장치(예: 냉장고)에 유용합니다.
항상 최소 (상승/하강) PWM 출력 전용. 활성화하면 듀티 사이클이 최소값 아래로 설정되지 않습니다.
설정값 추적 방법 시간에 따라 설정값을 변경하는 방법을 설정합니다.

PID 출력 계산~

PID 컨트롤러는 지속 시간, 볼륨 또는 PWM 듀티 사이클과 같은 다양한 출력 유형을 제어할 수 있습니다. 대부분의 출력 유형에서는 PID 출력(제어 변수)이 비례적으로 작동합니다(예: 출력 지속 시간 = PID 제어 변수). 그러나 듀티 사이클을 출력할 때는 듀티 사이클 = (제어 변수 / 주기) * 100으로 계산됩니다.

참고

제어 변수 = P 출력 + I 출력 + D 출력. 듀티 사이클은 0 - 100 % 범위와 설정된 최소 듀티 사이클 및 최대 듀티 사이클 내에서 제한됩니다. 출력 지속 시간은 설정된 최소 켜짐 지속 시간 및 최대 켜짐 지속 시간에 의해 제한되며, 출력 볼륨도 유사하게 제한됩니다.

PID 튜닝~

PID 튜닝은 사용되는 출력 장치와 제어 대상 환경 또는 시스템에 따라 복잡한 과정이 될 수 있습니다. 큰 변동이 있는 시스템은 안정적인 시스템보다 제어하기 더 어렵습니다. 마찬가지로 적합하지 않은 출력 장치는 PID 튜닝을 어렵게 하거나 불가능하게 만들 수 있습니다. PID 컨트롤러의 작동 방식과 튜닝 이론을 배우면 PID 컨트롤러를 더 잘 운영할 수 있을 뿐만 아니라 시스템 개발, 출력 장치 선택 및 구현에도 도움이 됩니다.

PID 튜닝 리소스~

PID 제어 이론~

PID 컨트롤러는 단순한 제어부터 복잡한 제어까지 처리할 수 있는 능력으로 인해 산업 환경에서 가장 일반적으로 사용되는 규제 컨트롤러입니다. PID 컨트롤러는 비례, 적분, 미분의 세 가지 경로를 가집니다.

P(비례)는 오차를 KP 상수로 곱하여 출력 값을 생성합니다. 오차가 클수록 비례 출력도 커집니다.

I(적분)는 오차를 KI로 곱한 후 이를 적분합니다(KI · 1/s). 시간이 지남에 따라 오차가 변화하면 적분은 이를 지속적으로 합산하고 KI 상수로 곱합니다. 적분은 제어 시스템에서 지속적인 오차를 제거하는 데 사용됩니다. KP만 사용하여 출력이 지속적인 오차를 생성하는 경우(즉, 센서 측정값이 설정값에 도달하지 못하는 경우), 적분은 출력 값을 증가시켜 오차를 줄이고 설정값에 도달하도록 합니다.

D(미분)는 오차를 KD로 곱한 후 이를 미분합니다(KD · s). 시간이 지남에 따라 오차 변화율이 달라지면 출력 신호도 변화합니다. 오차 변화가 빠를수록 미분 경로가 커지며 출력 변화율을 감소시킵니다. 이는 설정값의 오버슈트와 언더슈트(진동)를 줄이는 효과를 가집니다.

KP, KI, KD 게인은 P, I, D 변수가 최종 PID 출력 값에 얼마나 영향을 미치는지를 결정합니다. 예를 들어, 게인 값이 클수록 해당 변수가 출력에 미치는 영향이 커집니다.

PID 컨트롤러의 출력은 여러 방식으로 사용할 수 있습니다. 간단한 사용 사례는 이 값을 주기적 간격(Period) 동안 출력이 켜지는 시간(초)으로 사용하는 것입니다. 예를 들어, 주기가 30초로 설정된 경우 PID 방정식은 원하는 측정값과 실제 측정값을 사용하여 매 30초마다 PID 출력을 계산합니다. 이 주기 동안 출력이 켜지는 시간이 길수록 시스템에 더 큰 영향을 미칩니다. 예를 들어, 출력이 매 30초마다 15초 동안 켜져 있으면 50 % 듀티 사이클이며, 출력이 매 30초마다 30초 동안 켜져 있으면 100 % 듀티 사이클로 시스템에 두 배의 영향을 미칩니다. PID 컨트롤러는 실제 측정값이 원하는 측정값과 얼마나 차이가 나는지(오차)를 기반으로 출력을 계산합니다. 오차가 증가하거나 지속되면 출력이 증가하여 주기 내에서 출력이 더 오래 켜지게 되고, 이는 일반적으로 측정 조건을 변경하여 오차를 줄이는 결과를 가져옵니다. 오차가 줄어들면 제어 변수가 감소하여 출력이 더 짧은 시간 동안 켜지게 됩니다. 잘 조정된 PID 컨트롤러의 궁극적인 목표는 실제 측정값을 설정값으로 빠르게 가져가고, 오버슈트를 최소화하며, 설정값을 최소한의 진동으로 유지하는 것입니다.

온도를 예로 들면, 프로세스 변수(PV)는 측정된 온도, 설정값(SP)은 원하는 온도, 그리고 오차(e)는 측정된 온도와 원하는 온도 간의 차이를 나타냅니다(즉, 실제 온도가 너무 높거나 낮은 정도를 나타냄). 이 오차는 PID의 세 가지 구성 요소 각각에 의해 조작되어 조작 변수(MV) 또는 제어 변수(CV)라고 불리는 출력을 생성합니다. 각 경로가 출력 값에 기여하는 정도를 제어하기 위해 각 경로는 게인(KP, KI, KD)으로 곱해집니다. 게인을 조정하면 시스템이 각 경로에 얼마나 민감하게 반응하는지가 영향을 받습니다. 세 경로를 모두 합산하면 PID 출력이 생성됩니다. 게인을 0으로 설정하면 해당 경로는 출력에 기여하지 않으며 사실상 꺼진 상태가 됩니다.

출력은 여러 방식으로 사용될 수 있지만, 이 컨트롤러는 출력을 사용하여 측정된 값(PV)에 영향을 미치도록 설계되었습니다. 이 피드백 루프는 적절히 튜닝된 PID 컨트롤러를 통해 짧은 시간 안에 설정값에 도달하고, 진동을 최소화하며, 방해 요소에 빠르게 반응할 수 있습니다.

따라서 온도를 조절하려는 경우, 센서는 온도 센서가 되고 피드백 장치는 가열 및 냉각이 가능한 장치가 됩니다. 온도가 설정값보다 낮으면 출력 값이 양수가 되어 히터가 작동합니다. 온도는 원하는 온도를 향해 상승하며, 이로 인해 오차가 감소하고 더 낮은 출력이 생성됩니다. 이 피드백 루프는 오차가 0에 도달할 때까지 계속됩니다(이 시점에서 출력은 0이 됩니다). 온도가 설정값을 초과하여 계속 상승하는 경우(허용 가능한 범위 내일 수 있음), PID는 음수 출력을 생성하여 냉각 장치가 온도를 다시 낮추고 오차를 줄이도록 할 수 있습니다. 온도가 냉각 장치의 도움 없이도 자연적으로 낮아질 수 있다면, 냉각 장치를 생략하고 시스템을 단순화할 수 있습니다.

KP, KI, KD를 효과적으로 활용하는 컨트롤러를 구현하는 것은 어려울 수 있으며, 종종 불필요하기도 합니다. 예를 들어, KI와 KD를 0으로 설정하면 해당 경로가 꺼지고 매우 간단한 P 컨트롤러가 생성됩니다. 또한 PI 컨트롤러도 인기가 많습니다. KP만 활성화된 상태로 시작한 후 KP와 KI를 실험적으로 조합해 보고, 마지막으로 세 가지를 모두 사용하는 것을 권장합니다. 시스템은 공기 공간의 부피, 단열 정도, 연결된 장치의 영향 정도 등 다양한 요인에 따라 달라지므로, 각 경로는 실험을 통해 효과적인 출력을 생성하도록 조정해야 합니다.

빠른 설정 예제~

이 예제 설정은 특정 방향으로의 조절을 구성하는 방법을 보여주기 위한 것이며, KP, KI, KD 게인을 구성하기 위한 이상적인 값을 달성하려는 것이 아닙니다. PID 값을 결정하기 위해 개발된 다양한 기술과 방법을 다루는 온라인 리소스가 많이 있으므로(여기, 여기, 여기, 여기, 여기), 변수를 이해하고 효과적으로 구현하기 위해 자체 연구와 실험을 수행하는 것이 필수적입니다.

PID 값의 변동성을 단순히 예로 들자면, 한 설정에서는 온도 PID 값(상승 조절)이 KP = 30, KI = 1.0, KD = 0.5였고, 습도 PID 값(상승 조절)이 KP = 1.0, KI = 0.2, KD = 0.5였습니다. 또한, 이러한 값이 최적은 아니었지만 환경 챔버 조건에서는 잘 작동했습니다.

정확한 온도 조절~

이 시스템은 두 개의 조절 장치(하나는 가열, 다른 하나는 냉각)를 사용하여 특정 온도로 온도를 올리고 내리도록 설정합니다.

센서를 추가한 다음, 각 센서에 적합한 장치와 핀/주소를 저장하고 센서를 활성화합니다.

두 개의 출력을 추가한 다음, 각 GPIO와 On Trigger 상태를 저장합니다.

PID를 추가한 다음, 새로 생성된 센서를 선택합니다. 설정값(Setpoint)을 원하는 온도로 변경하고, 조절 방향(Regulate Direction)을 "양방향(Both)"으로 설정합니다. 상승 출력(Raise Output)을 가열 장치에 연결된 릴레이로 설정하고, 하강 릴레이(Lower Relay)를 냉각 장치에 연결된 릴레이로 설정합니다.

KP = 1, KI = 0, KD = 0으로 설정한 다음 PID를 활성화합니다.

온도가 설정값보다 낮으면 PID 컨트롤러가 결정한 간격에 따라 히터가 활성화되어 온도가 설정값까지 상승합니다. 온도가 설정값(또는 설정값 + 버퍼)보다 높아지면 냉각 장치가 활성화되어 온도가 설정값으로 돌아갑니다. 온도가 설정값에 도달하지 않는 경우, KP 값을 증가시키고 시스템에 미치는 영향을 확인합니다. 읽기 간격(Read Interval)과 KP만 조정하여 적절한 조절을 달성하기 위해 실험해 보세요. KP만으로 작동하는 조절이 이루어질 때까지 KI와 KD는 0으로 유지하세요.

6~12시간의 시간 범위에서 그래프를 확인하여 온도가 설정값에 얼마나 잘 조절되는지 확인하세요. "잘 조절된다"는 것은 특정 응용 프로그램과 허용 오차에 따라 다릅니다. 대부분의 PID 컨트롤러 응용 프로그램에서는 적절한 온도가 합리적인 시간 내에 도달하고 설정값 주변에서 진동이 적은 것을 선호합니다.

조절이 이루어진 후, KP를 약간(~25%) 줄이고 KI를 낮은 값(예: 0.1 또는 0.01)으로 증가시키며 PID를 시작하고 컨트롤러가 얼마나 잘 조절하는지 관찰하세요. KI를 천천히 증가시켜 조절이 빠르고 진동이 적어지도록 만드세요. 이 시점에서 시스템과 KD 값을 실험할 준비가 되었으며, KP와 KI가 조정된 후 KD를 실험해 보세요.

고온 조절~

양방향 조절이 필요하지 않은 경우 시스템을 단순화할 수 있습니다. 예를 들어, 냉각이 불필요한 경우 이를 시스템에서 제거하고 상승 조절만 사용할 수 있습니다.

정확한 온도 조절 예제와 동일한 구성을 사용하되, 조절 방향(Regulate Direction)을 "상승(Raise)"으로 변경하고 "하강 릴레이(Down Relay)" 섹션은 건드리지 마세요.

PID 자동 튜닝~

경고

이 기능은 실험적입니다. PID의 이론, 작동 방식 및 튜닝에 익숙해진 후 사용하는 것이 좋습니다.

자동 튜닝 기능은 PID 컨트롤러에서 사용할 적절한 Kp, Ki, Kd 게인을 결정하는 데 유용한 독립형 컨트롤러입니다. 자동 튜너는 출력을 조작하고 특정 환경/시스템에서 측정된 반응을 분석합니다. PID 게인을 계산하기 위해 충분한 데이터를 수집하려면 선택한 출력으로 시스템을 여러 번 교란해야 합니다. 이 기능을 사용하려면 측정값과 측정된 특정 조건을 조정할 수 있는 출력을 선택하세요. 그런 다음, 노이즈 밴드와 출력 단계를 구성하고 기능을 활성화합니다. 자동 튜너의 로그는 데몬 로그([톱니바퀴 아이콘] -> AoT 로그 -> 데몬 로그)에 나타납니다. 자동 튜닝이 수행되는 동안, 대시보드 그래프를 생성하여 측정값과 출력을 포함시키는 것이 좋습니다. 이를 통해 PID 자동 튜너가 수행하는 작업과 구성된 자동 튜닝 설정에서 발생할 수 있는 문제를 확인할 수 있습니다. 자동 튜닝이 완료되는 데 시간이 오래 걸리는 경우, 조작 중인 시스템에 충분한 안정성이 없어 신뢰할 수 있는 PID 게인 세트를 계산하지 못할 수 있습니다. 이는 시스템에 너무 많은 교란이 있거나 조건이 너무 빠르게 변하여 일관된 측정값 진동을 얻을 수 없는 경우일 수 있습니다. 이 경우, 시스템을 수정하여 안정성을 높이고 일관된 측정값 진동을 생성하도록 시도하세요. 자동 튜닝이 성공적으로 완료되면, PID 컨트롤러가 이를 처리할 수 있도록 교란을 다시 도입하여 추가 튜닝을 수행할 수 있습니다.

설정 설명
측정값 출력이 영향을 미칠 특정 조건을 측정하는 입력 또는 기능 측정값입니다. 예를 들어, 온도 측정값일 수 있으며 출력은 히터일 수 있습니다.
출력 측정값에 영향을 미칠 출력입니다. 자동 튜닝 기능은 이 출력을 주기적으로 켜서 측정값이 설정값을 초과하도록 합니다.
주기 출력이 켜지는 간격입니다. 이는 PID 컨트롤러에 사용할 주기와 동일하게 설정해야 합니다. 다른 주기는 자동 튜닝이 생성하는 PID 게인에 큰 영향을 미칠 수 있습니다.
설정값 원하는 측정 조건 값입니다. 예를 들어, 온도를 측정하는 경우, 현재 온도보다 몇 도 높은 값으로 설정하여 출력이 활성화될 때 온도가 설정값을 초과하도록 해야 합니다.
노이즈 밴드 출력이 꺼지기 전에 측정 조건이 설정값을 초과해야 하는 범위입니다. 또한, 출력이 다시 켜지기 전에 측정 조건이 설정값 아래로 떨어져야 하는 범위입니다.
출력 단계 PID 주기마다 출력이 켜지는 시간(초)입니다. 예를 들어, 50% 출력으로 자동 튜닝하려면 출력 단계를 PID 주기의 절반 값으로 설정하세요.
방향 출력이 측정값에 영향을 미치는 방향입니다. 예를 들어, 히터는 온도를 올리고, 냉각기는 온도를 낮춥니다.

일반적인 그래프 출력은 다음과 같습니다:

그리고 일반적인 데몬 로그 출력은 다음과 같습니다:

2018-08-04 23:32:20,876 - aot.pid_3b533dff - INFO - Activated in 187.2 ms
2018-08-04 23:32:20,877 - aot.pid_autotune - INFO - PID Autotune started
2018-08-04 23:33:50,823 - aot.pid_autotune - INFO -
2018-08-04 23:33:50,830 - aot.pid_autotune - INFO - Cycle: 19
2018-08-04 23:33:50,831 - aot.pid_autotune - INFO - switched state: relay step down
2018-08-04 23:33:50,832 - aot.pid_autotune - INFO - input: 32.52
2018-08-04 23:36:00,854 - aot.pid_autotune - INFO -
2018-08-04 23:36:00,860 - aot.pid_autotune - INFO - Cycle: 45
2018-08-04 23:36:00,862 - aot.pid_autotune - INFO - found peak: 34.03
2018-08-04 23:36:00,863 - aot.pid_autotune - INFO - peak count: 1
2018-08-04 23:37:20,802 - aot.pid_autotune - INFO -
2018-08-04 23:37:20,809 - aot.pid_autotune - INFO - Cycle: 61
2018-08-04 23:37:20,810 - aot.pid_autotune - INFO - switched state: relay step up
2018-08-04 23:37:20,811 - aot.pid_autotune - INFO - input: 31.28
2018-08-04 23:38:30,867 - aot.pid_autotune - INFO -
2018-08-04 23:38:30,874 - aot.pid_autotune - INFO - Cycle: 75
2018-08-04 23:38:30,876 - aot.pid_autotune - INFO - found peak: 32.17
2018-08-04 23:38:30,878 - aot.pid_autotune - INFO - peak count: 2
2018-08-04 23:38:40,852 - aot.pid_autotune - INFO -
2018-08-04 23:38:40,858 - aot.pid_autotune - INFO - Cycle: 77
2018-08-04 23:38:40,860 - aot.pid_autotune - INFO - switched state: relay step down
2018-08-04 23:38:40,861 - aot.pid_autotune - INFO - input: 32.85
2018-08-04 23:40:50,834 - aot.pid_autotune - INFO -
2018-08-04 23:40:50,835 - aot.pid_autotune - INFO - Cycle: 103
2018-08-04 23:40:50,836 - aot.pid_autotune - INFO - found peak: 33.93
2018-08-04 23:40:50,836 - aot.pid_autotune - INFO - peak count: 3
2018-08-04 23:42:05,799 - aot.pid_autotune - INFO -
2018-08-04 23:42:05,805 - aot.pid_autotune - INFO - Cycle: 118
2018-08-04 23:42:05,806 - aot.pid_autotune - INFO - switched state: relay step up
2018-08-04 23:42:05,807 - aot.pid_autotune - INFO - input: 31.27
2018-08-04 23:43:15,816 - aot.pid_autotune - INFO -
2018-08-04 23:43:15,822 - aot.pid_autotune - INFO - Cycle: 132
2018-08-04 23:43:15,824 - aot.pid_autotune - INFO - found peak: 32.09
2018-08-04 23:43:15,825 - aot.pid_autotune - INFO - peak count: 4
2018-08-04 23:43:25,790 - aot.pid_autotune - INFO -
2018-08-04 23:43:25,796 - aot.pid_autotune - INFO - Cycle: 134
2018-08-04 23:43:25,797 - aot.pid_autotune - INFO - switched state: relay step down
2018-08-04 23:43:25,798 - aot.pid_autotune - INFO - input: 32.76
2018-08-04 23:45:30,802 - aot.pid_autotune - INFO -
2018-08-04 23:45:30,808 - aot.pid_autotune - INFO - Cycle: 159
2018-08-04 23:45:30,810 - aot.pid_autotune - INFO - found peak: 33.98
2018-08-04 23:45:30,811 - aot.pid_autotune - INFO - peak count: 5
2018-08-04 23:45:30,812 - aot.pid_autotune - INFO -
2018-08-04 23:45:30,814 - aot.pid_autotune - INFO - amplitude: 0.9099999999999989
2018-08-04 23:45:30,815 - aot.pid_autotune - INFO - amplitude deviation: 0.06593406593406595
2018-08-04 23:46:40,851 - aot.pid_autotune - INFO -
2018-08-04 23:46:40,857 - aot.pid_autotune - INFO - Cycle: 173
2018-08-04 23:46:40,858 - aot.pid_autotune - INFO - switched state: relay step up
2018-08-04 23:46:40,859 - aot.pid_autotune - INFO - input: 31.37
2018-08-04 23:47:55,860 - aot.pid_autotune - INFO -
2018-08-04 23:47:55,866 - aot.pid_autotune - INFO - Cycle: 188
2018-08-04 23:47:55,868 - aot.pid_autotune - INFO - found peak: 32.36
2018-08-04 23:47:55,869 - aot.pid_autotune - INFO - peak count: 6
2018-08-04 23:47:55,870 - aot.pid_autotune - INFO -
2018-08-04 23:47:55,871 - aot.pid_autotune - INFO - amplitude: 0.9149999999999979
2018-08-04 23:47:55,872 - aot.pid_autotune - INFO - amplitude deviation: 0.032786885245900406
2018-08-04 23:47:55,873 - aot.pid_3b533dff - INFO - time:  16 min
2018-08-04 23:47:55,874 - aot.pid_3b533dff - INFO - state: succeeded
2018-08-04 23:47:55,874 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,875 - aot.pid_3b533dff - INFO - rule: ziegler-nichols
2018-08-04 23:47:55,876 - aot.pid_3b533dff - INFO - Kp: 0.40927018474290117
2018-08-04 23:47:55,877 - aot.pid_3b533dff - INFO - Ki: 0.05846588600007114
2018-08-04 23:47:55,879 - aot.pid_3b533dff - INFO - Kd: 0.7162385434443115
2018-08-04 23:47:55,880 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,881 - aot.pid_3b533dff - INFO - rule: tyreus-luyben
2018-08-04 23:47:55,887 - aot.pid_3b533dff - INFO - Kp: 0.3162542336649691
2018-08-04 23:47:55,889 - aot.pid_3b533dff - INFO - Ki: 0.010165091543194185
2018-08-04 23:47:55,890 - aot.pid_3b533dff - INFO - Kd: 0.7028026111719073
2018-08-04 23:47:55,891 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,892 - aot.pid_3b533dff - INFO - rule: ciancone-marlin
2018-08-04 23:47:55,892 - aot.pid_3b533dff - INFO - Kp: 0.21083615577664605
2018-08-04 23:47:55,893 - aot.pid_3b533dff - INFO - Ki: 0.06626133746674728
2018-08-04 23:47:55,893 - aot.pid_3b533dff - INFO - Kd: 0.3644161687558038
2018-08-04 23:47:55,894 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,894 - aot.pid_3b533dff - INFO - rule: pessen-integral
2018-08-04 23:47:55,895 - aot.pid_3b533dff - INFO - Kp: 0.49697093861638
2018-08-04 23:47:55,895 - aot.pid_3b533dff - INFO - Ki: 0.0887428626786794
2018-08-04 23:47:55,896 - aot.pid_3b533dff - INFO - Kd: 1.04627757151908
2018-08-04 23:47:55,896 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,897 - aot.pid_3b533dff - INFO - rule: some-overshoot
2018-08-04 23:47:55,898 - aot.pid_3b533dff - INFO - Kp: 0.23191977135431066
2018-08-04 23:47:55,898 - aot.pid_3b533dff - INFO - Ki: 0.03313066873337365
2018-08-04 23:47:55,899 - aot.pid_3b533dff - INFO - Kd: 1.0823160212047374
2018-08-04 23:47:55,899 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,900 - aot.pid_3b533dff - INFO - rule: no-overshoot
2018-08-04 23:47:55,900 - aot.pid_3b533dff - INFO - Kp: 0.1391518628125864
2018-08-04 23:47:55,901 - aot.pid_3b533dff - INFO - Ki: 0.01987840124002419
2018-08-04 23:47:55,901 - aot.pid_3b533dff - INFO - Kd: 0.6493896127228425
2018-08-04 23:47:55,902 - aot.pid_3b533dff - INFO -
2018-08-04 23:47:55,902 - aot.pid_3b533dff - INFO - rule: brewing
2018-08-04 23:47:55,903 - aot.pid_3b533dff - INFO - Kp: 5.566074512503456
2018-08-04 23:47:55,904 - aot.pid_3b533dff - INFO - Ki: 0.11927040744014512
2018-08-04 23:47:55,904 - aot.pid_3b533dff - INFO - Kd: 4.101408080354794

조건부 기능~

조건부 기능은 사용자가 생성한 Python 코드를 기반으로 간단한 작업부터 복잡한 작업까지 수행하는 데 사용됩니다. 조건부 기능은 Python 3 코드를 실행하고, 코드 내에서 AoT와 상호작용하기 위해 조건(Conditions) 및 작업(Actions)을 사용할 수 있도록 합니다. 조건은 일반적으로 AoT에서 데이터를 가져오는 데 사용되며(예: 입력 측정값), 작업은 AoT에 영향을 미치는 데 사용됩니다(예: 출력 작동 또는 PID 컨트롤러 일시 중지). 추가한 각 조건과 작업은 Python 코드에서 사용하는 방법을 보여주는 설명과 예제 코드를 제공합니다.

참고

TimeoutRun Python Code가 실행되는 데 걸리는 시간보다 길게 설정해야 합니다(Timeout이 너무 짧게 설정되면 코드의 일부만 실행될 수 있습니다).

참고

PeriodRun Python Code가 실행되는 데 걸리는 시간보다 길게 설정해야 합니다. 그렇지 않으면 이전 실행이 끝나기 전에 코드가 다시 실행됩니다.

참고

코드는 AoT가 실행되는 동일한 Python 가상 환경 내에서 실행됩니다. 따라서 코드에서 Python 라이브러리를 사용하려면 해당 환경에 라이브러리를 설치해야 합니다. 이 가상 환경은 /opt/AoT/env에 위치하며, 예를 들어 "my_library"를 pip로 설치하려면 sudo /opt/AoT/env/bin/pip install my_library를 실행하면 됩니다.

조건부 옵션~

설정 설명
Python 코드 가져오기 Python 라이브러리를 가져오는 데 사용되는 Python 3 코드입니다. 이는 조건부 기능 코드가 생성될 때 클래스가 생성되기 전에 실행됩니다.
Python 코드 초기화 클래스의 초기화 중에 실행되는 Python 3 코드입니다(__init__() 내에서 실행). 여기에서 클래스 내에서 사용할 변수를 초기화합니다.
Python 코드 실행 설정된 주기마다 실행되는 Python 3 코드입니다. 여기에서 조건과 작업이 실행됩니다. 조건 또는 작업이 추가되면 각 조건 또는 작업에 대해 실행할 수 있는 함수가 해당 조건 또는 작업 위에 표시됩니다.
Python 코드 상태 다른 컨트롤러 및 위젯에 정보를 전달할 수 있는 딕셔너리를 반환할 수 있습니다. 예를 들어, 기능 상태 위젯은 대시보드에 이 정보를 표시합니다. 정보를 반환하지 않으려면 이 코드를 제거할 수 있습니다.
주기(초) `Run Python Code`가 실행되는 주기(초)입니다.
시작 오프셋(초) 조건부 기능이 활성화된 후 처음 실행되기 전에 대기할 시간(초)입니다.
로그 레벨: 디버그 데몬 로그에 디버그 라인을 표시합니다.
메시지에 코드 포함 작업에 전달되는 메시지(self.message)에 Python 코드를 포함합니다.

조건은 Run Python Code 내에서 사용할 수 있는 함수로, 특정 정보를 반환합니다.

조건 설명
측정값 (단일, 최신) 입력 또는 장치에서 최신 측정값을 가져옵니다. 최대 연령(초)을 설정하여 값을 허용할 시간을 제한할 수 있습니다. 최신 값이 이 기간보다 오래된 경우, "None"이 반환됩니다.
측정값 (단일, 과거, 평균) 입력 또는 장치에서 과거 측정값을 가져온 후 평균을 계산합니다. 최대 연령(초)을 설정하여 값을 허용할 시간을 제한할 수 있습니다. 모든 값이 이 기간보다 오래된 경우, "None"이 반환됩니다.
측정값 (단일, 과거, 합계) 입력 또는 장치에서 과거 측정값을 가져온 후 합계를 계산합니다. 최대 연령(초)을 설정하여 값을 허용할 시간을 제한할 수 있습니다. 모든 값이 이 기간보다 오래된 경우, "None"이 반환됩니다.
측정값 (다중, 과거) 입력 또는 장치에서 과거 측정값을 가져옵니다. 최대 연령(초)을 설정하여 값을 허용할 시간을 제한할 수 있습니다. 이 기간 내에 값을 찾을 수 없는 경우, "None"이 반환됩니다. 이는 "측정값 (단일)" 조건과 다르게 'time' 및 'value' 키 쌍을 포함한 딕셔너리 목록을 반환합니다.
GPIO 상태 현재 GPIO 상태를 가져오며, HIGH일 경우 1, LOW일 경우 0을 반환합니다. 최신 값이 이 기간보다 오래된 경우, "None"이 반환됩니다.
출력 상태 출력이 현재 켜져 있으면 'on', 꺼져 있으면 'off'를 반환합니다.
출력 켜짐 지속 시간 출력이 현재 켜져 있는 시간을 초 단위로 반환합니다. 꺼져 있으면 0을 반환합니다.
컨트롤러 실행 상태 컨트롤러가 활성 상태이면 True, 비활성 상태이면 False를 반환합니다.
태양: 주간/야간 해당 위치가 지금 주간이면 True, 야간이면 False를 반환합니다. 위치는 이 조건부 기능이 지도에서 갖는 위치(없으면 소속 구역·사이트)를 상속하므로 좌표를 따로 설정하지 않습니다. 일출·일몰 오프셋(분)으로 주간 구간의 양 끝을 밀 수 있습니다 — 예를 들어 일몰 오프셋 -30은 일몰 30분 전부터 야간으로 봅니다. 극지에서 백야면 항상 True, 극야면 항상 False입니다.
태양: 이벤트까지 남은 시간 선택한 다음 태양 이벤트(일출·일몰·남중·시민박명 시작/끝)까지 남은 시간을 초 단위로 반환합니다. 이벤트 오프셋(분)을 음수로 주면 이벤트 이전 시점까지의 시간을 셉니다 — 예를 들어 -60은 일몰 1시간 전까지 남은 초입니다. 위치는 조건부 기능에서 상속합니다. 해당 날짜에 그 이벤트가 없으면(극지) "None"이 반환됩니다.
최대 연령 (초) 측정값이 가질 수 있는 최대 연령(초)입니다. 마지막 측정값이 이보다 오래된 경우, 측정값 대신 "None"이 반환됩니다.

조건부 설정 가이드~

Python 3 환경에서 이러한 조건부 기능이 실행됩니다. Python 코드 내에서 다음 함수를 사용할 수 있습니다.

참고

Python 코드 들여쓰기는 반드시 4개의 공백을 사용해야 합니다(2개의 공백, 탭 등은 사용할 수 없습니다).

### 조건부 함수 사용 예제

다음은 조건부 함수에서 사용할 수 있는 몇 가지 예제입니다. 각 `self.condition("ID")`은 해당 조건의 최신 측정값을 반환하며, 설정된 최대 연령(Max Age) 내에 있는 경우에만 값을 반환합니다.

```python
# 예제 1: 측정값이 None인 경우
# 입력이 작동하지 않을 때 이메일 알림 작업을 실행하는 데 유용합니다.
if self.condition("asdf1234") is None:
    self.run_all_actions()  # 모든 작업 실행

# 예제 2: 두 개의 측정값 조건 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    # 두 측정값이 모두 None이 아닌 경우
    if measure_1 < 20 and measure_2 > 10:
        self.run_all_actions()  # 모든 작업 실행

# 예제 3: 두 측정값과 측정값 합계 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    sum_ = measure_1 + measure_2
    if measure_1 > 2 and 10 < measure_2 < 23 and sum_ < 30.5:
        self.run_all_actions()

# 예제 4: 조건 결합
measurement = self.condition("asdf1234")
if measurement is not None and 20 < measurement < 30:  # 조건 결합
    self.run_all_actions()

# 예제 5: 두 측정값 테스트
# Edge Input을 0 또는 1에서 True 또는 False로 변환
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if bool(measure_1) and measure_2 > 10:
        self.run_all_actions()

# 예제 6: "or" 조건 및 반올림된 측정값 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if measure_1 > 20 or int(round(measure_2)) in [20, 21, 22]:
        self.run_all_actions()

# 예제 7: self를 사용하여 여러 실행 간 변수 저장
measurement = self.condition("asdf1234")
if not hasattr(self, "stored_measurement"):  # 변수 초기화
    self.stored_measurement = measurement
if measurement is not None:
    if abs(measurement - self.stored_measurement) > 10:
        self.run_all_actions()  # 차이가 10보다 큰 경우
    self.stored_measurement = measurement  # 측정값 저장

위 예제는 조건부 함수의 기본적인 사용법을 보여줍니다. 필요에 따라 코드를 수정하여 특정 요구 사항에 맞게 조정하세요.

```python
# 예제 1: 측정값이 None인 경우
# 입력이 작동하지 않을 때 이메일 알림 작업을 실행하는 데 유용합니다.
if self.condition("asdf1234") is None:
    self.run_all_actions()  # 모든 작업 실행

# 예제 2: 두 개의 측정값 조건 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    # 두 측정값이 모두 None이 아닌 경우
    if measure_1 < 20 and measure_2 > 10:
        # measure_1이 20보다 작고 measure_2가 10보다 큰 경우
        self.run_all_actions()  # 모든 작업 실행

# 예제 3: 두 측정값과 측정값 합계 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    sum_ = measure_1 + measure_2
    if measure_1 > 2 and 10 < measure_2 < 23 and sum_ < 30.5:
        self.run_all_actions()

# 예제 4: 조건 결합
measurement = self.condition("asdf1234")
if measurement is not None and 20 < measurement < 30:  # 조건 결합
    self.run_all_actions()

# 예제 5: 두 측정값 테스트
# Edge Input을 0 또는 1에서 True 또는 False로 변환
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if bool(measure_1) and measure_2 > 10:
        self.run_all_actions()

# 예제 6: "or" 조건 및 반올림된 측정값 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if measure_1 > 20 or int(round(measure_2)) in [20, 21, 22]:
        self.run_all_actions()

# 예제 7: self를 사용하여 여러 실행 간 변수 저장
measurement = self.condition("asdf1234")
if not hasattr(self, "stored_measurement"):  # 변수 초기화
    self.stored_measurement = measurement
if measurement is not None:
    if abs(measurement - self.stored_measurement) > 10:
        self.run_all_actions()  # 차이가 10보다 큰 경우
    self.stored_measurement = measurement  # 측정값 저장
"측정값 (다중)" 조건은 설정된 최대 연령(Max Age) 내에서 마지막 측정값뿐만 아니라 과거의 특정 값이 저장되었는지 확인하려는 경우 유용합니다. 이는 각 숫자 값이 확인해야 할 서로 다른 경고를 나타내는 경고 시스템이 있는 경우, 과거 값 중 특정 값이 발생했는지 확인하는 데 사용할 수 있습니다. 아래는 지난 30분(최대 연령: 1800초) 동안의 모든 측정값을 가져와 반환된 목록에서 "119"와 같은 값이 있는지 확인하는 예제입니다. "119"가 존재하면 작업이 실행되고 break를 사용하여 for 루프를 종료합니다.

# 예제 1: 지난 30분 동안의 측정값에서 특정 값 찾기 (최대 연령: 1800초)
measurements = self.condition_dict("asdf1234")
if measurements:  # 목록이 비어 있지 않은 경우
    for each_measure in measurements:  # 목록의 각 측정값을 반복
        if each_measure['value'] == 119:
            self.logger.info("119 경고가 타임스탬프 {time}에서 발견되었습니다.".format(
                time=each_measure['time']))
            self.run_all_actions()
            break  # for 루프 종료

고급 조건부 Run Python Code 예제:

이 예제들은 위의 초급 예제를 확장하여 특정 작업을 활성화합니다. 다음 예제에서는 조건부 기능의 Actions 섹션에서 찾을 수 있는 ID를 참조하는 작업을 사용합니다. 두 개의 예제 작업 ID가 사용됩니다: "qwer1234" 및 "uiop5678". 추가로, self.run_all_actions()는 여기서 사용되며, 생성된 순서대로 모든 작업을 실행합니다.

# 예제 1
measurement = self.condition("asdf1234")
if measurement is None:
    self.run_action("qwer1234")
elif measurement > 23:
    self.run_action("uiop5678")
else:
    self.run_all_actions()

# 예제 2: 두 개의 측정값 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if measure_1 < 20 and measure_2 > 10:
        self.run_action("qwer1234")
        self.run_action("uiop5678")

# 예제 3: 두 개의 측정값과 측정값 합계 테스트
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    sum_ = measure_1 + measure_2
    if measure_1 > 2 and 10 < measure_2 < 23 and sum_ < 30.5:
        self.run_action("qwer1234")
    else:
        self.run_action("uiop5678")

# 예제 4: 하나의 조건으로 결합
measurement = self.condition("asdf1234")
if measurement is not None and 20 < measurement < 30:
    self.run_action("uiop5678")

# 예제 5: 두 개의 측정값 테스트, Edge 입력을 0/1에서 True/False로 변환
measure_1 = self.condition("asdf1234")
measure_2 = self.condition("hjkl5678")
if None not in [measure_1, measure_2]:
    if bool(measure_1) and measure_2 > 10:
        self.run_all_actions()

# 예제 6: "or" 조건 및 반올림된 측정값 테스트
measure_1 = self.measure("asdf1234")
measure_2 = self.measure("hjkl5678")
if None not in [measure_1, measure_2]:
    if measure_1 > 20 or int(round(measure_2)) in [20, 21, 22]:
        self.run_action("qwer1234")
        if measure_1 > 30:
            self.run_action("uiop5678")

작업이 메시지(E-Mail 또는 Note)를 수신하는 유형인 경우, 이 메시지에 추가 정보를 포함하도록 수정할 수 있습니다. 이를 통해 새로운 정보가 Note, E-Mail 등으로 전달되기 전에 함수에 전달됩니다. 이를 수행하려면 문자열을 변수 self.message에 추가하고, 이를 self.run_action() 또는 self.run_all_actions()message 매개변수에 추가하세요. 아래는 몇 가지 예제입니다. = 대신 +=를 사용하여 문자열을 변수 self.message에 추가하는 점에 유의하세요. 이는 기존 값을 덮어쓰지 않고 추가합니다.

# 예제 1
measurement = self.measure("asdf1234")
if measurement is None and measurement > 23:
    self.message += "측정값은 {}".format(measurement)
    self.run_action("uiop5678", message=self.message)

# 예제 2
measure_1 = self.measure("asdf1234")
measure_2 = self.measure("hjkl5678")
if None not in [measure_1, measure_2]:
    if measure_1 < 20 and measure_2 > 10:
        self.message += "측정값 1: {m1}, 측정값 2: {m2}".format(
            m1=measure_1, m2=measure_2)
        self.run_all_actions(message=self.message)

로깅은 self.logger를 사용하여 데몬 로그에 메시지를 기록하는 데도 사용할 수 있습니다. 로깅 수준에는 "info", "warning", "error", "debug"가 포함됩니다. 디버그 로그 라인은 입력의 로깅 수준이 "Debug"로 설정된 경우에만 데몬 로그에 나타납니다.

# 예제 1
measurement = self.measure("asdf1234")
if measurement is None and measurement > 23:
    self.logger.error("경고, 측정값은 {}".format(measurement))
    self.message += "측정값은 {}".format(measurement)
    self.run_action("uiop5678", message=self.message)

조건부를 활성화하기 전에 모든 가능한 시나리오를 철저히 탐색하고 충돌을 제거하는 구성을 계획하는 것이 좋습니다. 일부 장치나 출력은 빠르게 켜고 끄는 경우 비정상적으로 반응하거나 실패할 수 있습니다. 따라서 장치를 출력에 연결하기 전에 구성을 시험 실행해 보세요.

트리거~

트리거 컨트롤러는 출력이 켜지거나 꺼지는 것, GPIO 핀이 전압 상태를 변경하는 것(에지 감지, 상승 또는 하강), 다양한 타이머(지속 시간, 시간 간격, 특정 시간 등)를 포함한 시간 이벤트, 또는 특정 위도와 경도의 일출/일몰 시간과 같은 이벤트가 트리거될 때 작업을 실행합니다. 트리거가 구성되면, 해당 이벤트가 트리거될 때 실행할 작업을 원하는 만큼 추가하세요.

출력 (켜짐/꺼짐) 옵션~

출력 상태를 모니터링합니다.

설정 설명
출력 조건 상태 변경을 모니터링할 출력입니다.
상태 조건 출력 상태가 켜짐(On) 또는 꺼짐(Off)으로 변경되면 조건이 트리거됩니다. "켜짐(모든 지속 시간)"을 선택하면 출력이 켜지는 시간이 얼마든지 조건이 트리거되며, "켜짐"만 선택하면 출력이 설정된 "지속 시간(초)" 동안 켜질 때만 조건이 트리거됩니다.
지속 시간 조건 (초) "켜짐"이 선택된 경우, 출력이 특정 지속 시간(초) 동안 켜질 때만 조건이 트리거되도록 설정할 수 있습니다.

출력 (PWM) 옵션~

PWM 출력 상태를 모니터링합니다.

설정 설명
출력 조건 상태 변경을 모니터링할 출력입니다.
상태 조건 출력의 듀티 사이클이 설정된 값보다 크거나, 작거나, 같을 때 조건부 작업이 트리거됩니다.
듀티 사이클 조건 (%) 출력의 듀티 사이클을 비교할 기준 값입니다.

에지 옵션~

핀 상태의 상승 및/또는 하강 에지를 모니터링합니다.

설정 설명
에지 감지 시 상태 변화가 감지되면 조건이 트리거됩니다. LOW(0볼트)에서 HIGH(3.5볼트)로 상태가 변경될 때 상승 에지, HIGH(3.3볼트)에서 LOW(0볼트)로 상태가 변경될 때 하강 에지, 또는 상승 및 하강 모두를 선택할 수 있습니다.

PWM 메서드 실행 옵션~

지속 시간 메서드를 선택하면 선택한 PWM 출력에 메서드에서 지정한 듀티 사이클이 설정됩니다.

설정 설명
지속 시간 메서드 사용할 메서드를 선택합니다.
PWM 출력 사용할 PWM 출력을 선택합니다.
주기(초) 듀티 사이클을 계산할 시간 간격을 선택한 다음, PWM 출력에 적용합니다.
주기마다 트리거 주기마다 조건부 작업을 트리거합니다.
활성화 시 트리거 조건부가 활성화될 때 조건부 작업을 트리거합니다.

참고

"활성화 시 트리거"는 활성화나 설정 재로드 시점이 아니라 트리거의 다음 점검에서 발화합니다 — 최대 한 표본 주기(데몬의 일반 트리거·조건부 표본 주기) 만큼 늦어질 수 있습니다. 그 자리에서 곧바로(동기적으로) 발화시키면 설정을 다시 읽는 중에 드물게 교착이 걸려, 트리거가 그대로 멈춰 다시는 발화하지 않을 위험이 있었습니다.

선택한 지속 시간 메서드가 아직 값을 내지 못하는 상태라면(예: 활성화 직후 시작 시각이 아직 기록되지 않은 경우) 잘못된 명령을 보내는 대신 PWM 출력을 건드리지 않고 다음 주기를 기다립니다. 계산 자체는 됐는데 출력이 명령을 거부한 경우(무응답·시간 초과)는 이제 오류로 남습니다 — 설정점 곡선은 출력에 닿았는지와 무관하게 계속 진행됩니다.

일출/일몰 옵션~

위도와 경도를 기준으로 일출 또는 일몰(또는 해당 시간의 오프셋) 시 이벤트를 트리거합니다.

설정 설명
일출 또는 일몰 조건부를 트리거할 시점을 선택합니다. 일출 또는 일몰 중 하나를 선택합니다.
위도(소수점) 일출/일몰의 위도를 소수점 형식으로 입력합니다.
경도(소수점) 일출/일몰의 경도를 소수점 형식으로 입력합니다.
천정각 태양의 천정각을 설정합니다.
날짜 오프셋(일) 일출/일몰 시간에 대한 날짜 오프셋을 설정합니다(양수 또는 음수).
시간 오프셋(분) 일출/일몰 시간에 대한 시간 오프셋을 설정합니다(양수 또는 음수).

타이머 (지속 시간) 옵션~

타이머를 실행하여 설정된 주기마다 조건부 작업을 트리거합니다.

설정 설명
주기 (초) 조건부 작업을 트리거하는 시간 간격(초)입니다.
시작 오프셋 (초) 조건부가 활성화된 후 첫 번째 트리거가 실행되기까지 대기할 시간(초)을 설정합니다.

타이머 (일일 특정 시간) 옵션~

매일 특정 시간에 조건부 작업을 트리거하는 타이머를 실행합니다.

설정 설명
시작 시간 (HH:MM) 조건부 작업을 트리거할 시간을 "HH:MM" 형식으로 설정합니다. HH는 시간을, MM은 분을 나타내며, 24시간 형식으로 입력합니다.

타이머 (일일 시간 범위) 옵션~

설정된 시작 시간과 종료 시간 사이에서 특정 주기로 조건부 작업을 트리거하는 타이머를 실행합니다. 예를 들어, 시작 시간을 10:00, 종료 시간을 11:00, 주기를 120초로 설정하면, 10시부터 11시 사이에 120초마다 조건부 작업이 트리거됩니다.

이 기능은 특정 시간 동안 출력이 켜져 있어야 하며, 단순한 특정 시간 타이머가 전원 장애로 인해 사이클이 중단되는 것을 방지하고자 할 때 유용합니다. 예를 들어, 시작 -> 종료 시간 동안 몇 분마다 출력을 켜도록 설정하면, 해당 시간 동안 출력이 유지되도록 보장할 수 있습니다.

설정 설명
시작 시간 (HH:MM) 조건부 작업을 트리거할 시작 시간을 "HH:MM" 형식으로 설정합니다. HH는 시간을, MM은 분을 나타내며, 24시간 형식으로 입력합니다.
종료 시간 (HH:MM) 조건부 작업을 트리거할 종료 시간을 "HH:MM" 형식으로 설정합니다. HH는 시간을, MM은 분을 나타내며, 24시간 형식으로 입력합니다.
주기 (초) 조건부 작업을 트리거하는 시간 간격(초)입니다.

Trigger - Sequence~

시퀀스는 여러 개의 출력 장치(밸브·펌프·조명 등)를 정해진 순서와 시간표에 따라 자동으로 켜고 끄는 기능입니다. "밸브 A를 30분 열고, 그다음 밸브 B를 30분 열고…" 처럼 여러 단계로 이어지는 작업을 사람이 일일이 조작하지 않고 반복 실행하도록 자동화할 때 사용합니다. 관수 순환, 다단계 환기(창문 열기 → 대기 → 팬 가동) 등에 특히 유용합니다.

예시로 알아보기: 급수 펌프 + 밸브 3개 순차 관수~

가장 흔한 사용 사례로 살펴보겠습니다. 메인 급수 펌프 1대와, 구역별로 순서대로 물을 주는 밸브 A·B·C가 있다고 가정합니다. 원하는 동작은 다음과 같습니다.

  • 펌프는 관수가 진행되는 동안 처음부터 끝까지 계속 켜져 있어야 합니다.
  • 밸브 A → B → C 순서로 각각 30분씩 급수한 뒤 자동으로 꺼집니다.

설정 방법:

  1. 함수 목록에서 시퀀스를 새로 추가합니다.
  2. 동작 추가에서 "출력: 켜기/끄기/지속시간"을 선택하고 메인 펌프의 출력을 골라 추가합니다. 모드는 전체(Total)로 지정합니다 — 이 모드는 "시퀀스가 도는 내내 계속 켜져 있어야 하는 장치"에 씁니다.
  3. 밸브 A, B, C를 이 순서로 추가합니다. 각각 모드는 단일(Single), 지속시간은 1800초(30분)로 지정합니다.
  4. 저장하면 등록한 순서(위젯에서는 드래그로 재배열 가능)대로 실행됩니다.

시간이 흐르는 모습을 표로 보면 다음과 같습니다.

경과 시간 0~30분 30~60분 60~90분
펌프 (전체) ON ON ON
밸브 A (단일) ON → OFF
밸브 B (단일) ON → OFF
밸브 C (단일) ON → OFF

전체(Total) 모드는 이렇게 "다른 단계가 전부 끝날 때까지 켜져 있어야 하는" 장치 한 개(보통 메인 펌프)에 사용하고, 나머지 단계들은 단일(Single) 모드로 순서대로 실행되는 것이 기본 패턴입니다.

기본값 그대로 두면 펌프는 밸브 A와 같은 순간에 켜지고 밸브 C와 같은 순간에 꺼집니다. 닫힌 밸브를 상대로 펌프가 먼저 돌거나, 펌프가 도는 채로 밸브가 닫히면 배관에 무리가 가므로, 펌프 단계에 여유 시간(시작 지연 / 조기 정지) 을 주어 펌프 구간을 밸브 구간 안쪽으로 넣는 것을 권장합니다. 아래 여유 시간을 참고하세요.

예시로 알아보기: 밸브 여러 개를 동시에 열기~

위 예시에서 밸브 B와 C를 순서대로가 아니라 동시에 열고 싶다면 어떻게 할까요? 이럴 때는 두 단계를 장치 그룹으로 묶습니다. 위젯이나 통합 모달에서 밸브 B·C의 그룹 칸에 같은 이름(예: 구역2)을 입력하면, 두 밸브가 하나의 슬롯으로 묶여 같은 시간에 함께 켜지고 꺼집니다.

경과 시간 0~30분 30~60분
펌프 (전체) ON ON
밸브 A (단일) ON → OFF
밸브 B + C (그룹 구역2) ON → OFF (동시)

이렇게 순차 실행(펌프+단일 단계)과 동시 실행(장치 그룹)을 섞어서 쓸 수 있습니다. 그룹으로 묶인 밸브들은 같은 지속시간을 공유하며, 자세한 규칙은 아래 장치 그룹을 참고하세요.

실행 모드: 단일 vs 전체~

  • 단일 (Single, 기본값): 각 단계마다 독립적인 실행 시간을 적용합니다.
    • 계산식: 총 활성 시간 = Head Overlap + Base Duration + Tail Overlap
    • 동작: 이전 단계가 끝나기 Overlap 초 전에 다음 단계가 시작되어 부드러운 전환을 지원합니다(예: 밸브를 닫기 몇 초 전에 다음 밸브를 미리 열어 배관 압력이 갑자기 떨어지지 않게 함).
  • 전체 (Total, Full-span): 위 펌프 예시처럼, 전체 시퀀스가 실행되는 동안 계속 켜져 있어야 하는 단계에 사용합니다. 시퀀스의 시작부터 마지막 단일 단계가 종료될 때까지 유지되며, 여유 시간으로 그 구간을 양쪽에서 좁힐 수 있습니다(아래 참조).

여유 시간: 펌프를 밸브 안쪽에 두기~

전체(Total) 모드 단계는 기본적으로 사이클의 0초에 켜져 마지막 단계와 동시에 꺼집니다. 즉 첫 밸브와 펌프가 같은 순간에 켜지고, 마지막 밸브와 펌프가 같은 순간에 꺼집니다. 실제 관수에서는 이 순서가 중요합니다.

  • 밸브가 아직 열리지 않았는데 펌프가 돌면 펌프가 압력을 그대로 받습니다.
  • 펌프가 도는 상태에서 밸브가 닫히면 수격(water hammer)이 발생합니다.

이를 위해 전체 모드 단계에 두 값을 줄 수 있습니다. 단위는 초이고, 기본값은 둘 다 0(기존 동작 그대로)입니다.

하는 일
시작 지연 (total_lead) 시퀀스가 시작되고 이 초만큼 지난 뒤에 켭니다. 그 사이 밸브가 먼저 열립니다.
조기 정지 (total_lag) 시퀀스가 끝나기 이 초 전에 끕니다. 밸브가 닫히기 전에 압력이 빠집니다.

예를 들어 시작 지연 10초, 조기 정지 15초를 주면 90분 사이클에서 펌프는 다음과 같이 동작합니다.

시작 종료
밸브 (단일 단계 전체) 0초 5400초
펌프 (전체) 10초 5385초

펌프 구간이 밸브 구간 안쪽에 완전히 들어가므로, 항상 "밸브 열림 → 펌프 켜짐 … 펌프 꺼짐 → 밸브 닫힘" 순서가 됩니다. 정지할 때도 전체 모드 단계를 먼저 끄고 나머지를 끕니다.

설정 위치는 두 곳입니다. 어느 쪽이든 전체(Total) 모드를 고른 단계에서만 보입니다.

  • 함수 설정 화면의 단계 행: 모드를 전체로 바꾸면 시작 지연·조기 정지 칸이 나타납니다.
  • 대시보드 시퀀스 위젯: 단계 이름을 눌러 열리는 설정 창의 여유 시간 (초).

여유 시간이 사이클 길이보다 커서 펌프가 아예 못 도는 설정이 되면, 그 값은 무시하고 전체 구간을 그대로 사용하며 로그에 경고를 남깁니다.

그 밖의 주요 개념~

  • 동적 기간 (Dynamic Duration): action_duration_id 옵션을 통해 특정 입력(Input)의 측정값을 실행 시간으로 사용할 수 있습니다.
    • 형식: Input_UUID 또는 Input_UUID,Measurement_UUID.
    • 유효성: time_offset_minutes 내의 최신 측정값만 사용하며, 없을 경우 설정된 기본 action_duration을 사용합니다.
  • 오버랩 (Overlaps): output_duration 설정값을 사용하여 단계 사이의 전환 시간을 결정합니다. 첫 번째 액션은 Tail Overlap만, 중간 액션은 Head & Tail Overlap을, 마지막 액션은 Head Overlap만 가집니다.
  • 제약 사항 (Window & Latency):
    • Execution Window: timer_start_time ~ timer_end_time 사이에만 시퀀스가 시작되거나 실행됩니다. 범위를 벗어나면 강제 종료됩니다.
    • Start Latency: 트리거(활성화) 발생 후 실제 시퀀스 시작까지의 대기 시간(timer_start_offset)을 초 단위로 설정합니다.

설정 옵션 명세~

설정 키 설명
period 전체 시퀀스 사이클의 반복 주기 (초 단위).
output_duration 액션 간의 오버랩 시간 (초 단위).
timer_start_offset 활성화 후 시퀀스 시작까지의 지연 시간.
time_offset_minutes 동적 기간 측정값의 최대 유효 시간 (분 단위).
enabled 개별 액션의 활성화 여부.
sequence_mode 'single' 또는 'total' 선택.
total_lead (전체 모드 전용) 시퀀스 시작 후 이 초만큼 지나서 켭니다. 기본 0.
total_lag (전체 모드 전용) 시퀀스 종료 이 초 전에 끕니다. 기본 0.
action_duration 해당 단계의 기본 실행 시간 (초 단위).
action_duration_id 동적 실행 시간을 가져올 장치/측정값 ID.
group_name 장치 그룹 이름. 같은 이름을 가진 단계들은 하나의 슬롯으로 묶여 동시에 작동합니다(아래 참조). 비어 있으면 단독 작동합니다.
display_name 위젯 목록에 표시할 사용자 지정 라벨. 비우면 장치 이름으로 대체됩니다.

장치 그룹 (동시 작동)~

앞의 예시에서 본 것처럼, 여러 단계를 하나의 장치 그룹으로 묶으면 순차 실행이 아니라 같은 시간 창에서 동시에 켜지고 꺼집니다. 상세 규칙은 다음과 같습니다.

  • 묶는 방법: 위젯 또는 통합 모달에서 단계 이름을 클릭 → 그룹 칸에 같은 그룹 이름을 입력합니다. 같은 이름을 가진 단계들이 하나의 슬롯으로 접혀 표시됩니다. 그룹 칸을 비우면 해당 단계가 그룹에서 빠져 단독 작동으로 돌아갑니다.
  • 공통 기간 (리더 상속): 그룹은 하나의 공통 작동 시간을 공유합니다. 어떤 단계가 기존 그룹에 합류하면 그 그룹의 공통 action_duration(및 동적 기간 리더 action_duration_id)을 자동으로 상속합니다. 슬롯의 대표(가장 앞선 위치의 멤버)가 그룹 전체의 기간·동적 리더를 결정합니다.
  • 실행 순서: 슬롯 순서는 각 그룹이 처음 등장한 위치를 따릅니다. 이미 존재하는 그룹의 뒤쪽 멤버는 앞선 슬롯으로 folding(접힘)되어 새 슬롯을 만들지 않습니다.
  • 제약: total(Full-span) 모드 단계는 그룹으로 묶을 수 없습니다. 그룹은 본질적으로 single(동시-단일) 모드입니다.

요일별 스케줄 (Weekly Schedule)~

기존의 단일 시작·종료 시간 대신, 요일마다 다른 작동 시간·주기를 설정할 수 있습니다. 스케줄은 Trigger.timer_schedule에 JSON으로 저장되며 두 가지 모드를 지원합니다.

  • shared (공유 모드): 모든 요일이 하나의 시작·종료·주기를 공유합니다. 활성 요일만 선택합니다(기존 방식과 동일).
  • per_day (요일별 모드): 월~일 각 요일이 독립적인 시작(start)·종료(end)·주기(period)와 활성 여부(enabled)를 가집니다. 나아가 요일별로 다음을 개별 오버라이드할 수 있습니다.
    • actions: 요일별 단계 활성/비활성 (전역 enabled 플래그를 덮어씀).
    • groups: 요일별 장치 그룹 소속 (전역 group_name을 덮어씀). 빈 문자열은 "그 요일에는 그룹에서 제외"를 의미합니다.
    • durations: 요일별 작동 시간 (전역 action_duration을 덮어씀).

시간 규칙:

  • 요일 인덱스는 Python 관례를 따릅니다: 0=월, 1=화, … 6=일.
  • 시간은 장치 로컬 타임존의 벽시계 HH:MM 형식입니다.
  • 종료 시간 24:00은 "하루의 끝"을 의미하며 내부적으로 1440분으로 저장됩니다.
  • 자정을 넘기는 창(start >= end)은 허용되지 않습니다. 각 요일은 00:00~24:00 범위 안에서만 작동합니다.
  • 자정 연속성: 앞 요일의 종료가 24:00이고 다음 요일의 시작이 00:00이며 두 요일이 모두 활성이고 인접해 있으면, 데몬은 자정에 사이클을 리셋하지 않고 연속으로 이어서 실행합니다.

참고

주기(period)가 그 요일의 작동 창 길이보다 길면 사이클이 창 안에서 잘립니다. 설정 화면은 이 경우 경고를 표시합니다.

창이 닫힐 때까지 남은 시간이 한 바퀴를 다 돌기에 모자라면, 그 사이클은 시작하지 않고 건너뜁니다. 예전에는 그런 경우에도 사이클을 시작해 밸브가 열리자마자 창이 닫히며 강제로 끊겼는데, 그렇게 끊긴 단계는 개방 시간이 기록되지 않아 얼마나 물을 주었는지 확인할 수 없었습니다.

창 길이와 한 바퀴가 딱 맞아떨어지는 설정에서는 마지막 단계의 종료와 창 닫힘이 같은 순간에 옵니다. 이때 창 판정이 먼저 이기면 단계가 스스로 끝나기 전에 꺼져 개방 시간이 남지 않으므로, 종료가 코앞인 단계는 몇 초 기다려 정상적으로 끝냅니다. 아직 한참 남은 단계는 기다리지 않습니다 — 그건 자연 종료가 아니라 실제 중단이고, 기다려 봐야 창만 넘깁니다.

시퀀스를 끄면 바로 멈춥니다~

함수 목록이나 위젯에서 시퀀스를 비활성화하면 돌고 있던 제어가 그 자리에서 정지하고, 그때 열려 있던 단계는 정상적으로 닫힙니다. 다음 작동 시간이 와도 다시 시작하지 않습니다.

끄는 조작이 제어 데몬까지 닿지 못했다면 화면이 "여전히 동작 중" 이라고 알려 줍니다. 그때는 설정이 저장됐더라도 밸브가 계속 돌고 있을 수 있으니, 잠시 뒤 다시 시도하거나 출력을 직접 확인하십시오. 예전에는 이 경우에도 "저장됨"으로 보여, 화면은 꺼진 것으로 보이는데 실제로는 관수가 계속되는 상태가 만들어졌습니다.

주의

요일별 모드에서 위젯의 주기를 바꾸면 그날 요일에만 적용됩니다. 위젯이 보여주는 주기는 오늘의 값이므로, 다른 요일의 주기는 그대로 남습니다. 요일마다 다르게 정하려면 각 요일을 선택한 상태에서 바꾸거나 통합 모달의 요일별 설정을 쓰십시오.

다시 켤 때: 이어서 vs 처음부터~

시퀀스를 껐다가 다시 켜면 기본적으로 멈춘 지점부터 이어서 진행합니다. 잠깐 멈췄다 재개하는 경우를 위한 동작입니다. 설정을 고치고 처음부터 새로 돌리고 싶다면 시퀀스 위젯 설정의 다시 켤 때 항목을 「처음부터 시작」으로 바꾸십시오.

다시 켰을 때
멈춘 지점부터 이어서 (기본) 중단된 사이클의 남은 단계부터 계속합니다
처음부터 시작 첫 단계부터 새로 실행합니다

「이어서」로 두어도 중단된 지 한 주기가 넘었으면 처음부터 시작합니다 — 그만큼 지났다면 이어 붙일 사이클이 남아 있지 않기 때문입니다.

참고

데몬 재시작은 이 설정과 무관합니다. 시스템 업데이트나 재부팅으로 제어가 잠시 멈춘 것은 사용자가 끈 것이 아니므로, 진행 중이던 관수는 언제나 이어서 진행합니다. 이 설정이 정하는 것은 사람이 직접 껐다가 켜는 경우뿐입니다.

참고

진짜 처음 켜는 경우, 첫 사이클은 정해진 눈금이 아니라 지금 이 순간부터 시작합니다. 이어받을 이전 사이클이 전혀 없을 때만(신규 시퀀스이거나 「처음부터 시작」으로 이전 사이클을 비운 뒤) 그렇습니다. 예전에는 이 경우도 데몬이 같은 날의 사이클이 표류하지 않게 쓰는 고정 눈금(위 "시작하지 않고 건너뜁니다" 항목의 그 눈금)을 그대로 기준으로 삼아서, 창 중간에 시퀀스를 켜면 새 사이클이 이미 몇 시간 지난 것으로 계산돼 그 안의 단계가 전부 이미 끝난 것처럼 처리되는 경우가 있었습니다. 이전 사이클의 상태가 남아 있다면 (너무 오래돼 이어받지 못하더라도) 여전히 그 눈금을 기준으로 삼습니다 — 그 경우는 사용자가 방금 켠 것이 아니라 데몬이 내려가 있던 것이기 때문입니다.

공통 ↔ 요일별 전환~

요일별 모드에서 공통 모드로 되돌리면, 첫 활성 요일의 설정이 공통값으로 올라옵니다 — 작동 시간(시작·종료·주기)뿐 아니라 그 요일의 장치 그룹 구성과 단계별 작동 시간도 함께 옮겨집니다. 공통 모드는 요일별 맵을 읽지 않으므로, 옮기지 않으면 요일마다 짜 둔 구성이 버튼 한 번에 사라집니다.

기준이 되는 요일은 작동 시간과 같은 규칙(첫 활성 요일)입니다. 여러 요일을 서로 다르게 구성해 두었다면 전환 뒤 나머지 요일의 차이는 남지 않으니, 어느 요일을 공통으로 삼을지 정한 뒤에 전환하십시오.

설정 모달은 항상 지금 값을 보여줍니다~

시작·종료 시간, 주기, 교차 시간, 재개 방식은 요일별 시간 편집기(시간휠)나 함수 설정 화면 등 이 위젯이 아닌 다른 경로로도 바뀔 수 있습니다. 그렇게 바뀐 값은 관수에 즉시 반영되고, 이 위젯의 설정 모달도 대시보드를 열 때마다 그 값을 다시 읽어 보여줍니다 — 옛 값을 저장해 둔 채 있다가 다음에 이 위젯을 저장할 때에야 맞춰지는 일이 없습니다. 예전에는 함수 설정 화면에서 시작·종료 시간이나 주기를 고치면, 이미 요일별 스케줄을 쓰고 있는 시퀀스에서는 저장은 성공했다고 나오면서 데몬이 실제로 따르는 스케줄은 그대로인 경우가 있었습니다 — 지금은 어느 저장 경로를 쓰든 같은 정본 스케줄에 기록됩니다.

참고

요일별 모드에서는 함수 자체의 설정 화면에 있는 시작·종료·주기 칸도 반영되지 않습니다 — 전역 값 하나가 요일별로 짜 둔 구성을 통째로 지워버리기 때문에 같은 이유로 막습니다. 이제는 저장할 때 조용히 성공이라고 답하는 대신 그 사실을 알려줍니다 — 요일별 시간은 시퀀스 위젯의 단계 목록에서 고치십시오.

시퀀스가 작동하는 중에 단계의 설정 — 작동 시간, 장치 그룹, 요일별 켬/끔 — 을 바꾸면 그 변경도 다음 사이클이 아니라 지금 돌고 있는 사이클에 바로 반영됩니다. 예전에는 화면에는 곧바로 저장됐다고 나와도, 실제 반영은 다음 사이클이 시작될 때까지(주기가 길면 몇 시간 뒤까지) 미뤄졌습니다. 작동 중 편집이 엉뚱하게 작동하지 않도록 두 가지 안전장치를 함께 넣었습니다 — 이번 사이클에 이미 끝난 단계는, 앞 단계를 줄여 슬롯 경계가 당겨져서 그 창 안에 지금 경과 시간이 다시 들어오더라도 다시 켜지지 않습니다. 그리고 오늘 계획에서 빠진 단계 (오늘 요일에서 끄거나 아예 지운 단계)는 추적만 끊는 것이 아니라 명시적으로 끕니다 — 그래야 그 출력이 켜진 채 아무도 모르게 남지 않습니다.

자정을 넘는 창(같은 날 안에서 종료 시각이 시작 시각보다 이른 경우)은 저장을 시도하는 시점에 이유와 함께 거부됩니다. 예전에는 함수 설정 화면에서 그런 창도 저장할 수 있었는데, 그러면 데몬이 창을 그날 안으로 잘라 쓰면서 나머지 요일별 구성을 조용히 버렸습니다.

참고

시퀀스 위젯 자체의 설정 대화상자에는 더 이상 시작 시각·종료 시각 텍스트 입력칸이 없습니다 — 요일별 스케줄을 쓰는 순간부터는 그 두 칸이 애초에 제대로 동작하지 않았으므로, 시작·종료 시각은 아래에서 설명하는 통합 모달의 시간휠로만 정합니다.

통합 모달 · 시간휠~

시퀀스 위젯의 단계 목록에서 시간·그룹·이름·요일 스케줄을 하나의 통합 모달에서 편집합니다.

  • 단계 이름을 클릭하면 표시 이름(display_name)·모드(single/total)·장치 그룹을 함께 수정하는 모달이 열립니다.
  • 시작·종료 시간은 시간휠(time wheel) 컴포넌트로 입력하여 모바일에서도 정확하게 시:분을 선택할 수 있습니다.
  • 레거시 시작·종료·요일 컬럼은 스케줄 저장 시 자동으로 동기화되어(하위 호환), 스케줄을 사용하지 않는 구형 뷰에서도 대표 값이 유지됩니다.

통합 환경 제어 - 육묘장 모드~

통합 환경 제어 기능은 등록된 모든 액추에이터를 VPD 목표에 맞춰 조율합니다. 이 절은 육묘장 모드 옵션만 다루며, 나머지 옵션은 기능 설정 화면에서 설명합니다.

육묘 모종을 다르게 다뤄야 하는 이유~

분무는 VPD를 가장 빠르게 낮추는 수단입니다. 온도를 내리면서 습도를 동시에 올리기 때문입니다. 그래서 조율기는 VPD가 오르면 분무를 가장 먼저 고릅니다.

문제는 VPD가 가장 높아지는 시각이 곧 햇빛이 가장 강한 시각이라는 점입니다. 성체 작물이라면 괜찮습니다. 잎이 젖어도 견디고 증발냉각이 오히려 도움이 됩니다. 그러나 상토를 막 뚫고 나온 모종은 아직 큐티클이 없어서, 강한 햇빛 아래 자엽에 맺힌 물방울이 빛을 잎면에 모으고 마르면서 녹아 있던 염류를 농축시킵니다. 그 자리가 탑니다.

육묘장 모드는 VPD 목표를 바꾸지 않습니다. 목표에 도달하는 수단의 순서를 바꿉니다. 햇빛이 강한 동안 분무를 뒤로 미루고 차광과 환기를 먼저 쓰게 합니다.

육묘장 모드 옵션~

설정 설명
육묘장(모종) 모드 아래 보호 기능을 켭니다. 어떤 노즐이 잎을 적시는 형태인지는 시설 도면의 노즐 배치(유량·살수 반경·분사 방향)에서 자동으로 판정하므로 노즐 사양을 따로 입력하지 않습니다. 점적 배관과 진짜 고압 미세포그는 대상이 아닙니다.
분무 잠금 일사량 (W/m²) 실내 광량이 이 값 이상이면 습윤형 분무를 완전히 차단합니다. 실내 추정 광량을 기준으로 하므로 차광막을 닫으면 잠금이 완화됩니다.
분무 해제 일사량 (W/m²) 광량이 이 값 아래로 내려가면 분무를 다시 허용하고, 이 값과 잠금 임계 사이에서는 선형으로 줄입니다. 두 값의 간격이 구름이 지나갈 때마다 분무가 켜졌다 꺼졌다 하는 것을 막습니다.
1회 분무 최대 시간 (초) 한 번에 분무할 수 있는 최대 시간입니다. 가습량은 얼마나 오래 뿌리느냐가 아니라 얼마나 자주 뿌리느냐로 조절됩니다. 관수가 일정 간격으로 정량을 주는 방식과 같습니다.
강제 건조 간격 (초) 한 번 분무가 끝난 뒤 이 시간 동안은 전혀 분무하지 않아 잎이 마를 시간을 줍니다.
일몰 전 분무 허용 관수는 보통 일출과 일몰 무렵에 하는데, 저녁 분무는 잎이 젖은 채로 밤을 넘기게 만듭니다. 잎이 젖어 있는 시간이 길수록 잿빛곰팡이와 노균병 위험이 커집니다. 끄면 일몰 전에 분무를 중단합니다. 저녁 관수가 필요한 작물도 있으므로 선택은 재배자 몫입니다. 이 설정은 습도 관리용 분무만 제어하며, 같은 밸브에 걸린 별도 관수 일정에는 영향을 주지 않습니다.
일몰 전 분무 중단 시간 (분) 위 옵션을 껐을 때 일몰 몇 분 전부터 분무를 중단할지 정합니다. 중단은 다음 일출까지 이어집니다. 보통 2시간이면 어두워지기 전에 잎이 마릅니다. 일몰 시각은 지도상 시설 위치에서 계산하며, 좌표를 해석하지 못하면 차단하지 않습니다.
분무 원수 처리하지 않은 지하수는 대개 경도가 높고 수온이 낮아, 물방울이 마르면서 염류가 농축되어 남고 햇빛을 받는 잎에 저온 충격을 줄 수 있습니다. 지하수를 선택하면 잠금 임계가 자동으로 낮아집니다.

펄스 도징~

분무기를 개도(%)로 조절하면 매 사이클의 그 비율만큼 계속 뿌리게 되어 잎이 마를 틈이 없습니다. 그래서 잎을 적시는 형태의 분무기는 육묘장 모드와 무관하게 항상 짧은 펄스와 강제 휴지로 나뉩니다. 연속 분무는 성체 작물에서도 병해를 부르기 때문입니다. 육묘장 모드는 그 수치를 더 조일 뿐입니다. 잎에 닿기 전에 증발하는 고압 미세포그와 점적 배관은 기존의 연속 조절을 그대로 유지합니다.

참고~

분무 잠금은 폭염 비상 대응과 최저 습도 하한보다 우선합니다. 한여름 정오는 폭염 비상이 발동하는 시각이자 일소 위험이 최대인 시각이고, 습도 목표를 채우자고 모종을 잃을 수는 없기 때문입니다. 멈추는 것은 분무뿐이며 창호·커튼·팬은 정상 운전합니다.

순화가 진행되면 보호를 한 번에 끄지 말고 점진적으로 완화하십시오. 한낮 분무를 전혀 하지 않다가 하루아침에 전면 허용으로 바꾸면, 이 모드가 막으려던 조건을 조금 자란 모종에 그대로 재현하게 됩니다.

일소로 보이는 피해 중 일부는 빛이 아니라 물에서 옵니다. 경수는 광물 성분을 남기고, 찬물은 햇빛 받는 잎에 충격을 주며, 철분 침착은 일소와 거의 구별되지 않습니다. 반점을 묽은 산으로 닦아 지워지면 철분입니다. 물 자체가 나쁘면 어떤 제어 설정으로도 해결되지 않습니다.

관수와 가습을 한 장치로 처리하는 경우~

육묘장에서는 머리 위 스프링클러 한 벌로 관수와 가습을 함께 해결하는 일이 흔합니다. 이때 통합 환경 제어에 그 노즐을 맡기면 관수 쪽과 부딪힙니다. 조율기는 지금 쓰지 않는 액추에이터에도 "꺼 두라"는 명령을 매 주기 내보내기 때문에, 별도로 돌리는 관수 제어가 밸브를 열어도 다음 주기에 닫힙니다. 가습이 필요 없는 새벽일수록 이 명령은 더 확실하게 나갑니다.

물이 남는 방식도 다릅니다. 관수용으로 고른 스프링클러는 가습에 필요한 양보다 훨씬 굵게 뿌리므로, 가습용으로 짧게 끊어 쓰면 잎에 얇은 수막만 남습니다. 이 수막은 흘러내리지 못하고 그 자리에서 마르며 물에 녹아 있던 성분을 잎에 남깁니다. 반대로 아침에 흠뻑 주는 관수는 대부분 흘러내리면서 앞서 남은 것까지 씻어 냅니다. 같은 물, 같은 노즐인데 결과가 갈리는 이유가 이것입니다.

가습에 습윤형 분무 사용을 끄면 통합 환경 제어가 그 노즐에 아무 명령도 내리지 않습니다. 관수 일정이나 토양 수분 제어에 온전히 맡기고, 습도는 스크린·개구부·팬으로 관리합니다. 잎에 닿기 전에 증발하는 고압 미세포그는 이 설정과 무관하게 계속 가습에 쓰이며, 가습기와 점적 배관을 비롯한 다른 액추에이터도 영향을 받지 않습니다.

한낮 습도 관리를 분무에 의존하지 못하게 되지만, 대개는 손해가 아닙니다. 차광 스크린을 한 겹 더 치는 것이 분무와 비슷한 냉각 효과를 내면서 물도 염류도 쓰지 않기 때문입니다.