Skip to content

Commit 3691cd4

Browse files
Merge pull request #228 from OpenDTU-App/30-implement-ntp-settings
2 parents 51747cb + 8efbbad commit 3691cd4

22 files changed

+1298
-79
lines changed

.idea/i18nally.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ios/Podfile.lock

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,27 @@ PODS:
15941594
- React-Core
15951595
- SDWebImage (~> 5.11.1)
15961596
- SDWebImageWebPCoder (~> 0.8.4)
1597+
- RNFlashList (1.7.1):
1598+
- DoubleConversion
1599+
- glog
1600+
- hermes-engine
1601+
- RCT-Folly (= 2024.01.01.00)
1602+
- RCTRequired
1603+
- RCTTypeSafety
1604+
- React-Core
1605+
- React-debug
1606+
- React-Fabric
1607+
- React-featureflags
1608+
- React-graphics
1609+
- React-ImageManager
1610+
- React-NativeModulesApple
1611+
- React-RCTFabric
1612+
- React-rendererdebug
1613+
- React-utils
1614+
- ReactCodegen
1615+
- ReactCommon/turbomodule/bridging
1616+
- ReactCommon/turbomodule/core
1617+
- Yoga
15971618
- RNFS (2.20.0):
15981619
- React-Core
15991620
- RNReanimated (3.16.0):
@@ -1818,6 +1839,7 @@ DEPENDENCIES:
18181839
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
18191840
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
18201841
- RNFastImage (from `../node_modules/react-native-fast-image`)
1842+
- "RNFlashList (from `../node_modules/@shopify/flash-list`)"
18211843
- RNFS (from `../node_modules/react-native-fs`)
18221844
- RNReanimated (from `../node_modules/react-native-reanimated`)
18231845
- RNScreens (from `../node_modules/react-native-screens`)
@@ -1992,6 +2014,8 @@ EXTERNAL SOURCES:
19922014
:path: "../node_modules/@react-native-masked-view/masked-view"
19932015
RNFastImage:
19942016
:path: "../node_modules/react-native-fast-image"
2017+
RNFlashList:
2018+
:path: "../node_modules/@shopify/flash-list"
19952019
RNFS:
19962020
:path: "../node_modules/react-native-fs"
19972021
RNReanimated:
@@ -2087,6 +2111,7 @@ SPEC CHECKSUMS:
20872111
RNCClipboard: 5e503962f0719ace8f7fdfe9c60282b526305c85
20882112
RNCMaskedView: 090213d32d8b3bb83a4dcb7d12c18f0152591906
20892113
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
2114+
RNFlashList: 115dd44377580761bff386a0caebf165424cf16f
20902115
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
20912116
RNReanimated: f6a10979b3701f8029c71dbfe35d0ff4328dce4c
20922117
RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06

licenses.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
"licenseUrl": "https://github.com/reduxjs/redux-toolkit/raw/master/LICENSE",
6060
"parents": "opendtu-react-native"
6161
},
62+
"@shopify/[email protected]": {
63+
"licenses": "MIT",
64+
"repository": "https://github.com/Shopify/flash-list",
65+
"licenseUrl": "https://github.com/Shopify/flash-list/raw/master/LICENSE.md",
66+
"parents": "opendtu-react-native"
67+
},
6268
6369
"licenses": "MIT",
6470
"repository": "https://github.com/mathiasbynens/base64",
@@ -83,6 +89,12 @@
8389
"licenseUrl": "https://github.com/epoberezkin/fast-deep-equal/raw/master/LICENSE",
8490
"parents": "opendtu-react-native"
8591
},
92+
93+
"licenses": "Apache-2.0",
94+
"repository": "https://github.com/krisk/Fuse",
95+
"licenseUrl": "https://github.com/krisk/Fuse/raw/master/LICENSE",
96+
"parents": "opendtu-react-native"
97+
},
8698
8799
"licenses": "MIT",
88100
"repository": "https://github.com/i18next/i18next",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242
"@react-navigation/native": "^6.1.18",
4343
"@react-navigation/native-stack": "^6.11.0",
4444
"@reduxjs/toolkit": "^2.3.0",
45+
"@shopify/flash-list": "^1.7.1",
4546
"base-64": "^1.0.0",
4647
"compare-versions": "^6.1.1",
4748
"expo": "^51.0.38",
4849
"fast-deep-equal": "^3.1.3",
50+
"fuse.js": "^7.0.0",
4951
"i18next": "^23.16.0",
5052
"intl-pluralrules": "^2.0.1",
5153
"ip-regex": "^5.0.0",

