Skip to content

Commit b9acc54

Browse files
authored
feat(ota): add support for scheduled OTA (#2449)
* feat: Add support for scheduled OTA * fix: Use unschedule endpoint + linting
1 parent 7d2e12b commit b9acc54

File tree

4 files changed

+73
-15
lines changed

4 files changed

+73
-15
lines changed

src/actions/OtaApi.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@ import api from '../ws-client';
22

33
export interface OtaApi {
44
checkOTA(deviceName: string): Promise<void>;
5+
scheduleOTA(deviceName: string): Promise<void>;
6+
unscheduleOTA(deviceName: string): Promise<void>;
57
updateOTA(deviceName: string): Promise<void>;
68
}
79
export default {
810
checkOTA: (state, deviceName: string): Promise<void> => {
911
return api.send("bridge/request/device/ota_update/check", { id: deviceName });
1012
},
13+
scheduleOTA: (state, deviceName: string): Promise<void> => {
14+
return api.send("bridge/request/device/ota_update/schedule", { id: deviceName });
15+
},
16+
unscheduleOTA: (state, deviceName: string): Promise<void> => {
17+
return api.send("bridge/request/device/ota_update/unschedule", { id: deviceName });
18+
},
1119
updateOTA: (state, deviceName: string): Promise<void> => {
1220
return api.send("bridge/request/device/ota_update/update", { id: deviceName });
1321
},

src/components/ota-page/index.tsx

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ModelLink, OTALink, VendorLink } from '../vendor-links/vendor-links';
1313
import { useTranslation, WithTranslation, withTranslation } from 'react-i18next';
1414
import { Column } from 'react-table';
1515
import { Table } from '../grid/ReactTableCom';
16+
import cx from 'classnames';
1617

1718
type OtaRowProps = {
1819
device: Device;
@@ -21,7 +22,7 @@ type OtaRowProps = {
2122

2223
const StateCell: FunctionComponent<OtaRowProps & OtaApi> = (props) => {
2324
const { t } = useTranslation('ota');
24-
const { device, state, checkOTA, updateOTA } = props;
25+
const { device, state, checkOTA, scheduleOTA, unscheduleOTA, updateOTA } = props;
2526
const otaState = (state?.update ?? {}) as OTAState;
2627
switch (otaState.state) {
2728
case 'updating':
@@ -40,20 +41,67 @@ const StateCell: FunctionComponent<OtaRowProps & OtaApi> = (props) => {
4041
);
4142
case 'available':
4243
return (
43-
<Button<string>
44-
className="btn btn-danger btn-sm"
45-
onClick={updateOTA}
46-
item={device.friendly_name}
47-
prompt
48-
>
49-
{t('update')}
50-
</Button>
44+
<div className="btn-group btn-group-sm" role="group">
45+
<Button<string>
46+
className="btn btn-danger btn-sm me-1"
47+
onClick={updateOTA}
48+
item={device.friendly_name}
49+
prompt
50+
>
51+
{t('update')}
52+
</Button>
53+
<Button<string>
54+
className="btn btn-sm btn-info"
55+
onClick={scheduleOTA}
56+
item={device.friendly_name}
57+
title={t('schedule')}
58+
prompt
59+
>
60+
<i className={cx('fa', 'fa-clock')} />
61+
</Button>
62+
</div>
63+
);
64+
case 'scheduled':
65+
return (
66+
<div className="btn-group btn-group-sm" role="group">
67+
<Button<string>
68+
className="btn btn-primary btn-sm me-1"
69+
onClick={checkOTA}
70+
item={device.friendly_name}
71+
>
72+
{t('check')}
73+
</Button>
74+
<Button<string>
75+
className={cx('btn', 'btn-sm', 'btn-danger')}
76+
onClick={unscheduleOTA}
77+
item={device.friendly_name}
78+
title={t('scheduled')}
79+
prompt
80+
>
81+
<i className={cx('fa', 'fa-clock')} />
82+
</Button>
83+
</div>
5184
);
5285
default:
5386
return (
54-
<Button<string> className="btn btn-primary btn-sm" onClick={checkOTA} item={device.friendly_name}>
55-
{t('check')}
56-
</Button>
87+
<div className="btn-group btn-group-sm" role="group">
88+
<Button<string>
89+
className="btn btn-primary btn-sm me-1"
90+
onClick={checkOTA}
91+
item={device.friendly_name}
92+
>
93+
{t('check')}
94+
</Button>
95+
<Button<string>
96+
className={cx('btn', 'btn-sm', 'btn-info')}
97+
onClick={scheduleOTA}
98+
item={device.friendly_name}
99+
title={t('schedule')}
100+
prompt
101+
>
102+
<i className={cx('fa', 'fa-clock')} />
103+
</Button>
104+
</div>
57105
);
58106
}
59107
};
@@ -85,8 +133,8 @@ class OtaPage extends Component<PropsFromStore & OtaApi & WithTranslation<'ota'>
85133
otaDevices.forEach(({ device }) => checkOTA(device.friendly_name));
86134
};
87135
render() {
88-
const { checkOTA, updateOTA, t } = this.props;
89-
const otaApi = { checkOTA, updateOTA };
136+
const { checkOTA, scheduleOTA, unscheduleOTA, updateOTA, t } = this.props;
137+
const otaApi = { checkOTA, scheduleOTA, unscheduleOTA, updateOTA };
90138
const otaDevices = this.getAllOtaDevices();
91139
const columns: Column<OtaGridData>[] = [
92140
{

src/i18n/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2581,6 +2581,8 @@
25812581
"check_all": "Check all",
25822582
"empty_ota_message": "You don't have any devices that support OTA",
25832583
"remaining_time": "Remaining time {{- remaining}}",
2584+
"schedule": "Schedule update on next device OTA request",
2585+
"scheduled": "Remove scheduled update on next OTA request",
25842586
"update": "Update device firmware"
25852587
},
25862588
"settings": {

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type FriendlyName = string;
66
export type IEEEEAddress = string;
77

88
export type OTAState = {
9-
state: 'available' | 'updating';
9+
state: 'available' | 'updating' | 'scheduled';
1010
progress: number;
1111
remaining: number;
1212
};

0 commit comments

Comments
 (0)