Merge branch 'i2c_detect' into dev_environment

This commit is contained in:
csawatzky 2025-06-30 11:42:11 -06:00
commit 0b31f5585d
17 changed files with 511 additions and 31 deletions

2
package-lock.json generated
View file

@ -10889,7 +10889,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#bb403eef199a68f470fc025ae97fed2185682504", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#091928b04970731f9b543fcecc313b6f4ea7d4e2",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -12,10 +12,10 @@
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview", "preview": "vite preview",
"build:staging": "VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=wss://stagingapi.brandxtech.ca/v1/live NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_STAGING} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build", "build:staging": "VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=wss://stagingapi.brandxtech.ca/v1/live NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_STAGING} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP=${OPEN_WEATHERMAP} vite build",
"build:development": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_APP_API_URL=https://bxt-dev.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_DEVELOPMENT} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build", "build:development": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_APP_API_URL=https://bxt-dev.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_DEVELOPMENT} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP=${OPEN_WEATHERMAP} vite build",
"build:production": "VITE_AUTH0_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=api.brandxtech.ca VITE_APP_API_URL=https://api.brandxtech.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build", "build:production": "VITE_AUTH0_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=api.brandxtech.ca VITE_APP_API_URL=https://api.brandxtech.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP=${OPEN_WEATHERMAP} vite build",
"build:streamline": "VITE_AUTH0_CLIENT_ID=HwUV0hHNdVvU96zuMBTAU8i7nFdwwgIX VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=streamline.api.adaptiveagriculture.ca VITE_APP_API_URL=https://streamline.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build", "build:streamline": "VITE_AUTH0_CLIENT_ID=HwUV0hHNdVvU96zuMBTAU8i7nFdwwgIX VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=streamline.api.adaptiveagriculture.ca VITE_APP_API_URL=https://streamline.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP=${OPEN_WEATHERMAP} vite build",
"build:offline": "npx env-cmd offline,whitelabel npm run build", "build:offline": "npx env-cmd offline,whitelabel npm run build",
"test": "vitest" "test": "vitest"
}, },

View file

