Modbus TCP Server / Tecnoalarm panel gateway
This tutorial will guide you in setting up HSYCO as a gateway to enable communication between Modbus TCP and one of the security panels integrated into HSYCO. HSYCO will act as a Modbus TCP server.
To access HSYCO Manager open a browser and type the URL:
https://192.168.1.50/hsycoserver/manager
The IP address and the URLKey written above are the default ones
Add a security control panel I/O Server
Follow these steps of this tutorial:
Add a Modbus Tcp server I/O Server
1. Go back to the Manager splash screen
2. Click on Settings.

3. Add a new I/O server.

4. Click the type input field.

5. Type "mod"
6. Select Modbus TCP Server.

7. Select the name input field.

8. Assign a unique name to the I/O server, for example "mserver"
9. Click the port input field.

10. Type "502", the default Modbus port.
11. Click "Confirm"

12. Click on the Options icon.

13. Add a new option.

14. Click the option field.

15. Select "eventslog".

16. Click the value input field.

17. Select "true".

18. Click "Confirm".

19. Close the Options popup.

20. Click "Save Apply & restart".

21. Confirm by clicking the Save button.

Check the connection status
1. Let’s now check the connection status of the I/O Servers we just added. Go back to the Manager splash screen and click on Status Browser.

2. Click the "Filter" field to search for the connection datapoint.

3. Type " tecno.conn"
4. If the datapoint ‘tecno.connection’ is equal to ‘online,’ this means that the Tecnoalarm I/O server is successfully connected to HSYCO.
EVENTS scripting
1. From the Manager splash screen select Code Editor.

2. Click "New Events File"

3. Give a name to your script, for example: "gateway.txt"
4. Click "New File"

