@seamapi/react 3.0.2 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/elements.js +7485 -7348
- package/dist/elements.js.map +1 -1
- package/dist/index.css +3 -0
- package/dist/index.css.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.css.map +1 -1
- package/lib/seam/access-codes/use-delete-access-code.js +9 -0
- package/lib/seam/access-codes/use-delete-access-code.js.map +1 -1
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.d.ts +5 -2
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js +65 -10
- package/lib/seam/components/AccessCodeDetails/AccessCodeDetails.js.map +1 -1
- package/lib/seam/components/AccessCodeTable/AccessCodeMenu.d.ts +1 -0
- package/lib/seam/components/AccessCodeTable/AccessCodeMenu.js +13 -5
- package/lib/seam/components/AccessCodeTable/AccessCodeMenu.js.map +1 -1
- package/lib/seam/components/AccessCodeTable/AccessCodeRow.d.ts +2 -1
- package/lib/seam/components/AccessCodeTable/AccessCodeRow.js +10 -5
- package/lib/seam/components/AccessCodeTable/AccessCodeRow.js.map +1 -1
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js +40 -24
- package/lib/seam/components/AccessCodeTable/AccessCodeTable.js.map +1 -1
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js +5 -5
- package/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.js.map +1 -1
- package/lib/seam/thermostats/temperature-bounds.d.ts +1 -2
- package/lib/seam/thermostats/temperature-bounds.js +0 -16
- package/lib/seam/thermostats/temperature-bounds.js.map +1 -1
- package/lib/seam/thermostats/thermostat-device.d.ts +1 -3
- package/lib/seam/thermostats/thermostat-device.js.map +1 -1
- package/lib/seam/thermostats/use-cool-thermostat.js +0 -2
- package/lib/seam/thermostats/use-cool-thermostat.js.map +1 -1
- package/lib/seam/thermostats/use-heat-cool-thermostat.js +0 -2
- package/lib/seam/thermostats/use-heat-cool-thermostat.js.map +1 -1
- package/lib/seam/thermostats/use-heat-thermostat.js +0 -2
- package/lib/seam/thermostats/use-heat-thermostat.js.map +1 -1
- package/lib/seam/thermostats/use-set-thermostat-off.js +0 -2
- package/lib/seam/thermostats/use-set-thermostat-off.js.map +1 -1
- package/lib/ui/ClimateSettingForm/set-point-bounds.js +3 -3
- package/lib/ui/ClimateSettingForm/set-point-bounds.js.map +1 -1
- package/lib/ui/Menu/Menu.d.ts +2 -1
- package/lib/ui/Menu/Menu.js +2 -1
- package/lib/ui/Menu/Menu.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -3
- package/src/lib/seam/access-codes/use-delete-access-code.ts +15 -0
- package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.element.ts +3 -0
- package/src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx +169 -71
- package/src/lib/seam/components/AccessCodeTable/AccessCodeMenu.tsx +34 -13
- package/src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx +24 -9
- package/src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx +62 -53
- package/src/lib/seam/components/DeviceDetails/ThermostatDeviceDetails.tsx +5 -5
- package/src/lib/seam/thermostats/temperature-bounds.ts +1 -26
- package/src/lib/seam/thermostats/thermostat-device.ts +0 -4
- package/src/lib/seam/thermostats/use-cool-thermostat.ts +0 -2
- package/src/lib/seam/thermostats/use-heat-cool-thermostat.ts +0 -2
- package/src/lib/seam/thermostats/use-heat-thermostat.ts +0 -2
- package/src/lib/seam/thermostats/use-set-thermostat-off.ts +0 -2
- package/src/lib/ui/ClimateSettingForm/set-point-bounds.ts +3 -3
- package/src/lib/ui/Menu/Menu.tsx +3 -0
- package/src/lib/version.ts +1 -1
- package/src/styles/_seam-table.scss +4 -0
|
@@ -11,54 +11,75 @@ import { useToggle } from 'lib/ui/use-toggle.js'
|
|
|
11
11
|
export interface AccessCodeMenuProps {
|
|
12
12
|
accessCode: AccessCode
|
|
13
13
|
onEdit: () => void
|
|
14
|
+
onDeleteSuccess: () => void
|
|
14
15
|
onViewDetails: () => void
|
|
15
16
|
disableEditAccessCode: boolean
|
|
16
17
|
disableDeleteAccessCode: boolean
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function AccessCodeMenu(props: AccessCodeMenuProps): JSX.Element {
|
|
21
|
+
const [deleteConfirmationVisible, toggleDeleteConfirmation] = useToggle()
|
|
22
|
+
|
|
20
23
|
return (
|
|
21
24
|
<MoreActionsMenu
|
|
22
25
|
menuProps={{
|
|
23
26
|
backgroundProps: {
|
|
24
27
|
className: 'seam-table-action-menu',
|
|
25
28
|
},
|
|
29
|
+
onClose: () => {
|
|
30
|
+
if (deleteConfirmationVisible) {
|
|
31
|
+
toggleDeleteConfirmation()
|
|
32
|
+
}
|
|
33
|
+
},
|
|
26
34
|
}}
|
|
27
35
|
>
|
|
28
|
-
<Content
|
|
36
|
+
<Content
|
|
37
|
+
{...props}
|
|
38
|
+
deleteConfirmationVisible={deleteConfirmationVisible}
|
|
39
|
+
toggleDeleteConfirmation={toggleDeleteConfirmation}
|
|
40
|
+
/>
|
|
29
41
|
</MoreActionsMenu>
|
|
30
42
|
)
|
|
31
43
|
}
|
|
32
44
|
|
|
45
|
+
interface ContentProps extends AccessCodeMenuProps {
|
|
46
|
+
deleteConfirmationVisible: boolean
|
|
47
|
+
toggleDeleteConfirmation: () => void
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
function Content({
|
|
34
51
|
accessCode,
|
|
35
52
|
onViewDetails,
|
|
36
53
|
disableEditAccessCode,
|
|
37
54
|
disableDeleteAccessCode,
|
|
38
55
|
onEdit,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
onDeleteSuccess,
|
|
57
|
+
deleteConfirmationVisible,
|
|
58
|
+
toggleDeleteConfirmation,
|
|
59
|
+
}: ContentProps): JSX.Element {
|
|
42
60
|
const deleteAccessCode = useDeleteAccessCode()
|
|
61
|
+
const isAccessCodeBeingRemoved = accessCode.status === 'removing'
|
|
43
62
|
|
|
44
63
|
if (deleteConfirmationVisible) {
|
|
45
64
|
return (
|
|
46
65
|
<div className='seam-delete-confirmation'>
|
|
47
66
|
<span>{t.deleteCodeConfirmation}</span>
|
|
48
67
|
<div className='seam-actions'>
|
|
49
|
-
<Button
|
|
50
|
-
onClick={toggleDeleteConfirmation}
|
|
51
|
-
disabled={deleteAccessCode.isPending}
|
|
52
|
-
>
|
|
68
|
+
<Button disabled={deleteAccessCode.isPending}>
|
|
53
69
|
{t.cancelDelete}
|
|
54
70
|
</Button>
|
|
55
71
|
<Button
|
|
56
72
|
variant='solid'
|
|
57
73
|
disabled={deleteAccessCode.isPending}
|
|
58
74
|
onClick={() => {
|
|
59
|
-
deleteAccessCode.mutate(
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
deleteAccessCode.mutate(
|
|
76
|
+
{
|
|
77
|
+
access_code_id: accessCode.access_code_id,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
onSuccess: onDeleteSuccess,
|
|
81
|
+
}
|
|
82
|
+
)
|
|
62
83
|
}}
|
|
63
84
|
>
|
|
64
85
|
{t.confirmDelete}
|
|
@@ -84,10 +105,10 @@ function Content({
|
|
|
84
105
|
</MenuItem>
|
|
85
106
|
<div className='seam-divider' />
|
|
86
107
|
<MenuItem onClick={onViewDetails}>{t.viewCodeDetails}</MenuItem>
|
|
87
|
-
{!disableEditAccessCode && (
|
|
108
|
+
{!disableEditAccessCode && !isAccessCodeBeingRemoved && (
|
|
88
109
|
<MenuItem onClick={onEdit}>{t.editCode}</MenuItem>
|
|
89
110
|
)}
|
|
90
|
-
{!disableDeleteAccessCode && (
|
|
111
|
+
{!disableDeleteAccessCode && !isAccessCodeBeingRemoved && (
|
|
91
112
|
<>
|
|
92
113
|
<div className='seam-divider' />
|
|
93
114
|
<MenuItem
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AccessCode } from '@seamapi/types/connect'
|
|
2
|
+
import classNames from 'classnames'
|
|
2
3
|
|
|
3
4
|
import { ExclamationCircleOutlineIcon } from 'lib/icons/ExclamationCircleOutline.js'
|
|
4
5
|
import { TriangleWarningOutlineIcon } from 'lib/icons/TriangleWarningOutline.js'
|
|
@@ -13,6 +14,7 @@ export interface AccessCodeRowProps {
|
|
|
13
14
|
accessCode: AccessCode
|
|
14
15
|
onClick: () => void
|
|
15
16
|
onEdit: () => void
|
|
17
|
+
onDeleteSuccess: () => void
|
|
16
18
|
disableEditAccessCode: boolean
|
|
17
19
|
disableDeleteAccessCode: boolean
|
|
18
20
|
}
|
|
@@ -21,25 +23,37 @@ export function AccessCodeRow({
|
|
|
21
23
|
onClick,
|
|
22
24
|
accessCode,
|
|
23
25
|
onEdit,
|
|
26
|
+
onDeleteSuccess,
|
|
24
27
|
disableEditAccessCode,
|
|
25
28
|
disableDeleteAccessCode,
|
|
26
29
|
}: AccessCodeRowProps): JSX.Element {
|
|
30
|
+
const isAccessCodeBeingRemoved = accessCode.status === 'removing'
|
|
31
|
+
|
|
27
32
|
const errorCount = accessCode.errors.length
|
|
28
33
|
const warningCount = accessCode.warnings.length
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const warningIconTitle =
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const errorIconTitle =
|
|
35
|
+
errorCount === 0 || errorCount > 1
|
|
36
|
+
? `${errorCount} ${t.codeIssues}`
|
|
37
|
+
: `${errorCount} ${t.codeIssue}`
|
|
38
|
+
const warningIconTitle =
|
|
39
|
+
warningCount === 0 || warningCount > 1
|
|
40
|
+
? `${warningCount} ${t.codeIssues}`
|
|
41
|
+
: `${warningCount} ${t.codeIssue}`
|
|
36
42
|
|
|
37
43
|
return (
|
|
38
44
|
<TableRow onClick={onClick}>
|
|
39
|
-
<TableCell
|
|
45
|
+
<TableCell
|
|
46
|
+
className={classNames('seam-icon-cell', {
|
|
47
|
+
'seam-grayed-out': isAccessCodeBeingRemoved,
|
|
48
|
+
})}
|
|
49
|
+
>
|
|
40
50
|
<AccessCodeMainIcon accessCode={accessCode} />
|
|
41
51
|
</TableCell>
|
|
42
|
-
<TableCell
|
|
52
|
+
<TableCell
|
|
53
|
+
className={classNames('seam-name-cell', {
|
|
54
|
+
'seam-grayed-out': isAccessCodeBeingRemoved,
|
|
55
|
+
})}
|
|
56
|
+
>
|
|
43
57
|
<Title className='seam-truncated-text'>{accessCode.name}</Title>
|
|
44
58
|
<CodeDetails accessCode={accessCode} />
|
|
45
59
|
</TableCell>
|
|
@@ -63,6 +77,7 @@ export function AccessCodeRow({
|
|
|
63
77
|
<AccessCodeMenu
|
|
64
78
|
accessCode={accessCode}
|
|
65
79
|
onEdit={onEdit}
|
|
80
|
+
onDeleteSuccess={onDeleteSuccess}
|
|
66
81
|
onViewDetails={onClick}
|
|
67
82
|
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
68
83
|
disableEditAccessCode={disableEditAccessCode}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AccessCode } from '@seamapi/types/connect'
|
|
2
2
|
import classNames from 'classnames'
|
|
3
|
-
import { useCallback, useMemo, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
import { compareByCreatedAtDesc } from 'lib/dates.js'
|
|
6
6
|
import { AddIcon } from 'lib/icons/Add.js'
|
|
@@ -48,20 +48,6 @@ export interface AccessCodeTableProps extends CommonProps {
|
|
|
48
48
|
heading?: string | null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const defaultAccessCodeFilter = (
|
|
52
|
-
accessCode: AccessCode,
|
|
53
|
-
searchInputValue: string
|
|
54
|
-
): boolean => {
|
|
55
|
-
const value = searchInputValue.trim().toLowerCase()
|
|
56
|
-
if (value === '') return true
|
|
57
|
-
const name = accessCode.name ?? ''
|
|
58
|
-
const code = accessCode.code ?? ''
|
|
59
|
-
return (
|
|
60
|
-
name.trim().toLowerCase().includes(value) ||
|
|
61
|
-
code.trim().toLowerCase().includes(value)
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
51
|
export function AccessCodeTable({
|
|
66
52
|
deviceId,
|
|
67
53
|
disableSearch = false,
|
|
@@ -127,11 +113,21 @@ export function AccessCodeTable({
|
|
|
127
113
|
)
|
|
128
114
|
|
|
129
115
|
const [accessCodeResult, setAccessCodeResult] = useState<
|
|
130
|
-
'created' | 'updated' | null
|
|
116
|
+
'created' | 'updated' | 'deleted' | null
|
|
131
117
|
>(null)
|
|
118
|
+
const [snackbarMessage, setSnackbarMessage] = useState<string>('')
|
|
132
119
|
|
|
133
|
-
const
|
|
134
|
-
|
|
120
|
+
const handleAccessCodeDeleteSuccess = useCallback((): void => {
|
|
121
|
+
setAccessCodeResult('deleted')
|
|
122
|
+
}, [setAccessCodeResult])
|
|
123
|
+
|
|
124
|
+
// Circumvent Snackbar bug that causes it to switch to default message
|
|
125
|
+
// while the dismiss animation is playing
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (accessCodeResult !== null) {
|
|
128
|
+
setSnackbarMessage(accessCodeResultToMessage(accessCodeResult))
|
|
129
|
+
}
|
|
130
|
+
}, [accessCodeResult])
|
|
135
131
|
|
|
136
132
|
if (selectedEditAccessCodeId != null) {
|
|
137
133
|
return (
|
|
@@ -159,38 +155,22 @@ export function AccessCodeTable({
|
|
|
159
155
|
|
|
160
156
|
if (selectedViewAccessCodeId != null) {
|
|
161
157
|
return (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
warningFilter={warningFilter}
|
|
179
|
-
disableLockUnlock={disableLockUnlock}
|
|
180
|
-
disableCreateAccessCode={disableCreateAccessCode}
|
|
181
|
-
disableEditAccessCode={disableEditAccessCode}
|
|
182
|
-
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
183
|
-
disableResourceIds={disableResourceIds}
|
|
184
|
-
disableConnectedAccountInformation={
|
|
185
|
-
disableConnectedAccountInformation
|
|
186
|
-
}
|
|
187
|
-
disableClimateSettingSchedules={disableClimateSettingSchedules}
|
|
188
|
-
onBack={() => {
|
|
189
|
-
setSelectedViewAccessCodeId(null)
|
|
190
|
-
}}
|
|
191
|
-
className={className}
|
|
192
|
-
/>
|
|
193
|
-
</>
|
|
158
|
+
<NestedAccessCodeDetails
|
|
159
|
+
accessCodeId={selectedViewAccessCodeId}
|
|
160
|
+
errorFilter={errorFilter}
|
|
161
|
+
warningFilter={warningFilter}
|
|
162
|
+
disableLockUnlock={disableLockUnlock}
|
|
163
|
+
disableCreateAccessCode={disableCreateAccessCode}
|
|
164
|
+
disableEditAccessCode={disableEditAccessCode}
|
|
165
|
+
disableDeleteAccessCode={disableDeleteAccessCode}
|
|
166
|
+
disableResourceIds={disableResourceIds}
|
|
167
|
+
disableConnectedAccountInformation={disableConnectedAccountInformation}
|
|
168
|
+
disableClimateSettingSchedules={disableClimateSettingSchedules}
|
|
169
|
+
onBack={() => {
|
|
170
|
+
setSelectedViewAccessCodeId(null)
|
|
171
|
+
}}
|
|
172
|
+
className={className}
|
|
173
|
+
/>
|
|
194
174
|
)
|
|
195
175
|
}
|
|
196
176
|
|
|
@@ -220,7 +200,7 @@ export function AccessCodeTable({
|
|
|
220
200
|
<>
|
|
221
201
|
<Snackbar
|
|
222
202
|
variant='success'
|
|
223
|
-
message={
|
|
203
|
+
message={snackbarMessage}
|
|
224
204
|
visible={accessCodeResult != null}
|
|
225
205
|
autoDismiss
|
|
226
206
|
onClose={() => {
|
|
@@ -267,6 +247,7 @@ export function AccessCodeTable({
|
|
|
267
247
|
accessCodes={filteredAccessCodes}
|
|
268
248
|
onAccessCodeClick={handleAccessCodeClick}
|
|
269
249
|
onAccessCodeEdit={handleAccessCodeEdit}
|
|
250
|
+
onAccessCodeDeleteSuccess={handleAccessCodeDeleteSuccess}
|
|
270
251
|
errorFilter={errorFilter}
|
|
271
252
|
warningFilter={warningFilter}
|
|
272
253
|
disableEditAccessCode={disableEditAccessCode}
|
|
@@ -295,6 +276,7 @@ function Content(props: {
|
|
|
295
276
|
accessCodes: AccessCode[]
|
|
296
277
|
onAccessCodeClick: (accessCodeId: string) => void
|
|
297
278
|
onAccessCodeEdit: (accessCodeId: string) => void
|
|
279
|
+
onAccessCodeDeleteSuccess: (accessCodeId: string) => void
|
|
298
280
|
errorFilter: (error: AccessCode['errors'][number]) => boolean
|
|
299
281
|
warningFilter: (warning: AccessCode['warnings'][number]) => boolean
|
|
300
282
|
disableEditAccessCode: boolean
|
|
@@ -304,6 +286,7 @@ function Content(props: {
|
|
|
304
286
|
accessCodes,
|
|
305
287
|
onAccessCodeClick,
|
|
306
288
|
onAccessCodeEdit,
|
|
289
|
+
onAccessCodeDeleteSuccess,
|
|
307
290
|
errorFilter,
|
|
308
291
|
warningFilter,
|
|
309
292
|
disableEditAccessCode,
|
|
@@ -350,18 +333,44 @@ function Content(props: {
|
|
|
350
333
|
onEdit={() => {
|
|
351
334
|
onAccessCodeEdit(accessCode.access_code_id)
|
|
352
335
|
}}
|
|
336
|
+
onDeleteSuccess={() => {
|
|
337
|
+
onAccessCodeDeleteSuccess(accessCode.access_code_id)
|
|
338
|
+
}}
|
|
353
339
|
/>
|
|
354
340
|
))}
|
|
355
341
|
</>
|
|
356
342
|
)
|
|
357
343
|
}
|
|
358
344
|
|
|
345
|
+
const defaultAccessCodeFilter = (
|
|
346
|
+
accessCode: AccessCode,
|
|
347
|
+
searchInputValue: string
|
|
348
|
+
): boolean => {
|
|
349
|
+
const value = searchInputValue.trim().toLowerCase()
|
|
350
|
+
if (value === '') return true
|
|
351
|
+
const name = accessCode.name ?? ''
|
|
352
|
+
const code = accessCode.code ?? ''
|
|
353
|
+
return (
|
|
354
|
+
name.trim().toLowerCase().includes(value) ||
|
|
355
|
+
code.trim().toLowerCase().includes(value)
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const accessCodeResultToMessage = (
|
|
360
|
+
result: 'created' | 'updated' | 'deleted'
|
|
361
|
+
): string => {
|
|
362
|
+
if (result === 'created') return t.accessCodeCreated
|
|
363
|
+
if (result === 'deleted') return t.accessCodeDeleted
|
|
364
|
+
return t.accessCodeUpdated
|
|
365
|
+
}
|
|
366
|
+
|
|
359
367
|
const t = {
|
|
360
368
|
accessCodes: 'Access Codes',
|
|
361
369
|
noAccessCodesMessage: 'Sorry, no access codes were found',
|
|
362
370
|
loading: 'Loading access codes',
|
|
363
|
-
|
|
364
|
-
|
|
371
|
+
accessCodeUpdated: 'Access code updated',
|
|
372
|
+
accessCodeCreated: 'Access code created',
|
|
373
|
+
accessCodeDeleted: 'Access code is being removed',
|
|
365
374
|
tryAgain: 'Try again',
|
|
366
375
|
fallbackErrorMessage: 'Access codes could not be loaded',
|
|
367
376
|
}
|
|
@@ -8,7 +8,6 @@ import { NestedClimateSettingScheduleTable } from 'lib/seam/components/ClimateSe
|
|
|
8
8
|
import type { NestedSpecificDeviceDetailsProps } from 'lib/seam/components/DeviceDetails/DeviceDetails.js'
|
|
9
9
|
import { DeviceInfo } from 'lib/seam/components/DeviceDetails/DeviceInfo.js'
|
|
10
10
|
import { useClimateSettingSchedules } from 'lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js'
|
|
11
|
-
import { getSupportedThermostatModes } from 'lib/seam/thermostats/temperature-bounds.js'
|
|
12
11
|
import type {
|
|
13
12
|
HvacModeSetting,
|
|
14
13
|
ThermostatDevice,
|
|
@@ -252,12 +251,13 @@ function ClimateSettingRow({
|
|
|
252
251
|
const deviceCoolValue =
|
|
253
252
|
device.properties.current_climate_setting.cooling_set_point_fahrenheit
|
|
254
253
|
|
|
255
|
-
const
|
|
254
|
+
const availableHvacModes = device.properties.available_hvac_mode_settings
|
|
256
255
|
|
|
257
256
|
const [showSuccess, setShowSuccess] = useState(false)
|
|
258
257
|
const [mode, setMode] = useState<HvacModeSetting>(
|
|
259
|
-
(
|
|
260
|
-
'
|
|
258
|
+
(availableHvacModes.includes('heat_cool')
|
|
259
|
+
? 'heat_cool'
|
|
260
|
+
: availableHvacModes[0]) ?? 'off'
|
|
261
261
|
)
|
|
262
262
|
|
|
263
263
|
const [heatValue, setHeatValue] = useState(
|
|
@@ -413,7 +413,7 @@ function ClimateSettingRow({
|
|
|
413
413
|
<ClimateModeMenu
|
|
414
414
|
mode={mode}
|
|
415
415
|
onChange={setMode}
|
|
416
|
-
supportedModes={
|
|
416
|
+
supportedModes={availableHvacModes}
|
|
417
417
|
/>
|
|
418
418
|
</div>
|
|
419
419
|
</AccordionRow>
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
HvacModeSetting,
|
|
3
|
-
ThermostatDevice,
|
|
4
|
-
} from 'lib/seam/thermostats/thermostat-device.js'
|
|
1
|
+
import type { HvacModeSetting } from 'lib/seam/thermostats/thermostat-device.js'
|
|
5
2
|
|
|
6
3
|
export interface ControlBounds {
|
|
7
4
|
mode: Exclude<HvacModeSetting, 'off'>
|
|
@@ -54,25 +51,3 @@ export const getTemperatureBounds = (
|
|
|
54
51
|
heat: getHeatBounds(controlBounds),
|
|
55
52
|
cool: getCoolBounds(controlBounds),
|
|
56
53
|
})
|
|
57
|
-
|
|
58
|
-
export const getSupportedThermostatModes = (
|
|
59
|
-
device: ThermostatDevice
|
|
60
|
-
): HvacModeSetting[] => {
|
|
61
|
-
const allModes: HvacModeSetting[] = ['heat', 'cool', 'heat_cool', 'off']
|
|
62
|
-
|
|
63
|
-
return allModes.filter((mode) => {
|
|
64
|
-
switch (mode) {
|
|
65
|
-
case 'cool':
|
|
66
|
-
return device.properties.is_cooling_available ?? false
|
|
67
|
-
case 'heat':
|
|
68
|
-
return device.properties.is_heating_available ?? false
|
|
69
|
-
case 'heat_cool':
|
|
70
|
-
return (
|
|
71
|
-
(device.properties.is_heating_available ?? false) &&
|
|
72
|
-
(device.properties.is_cooling_available ?? false)
|
|
73
|
-
)
|
|
74
|
-
default:
|
|
75
|
-
return true
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
}
|
|
@@ -10,8 +10,6 @@ export type ThermostatDevice = Omit<Device, 'properties'> & {
|
|
|
10
10
|
| 'is_heating'
|
|
11
11
|
| 'is_cooling'
|
|
12
12
|
| 'is_fan_running'
|
|
13
|
-
| 'is_cooling_available'
|
|
14
|
-
| 'is_heating_available'
|
|
15
13
|
| 'available_hvac_mode_settings'
|
|
16
14
|
| 'fan_mode_setting'
|
|
17
15
|
| 'current_climate_setting'
|
|
@@ -28,8 +26,6 @@ export type HvacModeSetting =
|
|
|
28
26
|
|
|
29
27
|
// UPSTREAM: ClimateSetting missing in @seamapi/types.
|
|
30
28
|
export interface ClimateSetting {
|
|
31
|
-
automatic_heating_enabled?: boolean
|
|
32
|
-
automatic_cooling_enabled?: boolean
|
|
33
29
|
hvac_mode_setting?: HvacModeSetting
|
|
34
30
|
cooling_set_point_celsius?: number
|
|
35
31
|
heating_set_point_celsius?: number
|
|
@@ -100,8 +100,6 @@ const getUpdatedDevice = (
|
|
|
100
100
|
current_climate_setting: {
|
|
101
101
|
...properties.current_climate_setting,
|
|
102
102
|
hvac_mode_setting: 'cool',
|
|
103
|
-
automatic_heating_enabled: false,
|
|
104
|
-
automatic_cooling_enabled: true,
|
|
105
103
|
heating_set_point_celsius: undefined,
|
|
106
104
|
heating_set_point_fahrenheit: undefined,
|
|
107
105
|
cooling_set_point_celsius: getCoolingSetPointCelsius(
|
|
@@ -106,8 +106,6 @@ const getUpdatedDevice = (
|
|
|
106
106
|
current_climate_setting: {
|
|
107
107
|
...properties.current_climate_setting,
|
|
108
108
|
hvac_mode_setting: 'heat_cool',
|
|
109
|
-
automatic_heating_enabled: true,
|
|
110
|
-
automatic_cooling_enabled: true,
|
|
111
109
|
heating_set_point_celsius: getHeatingSetPointCelsius(
|
|
112
110
|
variables,
|
|
113
111
|
device
|
|
@@ -100,8 +100,6 @@ const getUpdatedDevice = (
|
|
|
100
100
|
current_climate_setting: {
|
|
101
101
|
...properties.current_climate_setting,
|
|
102
102
|
hvac_mode_setting: 'heat',
|
|
103
|
-
automatic_heating_enabled: true,
|
|
104
|
-
automatic_cooling_enabled: false,
|
|
105
103
|
cooling_set_point_celsius: undefined,
|
|
106
104
|
cooling_set_point_fahrenheit: undefined,
|
|
107
105
|
heating_set_point_celsius: getHeatingSetPointCelsius(
|
|
@@ -92,8 +92,6 @@ const getUpdatedDevice = (device: Device): Device => {
|
|
|
92
92
|
is_heating: false,
|
|
93
93
|
current_climate_setting: {
|
|
94
94
|
...properties.current_climate_setting,
|
|
95
|
-
automatic_cooling_enabled: false,
|
|
96
|
-
automatic_heating_enabled: false,
|
|
97
95
|
hvac_mode_setting: 'off',
|
|
98
96
|
heating_set_point_celsius: undefined,
|
|
99
97
|
heating_set_point_fahrenheit: undefined,
|
|
@@ -13,17 +13,17 @@ export const getSetPointBounds = (device: ThermostatDevice): SetPointBounds => {
|
|
|
13
13
|
|
|
14
14
|
const setPointBounds: SetPointBounds = {}
|
|
15
15
|
|
|
16
|
-
if (properties.
|
|
16
|
+
if (properties.available_hvac_mode_settings.includes('cool')) {
|
|
17
17
|
setPointBounds.minCool = properties.min_cooling_set_point_fahrenheit
|
|
18
18
|
setPointBounds.maxCool = properties.max_cooling_set_point_fahrenheit
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
if (properties.
|
|
21
|
+
if (properties.available_hvac_mode_settings.includes('heat')) {
|
|
22
22
|
setPointBounds.minHeat = properties.min_heating_set_point_fahrenheit
|
|
23
23
|
setPointBounds.maxHeat = properties.max_heating_set_point_fahrenheit
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
if (properties.
|
|
26
|
+
if (properties.available_hvac_mode_settings.includes('heat_cool')) {
|
|
27
27
|
setPointBounds.delta = properties.min_heating_cooling_delta_fahrenheit
|
|
28
28
|
}
|
|
29
29
|
|
package/src/lib/ui/Menu/Menu.tsx
CHANGED
|
@@ -23,6 +23,7 @@ export interface MenuProps extends PropsWithChildren {
|
|
|
23
23
|
backgroundProps?: Partial<{
|
|
24
24
|
className?: string
|
|
25
25
|
}>
|
|
26
|
+
onClose?: () => void
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
interface MenuContext {
|
|
@@ -40,6 +41,7 @@ export function Menu({
|
|
|
40
41
|
children,
|
|
41
42
|
renderButton,
|
|
42
43
|
backgroundProps,
|
|
44
|
+
onClose,
|
|
43
45
|
}: MenuProps): JSX.Element | null {
|
|
44
46
|
const { Provider } = menuContext
|
|
45
47
|
const [documentEl, setDocumentEl] = useState<null | HTMLElement>(null)
|
|
@@ -59,6 +61,7 @@ export function Menu({
|
|
|
59
61
|
}, [setDocumentEl])
|
|
60
62
|
|
|
61
63
|
const handleClose = (): void => {
|
|
64
|
+
onClose?.()
|
|
62
65
|
setAnchorEl(null)
|
|
63
66
|
}
|
|
64
67
|
|
package/src/lib/version.ts
CHANGED