Integracja platformy SCADA/HMI Ignition (Inductive Automation) ze sprzętową bramką SMS SMSEagle pozwala automatycznie powiadamiać o alarmach procesowych wiadomością SMS, połączeniem budzącym lub połączeniem głosowym z syntezą mowy (TTS Advanced). Powstaje w ten sposób kanał powiadomień niezależny od sieci IT, kluczowy dla ciągłości nadzoru nad instalacją przemysłową.
Krytyczne alarmy docierają SMS-em lub połączeniem głosowym nawet wtedy, gdy zawiedzie łącze WAN, serwer poczty lub dostawca chmurowy. Sieci OT są często całkowicie odcięte od internetu, co wyklucza korzystanie z bramek chmurowych.
Operator otrzymuje alarm bezpośrednio na telefon, bez konieczności logowania się do klienta Perspective lub Vision. Połączenie głosowe TTS Advanced budzi dyżurnego w nocy i odczytuje treść alarmu naturalnie brzmiącym głosem w wybranym języku.
Integracja opiera się wyłącznie na module Alarm Notification, który klient zwykle już posiada. Nie wymaga płatnych modułów SMS Notification ani Voice Notification, modemu Sierra Wireless AirLink ani systemu telefonicznego SIP.
Bramka pracuje lokalnie w sieci zakładu. Treść alarmów, numery telefonów i dane procesowe nie opuszczają infrastruktury klienta, co ułatwia spełnienie wymagań NIS2, ISO 27001 oraz zasad segmentacji sieci OT/IT.
Ignition wykrywa alarm na tagu, np. przekroczenie temperatury lub zanik zasilania.
Alarm trafia do potoku powiadomień (Notification Pipeline), w którym blok Script wywołuje funkcję z biblioteki projektowej. Funkcja ta łączy się z interfejsem REST APIv2 bramki przez sieć lokalną.
SMSEagle wysyła wiadomość SMS lub wykonuje połączenie głosowe do wskazanej grupy odbiorców.
SMSEagle to sprzętowa bramka SMS, która umożliwia wysyłanie i odbieranie powiadomień bezpośrednio przez sieć komórkową. Działa lokalnie, dzięki czemu wszystkie dane pozostają pod pełną kontrolą użytkownika.
| Element | Wymaganie |
|---|---|
| Ignition | wersja 8.1 lub nowsza |
| Moduł Ignition | Alarm Notification (wymagany, udostępnia potoki powiadomień) |
| Urządzenie SMSEagle | zalecana najnowsza wersja software |
| Funkcje głosowe | dodatek VOICE add-on dla urządzenia SMSEagle |
| Sieć | dostęp z Ignition Gateway do bramki po HTTP (80) lub HTTPS (443) |
Przejdź do menu Calls > TTS Voice models i odczytaj numeryczne ID modelu głosu, którego chcesz używać. Dla języka angielskiego typową wartością jest 2. Wartość tę wpiszesz później do konfiguracji w Ignition.
Uwaga: Jeśli chcesz używać HTTPS między Ignition a SMSEagle, na bramce musi być zainstalowany prawidłowy certyfikat SSL, a certyfikat CA trzeba zaimportować do magazynu zaufanych certyfikatów Ignition Gateway (**Config > Networking > SSL/TLS**).
W przeglądarce tagów utwórz folder SMSEagle/Config, a następnie dodaj w nim tagi pamięciowe (Memory Tags):
| Tag | Typ | Przykładowa wartość |
|---|---|---|
gatewayUrl | String | https://192.168.1.101 (bez ukośnika na końcu) |
accessToken | String | token APIv2 z kroku 1 |
voiceId | Integer | 2 |
callDuration | Integer | 20 (sekundy) |
verifySsl | Boolean | true |
defaultRecipients | String | +48501234567,+48501234568 |
Bezpieczeństwo: Token API jest przechowywany w tagu. Ogranicz uprawnienia odczytu folderu SMSEagle/Config do kont administracyjnych i zamiast HTTP stosuj HTTPS z prawidłowym certyfikatem.
W Designerze otwórz Project Browser > Scripting > Project Library, utwórz skrypt o nazwie SMSEagle, a następnie wklej do niego poniższy kod.
import system
LOGGER = system.util.getLogger("SMSEagle")
CONFIG\_PATH = "\[default]SMSEagle/Config"
def getConfig():
"""Czyta konfiguracje bramki z tagow."""
paths = \[
"%s/gatewayUrl" % CONFIG\_PATH,
"%s/accessToken" % CONFIG\_PATH,
"%s/voiceId" % CONFIG\_PATH,
"%s/callDuration" % CONFIG\_PATH,
"%s/verifySsl" % CONFIG\_PATH,
]
v = system.tag.readBlocking(paths)
return {
"url": str(v\[0].value).rstrip("/"),
"token": str(v\[1].value),
"voiceId": int(v\[2].value or 2),
"duration": int(v\[3].value or 20),
"verifySsl": bool(v\[4].value),
}
def \_normalize(recipients):
"""Akceptuje string lub liste. Formaty odbiorcy:
"+48111222333" numer, "15:c" kontakt, "12:g" grupa z Phonebook.
"""
if recipients is None:
return \[]
if isinstance(recipients, basestring):
return \[r.strip() for r in recipients.split(",") if r.strip()]
return \[str(r).strip() for r in recipients if str(r).strip()]
def \_post(endpoint, payload, kind):
"""Wysyla zadanie do APIv2 i loguje wynik."""
cfg = getConfig()
if not cfg\["url"] or not cfg\["token"]:
LOGGER.error("Brak konfiguracji: gatewayUrl lub accessToken")
return False
client = system.net.httpClient(
timeout=10000,
bypass\_cert\_validation=not cfg\["verifySsl"]
)
headers = {"access-token": cfg\["token"],
"Content-Type": "application/json"}
try:
r = client.post(cfg\["url"] + endpoint, headers=headers, data=payload)
except Exception, e:
LOGGER.error("%s: blad polaczenia: %s" % (kind, str(e)))
return False
if r.good:
LOGGER.info("%s wyslany, HTTP %s" % (kind, r.statusCode))
return True
LOGGER.error("%s odrzucony, HTTP %s: %s" % (kind, r.statusCode, r.text))
return False
def sendSms(recipients, text, priority=None):
to = \_normalize(recipients)
if not to:
LOGGER.warn("sendSms: pusta lista odbiorcow")
return False
payload = {"to": to, "text": text}
if priority is not None:
payload\["priority"] = int(priority)
return \_post("/api/v2/messages/sms", payload, "SMS")
def ringCall(recipients, duration=None):
to = \_normalize(recipients)
if not to:
return False
cfg = getConfig()
payload = {"to": to, "duration": int(duration or cfg\["duration"])}
return \_post("/api/v2/calls/ring", payload, "Ring call")
def ttsAdvancedCall(recipients, text, voiceId=None, duration=None):
to = \_normalize(recipients)
if not to:
return False
cfg = getConfig()
payload = {
"to": to,
"text": text,
"voice\_id": int(voiceId or cfg\["voiceId"]),
"duration": int(duration or cfg\["duration"]),
}
return \_post("/api/v2/calls/tts\_advanced", payload, "TTS Advanced call")
def formatAlarm(event):
return "%s | %s | prio %s" % (
event.get("displayPath") or event.get("source"),
event.get("label"),
event.priority
)
def notifyAlarm(event, recipients=None, mode="SMS",
voiceId=None, duration=None):
"""Punkt wejscia dla bloku Script w Notification Pipeline."""
if recipients is None:
recipients = system.tag.readBlocking(
\["%s/defaultRecipients" % CONFIG\_PATH])\[0].value
text = formatAlarm(event)
mode = (mode or "SMS").upper()
ok = True
if mode in ("SMS", "SMS\_AND\_RING", "SMS\_AND\_TTS"):
ok = sendSms(recipients, text) and ok
if mode in ("RING", "SMS\_AND\_RING"):
ok = ringCall(recipients, duration) and ok
if mode in ("TTS", "SMS\_AND\_TTS"):
ok = ttsAdvancedCall(recipients, text, voiceId, duration) and ok
return ok
def testConnection():
"""Diagnostyka. Wywolaj z konsoli skryptowej Designera."""
cfg = getConfig()
if not cfg\["url"]:
return {"ok": False, "status": 0, "message": "Nie ustawiono gatewayUrl"}
client = system.net.httpClient(
timeout=10000,
bypass\_cert\_validation=not cfg\["verifySsl"]
)
headers = {"access-token": cfg\["token"]}
try:
r = client.get(cfg\["url"] + "/api/v2/modem/status", headers=headers)
except Exception, e:
return {"ok": False, "status": 0, "message": str(e)}
if r.good:
return {"ok": True, "status": r.statusCode,
"message": "Polaczenie poprawne"}
hints = {
401: "Nieprawidlowy lub wygasly token API",
403: "Uzytkownik API nie ma wymaganych uprawnien",
404: "Sprawdz adres bramki i wersje firmware (wymagane APIv2)",
}
return {"ok": False, "status": r.statusCode,
"message": hints.get(r.statusCode, r.text)}
SMSEagle Alerts.
SMSEagle.notifyAlarm(event, mode="SMS\_AND\_TTS")
4. W konfiguracji alarmu na tagu ustaw Active Pipeline na SMSEagle Alerts. Opcjonalnie wskaż również Clear Pipeline, jeśli chcesz powiadamiać o powrocie do stanu normalnego.
| Wartość | Działanie | Typowe zastosowanie |
|---|---|---|
SMS | tylko wiadomość tekstowa | alarmy informacyjne i ostrzeżenia |
RING | tylko połączenie budzące, sam sygnał dzwonienia bez komunikatu | dyskretne powiadomienie dyżurnego |
TTS | tylko połączenie głosowe TTS Advanced z odczytaniem treści alarmu | alarm krytyczny, gdy operator nie ma dostępu do ekranu |
SMS\_AND\_RING | wiadomość tekstowa oraz połączenie budzące | pełna treść alarmu i natychmiastowe zwrócenie uwagi odbiorcy |
SMS\_AND\_TTS | wiadomość tekstowa oraz połączenie głosowe z komunikatem | alarmy o najwyższym priorytecie, praca nocna i bezobsługowa |
Domyślnie funkcja odczytuje listę numerów z tagu defaultRecipients. Odbiorców można również wskazać bezpośrednio w wywołaniu:
# konkretne numery
SMSEagle.notifyAlarm(event, recipients=\["+48111222333"], mode="SMS")
# grupa z ksiazki telefonicznej SMSEagle (musi byc publiczna)
SMSEagle.notifyAlarm(event, recipients=\["12:g"], mode="SMS\_AND\_TTS")
# pojedynczy kontakt z ksiazki telefonicznej
SMSEagle.notifyAlarm(event, recipients=\["15:c"], mode="RING")
Prowadzenie listy odbiorców w książce telefonicznej SMSEagle pozwala korzystać z grup, harmonogramów zmian i eskalacji obsługiwanych po stronie bramki.
Jeśli powiadomienie ma być ponawiane do momentu potwierdzenia alarmu, rozbuduj potok o następującą sekwencję:
SMSEagle.notifyAlarm(event, mode="SMS")isAcked: jeśli alarm został potwierdzony, potok się kończySMSEagle.notifyAlarm(event, mode="SMS\_AND\_TTS")SMSEagle.notifyAlarm(event, recipients=\["20:g"], mode="SMS\_AND\_TTS")Każdy blok Script może korzystać z innego trybu i innej grupy odbiorców, dlatego kolejne poziomy eskalacji nie wymagają powielania konfiguracji.
SMSEagle.testConnection()
Oczekiwany wynik: {'ok': True, 'status': 200, 'message': 'Polaczenie poprawne'}.
2. Wyślij wiadomość testową:
SMSEagle.sendSms(\["+48111222333"], "Ignition test")
SMSEagle.ttsAdvancedCall(\["+48111222333"], "Ignition test call")
3. Wywołaj alarm na tagu testowym (tag pamięciowy typu Boolean z alarmem na wartość true).
4. Sprawdź logi Gateway: Status > Logs, filtr SMSEagle.
5. Sprawdź wpis w SMSEagle: menu Sent messages oraz Calls > Call history.
| Objaw | Przyczyna |
|---|---|
| HTTP 401 | nieprawidłowy lub wygasły token API |
| HTTP 403 | użytkownik API nie ma uprawnienia Send SMS, Make a ring call lub Make a TTS Advanced call |
| HTTP 400 przy połączeniu głosowym | brak pola voice\_id, wymaganego dla TTS Advanced |
| Błąd nawiązania sesji SSL | Ignition nie zna certyfikatu bramki; zaimportuj certyfikat CA do magazynu zaufanych certyfikatów Gateway lub na czas testów ustaw verifySsl na false |
| Brak wpisów w dzienniku | potok nie został przypisany do alarmu albo wpisy rejestratora SMSEagle są odfiltrowane |
NameError: SMSEagle | skrypt biblioteki ma inną nazwę niż SMSEagle albo projekt nie został zapisany |
Jeśli klient nie chce utrzymywać własnego kodu, można skorzystać z wbudowanego w Ignition profilu powiadomień Email i wskazać SMSEagle jako serwer SMTP.
+48501234567@192.168.1.101.Konfiguracja zajmuje kilka minut, nie daje jednak dostępu do połączeń głosowych. Ten wariant zalecamy wyłącznie dla alarmów niekrytycznych.
Ignition dobrze monitoruje proces, ale nie powiadomi o swojej własnej awarii. Wbudowana w SMSEagle funkcja Network Monitor pozwala niezależnie nadzorować serwer Gateway:
Gdy serwer przestaje odpowiadać, bramka samodzielnie wysyła alert SMS lub wykonuje połączenie telefoniczne, niezależnie od stanu systemu SCADA. Dzięki temu zawsze masz kontrolę nad stanem sprzętu serwera.
SMSEagle is a hardware & software solution that guarantees a swift delivery of your messages to designated recipients, whether it’s for notifications, alerts, or important updates.
After registering to a demo you get a remote access to our physical device NXS-9750.