Skip to content

Commit 40c9abf

Browse files
authored
fix datetime issue causing extra notifications (#5)
1 parent 3c6c564 commit 40c9abf

File tree

5 files changed

+148
-24
lines changed

5 files changed

+148
-24
lines changed

lambda/datetime-util.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
export function formatDate(date: Date, timezoneCode?: string): string {
3+
console.log(date, timezoneCode);
4+
return new Intl.DateTimeFormat("default", {
5+
month: "short",
6+
day: "numeric",
7+
hour: "numeric",
8+
minute: "numeric",
9+
hour12: true,
10+
timeZone: timezoneCode,
11+
timeZoneName: "short",
12+
}).format(date);
13+
}
14+
15+
export function determineThresholdDatetime(eventDate: Date): Date {
16+
const thresholdHours = Number(process.env.NOTIFICATION_THRESHOLD_HRS);
17+
const thresholdDatetime = new Date(eventDate.getTime());
18+
thresholdDatetime.setHours(eventDate.getHours() + thresholdHours);
19+
return thresholdDatetime;
20+
}
21+
22+
export function hasThresholdTimePassed(thresholdDatetime: Date): boolean {
23+
return Date.now() > thresholdDatetime.getTime();
24+
}

lambda/index.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { DeviceType } from "homebridge-lg-thinq/dist/lib/constants";
33
import { Device } from "homebridge-lg-thinq/dist/lib/Device";
44
import { URL } from "url";
55
import { ThinQApi } from "./api";
6+
import { determineThresholdDatetime, formatDate, hasThresholdTimePassed } from "./datetime-util";
67

78
export const handler = async (): Promise<void> => {
89
const region = process.env.AWS_REGION;
@@ -20,17 +21,18 @@ export const handler = async (): Promise<void> => {
2021
const mostRecentEvent = events[0];
2122
const eventDate = new Date(Number(mostRecentEvent.sendDate) * 1000);
2223
const eventMessage = JSON.parse(mostRecentEvent.message) as EventMessage;
23-
const formattedEventDate = formatDate(eventDate, dryer?.data.timezoneCode);
24+
const formattedEventDate = formatDate(
25+
eventDate,
26+
dryer?.data.timezoneCode
27+
);
2428

29+
const thresholdDatetime = determineThresholdDatetime(eventDate);
2530
console.log(`Most recent event was at ${formattedEventDate}`);
26-
27-
const thresholdHours = Number(process.env.NOTIFICATION_THRESHOLD_HRS);
28-
const thresholdTime = new Date();
29-
thresholdTime.setHours(new Date().getHours() - thresholdHours);
31+
console.log(`Threshold datetime is ${formatDate(thresholdDatetime, dryer?.data.timezoneCode)}`);
3032

3133
if (
32-
eventDate < thresholdTime &&
33-
shouldSendRepeatNotification(thresholdTime) &&
34+
hasThresholdTimePassed(thresholdDatetime) &&
35+
shouldSendRepeatNotification(thresholdDatetime) &&
3436
isWasherCycleFinished(eventMessage) &&
3537
!(await wasLatestWashTubClean(api, clientId, eventMessage))
3638
) {
@@ -48,18 +50,6 @@ export const handler = async (): Promise<void> => {
4850
}
4951
};
5052

51-
function formatDate(date: Date, timezoneCode?: string): string {
52-
return new Intl.DateTimeFormat("default", {
53-
month: "short",
54-
day: "numeric",
55-
hour: "numeric",
56-
minute: "numeric",
57-
hour12: true,
58-
timeZone: timezoneCode,
59-
timeZoneName: "short",
60-
}).format(date);
61-
}
62-
6353
async function getAppSecrets(
6454
region = "us-east-1"
6555
): Promise<Record<string, string>> {
@@ -165,17 +155,17 @@ function isDryerOff(dryer?: Device): boolean {
165155
);
166156
}
167157

168-
function shouldSendRepeatNotification(thresholdTime: Date): boolean {
158+
export function shouldSendRepeatNotification(thresholdDatetime: Date): boolean {
169159
const notificationFreqHrs = Number(process.env.NOTIFICATION_FREQ_HRS);
170160
const maxNotifications = Number(process.env.MAX_NOTIFICATIONS);
171161

172-
const msSinceThreshold = Date.now() - thresholdTime.getTime();
162+
const msSinceThreshold = Date.now() - thresholdDatetime.getTime();
173163
const hoursSinceThreshold = msSinceThreshold / (60 * 60 * 1000);
174164

175165
console.log({ hoursSinceThreshold, notificationFreqHrs, maxNotifications });
176166
return (
177167
hoursSinceThreshold % notificationFreqHrs < 1 &&
178-
Math.floor(hoursSinceThreshold / notificationFreqHrs) <= maxNotifications
168+
Math.floor(hoursSinceThreshold / notificationFreqHrs) < maxNotifications
179169
);
180170
}
181171

test/datetime-util.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { determineThresholdDatetime } from "../lambda/datetime-util";
2+
3+
describe("determineThresholdDatetime", () => {
4+
test("should return `NOTIFICATION_THRESHOLD_HRS` after event date", async () => {
5+
// given
6+
process.env.NOTIFICATION_THRESHOLD_HRS = "3";
7+
const eventDate = new Date("2021-11-01T12:00:00.000Z");
8+
9+
// when
10+
const result = determineThresholdDatetime(eventDate);
11+
12+
// then
13+
expect(result).toEqual(new Date("2021-11-01T15:00:00.000Z"));
14+
});
15+
});

test/index.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { shouldSendRepeatNotification } from "../lambda/index";
2+
3+
describe("test shouldSendRepeatNotification given `NOTIFICATION_FREQ_HRS`=3 and `MAX_NOTIFICATIONS`=2", () => {
4+
5+
beforeEach(() => {
6+
process.env.NOTIFICATION_FREQ_HRS = "3";
7+
process.env.MAX_NOTIFICATIONS = "2";
8+
});
9+
10+
test("should send repeat notification given washer finished 3 hours ago", async () => {
11+
// given
12+
const thresholdTime = new Date();
13+
thresholdTime.setHours(thresholdTime.getHours() - 3);
14+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
15+
16+
// when
17+
const result = shouldSendRepeatNotification(thresholdTime);
18+
19+
// then
20+
expect(result).toBe(true);
21+
});
22+
23+
test("should send repeat notification given washer finished 6 hours ago", async () => {
24+
// given
25+
const thresholdTime = new Date();
26+
thresholdTime.setHours(thresholdTime.getHours() - 6);
27+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
28+
29+
// when
30+
const result = shouldSendRepeatNotification(thresholdTime);
31+
32+
// then
33+
expect(result).toBe(true);
34+
});
35+
36+
test("should not send repeat notification given washer finished 9 hours ago", async () => {
37+
// given
38+
const thresholdTime = new Date();
39+
thresholdTime.setHours(thresholdTime.getHours() - 9);
40+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
41+
42+
// when
43+
const result = shouldSendRepeatNotification(thresholdTime);
44+
45+
// then
46+
expect(result).toBe(false);
47+
});
48+
49+
test("should not send repeat notification given washer finished 9 hours ago", async () => {
50+
// given
51+
const thresholdTime = new Date();
52+
thresholdTime.setHours(thresholdTime.getHours() - 9);
53+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
54+
55+
// when
56+
const result = shouldSendRepeatNotification(thresholdTime);
57+
58+
// then
59+
expect(result).toBe(false);
60+
});
61+
62+
test("should not send repeat notification given washer finished 4 hours ago", async () => {
63+
// given
64+
const thresholdTime = new Date();
65+
thresholdTime.setHours(thresholdTime.getHours() - 4);
66+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
67+
68+
// when
69+
const result = shouldSendRepeatNotification(thresholdTime);
70+
71+
// then
72+
expect(result).toBe(false);
73+
});
74+
75+
test("should not send repeat notification given washer finished 7 hours ago", async () => {
76+
// given
77+
const thresholdTime = new Date();
78+
thresholdTime.setHours(thresholdTime.getHours() - 7);
79+
thresholdTime.setMinutes(thresholdTime.getMinutes() - 20);
80+
81+
// when
82+
const result = shouldSendRepeatNotification(thresholdTime);
83+
84+
// then
85+
expect(result).toBe(false);
86+
});
87+
});

tsconfig.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@
2121
"strictPropertyInitialization": false,
2222
"typeRoots": [
2323
"./node_modules/@types"
24-
]
24+
],
25+
"skipLibCheck": true
2526
},
27+
"include": [
28+
"bin/**/*.ts",
29+
"lambda/**/*.ts",
30+
"lib/**/*.ts",
31+
"test/**/*.ts"
32+
],
2633
"exclude": [
2734
"node_modules",
28-
"cdk.out"
35+
"cdk.out",
36+
".github"
2937
]
3038
}

0 commit comments

Comments
 (0)