Skip to content

Commit 2e49f12

Browse files
committed
[MQTT] encode dates as unquoted ISO 8601 strings with offset and without timezone
1 parent 8b1749e commit 2e49f12

File tree

6 files changed

+31
-32
lines changed

6 files changed

+31
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- MQTT
1616
- **Breaking** The default refresh rate while the car is active has been changed to 30 seconds
1717
- **Breaking** The default refresh rate while the car is inactive has been changed to 24 hours
18+
- **Breaking** encode dates as unquoted ISO 8601 strings with offset and without timezone
1819
- support configuring `refresh/mode`, `refresh/period/active`, `refresh/period/inActive` and `refresh/period/inActiveGrace` via MQTT
1920
- Handle fallback for SOC when charge status update fails
2021
- ensure that a changed systemd configuration is picked up

saic-java-mqtt-gateway/src/main/java/net/heberling/ismart/mqtt/MessageHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import java.net.URI;
55
import java.nio.charset.StandardCharsets;
66
import java.time.Instant;
7+
import java.time.OffsetDateTime;
78
import java.time.ZoneId;
8-
import java.time.ZonedDateTime;
99
import net.heberling.ismart.Client;
1010
import net.heberling.ismart.asn1.v1_1.Message;
1111
import net.heberling.ismart.asn1.v1_1.MessageCoder;
@@ -89,7 +89,7 @@ private SaicMessage convert(net.heberling.ismart.asn1.v1_1.entity.Message messag
8989
message.getMessageId(),
9090
message.getMessageType(),
9191
new String(message.getTitle(), StandardCharsets.UTF_8),
92-
ZonedDateTime.ofInstant(
92+
OffsetDateTime.ofInstant(
9393
Instant.ofEpochSecond(message.getMessageTime().getSeconds()), ZoneId.systemDefault()),
9494
new String(message.getSender(), StandardCharsets.UTF_8),
9595
new String(message.getContent(), StandardCharsets.UTF_8),

saic-java-mqtt-gateway/src/main/java/net/heberling/ismart/mqtt/SaicMessage.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package net.heberling.ismart.mqtt;
22

3-
import java.time.ZonedDateTime;
3+
import java.time.OffsetDateTime;
44

55
public class SaicMessage {
66

@@ -10,7 +10,7 @@ public class SaicMessage {
1010

1111
private final String title;
1212

13-
private final ZonedDateTime messageTime;
13+
private final OffsetDateTime messageTime;
1414

1515
private final String sender;
1616

@@ -26,7 +26,7 @@ public SaicMessage(
2626
Long messageId,
2727
String messageType,
2828
String title,
29-
ZonedDateTime messageTime,
29+
OffsetDateTime messageTime,
3030
String sender,
3131
String content,
3232
Integer readStatus,
@@ -53,7 +53,7 @@ public String getTitle() {
5353
return title;
5454
}
5555

56-
public ZonedDateTime getMessageTime() {
56+
public OffsetDateTime getMessageTime() {
5757
return messageTime;
5858
}
5959

saic-java-mqtt-gateway/src/main/java/net/heberling/ismart/mqtt/VehicleHandler.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.net.URI;
77
import java.net.URISyntaxException;
88
import java.nio.charset.StandardCharsets;
9-
import java.time.ZonedDateTime;
9+
import java.time.OffsetDateTime;
1010
import java.util.*;
1111
import java.util.concurrent.ExecutionException;
1212
import java.util.concurrent.TimeoutException;
@@ -64,10 +64,10 @@ public VehicleHandler(
6464
void handleVehicle() throws MqttException, IOException {
6565
vehicleState.configure(vinInfo);
6666
// we just got started, force some updates
67-
ZonedDateTime startTime = ZonedDateTime.now();
67+
OffsetDateTime startTime = OffsetDateTime.now();
6868
vehicleState.notifyCarActivityTime(startTime, true);
6969
while (true) {
70-
if (!vehicleState.isComplete() && ZonedDateTime.now().isAfter(startTime.plusSeconds(10))) {
70+
if (!vehicleState.isComplete() && OffsetDateTime.now().isAfter(startTime.plusSeconds(10))) {
7171
vehicleState.configureMissing();
7272
}
7373
if (vehicleState.isComplete() && vehicleState.shouldRefresh()) {
@@ -296,7 +296,7 @@ private void sendCommand(byte type, SortedMap<Integer, byte[]> parameter)
296296
MessageCoder<OTA_RVCReq> otaRvcReqMessageCoder = new MessageCoder<>(OTA_RVCReq.class);
297297

298298
// we send a command end expect the car to wake up
299-
vehicleState.notifyCarActivityTime(ZonedDateTime.now(), false);
299+
vehicleState.notifyCarActivityTime(OffsetDateTime.now(), false);
300300

301301
OTA_RVCReq req = new OTA_RVCReq();
302302
req.setRvcReqType(new byte[] {type});
@@ -373,7 +373,7 @@ private void sendCharging(boolean state)
373373
new net.heberling.ismart.asn1.v3_0.MessageCoder<>(OTA_ChrgCtrlReq.class);
374374

375375
// we send a command end expect the car to wake up
376-
vehicleState.notifyCarActivityTime(ZonedDateTime.now(), false);
376+
vehicleState.notifyCarActivityTime(OffsetDateTime.now(), false);
377377

378378
OTA_ChrgCtrlReq req = new OTA_ChrgCtrlReq();
379379
req.setTboxV2XReq(0);

saic-java-mqtt-gateway/src/main/java/net/heberling/ismart/mqtt/VehicleState.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import java.nio.charset.StandardCharsets;
88
import java.time.Clock;
9-
import java.time.ZonedDateTime;
9+
import java.time.OffsetDateTime;
1010
import java.time.temporal.ChronoUnit;
1111
import java.util.HashMap;
1212
import java.util.Map;
@@ -27,10 +27,10 @@ public class VehicleState {
2727
private final IMqttClient client;
2828
private final String mqttVINPrefix;
2929
private Supplier<Clock> clockSupplier;
30-
private ZonedDateTime lastCarActivity;
31-
private ZonedDateTime lastSuccessfulRefresh;
32-
private ZonedDateTime lastCarShutdown;
33-
private ZonedDateTime lastVehicleMessage;
30+
private OffsetDateTime lastCarActivity;
31+
private OffsetDateTime lastSuccessfulRefresh;
32+
private OffsetDateTime lastCarShutdown;
33+
private OffsetDateTime lastVehicleMessage;
3434
// treat HV battery as active, if we don't have any other information
3535
private boolean hvBatteryActive = true;
3636
private Long refreshPeriodActive;
@@ -48,7 +48,7 @@ protected VehicleState(
4848
this.client = client;
4949
this.mqttVINPrefix = mqttAccountPrefix + "/" + VEHICLES + "/" + vin;
5050
this.clockSupplier = clockSupplier;
51-
lastCarShutdown = ZonedDateTime.now(clockSupplier.get());
51+
lastCarShutdown = OffsetDateTime.now(clockSupplier.get());
5252
}
5353

5454
public String getMqttVINPrefix() {
@@ -373,8 +373,7 @@ public void handleVehicleStatusMessage(
373373
}
374374

375375
msg =
376-
new MqttMessage(
377-
SaicMqttGateway.toJSON(ZonedDateTime.now(getClock())).getBytes(StandardCharsets.UTF_8));
376+
new MqttMessage(OffsetDateTime.now(getClock()).toString().getBytes(StandardCharsets.UTF_8));
378377
msg.setQos(0);
379378
msg.setRetained(true);
380379
client.publish(mqttVINPrefix + "/" + REFRESH_LAST_VEHICLE_STATE, msg);
@@ -466,19 +465,18 @@ public void handleChargeStatusMessage(
466465
client.publish(mqttVINPrefix + "/" + DRIVETRAIN_SOC, msg);
467466

468467
msg =
469-
new MqttMessage(
470-
SaicMqttGateway.toJSON(ZonedDateTime.now(getClock())).getBytes(StandardCharsets.UTF_8));
468+
new MqttMessage(OffsetDateTime.now(getClock()).toString().getBytes(StandardCharsets.UTF_8));
471469
msg.setQos(0);
472470
msg.setRetained(true);
473471
client.publish(mqttVINPrefix + "/" + REFRESH_LAST_CHARGE_STATE, msg);
474472
}
475473

476-
public void notifyCarActivityTime(ZonedDateTime now, boolean force) throws MqttException {
474+
public void notifyCarActivityTime(OffsetDateTime now, boolean force) throws MqttException {
477475
// if the car activity changed, notify the channel
478476
if (lastCarActivity == null || force || lastCarActivity.isBefore(now)) {
479477
lastCarActivity = now;
480478
MqttMessage msg =
481-
new MqttMessage(SaicMqttGateway.toJSON(lastCarActivity).getBytes(StandardCharsets.UTF_8));
479+
new MqttMessage(lastCarActivity.toString().getBytes(StandardCharsets.UTF_8));
482480
msg.setQos(0);
483481
msg.setRetained(true);
484482
client.publish(mqttVINPrefix + "/" + REFRESH_LAST_ACTIVITY, msg);
@@ -522,19 +520,19 @@ public boolean shouldRefresh() {
522520
if (hvBatteryActive
523521
|| lastCarShutdown
524522
.plus(refreshPeriodAfterShutdown, ChronoUnit.SECONDS)
525-
.isAfter(ZonedDateTime.now(getClock()))) {
523+
.isAfter(OffsetDateTime.now(getClock()))) {
526524
return lastSuccessfulRefresh.isBefore(
527-
ZonedDateTime.now(getClock()).minus(refreshPeriodActive, ChronoUnit.SECONDS));
525+
OffsetDateTime.now(getClock()).minus(refreshPeriodActive, ChronoUnit.SECONDS));
528526
} else {
529527
return lastSuccessfulRefresh.isBefore(
530-
ZonedDateTime.now(getClock()).minus(refreshPeriodInactive, ChronoUnit.SECONDS));
528+
OffsetDateTime.now(getClock()).minus(refreshPeriodInactive, ChronoUnit.SECONDS));
531529
}
532530
}
533531
}
534532

535533
public void setHVBatteryActive(boolean hvBatteryActive) throws MqttException {
536534
if (!hvBatteryActive && this.hvBatteryActive) {
537-
this.lastCarShutdown = ZonedDateTime.now(getClock());
535+
this.lastCarShutdown = OffsetDateTime.now(getClock());
538536
}
539537
this.hvBatteryActive = hvBatteryActive;
540538

@@ -545,7 +543,7 @@ public void setHVBatteryActive(boolean hvBatteryActive) throws MqttException {
545543
client.publish(mqttVINPrefix + "/" + DRIVETRAIN_HV_BATTERY_ACTIVE, msg);
546544

547545
if (hvBatteryActive) {
548-
notifyCarActivityTime(ZonedDateTime.now(getClock()), true);
546+
notifyCarActivityTime(OffsetDateTime.now(getClock()), true);
549547
}
550548
}
551549

@@ -617,7 +615,7 @@ public RefreshMode getRefreshMode() {
617615
}
618616

619617
public void markSuccessfulRefresh() {
620-
this.lastSuccessfulRefresh = ZonedDateTime.now(getClock());
618+
this.lastSuccessfulRefresh = OffsetDateTime.now(getClock());
621619
}
622620

623621
public void setRefreshPeriodAfterShutdown(long refreshPeriodAfterShutdown) {

saic-java-mqtt-gateway/src/test/java/net/heberling/ismart/mqtt/VehicleStateTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
import java.time.Clock;
77
import java.time.Instant;
8+
import java.time.OffsetDateTime;
89
import java.time.ZoneId;
9-
import java.time.ZonedDateTime;
1010
import org.eclipse.paho.client.mqttv3.MqttClient;
1111
import org.eclipse.paho.client.mqttv3.MqttException;
1212
import org.junit.jupiter.api.BeforeEach;
@@ -29,7 +29,7 @@ public void setUp() throws MqttException {
2929
clock = Clock.fixed(Instant.ofEpochSecond(REFERENCE_TIME), ZoneId.systemDefault());
3030
vehicleState = new VehicleState(mqttClient, "test/topic", "test", () -> this.clock);
3131
vehicleState.configureMissing();
32-
vehicleState.notifyCarActivityTime(ZonedDateTime.now(clock), true);
32+
vehicleState.notifyCarActivityTime(OffsetDateTime.now(clock), true);
3333
}
3434

3535
@Test
@@ -40,7 +40,7 @@ public void willAllwaysRefreshOnFirstCall() {
4040
@Test
4141
public void willRefreshIfCarWasActiveAfterLastRefresh() throws MqttException {
4242
clock = Clock.offset(clock, java.time.Duration.ofSeconds(60));
43-
vehicleState.notifyCarActivityTime(ZonedDateTime.now(clock), true);
43+
vehicleState.notifyCarActivityTime(OffsetDateTime.now(clock), true);
4444
assertThat(vehicleState.shouldRefresh(), is(true));
4545
}
4646

0 commit comments

Comments
 (0)