src/api/ApiHandler.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
setMqttStatus,
1616
setNetworkSettings,
1717
setNetworkStatus,
18+
setNTPSettings,
1819
setNtpStatus,
1920
setPowerStatus,
2021
setSystemStatus,
@@ -181,6 +182,10 @@ export const ApiProvider: FC<PropsWithChildren> = ({ children }) => {
181182
dispatch(setNetworkSettings({ data, index }));
182183
});
183184

185+
api.registerOnNtpSettingsHandler((data, index) => {
186+
dispatch(setNTPSettings({ data, index }));
187+
});
188+
184189
log.debug('Connecting API Handler');
185190

186191
api.connect();

src/api/opendtuapi.ts

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import type {
99
import type { EventLogData } from '@/types/opendtu/eventlog';
1010
import type { GridProfileData } from '@/types/opendtu/gridprofile';
1111
import type { InverterDeviceData } from '@/types/opendtu/inverterDevice';
12-
import type { NetworkSettings } from '@/types/opendtu/settings';
12+
import type {
13+
NetworkSettings,
14+
NTPSettings,
15+
NTPTime,
16+
TimezoneData,
17+
} from '@/types/opendtu/settings';
1318
import type { InverterItem } from '@/types/opendtu/state';
1419
import { DeviceState } from '@/types/opendtu/state';
1520
import type {
@@ -105,9 +110,14 @@ class OpenDtuApi {
105110
inverterSerial: InverterSerial,
106111
) => void)
107112
| null = null;
113+
114+
// settings
108115
private onNetworkSettingsHandler:
109116
| ((data: NetworkSettings, index: Index) => void)
110117
| null = null;
118+
private onNtpSettingsHandler:
119+
| ((data: NTPSettings, index: Index) => void)
120+
| null = null;
111121

112122
private ws: WebSocket | null = null;
113123
// communication
@@ -329,6 +339,18 @@ class OpenDtuApi {
329339
this.onNetworkSettingsHandler = null;
330340
}
331341

342+
public registerOnNtpSettingsHandler(
343+
handler: (data: NTPSettings, index: Index) => void,
344+
): void {
345+
log.debug('OpenDtuApi.registerOnNtpSettingsHandler()');
346+
this.onNtpSettingsHandler = handler;
347+
}
348+
349+
public unregisterOnNtpSettingsHandler(): void {
350+
log.debug('OpenDtuApi.unregisterOnNtpSettingsHandler()');
351+
this.onNtpSettingsHandler = null;
352+
}
353+
332354
public async getSystemStatusFromUrl(
333355
url: URL,
334356
): Promise<GetSystemStatusReturn> {
@@ -1238,6 +1260,147 @@ class OpenDtuApi {
12381260
return res.status === 200 && parsed.type === 'success';
12391261
}
12401262

1263+
public async getNTPConfig(): Promise<NTPSettings | null> {
1264+
if (!this.baseUrl) {
1265+
log.error('getNTPConfig', 'no base url');
1266+
return null;
1267+
}
1268+
1269+
const res = await this.makeAuthenticatedRequest('/api/ntp/config', 'GET');
1270+
1271+
if (!res) {
1272+
log.error('getNNTPConfig', 'no response');
1273+
return null;
1274+
}
1275+
1276+
if (res.status === 200) {
1277+
const json = await res.json();
1278+
1279+
if (this.onNtpSettingsHandler && this.index !== null) {
1280+
this.onNtpSettingsHandler(json, this.index);
1281+
}
1282+
1283+
log.debug('getNTPConfig', 'success');
1284+
1285+
return json;
1286+
}
1287+
1288+
log.error('getNTPConfig', 'invalid status', res.status);
1289+
1290+
return null;
1291+
}
1292+
1293+
public async setNTPConfig(config: NTPSettings): Promise<boolean | null> {
1294+
if (!this.baseUrl) {
1295+
log.error('setNTPConfig', 'no base url');
1296+
return null;
1297+
}
1298+
1299+
const formData = new FormData();
1300+
formData.append('data', JSON.stringify(config));
1301+
1302+
const res = await this.makeAuthenticatedRequest('/api/ntp/config', 'POST', {
1303+
body: formData,
1304+
});
1305+
1306+
if (!res) {
1307+
log.error('setNTPConfig', 'no response');
1308+
return null;
1309+
}
1310+
1311+
const parsed = await res.json();
1312+
1313+
log.debug('setNTPConfig', 'success', {
1314+
status: res.status,
1315+
parsed,
1316+
});
1317+
1318+
return res.status === 200 && parsed.type === 'success';
1319+
}
1320+
1321+
public async fetchTimezones(): Promise<TimezoneData | null> {
1322+
// fetch /zones.json
1323+
if (!this.baseUrl) {
1324+
log.error('fetchTimezones', 'no base url');
1325+
return null;
1326+
}
1327+
1328+
const res = await fetch(`${this.baseUrl}/zones.json`).catch(() => null);
1329+
1330+
if (!res) {
1331+
log.error('fetchTimezones', 'no response');
1332+
return null;
1333+
}
1334+
1335+
if (res.status === 200) {
1336+
const json = await res.json();
1337+
1338+
log.debug('fetchTimezones', 'success');
1339+
1340+
return json;
1341+
}
1342+
1343+
log.error('fetchTimezones', 'invalid status', res.status);
1344+
1345+
return null;
1346+
}
1347+
1348+
public async getNTPTime(): Promise<NTPTime | null> {
1349+
if (!this.baseUrl) {
1350+
log.error('getNTPTime', 'no base url');
1351+
return null;
1352+
}
1353+
1354+
const res = await this.makeAuthenticatedRequest('/api/ntp/time', 'GET');
1355+
1356+
if (!res) {
1357+
log.error('getNTPTime', 'no response');
1358+
return null;
1359+
}
1360+
1361+
if (res.status === 200) {
1362+
const json = await res.json();
1363+
1364+
log.debug('getNTPTime', 'success');
1365+
1366+
return json;
1367+
}
1368+
1369+
log.error('getNTPTime', 'invalid status', res.status);
1370+
1371+
return null;
1372+
}
1373+
1374+
public async setNTPTime(
1375+
config: Omit<NTPTime, 'ntp_status'>,
1376+
): Promise<boolean | null> {
1377+
if (!this.baseUrl) {
1378+
log.error('setNTPTime', 'no base url');
1379+
return null;
1380+
}
1381+
1382+
const formData = new FormData();
1383+
formData.append('data', JSON.stringify(config));
1384+
1385+
const res = await this.makeAuthenticatedRequest('/api/ntp/time', 'POST', {
1386+
body: formData,
1387+
});
1388+
1389+
if (!res) {
1390+
log.error('setNTPTime', 'no response');
1391+
return null;
1392+
}
1393+
1394+
const parsed = await res.json();
1395+
1396+
log.debug('setNTPTime', 'success', {
1397+
status: res.status,
1398+
parsed,
1399+
});
1400+
1401+
return res.status === 200 && parsed.type === 'success';
1402+
}
1403+
12411404
public async makeAuthenticatedRequest(
12421405
route: string,
12431406
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH',

src/components/ReleaseChangelog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { FC } from 'react';
2-
import { Text as RNText } from 'react-native/Libraries/Text/Text';
32
import type { RenderRules } from 'react-native-markdown-display';
43
import Markdown from 'react-native-markdown-display';
54
import { useTheme } from 'react-native-paper';
65

6+
import { Text as RNText } from 'react-native';
7+
78
export interface ReleaseChangelogProps {
89
releaseBody?: string;
910
}

src/components/modals/ChangeBooleanValueModal.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ const ChangeTextValueModal: FC<ChangeValueModalProps> = ({
3535
const drawerRef = useRef<BottomDrawerMethods>(null);
3636
const { t } = useTranslation();
3737

38-
const [initialHeight, setInitialHeight] = useState<number>(
39-
180 + (extraHeight ?? 0),
40-
);
38+
const [initialHeight, setInitialHeight] = useState<number>(0);
4139

4240
const [value, setValue] = useState<boolean>(defaultValue ?? false);
4341
const [wasModified, setWasModified] = useState<boolean>(false);

0 commit comments

Comments
 (0)