5. Now it's time to write some lines of code.
For further information about the logic of EVENTS programming, read EVENTS programming quickstart
Let's start with some simple functions:
#copy the status of a zone of the security panel to a Modbus register (Holding Register)
io tecno.zone.1.alarm = 1 : io mserver.1.1.hr = "ushort:"1
io tecno.zone.1.alarm = 0 : io mserver.1.1.hr = "ushort:"0
#copy the status of a zone of the security panel to a Modbus register (Coil)
io tecno.zone.2.alarm = 1 : io mserver.1.10.co = "bit:"1
io tecno.zone.2.alarm = 0 : io mserver.1.10.co = "bit:"0
#when a Modbus register is set to 1/0, arm/disarm a security panel program
io mserver.1.20.hr = 1 : io tecno.program.1 = arm
io mserver.1.20.hr = 0 : io tecno.program.1 = disarm
Click ‘Save’ in the upper right corner to activate this script.
The complete list of all the datapoints generated by an I/O Server is available in the Integrations section.
You can use Javascript as well. For example, this code automatically keeps the status of a Tecnoalarm panel aligned with a series of registers in a Modbus TCP Server, and allows sending commands to the panel by writing to some of these registers.
The gateway works as follows:
- when the panel goes online, after 15 seconds the gateway reads all programs, zones and general statuses and writes the complete Modbus table, then sets register 196 to 1
- from that moment, every change of the panel updates only the related register
- when the Modbus TCP server restarts, the table is written again
- writing 0 or 3 to a command register sends the command to the panel
Create a single EVENTS file with this code:
#********************************************************
# ************** GATEWAY TECNOALARM MODBUS **************
#********************************************************
#
# Single EVENTS file: replaces both previous gateway files (status + commands).
#
# Requirements:
# - If you change the I/O server ids (gwPanel / gwServer), change them also in
# the rules at the end of this file.
#
# Modbus map (holding registers):
# 1..64 program status (2 registers per program, LSB first)
# 65..127 program commands (odd registers): 0 = disarm, 3 = arm
# 196 connection status: 1 = gateway ready, 0 = not ready
# 198..1220 zone status (zone n = 196 + 2n)
# 1223..2245 zone commands (zone n = 1221 + 2n): 0 = include, 3 = exclude
# 3001..3009 panel general status
init : {
// ---------- CONFIGURATION ----------
var unitid = "1"; // Modbus unit id
var gwPanel = "tecno"; // Tecnoalarm I/O server id
var gwServer = "mserver"; // Modbus TCP server I/O server id
var gwMaxPrograms = 32; // program status/command tables size
var gwMaxZones = 512; // zone status/command tables size
var gwVerbose = true; // true = log panel events, register changes and Modbus commands
var gwDebug = false; // true = also log register bits and every write of the full sync
// ---------- TABLES ----------
// program sub state, bits 0..3 (Table 1.1)
var gwProgramStates = { off: 0, preout: 1, out: 2, armed: 3, armedpartial: 4, outpartial: 5, endpartial: 6 };
var gwProgramAttrs = { status: 1, prealarm: 1, alarm: 1, alarmmemory: 1 };
var gwZoneAttrs = { excluded: 1, cablecutstatus: 1, cablecutalarm: 1, lowbattery: 1, noradio: 1, alarm: 1, radiolearned: 1, active: 1, alarmmemory: 1 };
var gwSystemRegs = { fault: 3001, lowbattery: 3002, nopower: 3003, tamper: 3004, radiofault: 3005, techzone: 3006, normal: 3007, maintenance: 3008, panic: 3009 };
// ---------- STATE ----------
var gwReady = false; // false until the full status table has been written
var gwSyncing = false; // true while gwSync is writing the whole table
var gwWriteCount = 0; // registers written (used for the sync summary)
var gwRegCache = {}; // last value written to each register
var gwZoneMemory = {}; // alarm memory latched by the gateway (zone in alarm while active)
var gwPending = {}; // commands sent to the panel and not confirmed yet ("p1", "z5" -> state, time)
var gwPendingMs = 15000; // a command not confirmed within this time is forgotten
var gwEventName = null; // datapoint of the event being processed...
var gwEventValue = null; // ...and its new value
function gwLog(msg) {
messageLog("GATEWAY TM - " + msg);
}
function gwVerboseLog(msg) {
if (gwVerbose || gwDebug) gwLog(msg);
}
function gwDebugLog(msg) {
if (gwDebug) gwLog(msg);
}
// read a panel datapoint; the event being processed wins over ioGet
function gwRead(dp) {
var full = gwPanel + "." + dp;
if (full == gwEventName) return gwEventValue;
var v = ioGet(full);
return (v == null) ? null : String(v);
}
function gwFlag(dp) {
return (gwRead(dp) == "1") ? 1 : 0;
}
// write a whole 16 bit register, only if its value changed
function gwWrite(reg, word, desc) {
if (gwRegCache[reg] === word) return;
var bits = "";
for (var i = 15; i >= 0; i--) bits += (word >> i) & 1;
ioSet(gwServer + "." + unitid + "." + reg + ".hr", "bits:" + bits);
var old = gwRegCache[reg];
gwRegCache[reg] = word;
gwWriteCount++;
var msg = "MSERVER WRITE reg " + reg + " = " + word
+ (old !== undefined ? " (was " + old + ")" : "")
+ (desc ? " - " + desc : "")
+ (gwDebug ? " [" + bits + "]" : "");
if (gwSyncing) gwDebugLog(msg); else gwVerboseLog(msg);
}
// keep a command register aligned with the panel, so the next command is always a change.
// Writes made by EVENTS do not generate events, so they never come back to gwCommand
function gwWriteCommand(reg, word, desc) {
gwWrite(reg, word, desc);
}
// ---------- COMMANDS IN PROGRESS ----------
// the state the panel will have when the last command sent is executed, or null.
// Used for the commands and for the command registers, so that a command sent before
// the panel has executed the previous one is not lost
function gwPendingState(key) {
var c = gwPending[key];
if (c === undefined) return null;
if (new Date().getTime() - c.time > gwPendingMs) {
delete gwPending[key];
gwLog(key + " - command not confirmed by the panel");
return null;
}
return c.state;
}
function gwPendingSet(key, state) {
gwPending[key] = { state: state, time: new Date().getTime() };
}
// ---------- PROGRAMS ----------
// bits 0..3 sub state, 4 pre alarm, 5 alarm, 6 alarm memory
function gwUpdateProgram(p) {
if (p < 1 || p > gwMaxPrograms) return false;
var pre = "program." + p + ".";
var status = gwRead(pre + "status");
if (status == null) return false;
var state = gwProgramStates[status];
if (state === undefined) {
gwLog("program " + p + " unknown status: " + status);
state = 0;
}
var prealarm = gwFlag(pre + "prealarm");
var alarm = gwFlag(pre + "alarm");
var memory = gwFlag(pre + "alarmmemory");
var word = state | (prealarm << 4) | (alarm << 5) | (memory << 6);
var flags = [status];
if (prealarm) flags.push("prealarm");
if (alarm) flags.push("alarm");
if (memory) flags.push("alarm memory");
gwWrite(2 * p - 1, word, "program " + p + " status: " + flags.join(", "));
gwWrite(2 * p, 0, "program " + p + " status (high word)");
// arm is confirmed by any status other than off (exit time included)
var pending = gwPendingState("p" + p);
if (pending != null && (pending == "off") == (status == "off")) {
delete gwPending["p" + p];
gwVerboseLog("program " + p + " - command confirmed by the panel (" + status + ")");
pending = null;
}
var cmd = ((pending != null ? pending : status) == "off") ? 0 : 3;
gwWriteCommand(2 * p + 63, cmd, "program " + p + " command register aligned: " + (cmd == 3 ? "armed" : "disarmed") + (pending != null ? " (command pending)" : ""));
return true;
}
// ---------- ZONES ----------
// bits 0 exclusion, 2 cable cut, 3 cable cut alarm, 4 battery, 5 no radio,
// 6 open/close (open = 0, close = 1), 7 learned, 8 active, 11 alarm memory
function gwUpdateZone(z) {
if (z < 1 || z > gwMaxZones) return false;
var pre = "zone." + z + ".";
var alarm = gwRead(pre + "alarm");
if (alarm == null) return false;
var excluded = gwFlag(pre + "excluded");
var cablecut = gwFlag(pre + "cablecutstatus");
var cablecutalarm = gwFlag(pre + "cablecutalarm");
var lowbattery = gwFlag(pre + "lowbattery");
var noradio = gwFlag(pre + "noradio");
var learned = gwFlag(pre + "radiolearned");
var active = gwFlag(pre + "active");
var memory = (gwFlag(pre + "alarmmemory") || gwZoneMemory[z]) ? 1 : 0;
var word = excluded
| (cablecut << 2)
| (cablecutalarm << 3)
| (lowbattery << 4)
| (noradio << 5)
| ((alarm == "1" ? 0 : 1) << 6)
| (learned << 7)
| (active << 8)
| (memory << 11);
var flags = [alarm == "1" ? "open" : "closed"];
if (excluded) flags.push("excluded");
if (cablecut) flags.push("cable cut");
if (cablecutalarm) flags.push("cable cut alarm");
if (lowbattery) flags.push("low battery");
if (noradio) flags.push("no radio");
if (learned) flags.push("radio learned");
if (active) flags.push("active");
if (memory) flags.push("alarm memory");
gwWrite(196 + 2 * z, word, "zone " + z + " status: " + flags.join(", "));
var pending = gwPendingState("z" + z);
if (pending != null && pending == String(excluded)) {
delete gwPending["z" + z];
gwVerboseLog("zone " + z + " - command confirmed by the panel (" + (excluded ? "excluded" : "included") + ")");
pending = null;
}
var cmd = ((pending != null ? pending : String(excluded)) == "1") ? 3 : 0;
gwWriteCommand(1221 + 2 * z, cmd, "zone " + z + " command register aligned: " + (cmd == 3 ? "excluded" : "included") + (pending != null ? " (command pending)" : ""));
return true;
}
// ---------- PANEL GENERAL STATUS ----------
function gwUpdateSystem(key) {
var reg = gwSystemRegs[key];
if (reg === undefined) return;
var v = gwRead("system." + key);
if (v == null) return;
gwWrite(reg, (v == "1") ? 1 : 0, "system " + key + ": " + v);
}
// ---------- FULL SYNC ----------
// reads the whole panel status and writes the complete Modbus table
function gwSync() {
var t0 = new Date().getTime();
var programs = 0, zones = 0;
gwLog("SYNC - writing the full Modbus table");
// events arriving from now on are applied too, so no change can be lost
gwReady = true;
gwSyncing = true;
gwWriteCount = 0;
try {
for (var p = 1; p <= gwMaxPrograms; p++) {
if (gwUpdateProgram(p)) programs++;
}
for (var z = 1; z <= gwMaxZones; z++) {
if (gwFlag("zone." + z + ".alarm") && gwFlag("zone." + z + ".active")) gwZoneMemory[z] = true;
if (gwUpdateZone(z)) zones++;
}
for (var key in gwSystemRegs) gwUpdateSystem(key);
} finally {
gwSyncing = false;
}
gwWrite(196, 1, "connection: gateway ready");
gwLog("READY - programs: " + programs + " / zones: " + zones + " / registers written: " + gwWriteCount + " / " + (new Date().getTime() - t0) + " ms");
}
function gwOffline() {
gwReady = false;
gwPending = {};
gwWrite(196, 0, "connection: gateway not ready");
gwLog("TECNOALARM OFFLINE OR GATEWAY NOT READY");
}
// command registers generate events only while subscribed; the subscriptions are
// lost when the Modbus server restarts and when the EVENTS files are reloaded
function gwSubscribe() {
var r, count = 0;
for (r = 65; r <= 63 + 2 * gwMaxPrograms; r += 2) { ioSet(gwServer + "." + unitid + "." + r + ".hr", "subscribe:ushort"); count++; }
for (r = 1223; r <= 1221 + 2 * gwMaxZones; r += 2) { ioSet(gwServer + "." + unitid + "." + r + ".hr", "subscribe:ushort"); count++; }
gwLog("MSERVER - subscribed " + count + " command registers");
}
// the Modbus server (re)started: its registers are empty
function gwServerOnline() {
gwLog("MSERVER ONLINE - " + (gwReady ? "rewriting the full table" : "panel not ready yet"));
gwRegCache = {};
gwSubscribe();
if (gwReady) {
gwSync();
} else {
gwWrite(196, 0, "connection: gateway not ready");
}
}
// ---------- EVENTS FROM THE PANEL ----------
function gwPanelEvent(name, value) {
var parts = name.split(".");
gwEventName = name;
gwEventValue = value;
try {
if (parts.length == 3 && parts[1] == "system") {
if (gwSystemRegs[parts[2]] !== undefined) gwVerboseLog("TECNO EVENT " + name + " = " + value);
gwUpdateSystem(parts[2]);
} else if (parts.length == 4) {
var n = parseInt(parts[2], 10);
var attr = parts[3];
if (parts[1] == "program" && gwProgramAttrs[attr]) {
gwVerboseLog("TECNO EVENT " + name + " = " + value);
gwUpdateProgram(n);
} else if (parts[1] == "zone" && gwZoneAttrs[attr]) {
gwVerboseLog("TECNO EVENT " + name + " = " + value);
if (attr == "active") {
if (gwZoneMemory[n]) gwVerboseLog("zone " + n + " - gateway alarm memory cleared");
delete gwZoneMemory[n];
} else if (attr == "alarm" && value == "1" && gwFlag("zone." + n + ".active")) {
if (!gwZoneMemory[n]) gwVerboseLog("zone " + n + " - gateway alarm memory latched");
gwZoneMemory[n] = true;
}
gwUpdateZone(n);
}
}
gwDebugLog("event " + name + " = " + value);
} finally {
gwEventName = null;
gwEventValue = null;
}
}
// ---------- COMMANDS FROM MODBUS ----------
function gwCommand(name, value) {
var parts = name.split(".");
if (parts.length != 4 || parts[1] != unitid || parts[3] != "hr") return;
var reg = parseInt(parts[2], 10);
// HSYCO can run the event handlers in parallel and out of order: always use the last
// value written by the Modbus client, so that the last command wins
var last = ioGet(name);
if (last != null && String(last) != value) {
gwVerboseLog("MSERVER COMMAND reg " + reg + " = " + value + " (superseded by last value " + last + ")");
value = String(last);
} else {
gwVerboseLog("MSERVER COMMAND reg " + reg + " = " + value);
}
gwRegCache[reg] = parseInt(value, 10);
var isProgram = reg >= 65 && reg <= 63 + 2 * gwMaxPrograms && reg % 2 == 1;
var isZone = reg >= 1223 && reg <= 1221 + 2 * gwMaxZones && reg % 2 == 1;
if (value != "0" && value != "3") {
gwLog("register " + reg + ": invalid command value " + value);
// write back the value aligned with the panel (3 or 0)
if (gwReady && isProgram) gwUpdateProgram((reg - 63) / 2);
if (gwReady && isZone) gwUpdateZone((reg - 1221) / 2);
return;
}
if (!gwReady) {
gwLog("register " + reg + ": command ignored, gateway not ready");
return;
}
if (isProgram) {
var p = (reg - 63) / 2;
var status = gwRead("program." + p + ".status");
var pendingStatus = gwPendingState("p" + p);
if (pendingStatus != null) status = pendingStatus;
if (status == null) {
gwLog("T2 - program " + p + " does not exist");
} else if (value == "3" && status != "armed") {
ioSet(gwPanel + ".program." + p, "arm");
gwPendingSet("p" + p, "armed");
gwLog("T2 - program " + p + " arm (was " + status + ")");
} else if (value == "0" && status != "off") {
ioSet(gwPanel + ".program." + p, "disarm");
gwPendingSet("p" + p, "off");
gwLog("T2 - program " + p + " disarm (was " + status + ")");
} else {
gwVerboseLog("T2 - program " + p + " already " + status + (pendingStatus != null ? " (command pending)" : "") + ", nothing to do");
}
} else if (isZone) {
var z = (reg - 1221) / 2;
var excluded = gwRead("zone." + z + ".excluded");
var pendingExcluded = gwPendingState("z" + z);
if (excluded != null && pendingExcluded != null) excluded = pendingExcluded;
if (excluded == null) {
gwLog("T4 - zone " + z + " does not exist");
} else if (value == "3" && excluded != "1") {
ioSet(gwPanel + ".zone." + z, "disable");
gwPendingSet("z" + z, "1");
gwLog("T4 - zone " + z + " exclusion");
} else if (value == "0" && excluded == "1") {
ioSet(gwPanel + ".zone." + z, "enable");
gwPendingSet("z" + z, "0");
gwLog("T4 - zone " + z + " inclusion");
} else {
gwVerboseLog("T4 - zone " + z + " already " + (excluded == "1" ? "excluded" : "included") + (pendingExcluded != null ? " (command pending)" : "") + ", nothing to do");
}
} else {
gwVerboseLog("register " + reg + " is not a command register, ignored");
}
}
// EVENTS file reloaded while the Modbus server and the panel are already online
if (ioGet(gwServer + ".connection") == "online") gwSubscribe();
if (ioGet(gwPanel + ".connection") == "online") gwSync();
}
# for each event
function IOEvent(name, value) : {
var n = String(name);
if (n.indexOf(gwPanel + ".") == 0) {
// ignored until the full table has been written
if (gwReady) gwPanelEvent(n, String(value));
} else if (n.indexOf(gwServer + ".") == 0 && n != gwServer + ".connection") {
gwCommand(n, String(value));
}
}
# panel online: wait for the panel status to settle, then write the whole table
io tecno.connection = online : programtimer startgateway = set 15
io tecno.connection = offline : programtimer startgateway = clear
programtimer startgateway : {
gwSync();
}
io tecno.connection = offline : {
gwOffline();
}
# Modbus server (re)started: subscribe command registers and rewrite the table
io mserver.connection = online : {
gwServerOnline();
}
MODBUS tables
Supported Modbus functions, Modbus TCP/IP
03: Read Holding Registers
06: Write Single Register
The register numbers in the tables below are Modbus protocol addresses, unit id 1. Clients that use the 1-based "4xxxx" holding register notation must add 40001: for example, register 196 is 40197 and register 1225 is 41226.
Program Status Table
| Modbus address | Info |
|---|---|
| 1 | Program status 1 |
| 3 | Program status 2 |
| 5 | Program status 3 |
| 7 | Program status 4 |
| 9 | Program status 5 |
| 11 | Program status 6 |
| 13 | Program status 7 |
| 15 | Program status 8 |
| 17 | Program status 9 |
| 19 | Program status 10 |
| 21 | Program status 11 |
| 23 | Program status 12 |
| 25 | Program status 13 |
| 27 | Program status 14 |
| 29 | Program status 15 |
| 31 | Program status 16 |
| 33 | Program status 17 |
| 35 | Program status 18 |
| 37 | Program status 19 |
| 39 | Program status 20 |
| 41 | Program status 21 |
| 43 | Program status 22 |
| 45 | Program status 23 |
| 47 | Program status 24 |
| 49 | Program status 25 |
| 51 | Program status 26 |
| 53 | Program status 27 |
| 55 | Program status 28 |
| 57 | Program status 29 |
| 59 | Program status 30 |
| 61 | Program status 31 |
| 63 | Program status 32 |
Possible Values:
| bit | Description | note |
|---|---|---|
| 0..3 | sub state | See Table 1.1 |
| 4 | pre alarm | |
| 5 | alarm | |
| 6 | alarm memory | |
| 7..32 | not used |
Table 1.1
| Value | Status | Tecnoalarm program status |
|---|---|---|
| 0 | normal status | off |
| 1 | pre exit | preout |
| 2 | exit | out |
| 3 | armed | armed |
| 4 | armed partial | armedpartial |
| 5 | partial | outpartial |
| 6 | end of partialization | endpartial |
Program Commands Table
Send a command to the Tecnoalarm panel by writing 0 or 3 in one of this registers
| Modbus address | Info |
|---|---|
| 65 | Program command 1 |
| 67 | Program command 2 |
| 69 | Program command 3 |
| 71 | Program command 4 |
| 73 | Program command 5 |
| 75 | Program command 6 |
| 77 | Program command 7 |
| 79 | Program command 8 |
| 81 | Program command 9 |
| 83 | Program command 10 |
| 85 | Program command 11 |
| 87 | Program command 12 |
| 89 | Program command 13 |
| 91 | Program command 14 |
| 93 | Program command 15 |
| 95 | Program command 16 |
| 97 | Program command 17 |
| 99 | Program command 18 |
| 101 | Program command 19 |
| 103 | Program command 20 |
| 105 | Program command 21 |
| 107 | Program command 22 |
| 109 | Program command 23 |
| 111 | Program command 24 |
| 113 | Program command 25 |
| 115 | Program command 26 |
| 117 | Program command 27 |
| 119 | Program command 28 |
| 121 | Program command 29 |
| 123 | Program command 30 |
| 125 | Program command 31 |
| 127 | Program command 32 |
Possible Values:
| value | Description |
|---|---|
| 0 | disarm program |
| 3 | arm program |
The gateway keeps these registers aligned with the panel: 3 when the program is not off (armed, partial or exit time), 0 when it is off. This way a command is always accepted, even after the program has been armed or disarmed from a keypad.
Connection Status
| Modbus address | Info |
|---|---|
| 196 | 0 : HSYCO not connected with Tecnoalarm panel, or Modbus table not updated yet |
| 196 | 1 : HSYCO connected and Modbus table updated |
Zone Status Table
| Modbus address | Zone Status |
|---|---|
| 198 | Zone status 1 |
| 200 | Zone status 2 |
| 202 | Zone status 3 |
| ... | ... |
| ... | ... |
| 1220 | Zone status 512 |
Possible Values:
| bit | Description | note |
|---|---|---|
| 0 | permanent exclusion | |
| 1 | ||
| 2 | cable cut | |
| 3 | alarm cable cut | |
| 4 | battery status | |
| 5 | no radio | |
| 6 | open/close | open = 0, close = 1 |
| 7 | learned | |
| 8 | active | 1 = armed, 0 = disarmed |
| 9 | ||
| 10 | ||
| 11 | alarm memory |
Zone Commands Table
| Modbus address | Zone Status |
|---|---|
| 1223 | Zone command 1 |
| 1225 | Zone command 2 |
| 1227 | Zone command 3 |
| ... | ... |
| ... | ... |
| 2245 | Zone command 512 |
Possible Values:
| value | Description |
|---|---|
| 0 | include zone |
| 3 | exclude zone |
The gateway keeps these registers aligned with the panel: 3 when the zone is excluded, 0 when it is included.
Panel General Status
| Modbus address | Info |
|---|---|
| 3001 | 1 : At least one fault in the panel |
| 3001 | 0 : No faults |
| 3002 | 1 : Low battery |
| 3002 | 0 : No low battery |
| 3003 | 1 : No AC power |
| 3003 | 0 : AC power is present |
| 3004 | 1 : General tamper alarm |
| 3004 | 0 : No general tamper alarm |
| 3005 | 1 : General radio module fault |
| 3005 | 0 : No general radio module fault |
| 3006 | 1 : Tech zones open |
| 3006 | 0 : Tech zones not open |
| 3007 | 1 : Normal status |
| 3007 | 0 : Not in normal status |
| 3008 | 1 : Maintenance mode active |
| 3008 | 0 : Maintenance mode not active |
| 3009 | 1 : Panic mode active |
| 3009 | 0 : Panic mode not active |