@ -555,10 +555,10 @@ const componentType: data = {
key: "COMPONENT_TYPE_CAPACITANCE", key: "COMPONENT_TYPE_CAPACITANCE",
val: 24 val: 24
}, },
{ // {
key: "COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT", // key: "COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT", //this component type is deprecated?
val: 25 // val: 25
}, // },
{ {
key: "COMPONENT_TYPE_WEIGHT", key: "COMPONENT_TYPE_WEIGHT",
val: 26 val: 26

View file

@ -414,7 +414,7 @@ export default function BinComponents(props: Props) {
)} )}
<ListItemText inset={cIcon === undefined}> <ListItemText inset={cIcon === undefined}>
<Grid container direction="row" justifyContent="space-between"> <Grid container direction="row" justifyContent="space-between">
<Grid >{comp.name()}</Grid> <Grid >{comp.name() + " - " + comp.addressDescription(devMap.get(device)?.settings.product)}</Grid>
<Grid > <Grid >
{components?.get(comp.key()) !== undefined ? ( {components?.get(comp.key()) !== undefined ? (
<CheckBoxIcon /> <CheckBoxIcon />

View file

@ -238,6 +238,7 @@ export default function ComponentSettings(props: Props) {
componentType: quack.ComponentType componentType: quack.ComponentType
): number[] => { ): number[] => {
let positions = availablePositions.get(addressType); let positions = availablePositions.get(addressType);
console.log(positions)
if (!positions) return []; if (!positions) return [];
switch (addressType) { switch (addressType) {
case quack.AddressType.ADDRESS_TYPE_I2C: case quack.AddressType.ADDRESS_TYPE_I2C:
@ -412,6 +413,7 @@ export default function ComponentSettings(props: Props) {
for (let i = 0; i < addressTypes.length; i++) { for (let i = 0; i < addressTypes.length; i++) {
let addressType = addressTypes[i]; let addressType = addressTypes[i];
let availablePositions = getAvailablePositions(addressType, type); let availablePositions = getAvailablePositions(addressType, type);
console.log(availablePositions)
availableMenuItemPositions.push( availableMenuItemPositions.push(
<MenuItem key={addressType} divider disabled> <MenuItem key={addressType} divider disabled>

View file

@ -26,7 +26,8 @@ import {
import { GetDeviceProductIcon, GetDeviceProductLabel } from "products/DeviceProduct"; import { GetDeviceProductIcon, GetDeviceProductLabel } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack"; import { quack } from "protobuf-ts/quack";
import { useComponentAPI, useGlobalState, useSnackbar } from "providers"; import { useComponentAPI, useDeviceAPI, useGlobalState, useSnackbar } from "providers";
import React, { useState } from "react"; import React, { useState } from "react";
import DeviceSVG, { PortInformation } from "./deviceSVG"; import DeviceSVG, { PortInformation } from "./deviceSVG";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
@ -83,6 +84,8 @@ export default function DeviceWizard(props: Props) {
); );
const classes = useStyles(); const classes = useStyles();
const [selectedPort, setSelectedPort] = useState<PortInformation>(); const [selectedPort, setSelectedPort] = useState<PortInformation>();
const deviceAPI = useDeviceAPI()
const { openSnack } = useSnackbar()
//const [addressType, setAddressType] = useState<quack.AddressType>(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY); //const [addressType, setAddressType] = useState<quack.AddressType>(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY);
const wizardMenu = () => { const wizardMenu = () => {
@ -118,6 +121,22 @@ export default function DeviceWizard(props: Props) {
</MenuItem> </MenuItem>
</Tooltip> </Tooltip>
)} )}
{selectedPort && selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_I2C && device.featureSupported("detectI2C") && (
<Tooltip title="Used to tell the Device to scan its I2C port for any plugged in sensors">
<MenuItem
key={"scanI2C"}
onClick={() => {
deviceAPI.detectI2C(device.id())
.then(resp => {
openSnack("Device will scan port on next check-in")
}).catch(err => {
openSnack("Command failed to send to device")
})
}}>
Scan Port
</MenuItem>
</Tooltip>
)}
{portComponents.length > 0 && ( {portComponents.length > 0 && (
<List> <List>
<ListSubheader>Current Components</ListSubheader> <ListSubheader>Current Components</ListSubheader>

View file

@ -0,0 +1,219 @@
import { Box, Button, Card, DialogActions, DialogContent, DialogTitle, FormControlLabel, Grid2, Radio, RadioGroup, Step, StepLabel, Stepper, Typography } from "@mui/material";
import { cloneDeep } from "lodash";
import { getSubtypes, Subtype } from "pbHelpers/ComponentType";
import { pond, quack } from "protobuf-ts/pond";
import React from "react";
import { useEffect, useState } from "react";
import ScannedI2C from "./ScannedI2C";
import ComponentForm from "component/ComponentForm";
import { Component, Device } from "models";
import { DeviceAvailabilityMap, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useComponentAPI, useSnackbar } from "hooks";
interface Props {
scannedComponents: pond.ComponentAddressMap
device: Device
availablePositions: DeviceAvailabilityMap
availableOffsets: OffsetAvailabilityMap
refreshCallback: () => void
}
interface CompStep {
label: string;
completed?: boolean;
}
export default function DeviceScannedComponents(props: Props){
const {scannedComponents, device, availablePositions, availableOffsets, refreshCallback} = props
const compAPI = useComponentAPI();
const [scannedI2C, setScannedI2C] = useState<pond.DetectI2C>()
const [components, setComponents] = useState<Component[]>([])
const [steps, setSteps] = useState<CompStep[]>([]);
const { error, success } = useSnackbar();
const [currentStep, setCurrentStep] = useState(0)
const [settingsValid, setSettingsValid] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
useEffect(()=>{
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
//makes the array empty for testing
// if (scannedComponents.i2c) {
// let clone = cloneDeep(scannedComponents.i2c)
// if (clone.settings){
// clone.settings.foundAddresses = []
// }
// setScannedI2C(clone)
// }
},[scannedComponents])
useEffect(() => {
let steps: CompStep[] = []
components.forEach(comp => {
steps.push({
label: comp.name(),
completed: false
})
})
setSteps(steps)
},[components])
const stepper = () => {
return (
<Stepper nonLinear activeStep={currentStep} style={{ width: "100%" }}>
{steps.map(compStep => {
const labelProps: {
optional?: React.ReactNode;
} = {};
return (
<Step key={compStep.label} completed={compStep.completed}>
<StepLabel {...labelProps}>{compStep.label}</StepLabel>
</Step>
);
})}
</Stepper>
);
};
const addComponents = () => {
let c: pond.MultiComponentSettings = pond.MultiComponentSettings.create();
components.forEach(component => {
c.components.push(component.settings);
});
compAPI
.addMultiComponents(device.id(), c)
.then(resp => {
success("Components added to Device");
})
.catch(err => {
error("One or more component failed to add");
})
.finally(() => {
refreshCallback();
});
};
const addComponentDialog = () => {
return (
<ResponsiveDialog open={openDialog} onClose={()=>{setOpenDialog(false)}}>
<DialogTitle>Adding Components</DialogTitle>
<DialogContent>
{stepper()}
<ComponentForm
component={components[currentStep]}
canEdit
device={device}
componentChanged={(component, isValid) => {
components[currentStep].settings = component.settings;
setSettingsValid(isValid);
}}
/>
</DialogContent>
<DialogActions>
<Grid2 container direction="row" justifyContent="space-between">
<Grid2>
<Button
variant="contained"
color="primary"
onClick={() => {
close();
}}>
Cancel
</Button>
</Grid2>
<Grid2>
{currentStep !== 0 && (
<Button
sx={{marginX: 1}}
variant="contained"
color="primary"
onClick={() => {
setCurrentStep(currentStep - 1);
}}>
Back
</Button>
)}
{currentStep !== steps.length - 1 && (
<React.Fragment>
<Button
sx={{marginX: 1}}
variant="contained"
color="primary"
onClick={() => {
setCurrentStep(currentStep + 1);
}}>
Next
</Button>
</React.Fragment>
)}
<Button
sx={{marginX: 1}}
variant="contained"
color="primary"
onClick={() => {
setOpenDialog(false)
addComponents();
}}>
Confirm
</Button>
</Grid2>
</Grid2>
</DialogActions>
</ResponsiveDialog>
);
};
const componentSelection = (newComponent: Component, checked: boolean) => {
let cloneList = cloneDeep(components)
if(checked){
//add the component to the list
cloneList.push(newComponent)
}else{
//remove any components that match the component location: (type)-(addressType)-(address)
components.forEach((comp, i) => {
//check the components location against the one in the list
if(comp.locationString() === newComponent.locationString()){
cloneList.splice(i, 1)
}
})
}
setComponents(cloneList)
}
return (
<React.Fragment>
<Card raised sx={{padding: 1}}>
{scannedI2C !== undefined &&
<React.Fragment>
<Box>
<Typography sx={{fontWeight: 650}}>
I2C Sensors
</Typography>
</Box>
{scannedI2C.settings?.foundAddresses && scannedI2C.settings.foundAddresses.length > 0 ? scannedI2C?.settings?.foundAddresses.map((addr, index) => {
return (
<ScannedI2C key={index} sensorNum={index+1} decimalAddress={addr} deviceProduct={device.settings.product} componentSelectionCallback={componentSelection} availablePositions={availablePositions.get(quack.AddressType.ADDRESS_TYPE_I2C) ?? []}/>
)
}) :
<Box sx={{
margin: 1,
display: "flex",
justifyContent: "center"
}}>
<Typography>
No Sensors Found
</Typography>
</Box>
}
</React.Fragment>
}
<Box display="flex" flexDirection="row-reverse" marginTop={2}>
<Button disabled={components.length === 0} variant="contained" color="primary" onClick={()=>{setOpenDialog(true)}}>Add Components</Button>
</Box>
</Card>
{addComponentDialog()}
</React.Fragment>
)
}

View file

@ -0,0 +1,150 @@
import { Box, Button, Checkbox, FormControlLabel, Grid2, MenuItem, Radio, RadioGroup, TextField, Tooltip, Typography } from "@mui/material";
import { Component } from "models";
import { getAddressTypes, getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType";
import { ComponentAvailabilityMap, DevicePositions } from "pbHelpers/DeviceAvailability";
import { GetProductAvailability } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { useEffect, useState } from "react";
interface Props {
decimalAddress: number
deviceProduct: pond.DeviceProduct
componentSelectionCallback: (component: Component, checked: boolean) => void
availablePositions: DevicePositions
sensorNum?: number
}
export default function ScannedI2C(props: Props){
const {decimalAddress, deviceProduct, componentSelectionCallback, availablePositions, sensorNum} = props
const [types, setTypes] = useState<quack.ComponentType[]>([])
const [subtypeOptions, setSubtypeOptions] = useState<Array<Subtype>>([])
const [selectedType, setSelectedType] = useState<quack.ComponentType>(quack.ComponentType.COMPONENT_TYPE_INVALID)
const [selectedSubtype, setSelectedSubtype] = useState(0)
const [addressInUse, setAddressInUse] = useState(false)
useEffect(()=>{
let types: quack.ComponentType[] = []
//find what component types use that address, this is different than the available positions passed in
//this is meant to get all possibilities according to product type and the given address, availablePositions is the restriction of what is available
let productAvailability = GetProductAvailability(deviceProduct)
if(productAvailability){
let i2cMap = productAvailability.get(quack.AddressType.ADDRESS_TYPE_I2C)
if(i2cMap){
i2cMap.forEach((val, key) => {
let addresses = val as number[] ?? []
if(addresses.length > 0 && addresses.includes(decimalAddress)){
types.push(key)
}
})
}
}
//this if can be taken out if we dont want to select the first component type by default
if(types.length > 0) {
setSelectedType(types[0])
buildSubtypeOptions(types[0])
checkAddressAvailable(types[0])
}
setTypes(types)
},[decimalAddress, deviceProduct])
const buildSubtypeOptions = (compType: quack.ComponentType) => {
let subtypes = getSubtypes(compType)
let validSub: Subtype[] = []
//have to exclude subtypes that are not I2C
subtypes.forEach((sub,i) => {
let addrTypes = getAddressTypes(compType, sub.key)
if (addrTypes.includes(quack.AddressType.ADDRESS_TYPE_I2C)){
validSub.push(sub)
}
})
//set the selected on to be the first in the valid selection
if(validSub.length > 0){
setSelectedSubtype(validSub[0].key)
}
setSubtypeOptions(validSub)
}
const checkAddressAvailable = (compType: quack.ComponentType) => {
let i2cMap = availablePositions as ComponentAvailabilityMap
let compAddresses = i2cMap.get(compType) ?? []
if (!compAddresses.includes(decimalAddress)){
setAddressInUse(true)
}else{
setAddressInUse(false)
}
}
const componentSelected = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
//build a component with given information and defaults for the rest
let component = Component.create()
component.settings.type = selectedType
component.settings.subtype = selectedSubtype
component.settings.addressType = quack.AddressType.ADDRESS_TYPE_I2C
component.settings.address = decimalAddress
component.settings.name = getFriendlyName(selectedType, selectedSubtype)
//return the component and the checked status to the parent
componentSelectionCallback(component, checked)
}
return (
<Box sx={{marginY: 1}}>
{sensorNum && <Typography>Sensor {sensorNum}</Typography>}
<Grid2 container direction="row" alignItems="center" justifyContent="space-between">
<Grid2 size={3}>
<RadioGroup
value={selectedType}
onChange={(_, value) => {
let v = parseInt(value)
if (!isNaN(v)){
buildSubtypeOptions(v)
checkAddressAvailable(v)
}
}}>
{types.map((type, i) => {
return (
<FormControlLabel
key={i}
value={type}
control={<Radio />}
label={getFriendlyName(type)}
/>
)
})}
</RadioGroup>
</Grid2>
<Grid2 size={8}>
{subtypeOptions.length > 0 &&
<TextField
value={selectedSubtype}
fullWidth
onChange={(event)=>{
let val = +event.target.value
setSelectedSubtype(val)
}}
select>
{subtypeOptions.map(s => (
<MenuItem value={s.key}>{s.friendlyName}</MenuItem>
))}
</TextField>
}
</Grid2>
<Grid2 size={1}>
<Tooltip
title="Add to list to be added to the device"
children={
<Checkbox
onChange={componentSelected}
disabled={addressInUse}
/>
}
/>
</Grid2>
</Grid2>
</Box>
)
}

View file

@ -3,6 +3,7 @@ import { quack } from "protobuf-ts/pond";
import { or } from "utils/types"; import { or } from "utils/types";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { getSubtypes } from "pbHelpers/ComponentType"; import { getSubtypes } from "pbHelpers/ComponentType";
import { getFriendlyAddressTypeName, getHumanReadableAddress } from "pbHelpers/AddressType";
// import { getSubtypes } from "pbHelpers/ComponentType"; // import { getSubtypes } from "pbHelpers/ComponentType";
export class Component { export class Component {
@ -105,4 +106,33 @@ export class Component {
} }
return 0; return 0;
} }
public addressDescription = (deviceProduct?: pond.DeviceProduct) => {
if (
this.settings.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY ||
(this.settings.addressType >= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET1 &&
this.settings.addressType <= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET100)
) {
let addressDescription = getHumanReadableAddress(
this.settings.addressType,
this.settings.type,
this.settings.address,
deviceProduct
);
if (addressDescription === "") {
addressDescription = "Internal";
}
let port = "Port: " + addressDescription;
let cableID = "";
if (
this.settings.addressType >= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET1 &&
this.settings.addressType <= quack.AddressType.ADDRESS_TYPE_PIN_OFFSET100
) {
cableID = "Cable: " + (this.settings.addressType - 8);
}
return port + " " + cableID;
} else {
return getFriendlyAddressTypeName(this.settings.addressType);
}
};
} }

View file

@ -33,6 +33,21 @@ const featureVersions: Map<string, FeatureVersionByPlatform> = new Map([
v2CellBlue: "2.0.44", v2CellBlue: "2.0.44",
v2EthBlue: "2.0.44" v2EthBlue: "2.0.44"
} }
],[
"detectI2C",
{
photon: "NA",
electron: "NA",
v2Wifi: "2.1.6",
v2Cell: "2.1.6",
v2WifiS3: "2.1.6",
v2CellS3: "2.1.6",
v2CellBlack: "2.1.6",
v2CellGreen: "2.1.6",
v2WifiBlue: "2.1.6",
v2CellBlue: "2.1.6",
v2EthBlue: "2.1.6"
}
] ]
]); ]);
export class Device { export class Device {

View file

@ -1,4 +1,4 @@
import { Button, Divider, List, ListItem, Typography } from "@mui/material"; import { Button, Card, Divider, List, ListItem, Typography } from "@mui/material";
import Grid from '@mui/material/Grid2'; import Grid from '@mui/material/Grid2';
import { Component, Device, Interaction, User } from "models"; import { Component, Device, Interaction, User } from "models";
import { getContextKeys, getContextTypes } from "pbHelpers/Context"; import { getContextKeys, getContextTypes } from "pbHelpers/Context";
@ -20,6 +20,7 @@ import { isController } from "pbHelpers/ComponentType";
import { or } from "utils"; import { or } from "utils";
import ComponentDiagnostics from "component/ComponentDiagnostics"; import ComponentDiagnostics from "component/ComponentDiagnostics";
import DeviceWizard from "device/DeviceWizard"; import DeviceWizard from "device/DeviceWizard";
import DeviceScannedComponents from "device/autoDetect/DeviceScannedComponents";
export interface DevicePageData { export interface DevicePageData {
device: Device; device: Device;
@ -56,6 +57,7 @@ export default function DevicePage() {
const [components, setComponents] = useState<Map<string, Component>>(new Map()); const [components, setComponents] = useState<Map<string, Component>>(new Map());
const [diagnosticComponents, setDiagnosticComponents] = useState<Component[]>([]); const [diagnosticComponents, setDiagnosticComponents] = useState<Component[]>([]);
const [scannedAddresses, setScannedAddresses] = useState<pond.ComponentAddressMap | undefined>(undefined)
// const [components, setComponents] = useState<Component[]>([]); // const [components, setComponents] = useState<Component[]>([]);
const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false) const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false)
@ -162,8 +164,21 @@ export default function DevicePage() {
.finally(() => setLoading(false)); .finally(() => setLoading(false));
} }
const loadPortScan = () => {
deviceAPI.listFoundComponents(device.id())
.then(resp => {
if (resp.data.foundComponents?.i2c){
setScannedAddresses(resp.data.foundComponents ?? undefined)
}
})
.catch(err => {
console.log(err)
})
}
useEffect(() => { useEffect(() => {
loadDevice() loadDevice()
loadPortScan()
}, [deviceID, as]) }, [deviceID, as])
const toggleNotificationPreference = () => { const toggleNotificationPreference = () => {
@ -390,6 +405,8 @@ export default function DevicePage() {
refreshCallback={loadDevice} refreshCallback={loadDevice}
/> />
</Grid> </Grid>
{/* add grid card here for I2C detected components */}
{diagnosticComponents.map(comp => ( {diagnosticComponents.map(comp => (
<Grid size={{ xs: 12 }} key={comp.key()}> <Grid size={{ xs: 12 }} key={comp.key()}>
<ComponentDiagnostics <ComponentDiagnostics
@ -402,7 +419,7 @@ export default function DevicePage() {
</> </>
) : ( ) : (
<> <>
<Grid size={{ xs: 6, sm: 6, lg: 4, xl: 3 }} > <Grid size={{ xs: 6, sm: 6, lg: 4, xl: 4 }} >
<DeviceWizard <DeviceWizard
device={device} device={device}
//permissions={permissions} //permissions={permissions}
@ -410,10 +427,21 @@ export default function DevicePage() {
refreshCallback={loadDevice} refreshCallback={loadDevice}
/> />
</Grid> </Grid>
<Grid size={{ xs: 6, sm: 6, lg: 8, xl: 9 }} > {scannedAddresses &&
<Grid container direction="row" spacing={2}> <Grid size={{ xs: 6, sm: 6, lg: 4, xl: 4 }}>
<DeviceScannedComponents
scannedComponents={scannedAddresses}
device={device}
availablePositions={availablePositions}
availableOffsets={availableOffsets}
refreshCallback={loadDevice}/>
</Grid>
}
{diagnosticComponents.length > 0 &&
<Grid size={{ xs: 6, sm: 6, lg: 4, xl: 4 }}>
<Grid container direction="row" spacing={2} width={"100%"}>
{diagnosticComponents.map(comp => ( {diagnosticComponents.map(comp => (
<Grid size={{ sm: 12, lg: 6, xl: 4 }} key={comp.key()}> <Grid size={12} key={comp.key()}>
<ComponentDiagnostics <ComponentDiagnostics
component={comp} component={comp}
device={device} device={device}
@ -423,6 +451,7 @@ export default function DevicePage() {
))} ))}
</Grid> </Grid>
</Grid> </Grid>
}
</> </>
)} )}
</Grid> </Grid>
@ -443,7 +472,8 @@ export default function DevicePage() {
<DeviceActions <DeviceActions
device={device} device={device}
scanFunction={() => { scanFunction={() => {
deviceAPI.detectI2C(device.id()).then(resp => {console.log("No errors")}).catch(err => {console.log("error")}) //deviceAPI.detectI2C(device.id()).then(resp => {console.log("No errors")}).catch(err => {console.log("error")})
deviceAPI.listFoundComponents(device.id()).then(resp => console.log(resp)).catch(err => {console.log("errors")})
}} }}
isPaused={false} isPaused={false}
isLoading={loading} isLoading={loading}

View file

@ -12,7 +12,7 @@ export const I2C: AddressTypeExtension = {
[quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, 0x13], [quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, 0x13],
[quack.ComponentType.COMPONENT_TYPE_PH, 0x3f], [quack.ComponentType.COMPONENT_TYPE_PH, 0x3f],
[quack.ComponentType.COMPONENT_TYPE_DHT, 0x3f], [quack.ComponentType.COMPONENT_TYPE_DHT, 0x3f],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, 0x3f], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, 0x3f], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, 0x4f], [quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, 0x4f],
[quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE, 0x4f], [quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE, 0x4f],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, 0x68], [quack.ComponentType.COMPONENT_TYPE_SEN5X, 0x68],

View file

@ -54,7 +54,7 @@ const DefaultAvailability: DeviceAvailabilityMap = new Map<quack.AddressType, De
[quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, [0x14, 0x15, 0x16, 0x17]], [quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, [0x14, 0x15, 0x16, 0x17]],
[quack.ComponentType.COMPONENT_TYPE_PH, [0x40]], [quack.ComponentType.COMPONENT_TYPE_PH, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40, 0x61]], //TODO temp fix, needs to be fixed to use channels similar to PH [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40, 0x61]], //TODO temp fix, needs to be fixed to use channels similar to PH
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY, [0x58]], [quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY, [0x58]],
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, [0x50]], [quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, [0x50]],

View file

@ -19,7 +19,7 @@ export const BindaptPlusAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -48,7 +48,7 @@ export const BindaptPlusModAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
//[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], //[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -78,7 +78,7 @@ export const BindaptPlusProAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -109,7 +109,7 @@ export const BindaptPlusProModAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
//[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], //[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -136,7 +136,7 @@ export const BindaptPlusLiteAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -192,7 +192,7 @@ export const BindaptUltimateAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -219,7 +219,7 @@ export const BinMonitorAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]] [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]]
]) ])
@ -248,7 +248,7 @@ export const BindaptV2MonitorAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
//[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], //[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
@ -280,7 +280,7 @@ export const BindaptV2AutomateAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
//[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], //[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]], [quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]

View file

@ -64,7 +64,7 @@ const Weather: DeviceProductDescriber = {
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]] [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]]
]) ])
], ],

