mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[paradoxalarm] Add SYNC_TIME command to sync panel clock on demand (#20744)
* [paradoxalarm] Add SYNC_TIME command to sync panel clock on demand Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
This commit is contained in:
@@ -41,18 +41,23 @@ Currently binding supports the following panels: EVO192, EVO48 (not tested), EVO
|
||||
|
||||
### IP150 Bridge Channels
|
||||
|
||||
| Channel | Description |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------|
|
||||
| communicationCommand | Possible values [LOGOUT, LOGIN, RESET] |
|
||||
| communicationState | Shows the communication status to Paradox (may differ from Bridge status). Values: [Offline, Online]. |
|
||||
| Channel | Description |
|
||||
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| communicationCommand | Possible values [LOGOUT, LOGIN, RESET, SYNC_TIME] |
|
||||
| communicationState | Shows the communication status to Paradox (may differ from Bridge status). Values: [Offline, Online]. |
|
||||
|
||||
#### Communication Command Values
|
||||
|
||||
| Value | Description |
|
||||
|--------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| LOGOUT | Logs out and disconnects from Paradox alarm system. |
|
||||
| LOGIN | Creates socket if necessary, connects to Paradox system, and uses the logon data from the Thing parameters to connect. |
|
||||
| RESET | Does logout and then login with recreation of communicator objects inside the code. |
|
||||
| Value | Description |
|
||||
|-----------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| LOGOUT | Logs out and disconnects from Paradox alarm system. |
|
||||
| LOGIN | Creates socket if necessary, connects to Paradox system, and uses the logon data from the Thing parameters to connect. |
|
||||
| RESET | Does logout and then login with recreation of communicator objects inside the code. |
|
||||
| SYNC_TIME | Pushes the current openHAB host time to the panel clock on demand. |
|
||||
|
||||
**Note on SYNC_TIME timezone:** The time sent to the panel uses the JVM default timezone of the openHAB host (i.e. the timezone configured for the Java process or the OS).
|
||||
If the host runs in UTC (e.g. a Docker container without an explicit timezone setting) but the panel is installed in a different timezone, the panel clock will be set to the wrong local time.
|
||||
To avoid this, ensure the openHAB host timezone matches the timezone where the panel is located — for example, by setting the `TZ` environment variable on the container.
|
||||
|
||||
### Entities (zones, partitions) Configuration
|
||||
|
||||
@@ -183,7 +188,7 @@ Currently binding supports the following panels: EVO192, EVO48 (not tested), EVO
|
||||
Text label="Security" icon="lock" {
|
||||
Frame label="IP150 communication" {
|
||||
Text item=panelState valuecolor=[panelState=="Online"="green", panelState=="Offline"="red"]
|
||||
Selection item=paradoxSendCommand mappings=["LOGOUT"="Logout", "LOGIN"="Login", "RESET"="Reset"]
|
||||
Selection item=paradoxSendCommand mappings=["LOGOUT"="Logout", "LOGIN"="Login", "RESET"="Reset", "SYNC_TIME"="Sync Time"]
|
||||
}
|
||||
Frame label="Panel" {
|
||||
Text item=paradoxTime
|
||||
|
||||
+5
-1
@@ -120,7 +120,11 @@ public abstract class AbstractCommunicator implements IParadoxInitialLoginCommun
|
||||
byte[] packetBytes = request.getRequestPacket().getBytes();
|
||||
ParadoxUtil.printPacket("Tx Packet:", packetBytes);
|
||||
tx.write(packetBytes);
|
||||
syncQueue.moveRequest();
|
||||
if (request.isResponseExpected()) {
|
||||
syncQueue.moveRequest();
|
||||
} else {
|
||||
syncQueue.removeSendRequest();
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
logger.debug("Socket time out occurred. Informing listener. Request={}. Exception=", request, e);
|
||||
syncQueue.removeSendRequest();
|
||||
|
||||
+14
@@ -15,6 +15,7 @@ package org.openhab.binding.paradoxalarm.internal.communication;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -28,6 +29,7 @@ import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderMe
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPayload;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.RamRequestPayload;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.SyncTimePayload;
|
||||
import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException;
|
||||
import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxRuntimeException;
|
||||
import org.openhab.binding.paradoxalarm.internal.model.EntityType;
|
||||
@@ -326,6 +328,9 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
||||
CommunicationState.LOGOUT.runPhase(this);
|
||||
scheduler.schedule(this::startLoginSequence, 5, TimeUnit.SECONDS);
|
||||
return;
|
||||
case SYNC_TIME:
|
||||
syncTime(ZonedDateTime.now());
|
||||
return;
|
||||
default:
|
||||
logger.debug("Command {} not implemented.", command);
|
||||
}
|
||||
@@ -367,6 +372,15 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncTime(ZonedDateTime time) {
|
||||
logger.debug("Submitting SYNC_TIME request for {}", time);
|
||||
IPayload payload = new SyncTimePayload(time);
|
||||
ParadoxIPPacket packet = createSerialPassthroughPacket(payload);
|
||||
IRequest request = new SyncTimeRequest(packet, this);
|
||||
submitRequest(request);
|
||||
}
|
||||
|
||||
public static class EvoCommunicatorBuilder implements ICommunicatorBuilder {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EvoCommunicatorBuilder.class);
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ public class GenericCommunicator extends AbstractCommunicator implements IRespon
|
||||
} else if (type == RequestType.EPROM) {
|
||||
receiveEpromResponse(response);
|
||||
} else {
|
||||
logger.debug("Probably wrong sender in the request. Request type is not one of the supported methods.");
|
||||
logger.debug("Received response for unhandled request type: {}", type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -22,5 +22,6 @@ public enum IP150Command {
|
||||
LOGOUT,
|
||||
LOGIN,
|
||||
RESET,
|
||||
SYNC_TIME,
|
||||
UNIMPLEMENTED
|
||||
}
|
||||
|
||||
+10
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.paradoxalarm.internal.communication;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -40,4 +41,13 @@ public interface IParadoxCommunicator extends IParadoxInitialLoginCommunicator {
|
||||
void initializeData();
|
||||
|
||||
MemoryMap getMemoryMap();
|
||||
|
||||
/**
|
||||
* Pushes the supplied time to the panel clock via the EVO serial passthrough command.
|
||||
*
|
||||
* @param time the date/time to set on the panel; must include a valid time-zone so the correct wall-clock values
|
||||
* are
|
||||
* encoded
|
||||
*/
|
||||
void syncTime(ZonedDateTime time);
|
||||
}
|
||||
|
||||
+4
@@ -30,4 +30,8 @@ public interface IRequest {
|
||||
boolean isTimeStampExpired(long expirationTreshold);
|
||||
|
||||
RequestType getType();
|
||||
|
||||
default boolean isResponseExpected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -22,5 +22,6 @@ public enum RequestType {
|
||||
RAM,
|
||||
EPROM,
|
||||
PARTITION_COMMAND,
|
||||
ZONE_COMMAND
|
||||
ZONE_COMMAND,
|
||||
SYNC_TIME
|
||||
}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.paradoxalarm.internal.communication;
|
||||
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPPacket;
|
||||
|
||||
/**
|
||||
* The {@link SyncTimeRequest}. Request for setting the date/time on a Paradox EVO panel.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
public class SyncTimeRequest extends Request {
|
||||
|
||||
public SyncTimeRequest(IPPacket packet, IResponseReceiver receiver) {
|
||||
super(RequestType.SYNC_TIME, packet, receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResponseExpected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SyncTimeRequest [getType()=" + getType() + "]";
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.paradoxalarm.internal.communication.messages;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SyncTimePayload} builds the 13-byte EVO serial command to set the panel clock (command 0x30).
|
||||
* Seconds are not supported by the protocol.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SyncTimePayload implements IPayload {
|
||||
|
||||
private static final int LENGTH = 13;
|
||||
private static final byte COMMAND = 0x30;
|
||||
private static final byte PAYLOAD_LENGTH = 0x0D;
|
||||
|
||||
private final ZonedDateTime time;
|
||||
|
||||
public SyncTimePayload(ZonedDateTime time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
byte[] buf = new byte[LENGTH];
|
||||
buf[0] = COMMAND;
|
||||
buf[1] = PAYLOAD_LENGTH;
|
||||
// bytes 2-5: padding (zero)
|
||||
buf[6] = (byte) (time.getYear() / 100);
|
||||
buf[7] = (byte) (time.getYear() % 100);
|
||||
buf[8] = (byte) time.getMonthValue();
|
||||
buf[9] = (byte) time.getDayOfMonth();
|
||||
buf[10] = (byte) time.getHour();
|
||||
buf[11] = (byte) time.getMinute();
|
||||
// buf[12] left as 0x00 — ParadoxIPPacket(byte[]) overwrites the last byte with the checksum
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -14,6 +14,8 @@ package org.openhab.binding.paradoxalarm.internal.handlers;
|
||||
|
||||
import static org.openhab.binding.paradoxalarm.internal.handlers.ParadoxAlarmBindingConstants.*;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.paradoxalarm.internal.model.ParadoxInformation;
|
||||
import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
||||
@@ -57,7 +59,10 @@ public class ParadoxPanelHandler extends EntityBaseHandler {
|
||||
panelInformation.getApplicationVersion().toString());
|
||||
updateProperty(PANEL_BOOTLOADER_VERSION_PROPERTY_NAME, panelInformation.getBootLoaderVersion().toString());
|
||||
|
||||
updateState(PANEL_TIME, new DateTimeType(panel.getPanelTime()));
|
||||
ZonedDateTime panelTime = panel.getPanelTime();
|
||||
if (panelTime != null) {
|
||||
updateState(PANEL_TIME, new DateTimeType(panelTime));
|
||||
}
|
||||
updateState(PANEL_INPUT_VOLTAGE, new QuantityType<>(panel.getVdcLevel(), Units.VOLT));
|
||||
updateState(PANEL_BOARD_VOLTAGE, new QuantityType<>(panel.getDcLevel(), Units.VOLT));
|
||||
updateState(PANEL_BATTERY_VOLTAGE, new QuantityType<>(panel.getBatteryLevel(), Units.VOLT));
|
||||
|
||||
+4
@@ -91,6 +91,10 @@ public class ParadoxPanel implements IDataUpdateListener {
|
||||
}
|
||||
|
||||
ZoneStateFlags zoneStateFlags = communicator.getZoneStateFlags();
|
||||
if (zoneStateFlags == null) {
|
||||
logger.debug("Zone state flags not yet available, skipping zone update.");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < zones.size(); i++) {
|
||||
Zone zone = zones.get(i);
|
||||
zone.setZoneState(parser.calculateZoneState(zone.getId(), zoneStateFlags));
|
||||
|
||||
+8
@@ -72,6 +72,14 @@
|
||||
<item-type>String</item-type>
|
||||
<label>Communicator Command</label>
|
||||
<description>Send Command</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="LOGIN">Login</option>
|
||||
<option value="LOGOUT">Logout</option>
|
||||
<option value="RESET">Reset</option>
|
||||
<option value="SYNC_TIME">Sync Panel Time</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="communicationState">
|
||||
<item-type>String</item-type>
|
||||
|
||||
Reference in New Issue
Block a user