Skip to content

Commit b11c3cd

Browse files
author
WhisperN
committed
implementing massive frontend update and backend sleep sensor implementation. Fixes #4
1 parent c02afab commit b11c3cd

File tree

4 files changed

+164
-22
lines changed

4 files changed

+164
-22
lines changed

iot-backend/src/main/java/iot/microservice/controller/MicroController.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ public ResponseEntity<Response> lamp(@PathVariable String val) {
4040
return new ResponseEntity<>(res, HttpStatus.OK);
4141
}
4242

43+
@GetMapping("/bedroom/lamp/status")
44+
public ResponseEntity<Response> getLampStatus() {
45+
Response res = new Response();
46+
res.setMessage(service.shellyRelayStatus());
47+
return new ResponseEntity<>(res, HttpStatus.OK);
48+
}
49+
4350
@GetMapping("/bedroom/sleep/{day}")
4451
public ResponseEntity<Response> sleep(@PathVariable("day") int timeframe) {
4552
Response res = new Response();
@@ -57,14 +64,14 @@ public ResponseEntity<Response> sleepQualityScore() {
5764
@GetMapping("/bedroom/sleep/total-duration")
5865
public ResponseEntity<Response> sleepTotalDuration() {
5966
Response res = new Response();
60-
res.setMessage(service.retrieveSleepTotalDuration());
67+
res.setMessage(service.retrieveSleepTotalDuration("sensors/sleep"));
6168
return new ResponseEntity<>(res, HttpStatus.OK);
6269
}
6370

6471
@GetMapping("/bedroom/sleep/time-series")
6572
public ResponseEntity<Response> sleepTimeSeries() {
6673
Response res = new Response();
67-
res.setMessage(service.retrieveSleepTimeSeries());
74+
res.setMessage(service.retrieveSleepTimeSeries("sensors/sleep"));
6875
return new ResponseEntity<>(res, HttpStatus.OK);
6976
}
7077

@@ -85,7 +92,7 @@ public ResponseEntity<Response> sleepMedianRpm() {
8592
@GetMapping("/bedroom/sleep/states-series")
8693
public ResponseEntity<Response> sleepStatesSeries() {
8794
Response res = new Response();
88-
res.setMessage(service.retrieveSleepStateSeries());
95+
res.setMessage(service.retrieveSleepStateSeries("sensors/sleep"));
8996
return new ResponseEntity<>(res, HttpStatus.OK);
9097
}
9198
}

iot-backend/src/main/java/iot/microservice/repository/MqttDataRepository.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.springframework.stereotype.Repository;
77

88
import java.time.ZonedDateTime;
9-
import java.util.Date;
109
import java.util.List;
1110

1211
@Repository

iot-backend/src/main/java/iot/microservice/service/Calc.java

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,146 @@ public double standardDeviationCalculator(List<Integer> data, double median) {
5959
return sd;
6060
}
6161

62-
public Duration estimateDuration(String topic, ZonedDateTime start, ZonedDateTime end) {
62+
public String estimateDuration(String topic, ZonedDateTime start, ZonedDateTime end) {
6363
List<SleepData> sessionData = mqttDataRepository.findByTopicAndTimestampBetween(topic, start, end);
6464
if (sessionData.isEmpty()) {
65-
return Duration.ZERO;
65+
return Duration.ZERO.toString();
6666
}
6767
sessionData.sort(Comparator.comparing(SleepData::getTimestamp));
68+
final Duration maxGap = Duration.ofMinutes(30);
6869
Duration totalSleep = Duration.ZERO;
69-
// TODO...
70+
71+
ZonedDateTime segmentStart = null; // start of a contiguous in-bed segment
72+
ZonedDateTime prevTs = null; // previous sample timestamp
73+
74+
for (SleepData s : sessionData) {
75+
final ZonedDateTime ts = s.getTimestamp();
76+
final int inBed = s.getInBed();
77+
78+
// If there is a large gap, close any open segment at the previous timestamp
79+
if (prevTs != null && ts.isAfter(prevTs.plus(maxGap)) && segmentStart != null) {
80+
totalSleep = totalSleep.plus(Duration.between(segmentStart, prevTs));
81+
segmentStart = null;
82+
}
83+
84+
85+
if (inBed == 1) {
86+
// Open a segment if not already open
87+
if (segmentStart == null) {
88+
segmentStart = ts;
89+
}
90+
} else { // inBed == 0
91+
// Close an active segment at the last known timestamp (prevTs) to avoid over-counting
92+
if (segmentStart != null) {
93+
ZonedDateTime endTs = (prevTs != null && !prevTs.isBefore(segmentStart)) ? prevTs : ts;
94+
totalSleep = totalSleep.plus(Duration.between(segmentStart, endTs));
95+
segmentStart = null;
96+
}
97+
}
98+
99+
prevTs = ts;
100+
}
101+
102+
// If the last samples are still in-bed, close at the last timestamp we saw
103+
if (segmentStart != null && prevTs != null) {
104+
totalSleep = totalSleep.plus(Duration.between(segmentStart, prevTs));
105+
}
106+
107+
return totalSleep.isNegative() ? Duration.ZERO.toString() : totalSleep.toString();
108+
}
109+
110+
/*
111+
* return JSON with
112+
* - Event (turnoverNumber,
113+
* largeBodyMove,
114+
* minorBodyMove,
115+
* apneaEvents,
116+
* )
117+
* - Time stamp
118+
*/
119+
public String timeSeries(String topic, ZonedDateTime start, ZonedDateTime end) {
120+
List<SleepData> sessionData = mqttDataRepository.findByTopicAndTimestampBetween(topic, start, end);
121+
if (sessionData == null || sessionData.isEmpty()) {
122+
return "[]";
123+
}
124+
125+
sessionData.sort(Comparator.comparing(SleepData::getTimestamp));
126+
127+
List<java.util.Map<String, Object>> payload = new java.util.ArrayList<>(sessionData.size());
128+
129+
for (SleepData s : sessionData) {
130+
java.util.Map<String, Object> event = new java.util.LinkedHashMap<>();
131+
event.put("turnoverNumber", s.getTurnoverNumber());
132+
event.put("largeBodyMove", s.getLargeBodyMove());
133+
event.put("minorBodyMove", s.getMinorBodyMove());
134+
event.put("apneaEvents", s.getApneaEvents());
135+
136+
java.util.Map<String, Object> point = new java.util.LinkedHashMap<>();
137+
point.put("event", event);
138+
point.put("timestamp", s.getTimestamp());
139+
140+
payload.add(point);
141+
}
142+
143+
// Serialize using Jackson (ISO-8601 for ZonedDateTime)
144+
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
145+
mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
146+
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
147+
148+
try {
149+
return mapper.writeValueAsString(payload);
150+
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
151+
return "[]";
152+
}
153+
}
154+
155+
/*
156+
* return JSON with
157+
* - New sleepState
158+
* - Time stamp
159+
*/
160+
public String sleepStateSeries(String topic, ZonedDateTime start, ZonedDateTime end) {
161+
List<SleepData> sessionData = mqttDataRepository.findByTopicAndTimestampBetween(topic, start, end);
162+
if (sessionData == null || sessionData.isEmpty()) {
163+
return "[]";
164+
}
165+
166+
// Sort chronologically
167+
sessionData.sort(Comparator.comparing(SleepData::getTimestamp));
168+
169+
List<java.util.Map<String, Object>> changes = new java.util.ArrayList<>();
170+
171+
Integer prev = null;
172+
for (SleepData s : sessionData) {
173+
Integer curr = s.getSleepState();
174+
if (curr == null) {
175+
continue; // skip malformed samples
176+
}
177+
if (prev == null) {
178+
prev = curr; // initialize baseline without emitting an event
179+
continue;
180+
}
181+
if (!curr.equals(prev)) {
182+
java.util.Map<String, Object> change = new java.util.LinkedHashMap<>();
183+
change.put("NewState", curr);
184+
change.put("TimeStamp", s.getTimestamp());
185+
changes.add(change);
186+
prev = curr;
187+
}
188+
}
189+
190+
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
191+
mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
192+
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
193+
194+
try {
195+
return mapper.writeValueAsString(changes);
196+
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
197+
return "[]";
198+
}
199+
}
200+
201+
public ZonedDateTime getTimeDelta() {
202+
return ZonedDateTime.now().minusDays(1).withHour(16).withMinute(0).withSecond(0).withNano(0);
70203
}
71204
}

iot-backend/src/main/java/iot/microservice/service/MicroService.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ public String shellyRelaySwitch(String status) {
4545
.block();
4646
}
4747

48+
public String shellyRelayStatus() {
49+
WebClient webClient = WebClient.create("http://"+appProps.getLampIp());
50+
return webClient.get()
51+
.uri("/relay/0")
52+
.retrieve()
53+
.bodyToMono(String.class)
54+
.block();
55+
}
56+
4857
// Function here to retrieve data from Mongo DB
4958
// Format: topic, payload, timestamp
5059
public List<MqttDataEntity> retrieveSleep(String topic, int timeframe) {
@@ -58,18 +67,16 @@ public List<MqttDataEntity> retrieveSleep(String topic, int timeframe) {
5867
* - Sleep Quality Score
5968
*/
6069
public MedianStandardDeviation retrieveSleepQualityScore(String topic) {
61-
ZonedDateTime past = ZonedDateTime.now().minusDays(1).withHour(16).withMinute(0).withSecond(0).withNano(0);
62-
ZonedDateTime currentTime = ZonedDateTime.now();
6370
ToIntFunction<SleepData> extractor = SleepData::getSleepQualityScore;
64-
return calc.anyMedianStandardDeviation(topic, extractor, currentTime, past);
71+
return calc.anyMedianStandardDeviation(topic, extractor, ZonedDateTime.now(), calc.getTimeDelta());
6572
}
6673

6774
/*
6875
* return JSON with
6976
* - Total Duration
7077
*/
71-
public String retrieveSleepTotalDuration() {
72-
return "";
78+
public String retrieveSleepTotalDuration(String topic) {
79+
return calc.estimateDuration(topic, ZonedDateTime.now(), calc.getTimeDelta());
7380
}
7481

7582
/*
@@ -81,8 +88,8 @@ public String retrieveSleepTotalDuration() {
8188
* )
8289
* - Time stamp
8390
*/
84-
public String retrieveSleepTimeSeries() {
85-
return "Under development";
91+
public String retrieveSleepTimeSeries(String topic) {
92+
return calc.timeSeries(topic, ZonedDateTime.now(), calc.getTimeDelta());
8693
}
8794

8895
/*
@@ -91,10 +98,8 @@ public String retrieveSleepTimeSeries() {
9198
* - Standard Deviation
9299
*/
93100
public MedianStandardDeviation retrieveSleepMedianBpm(String topic) {
94-
ZonedDateTime past = ZonedDateTime.now().minusDays(1).withHour(16).withMinute(0).withSecond(0).withNano(0);
95-
ZonedDateTime currentTime = ZonedDateTime.now();
96101
ToIntFunction<SleepData> extractor = SleepData::getAverageHeartbeat;
97-
return calc.anyMedianStandardDeviation(topic, extractor, currentTime, past);
102+
return calc.anyMedianStandardDeviation(topic, extractor, ZonedDateTime.now(), calc.getTimeDelta());
98103
}
99104

100105
/*
@@ -103,18 +108,16 @@ public MedianStandardDeviation retrieveSleepMedianBpm(String topic) {
103108
* - Standard deviation
104109
*/
105110
public MedianStandardDeviation retrieveSleepMedianRPM(String topic) {
106-
ZonedDateTime past = ZonedDateTime.now().minusDays(1).withHour(16).withMinute(0).withSecond(0).withNano(0);
107-
ZonedDateTime currentTime = ZonedDateTime.now();
108111
ToIntFunction<SleepData> extractor = SleepData::getAverageRespiration;
109-
return calc.anyMedianStandardDeviation(topic, extractor, currentTime, past);
112+
return calc.anyMedianStandardDeviation(topic, extractor, ZonedDateTime.now(), calc.getTimeDelta());
110113
}
111114

112115
/*
113116
* return JSON with
114117
* - New sleepState
115118
* - Time stamp
116119
*/
117-
public String retrieveSleepStateSeries() {
118-
return "Under development";
120+
public String retrieveSleepStateSeries(String topic) {
121+
return calc.sleepStateSeries(topic, ZonedDateTime.now(), calc.getTimeDelta());
119122
}
120123
}

0 commit comments

Comments
 (0)