View file

@ -20,7 +20,7 @@ export const NexusSTAvailability: DeviceAvailabilityMap = new Map<
new Map<quack.ComponentType, number[]>([ new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]], [quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]], [quack.ComponentType.COMPONENT_TYPE_DHT, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], // [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]], [quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]] [quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]]
]) ])

View file

@ -23,6 +23,7 @@ export interface IDeviceAPIContext {
) => Promise<AxiosResponse<pond.GetDevicePageDataResponse>>; ) => Promise<AxiosResponse<pond.GetDevicePageDataResponse>>;
getMulti: (ids: number[] | string[], otherTeam?: string) => Promise<AxiosResponse<pond.GetMultiDeviceResponse>>; getMulti: (ids: number[] | string[], otherTeam?: string) => Promise<AxiosResponse<pond.GetMultiDeviceResponse>>;
detectI2C: (id: number, otherTeam?: string) => Promise<AxiosResponse<pond.DetectI2CResponse>>; detectI2C: (id: number, otherTeam?: string) => Promise<AxiosResponse<pond.DetectI2CResponse>>;
listFoundComponents: (id: number, otherTeam?: string) => Promise<AxiosResponse<pond.ListFoundComponentsResponse>>
getGeoJson: (id: number | string, demo?: boolean, otherTeam?: string) => Promise<any>; getGeoJson: (id: number | string, demo?: boolean, otherTeam?: string) => Promise<any>;
getMultiGeoJson: (ids: number[] | string[], otherTeam?: string) => Promise<any>; getMultiGeoJson: (ids: number[] | string[], otherTeam?: string) => Promise<any>;
list: ( list: (
@ -960,6 +961,19 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
}) })
} }
const listFoundComponents = (id: number, otherTeam?: string) => {
let url = "/devices/" + id + "/listScannedComponents";
const view = otherTeam ? otherTeam : as
if(view) url = url + "?as=" + view
return new Promise<AxiosResponse<pond.ListFoundComponentsResponse>>((resolve, reject) => {
get<pond.ListFoundComponentsResponse>(pondURL(url)).then(resp => {
return resolve(resp)
}).catch(err => {
return reject(err)
})
})
}
return ( return (
<DeviceAPIContext.Provider <DeviceAPIContext.Provider
value={{ value={{
@ -1006,7 +1020,8 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
buyData, buyData,
updateComponentPreferences, updateComponentPreferences,
listDeviceComponentPreferences, listDeviceComponentPreferences,
detectI2C detectI2C,
listFoundComponents
}}> }}>
{children} {children}
</DeviceAPIContext.Provider> </DeviceAPIContext.Provider>