@seamapi/react 2.0.2 → 2.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 +3 -3
- package/dist/elements.js +2917 -2829
- package/dist/elements.js.map +1 -1
- package/dist/index.css +60 -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-access-codes.js +11 -1
- package/lib/seam/access-codes/use-access-codes.js.map +1 -1
- package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.d.ts +6 -0
- package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js +21 -0
- package/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js.map +1 -0
- package/lib/seam/components/SupportedDeviceTable/use-device-models.js +12 -1
- package/lib/seam/components/SupportedDeviceTable/use-device-models.js.map +1 -1
- package/lib/seam/components/SupportedDeviceTable/use-manufacturers.js +12 -1
- package/lib/seam/components/SupportedDeviceTable/use-manufacturers.js.map +1 -1
- package/lib/seam/components/index.d.ts +1 -0
- package/lib/seam/components/index.js +1 -0
- package/lib/seam/components/index.js.map +1 -1
- package/lib/seam/devices/use-devices.js +0 -1
- package/lib/seam/devices/use-devices.js.map +1 -1
- package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js +0 -1
- package/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.js.map +1 -1
- package/lib/seam/thermostats/use-update-fan-mode.d.ts +6 -0
- package/lib/seam/thermostats/use-update-fan-mode.js +42 -0
- package/lib/seam/thermostats/use-update-fan-mode.js.map +1 -0
- package/lib/seam/thermostats/use-update-thermostat.d.ts +6 -0
- package/lib/seam/thermostats/use-update-thermostat.js +50 -0
- package/lib/seam/thermostats/use-update-thermostat.js.map +1 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.d.ts +6 -1
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js +39 -6
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js.map +1 -1
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.d.ts +13 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js +37 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js.map +1 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.d.ts +10 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js +24 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js.map +1 -0
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.d.ts +1 -2
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js +1 -2
- package/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js.map +1 -1
- package/lib/ui/thermostat/ClimateSettingControlGroup.d.ts +9 -0
- package/lib/ui/thermostat/ClimateSettingControlGroup.js +8 -0
- package/lib/ui/thermostat/ClimateSettingControlGroup.js.map +1 -0
- package/lib/ui/thermostat/TemperatureControlGroup.d.ts +6 -7
- package/lib/ui/thermostat/TemperatureControlGroup.js +1 -1
- package/lib/ui/thermostat/TemperatureControlGroup.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/lib/seam/access-codes/use-access-codes.ts +15 -1
- package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.element.ts +9 -0
- package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.tsx +65 -0
- package/src/lib/seam/components/SupportedDeviceTable/use-device-models.ts +15 -1
- package/src/lib/seam/components/SupportedDeviceTable/use-manufacturers.ts +15 -1
- package/src/lib/seam/components/elements.ts +1 -0
- package/src/lib/seam/components/index.ts +1 -0
- package/src/lib/seam/devices/use-devices.ts +0 -1
- package/src/lib/seam/thermostats/climate-setting-schedules/use-climate-setting-schedules.ts +0 -1
- package/src/lib/seam/thermostats/use-update-fan-mode.ts +81 -0
- package/src/lib/seam/thermostats/use-update-thermostat.ts +92 -0
- package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.tsx +77 -15
- package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.tsx +103 -0
- package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.tsx +79 -0
- package/src/lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.tsx +0 -3
- package/src/lib/ui/thermostat/ClimateSettingControlGroup.tsx +32 -0
- package/src/lib/ui/thermostat/TemperatureControlGroup.tsx +11 -11
- package/src/lib/version.ts +1 -1
- package/src/styles/_climate-setting-schedule-form.scss +54 -0
- package/src/styles/_main.scss +2 -0
- package/src/styles/_supported-device-table-manufacturer-keys.scss +19 -0
- package/src/styles/_thermostat.scss +15 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useQuery } from '@tanstack/react-query'
|
|
1
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
2
2
|
import type {
|
|
3
3
|
AccessCode,
|
|
4
4
|
AccessCodesListRequest,
|
|
@@ -19,6 +19,8 @@ export function useAccessCodes(
|
|
|
19
19
|
typeof params === 'string' ? { device_id: params } : params
|
|
20
20
|
|
|
21
21
|
const { client } = useSeamClient()
|
|
22
|
+
const queryClient = useQueryClient()
|
|
23
|
+
|
|
22
24
|
const { data, ...rest } = useQuery<
|
|
23
25
|
AccessCodesListResponse['access_codes'],
|
|
24
26
|
SeamError
|
|
@@ -29,6 +31,18 @@ export function useAccessCodes(
|
|
|
29
31
|
if (client == null) return []
|
|
30
32
|
return await client.accessCodes.list(normalizedParams)
|
|
31
33
|
},
|
|
34
|
+
onSuccess: (accessCodes) => {
|
|
35
|
+
for (const accessCode of accessCodes) {
|
|
36
|
+
queryClient.setQueryData(
|
|
37
|
+
[
|
|
38
|
+
'access_codes',
|
|
39
|
+
'get',
|
|
40
|
+
{ access_code_id: accessCode.access_code_id },
|
|
41
|
+
],
|
|
42
|
+
accessCode
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
},
|
|
32
46
|
})
|
|
33
47
|
|
|
34
48
|
return { ...rest, accessCodes: data }
|
package/src/lib/seam/components/SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.element.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ElementProps } from 'lib/element.js'
|
|
2
|
+
|
|
3
|
+
import type { SupportedDeviceTableManufacturerKeysProps } from './SupportedDeviceTableManufacturerKeys.js'
|
|
4
|
+
|
|
5
|
+
export const name = 'seam-supported-device-table-manufacturer-keys'
|
|
6
|
+
|
|
7
|
+
export const props: ElementProps<SupportedDeviceTableManufacturerKeysProps> = {}
|
|
8
|
+
|
|
9
|
+
export { SupportedDeviceTableManufacturerKeys as Component } from './SupportedDeviceTableManufacturerKeys.js'
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Manufacturer } from '@seamapi/types/devicedb'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
|
|
4
|
+
import { useComponentTelemetry } from 'lib/telemetry/index.js'
|
|
5
|
+
|
|
6
|
+
import { CopyIcon } from 'lib/icons/Copy.js'
|
|
7
|
+
import {
|
|
8
|
+
type CommonProps,
|
|
9
|
+
withRequiredCommonProps,
|
|
10
|
+
} from 'lib/seam/components/common-props.js'
|
|
11
|
+
import { copyToClipboard } from 'lib/ui/clipboard.js'
|
|
12
|
+
import { MenuItem } from 'lib/ui/Menu/MenuItem.js'
|
|
13
|
+
|
|
14
|
+
import { useManufacturers } from './use-manufacturers.js'
|
|
15
|
+
|
|
16
|
+
export interface SupportedDeviceTableManufacturerKeysProps
|
|
17
|
+
extends CommonProps {}
|
|
18
|
+
|
|
19
|
+
export const NestedSupportedDeviceTableManufacturerKeys =
|
|
20
|
+
withRequiredCommonProps(SupportedDeviceTableManufacturerKeys)
|
|
21
|
+
|
|
22
|
+
export function SupportedDeviceTableManufacturerKeys({
|
|
23
|
+
className,
|
|
24
|
+
}: SupportedDeviceTableManufacturerKeysProps = {}): JSX.Element {
|
|
25
|
+
useComponentTelemetry('SupportedDeviceTableManufacturerKeys')
|
|
26
|
+
|
|
27
|
+
const { manufacturers } = useManufacturers()
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={classNames(
|
|
32
|
+
'supported-device-table-manufacturer-keys',
|
|
33
|
+
className
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{manufacturers?.map((manufacturer) => (
|
|
37
|
+
<ManufacturerKey
|
|
38
|
+
key={manufacturer.manufacturer_id}
|
|
39
|
+
manufacturer={manufacturer}
|
|
40
|
+
/>
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ManufacturerKey({
|
|
47
|
+
manufacturer,
|
|
48
|
+
}: {
|
|
49
|
+
manufacturer: Manufacturer
|
|
50
|
+
}): JSX.Element {
|
|
51
|
+
const key = manufacturer.display_name
|
|
52
|
+
return (
|
|
53
|
+
<div className='seam-manufacturer-key'>
|
|
54
|
+
<div className='seam-manufacturer-key-value'>{key}</div>
|
|
55
|
+
<MenuItem
|
|
56
|
+
className='seam-copy-button'
|
|
57
|
+
onClick={() => {
|
|
58
|
+
void copyToClipboard(key)
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<CopyIcon />
|
|
62
|
+
</MenuItem>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
RouteRequestParams,
|
|
4
4
|
RouteResponse,
|
|
5
5
|
} from '@seamapi/types/devicedb'
|
|
6
|
-
import { useQuery } from '@tanstack/react-query'
|
|
6
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
7
7
|
import type { SeamError } from 'seamapi'
|
|
8
8
|
|
|
9
9
|
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
@@ -16,6 +16,7 @@ export function useDeviceModels(
|
|
|
16
16
|
params?: UseDeviceModelsParams
|
|
17
17
|
): UseSeamQueryResult<'deviceModels', UseDeviceModelsData> {
|
|
18
18
|
const { client: seam } = useSeamClient()
|
|
19
|
+
const queryClient = useQueryClient()
|
|
19
20
|
|
|
20
21
|
const { data, ...rest } = useQuery<
|
|
21
22
|
DeviceModelsListResponse['device_models'],
|
|
@@ -33,6 +34,19 @@ export function useDeviceModels(
|
|
|
33
34
|
)
|
|
34
35
|
return deviceModels
|
|
35
36
|
},
|
|
37
|
+
onSuccess: (deviceModels) => {
|
|
38
|
+
for (const deviceModel of deviceModels) {
|
|
39
|
+
queryClient.setQueryData(
|
|
40
|
+
[
|
|
41
|
+
'internal',
|
|
42
|
+
'device_models',
|
|
43
|
+
'get',
|
|
44
|
+
{ device_model_id: deviceModel.device_model_id },
|
|
45
|
+
],
|
|
46
|
+
deviceModel
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
},
|
|
36
50
|
})
|
|
37
51
|
|
|
38
52
|
return { ...rest, deviceModels: data }
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
RouteRequestParams,
|
|
4
4
|
RouteResponse,
|
|
5
5
|
} from '@seamapi/types/devicedb'
|
|
6
|
-
import { useQuery } from '@tanstack/react-query'
|
|
6
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
7
7
|
import type { SeamError } from 'seamapi'
|
|
8
8
|
|
|
9
9
|
import { useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
@@ -16,6 +16,7 @@ export function useManufacturers(
|
|
|
16
16
|
params?: UseManufacturersParams
|
|
17
17
|
): UseSeamQueryResult<'manufacturers', UseManufacturersData> {
|
|
18
18
|
const { client: seam } = useSeamClient()
|
|
19
|
+
const queryClient = useQueryClient()
|
|
19
20
|
|
|
20
21
|
const { data, ...rest } = useQuery<
|
|
21
22
|
ManufacturersListResponse['manufacturers'],
|
|
@@ -33,6 +34,19 @@ export function useManufacturers(
|
|
|
33
34
|
)
|
|
34
35
|
return manufacturers
|
|
35
36
|
},
|
|
37
|
+
onSuccess: (manufacturers) => {
|
|
38
|
+
for (const manufacturer of manufacturers) {
|
|
39
|
+
queryClient.setQueryData(
|
|
40
|
+
[
|
|
41
|
+
'internal',
|
|
42
|
+
'manufacturers',
|
|
43
|
+
'get',
|
|
44
|
+
{ manufacturer_id: manufacturer.manufacturer_id },
|
|
45
|
+
],
|
|
46
|
+
manufacturer
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
},
|
|
36
50
|
})
|
|
37
51
|
|
|
38
52
|
return { ...rest, manufacturers: data }
|
|
@@ -8,3 +8,4 @@ export * as DeviceDetails from './DeviceDetails/DeviceDetails.element.js'
|
|
|
8
8
|
export * as DeviceTable from './DeviceTable/DeviceTable.element.js'
|
|
9
9
|
export * as EditAccessCodeForm from './EditAccessCodeForm/EditAccessCodeForm.element.js'
|
|
10
10
|
export * as SupportedDeviceTable from './SupportedDeviceTable/SupportedDeviceTable.element.js'
|
|
11
|
+
export * as SupportedDeviceTableManufacturerKeys from './SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.element.js'
|
|
@@ -9,3 +9,4 @@ export * from './DeviceDetails/DeviceDetails.js'
|
|
|
9
9
|
export * from './DeviceTable/DeviceTable.js'
|
|
10
10
|
export * from './EditAccessCodeForm/EditAccessCodeForm.js'
|
|
11
11
|
export * from './SupportedDeviceTable/SupportedDeviceTable.js'
|
|
12
|
+
export * from './SupportedDeviceTable/SupportedDeviceTableManufacturerKeys.js'
|
|
@@ -33,7 +33,6 @@ export function useClimateSettingSchedules(
|
|
|
33
33
|
return await client.thermostats.climateSettingSchedules.list(params)
|
|
34
34
|
},
|
|
35
35
|
onSuccess: (schedules) => {
|
|
36
|
-
// Prime cache for each schedule.
|
|
37
36
|
for (const schedule of schedules) {
|
|
38
37
|
queryClient.setQueryData(
|
|
39
38
|
[
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMutation,
|
|
3
|
+
type UseMutationResult,
|
|
4
|
+
useQueryClient,
|
|
5
|
+
} from '@tanstack/react-query'
|
|
6
|
+
import type {
|
|
7
|
+
SeamError,
|
|
8
|
+
ThermostatDevice,
|
|
9
|
+
ThermostatSetFanModeRequest,
|
|
10
|
+
ThermostatsListResponse,
|
|
11
|
+
} from 'seamapi'
|
|
12
|
+
|
|
13
|
+
import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
14
|
+
|
|
15
|
+
// UPSTREAM: Missing ThermostatSetFanModeResponse in seamapi.
|
|
16
|
+
type UseUpdateFanModeData = Record<string, unknown>
|
|
17
|
+
type UseUpdateFanModeMutationParams = ThermostatSetFanModeRequest
|
|
18
|
+
|
|
19
|
+
export function useUpdateFanMode(): UseMutationResult<
|
|
20
|
+
UseUpdateFanModeData,
|
|
21
|
+
SeamError,
|
|
22
|
+
UseUpdateFanModeMutationParams
|
|
23
|
+
> {
|
|
24
|
+
const { client } = useSeamClient()
|
|
25
|
+
const queryClient = useQueryClient()
|
|
26
|
+
|
|
27
|
+
return useMutation<
|
|
28
|
+
UseUpdateFanModeData,
|
|
29
|
+
SeamError,
|
|
30
|
+
UseUpdateFanModeMutationParams
|
|
31
|
+
>({
|
|
32
|
+
mutationFn: async (mutationParams: UseUpdateFanModeMutationParams) => {
|
|
33
|
+
if (client === null) throw new NullSeamClientError()
|
|
34
|
+
|
|
35
|
+
return await client.thermostats.setFanMode(mutationParams)
|
|
36
|
+
},
|
|
37
|
+
onSuccess: (_data, variables) => {
|
|
38
|
+
queryClient.setQueryData<ThermostatDevice | null>(
|
|
39
|
+
['thermostats', 'get', { device_id: variables.device_id }],
|
|
40
|
+
(thermostat) => {
|
|
41
|
+
if (thermostat == null) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return getUpdatedThermostat(thermostat, variables)
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
queryClient.setQueryData<ThermostatsListResponse['thermostats']>(
|
|
50
|
+
['thermostats', 'list', { device_id: variables.device_id }],
|
|
51
|
+
(thermostats): ThermostatDevice[] => {
|
|
52
|
+
if (thermostats == null) {
|
|
53
|
+
return []
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return thermostats.map((thermostat) => {
|
|
57
|
+
if (thermostat.device_id === variables.device_id) {
|
|
58
|
+
return getUpdatedThermostat(thermostat, variables)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return thermostat
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getUpdatedThermostat(
|
|
70
|
+
thermostat: ThermostatDevice,
|
|
71
|
+
variables: UseUpdateFanModeMutationParams
|
|
72
|
+
): ThermostatDevice {
|
|
73
|
+
return {
|
|
74
|
+
...thermostat,
|
|
75
|
+
properties: {
|
|
76
|
+
...thermostat.properties,
|
|
77
|
+
fan_mode_setting:
|
|
78
|
+
variables.fan_mode_setting ?? thermostat.properties.fan_mode_setting,
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMutation,
|
|
3
|
+
type UseMutationResult,
|
|
4
|
+
useQueryClient,
|
|
5
|
+
} from '@tanstack/react-query'
|
|
6
|
+
import type {
|
|
7
|
+
SeamError,
|
|
8
|
+
ThermostatDevice,
|
|
9
|
+
ThermostatsListResponse,
|
|
10
|
+
ThermostatUpdateRequest,
|
|
11
|
+
} from 'seamapi'
|
|
12
|
+
|
|
13
|
+
import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
|
|
14
|
+
|
|
15
|
+
// UPSTREAM: Missing ThermostatUpdateResponse in seamapi.
|
|
16
|
+
type UseUpdateThermostatData = Record<string, unknown>
|
|
17
|
+
type UseUpdateThermostatMutationParams = ThermostatUpdateRequest
|
|
18
|
+
|
|
19
|
+
export function useUpdateThermostat(): UseMutationResult<
|
|
20
|
+
UseUpdateThermostatData,
|
|
21
|
+
SeamError,
|
|
22
|
+
UseUpdateThermostatMutationParams
|
|
23
|
+
> {
|
|
24
|
+
const { client } = useSeamClient()
|
|
25
|
+
const queryClient = useQueryClient()
|
|
26
|
+
|
|
27
|
+
return useMutation<
|
|
28
|
+
UseUpdateThermostatData,
|
|
29
|
+
SeamError,
|
|
30
|
+
UseUpdateThermostatMutationParams
|
|
31
|
+
>({
|
|
32
|
+
mutationFn: async (mutationParams: UseUpdateThermostatMutationParams) => {
|
|
33
|
+
if (client === null) throw new NullSeamClientError()
|
|
34
|
+
|
|
35
|
+
return await client.thermostats.update(mutationParams)
|
|
36
|
+
},
|
|
37
|
+
onSuccess: (_data, variables) => {
|
|
38
|
+
queryClient.setQueryData<ThermostatDevice | null>(
|
|
39
|
+
['thermostats', 'get', { device_id: variables.device_id }],
|
|
40
|
+
(thermostat) => {
|
|
41
|
+
if (thermostat == null) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return getUpdatedThermostat(thermostat, variables)
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
queryClient.setQueryData<ThermostatsListResponse['thermostats']>(
|
|
50
|
+
['thermostats', 'list', { device_id: variables.device_id }],
|
|
51
|
+
(thermostats): ThermostatDevice[] => {
|
|
52
|
+
if (thermostats == null) {
|
|
53
|
+
return []
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return thermostats.map((thermostat) => {
|
|
57
|
+
if (thermostat.device_id === variables.device_id) {
|
|
58
|
+
return getUpdatedThermostat(thermostat, variables)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return thermostat
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getUpdatedThermostat(
|
|
70
|
+
thermostat: ThermostatDevice,
|
|
71
|
+
variables: UseUpdateThermostatMutationParams
|
|
72
|
+
): ThermostatDevice {
|
|
73
|
+
return {
|
|
74
|
+
...thermostat,
|
|
75
|
+
|
|
76
|
+
properties: {
|
|
77
|
+
...thermostat.properties,
|
|
78
|
+
|
|
79
|
+
default_climate_setting: {
|
|
80
|
+
...thermostat.properties.default_climate_setting,
|
|
81
|
+
manual_override_allowed:
|
|
82
|
+
thermostat.properties?.default_climate_setting
|
|
83
|
+
?.manual_override_allowed != null
|
|
84
|
+
? thermostat.properties.default_climate_setting
|
|
85
|
+
.manual_override_allowed
|
|
86
|
+
: true,
|
|
87
|
+
|
|
88
|
+
...variables.default_climate_setting,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
|
-
import { useState } from 'react'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
3
|
import { useForm } from 'react-hook-form'
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
type ClimateSetting,
|
|
6
|
+
type HvacModeSetting,
|
|
7
|
+
isThermostatDevice,
|
|
8
|
+
} from 'seamapi'
|
|
9
|
+
|
|
10
|
+
import { useDevice } from 'lib/index.js'
|
|
5
11
|
|
|
6
12
|
import { getSystemTimeZone } from 'lib/dates.js'
|
|
13
|
+
import { ClimateSettingScheduleFormClimateSetting } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormClimateSetting.js'
|
|
14
|
+
import { ClimateSettingScheduleFormDefaultClimateSetting } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDefaultClimateSetting.js'
|
|
7
15
|
import { ClimateSettingScheduleFormDeviceSelect } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormDeviceSelect.js'
|
|
8
16
|
import { ClimateSettingScheduleFormNameAndSchedule } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormNameAndSchedule.js'
|
|
9
17
|
import { ClimateSettingScheduleFormTimeZonePicker } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleFormTimeZonePicker.js'
|
|
@@ -30,6 +38,12 @@ export interface ClimateSettingScheduleFormFields {
|
|
|
30
38
|
startDate: string
|
|
31
39
|
endDate: string
|
|
32
40
|
timeZone: string
|
|
41
|
+
climateSetting: {
|
|
42
|
+
hvacModeSetting: HvacModeSetting
|
|
43
|
+
// may have to ignore one or the other fields here on submit
|
|
44
|
+
heatingSetPoint: number
|
|
45
|
+
coolingSetPoint: number
|
|
46
|
+
}
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
export function ClimateSettingScheduleForm({
|
|
@@ -48,19 +62,29 @@ export function ClimateSettingScheduleForm({
|
|
|
48
62
|
function Content({
|
|
49
63
|
onBack,
|
|
50
64
|
}: Omit<ClimateSettingScheduleFormProps, 'className'>): JSX.Element {
|
|
51
|
-
const { control, watch } =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
const { control, watch, resetField } =
|
|
66
|
+
useForm<ClimateSettingScheduleFormFields>({
|
|
67
|
+
defaultValues: {
|
|
68
|
+
deviceId: '',
|
|
69
|
+
name: '',
|
|
70
|
+
startDate: '',
|
|
71
|
+
endDate: '',
|
|
72
|
+
timeZone: getSystemTimeZone(),
|
|
73
|
+
climateSetting: {
|
|
74
|
+
hvacModeSetting: 'heat_cool',
|
|
75
|
+
heatingSetPoint: 70,
|
|
76
|
+
coolingSetPoint: 75,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
})
|
|
60
80
|
|
|
61
81
|
const deviceId = watch('deviceId')
|
|
62
82
|
const timeZone = watch('timeZone')
|
|
63
83
|
|
|
84
|
+
const { device } = useDevice({
|
|
85
|
+
device_id: deviceId,
|
|
86
|
+
})
|
|
87
|
+
|
|
64
88
|
const [page, setPage] = useState<
|
|
65
89
|
| 'device_select'
|
|
66
90
|
| 'default_setting'
|
|
@@ -69,13 +93,38 @@ function Content({
|
|
|
69
93
|
| 'climate_setting'
|
|
70
94
|
>('device_select')
|
|
71
95
|
|
|
96
|
+
const returnToDeviceSelect = (): void => {
|
|
97
|
+
resetField('deviceId')
|
|
98
|
+
setPage('device_select')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (page === 'device_select' && device != null) {
|
|
103
|
+
if (!isThermostatDevice(device)) return
|
|
104
|
+
const defaultSetting = device.properties.default_climate_setting
|
|
105
|
+
if (defaultSetting != null) setPage('name_and_schedule')
|
|
106
|
+
else setPage('default_setting')
|
|
107
|
+
}
|
|
108
|
+
}, [device, page, setPage])
|
|
109
|
+
|
|
72
110
|
if (page === 'device_select') {
|
|
73
111
|
return (
|
|
74
112
|
<ClimateSettingScheduleFormDeviceSelect
|
|
75
113
|
title={t.addNewClimateSettingSchedule}
|
|
76
114
|
control={control}
|
|
77
115
|
onBack={onBack}
|
|
78
|
-
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (page === 'default_setting') {
|
|
121
|
+
return (
|
|
122
|
+
<ClimateSettingScheduleFormDefaultClimateSetting
|
|
123
|
+
title={t.addNewClimateSettingSchedule}
|
|
124
|
+
deviceId={deviceId}
|
|
125
|
+
onBack={returnToDeviceSelect}
|
|
126
|
+
onCancel={onBack}
|
|
127
|
+
onSave={() => {
|
|
79
128
|
setPage('name_and_schedule')
|
|
80
129
|
}}
|
|
81
130
|
/>
|
|
@@ -88,9 +137,7 @@ function Content({
|
|
|
88
137
|
title={t.addNewClimateSettingSchedule}
|
|
89
138
|
control={control}
|
|
90
139
|
deviceId={deviceId}
|
|
91
|
-
onBack={
|
|
92
|
-
setPage('device_select')
|
|
93
|
-
}}
|
|
140
|
+
onBack={returnToDeviceSelect}
|
|
94
141
|
onCancel={onBack}
|
|
95
142
|
onNext={() => {
|
|
96
143
|
setPage('climate_setting')
|
|
@@ -114,6 +161,21 @@ function Content({
|
|
|
114
161
|
)
|
|
115
162
|
}
|
|
116
163
|
|
|
164
|
+
if (page === 'climate_setting') {
|
|
165
|
+
return (
|
|
166
|
+
<ClimateSettingScheduleFormClimateSetting
|
|
167
|
+
title={t.addNewClimateSettingSchedule}
|
|
168
|
+
control={control}
|
|
169
|
+
deviceId={deviceId}
|
|
170
|
+
onBack={() => {
|
|
171
|
+
setPage('name_and_schedule')
|
|
172
|
+
}}
|
|
173
|
+
onCancel={onBack}
|
|
174
|
+
onSave={() => {}}
|
|
175
|
+
/>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
117
179
|
return <></>
|
|
118
180
|
}
|
|
119
181
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type Control, Controller } from 'react-hook-form'
|
|
2
|
+
|
|
3
|
+
import { useDevice } from 'lib/seam/devices/use-device.js'
|
|
4
|
+
import { Button } from 'lib/ui/Button.js'
|
|
5
|
+
import type { ClimateSettingScheduleFormFields } from 'lib/ui/ClimateSettingForm/ClimateSettingScheduleForm.js'
|
|
6
|
+
import { FormField } from 'lib/ui/FormField.js'
|
|
7
|
+
import { InputLabel } from 'lib/ui/InputLabel.js'
|
|
8
|
+
import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
|
|
9
|
+
import { ClimateSettingControlGroup } from 'lib/ui/thermostat/ClimateSettingControlGroup.js'
|
|
10
|
+
|
|
11
|
+
interface ClimateSettingScheduleFormClimateSettingProps {
|
|
12
|
+
title: string
|
|
13
|
+
control: Control<ClimateSettingScheduleFormFields>
|
|
14
|
+
deviceId: string
|
|
15
|
+
onBack: () => void
|
|
16
|
+
onCancel: (() => void) | undefined
|
|
17
|
+
onSave: () => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ClimateSettingScheduleFormClimateSetting({
|
|
21
|
+
title,
|
|
22
|
+
control,
|
|
23
|
+
deviceId,
|
|
24
|
+
onBack,
|
|
25
|
+
onCancel,
|
|
26
|
+
onSave,
|
|
27
|
+
}: ClimateSettingScheduleFormClimateSettingProps): JSX.Element {
|
|
28
|
+
const { device } = useDevice({
|
|
29
|
+
device_id: deviceId,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<ContentHeader
|
|
35
|
+
title={title}
|
|
36
|
+
onBack={onBack}
|
|
37
|
+
subheading={device?.properties.name}
|
|
38
|
+
/>
|
|
39
|
+
<div className='seam-main'>
|
|
40
|
+
<div className='seam-climate-setting-schedule-form-climate-setting'>
|
|
41
|
+
<div className='seam-content'>
|
|
42
|
+
<div>
|
|
43
|
+
<InputLabel>{t.climateSetting}</InputLabel>
|
|
44
|
+
<span className='seam-label'>{t.climateSettingSubHeading}</span>
|
|
45
|
+
</div>
|
|
46
|
+
<FormContent control={control} />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div className='seam-actions'>
|
|
50
|
+
<Button onClick={onCancel}>{t.cancel}</Button>
|
|
51
|
+
<Button variant='solid' onClick={onSave}>
|
|
52
|
+
{t.save}
|
|
53
|
+
</Button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface FormContentProps {
|
|
61
|
+
control: Control<ClimateSettingScheduleFormFields>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function FormContent({ control }: FormContentProps): JSX.Element {
|
|
65
|
+
return (
|
|
66
|
+
<FormField>
|
|
67
|
+
<Controller
|
|
68
|
+
name='climateSetting'
|
|
69
|
+
control={control}
|
|
70
|
+
render={({ field: { value, onChange } }) => (
|
|
71
|
+
<ClimateSettingControlGroup
|
|
72
|
+
mode={value.hvacModeSetting}
|
|
73
|
+
onModeChange={(mode) => {
|
|
74
|
+
onChange({ ...value, hvacModeSetting: mode })
|
|
75
|
+
}}
|
|
76
|
+
coolValue={value.coolingSetPoint}
|
|
77
|
+
heatValue={value.heatingSetPoint}
|
|
78
|
+
onCoolValueChange={(coolingSetPoint) => {
|
|
79
|
+
onChange({
|
|
80
|
+
...value,
|
|
81
|
+
coolingSetPoint,
|
|
82
|
+
})
|
|
83
|
+
}}
|
|
84
|
+
onHeatValueChange={(heatingSetPoint) => {
|
|
85
|
+
onChange({
|
|
86
|
+
...value,
|
|
87
|
+
heatingSetPoint,
|
|
88
|
+
})
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
/>
|
|
93
|
+
</FormField>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const t = {
|
|
98
|
+
climateSetting: 'Climate setting',
|
|
99
|
+
climateSettingSubHeading: 'Choose mode and adjust the climate',
|
|
100
|
+
cancel: 'Cancel',
|
|
101
|
+
save: 'Save',
|
|
102
|
+
next: 'Next',
|
|
103
|
+
}
|