intercom tab
This commit is contained in:
parent
324d97c0ce
commit
48edd149de
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
data/www/js/179.e21a.js.gz
Normal file
BIN
data/www/js/179.e21a.js.gz
Normal file
Binary file not shown.
Binary file not shown.
@ -11,7 +11,7 @@ import { Layout, RequireAdmin } from './components';
|
|||||||
import ProjectRouting from './project/ProjectRouting';
|
import ProjectRouting from './project/ProjectRouting';
|
||||||
|
|
||||||
import NetworkConnection from './framework/network/NetworkConnection';
|
import NetworkConnection from './framework/network/NetworkConnection';
|
||||||
import AccessPoint from './framework/ap/AccessPoint';
|
import Intercom from './framework/intercom/Intercom';
|
||||||
import NetworkTime from './framework/ntp/NetworkTime';
|
import NetworkTime from './framework/ntp/NetworkTime';
|
||||||
import Mqtt from './framework/mqtt/Mqtt';
|
import Mqtt from './framework/mqtt/Mqtt';
|
||||||
import System from './framework/system/System';
|
import System from './framework/system/System';
|
||||||
@ -42,7 +42,7 @@ const AuthenticatedRouting: FC = () => {
|
|||||||
<Route path={`/${PROJECT_PATH}/*`} element={<ProjectRouting />} />
|
<Route path={`/${PROJECT_PATH}/*`} element={<ProjectRouting />} />
|
||||||
)}
|
)}
|
||||||
<Route path="/network/*" element={<NetworkConnection />} />
|
<Route path="/network/*" element={<NetworkConnection />} />
|
||||||
<Route path="/intercom/*" element={<AccessPoint />} />
|
<Route path="/intercom/*" element={<Intercom />} />
|
||||||
{features.ntp && (
|
{features.ntp && (
|
||||||
<Route path="/ntp/*" element={<NetworkTime />} />
|
<Route path="/ntp/*" element={<NetworkTime />} />
|
||||||
)}
|
)}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { AxiosPromise } from "axios";
|
|
||||||
|
|
||||||
import { APSettings, APStatus } from "../types";
|
|
||||||
import { AXIOS } from "./endpoints";
|
|
||||||
|
|
||||||
export function readAPStatus(): AxiosPromise<APStatus> {
|
|
||||||
return AXIOS.get('/intercomStatus');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readAPSettings(): AxiosPromise<APSettings> {
|
|
||||||
return AXIOS.get('/intercomSettings');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateAPSettings(apSettings: APSettings): AxiosPromise<APSettings> {
|
|
||||||
return AXIOS.post('/intercomSettings', apSettings);
|
|
||||||
}
|
|
24
interface/src/api/intercom.ts
Normal file
24
interface/src/api/intercom.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { AxiosPromise } from "axios";
|
||||||
|
|
||||||
|
import { IntercomJournal, IntercomSettings, IntercomStatus, SwitchDoorDTO } from "../types";
|
||||||
|
import { AXIOS } from "./endpoints";
|
||||||
|
|
||||||
|
export function readIntercomStatus(): AxiosPromise<IntercomStatus> {
|
||||||
|
return AXIOS.get('/intercomStatus');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readIntercomJournal(): AxiosPromise<IntercomJournal> {
|
||||||
|
return AXIOS.get('/intercomJournal');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readIntercomSettings(): AxiosPromise<IntercomSettings> {
|
||||||
|
return AXIOS.get('/intercomSettings');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchDoor(switchDoorDTO: SwitchDoorDTO): AxiosPromise<IntercomSettings> {
|
||||||
|
return AXIOS.post('/switchDoor', switchDoorDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateIntercomSettings(intercomSettings: IntercomSettings): AxiosPromise<IntercomSettings> {
|
||||||
|
return AXIOS.post('/intercomSettings', intercomSettings);
|
||||||
|
}
|
@ -1,171 +0,0 @@
|
|||||||
import { FC, useState } from 'react';
|
|
||||||
import { ValidateFieldsError } from 'async-validator';
|
|
||||||
import { range } from 'lodash';
|
|
||||||
|
|
||||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
|
||||||
|
|
||||||
import * as APApi from "../../api/ap";
|
|
||||||
import { APProvisionMode, APSettings } from '../../types';
|
|
||||||
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedPasswordField, ValidatedTextField } from '../../components';
|
|
||||||
import { createAPSettingsValidator, validate } from '../../validators';
|
|
||||||
import { numberValue, updateValue, useRest } from '../../utils';
|
|
||||||
|
|
||||||
export const isAPEnabled = ({ provision_mode }: APSettings) => {
|
|
||||||
return provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
|
||||||
};
|
|
||||||
|
|
||||||
const APSettingsForm: FC = () => {
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
|
||||||
const {
|
|
||||||
loadData, saving, data, setData, saveData, errorMessage
|
|
||||||
} = useRest<APSettings>({ read: APApi.readAPSettings, update: APApi.updateAPSettings });
|
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
|
||||||
|
|
||||||
const content = () => {
|
|
||||||
if (!data) {
|
|
||||||
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateAndSubmit = async () => {
|
|
||||||
try {
|
|
||||||
setFieldErrors(undefined);
|
|
||||||
await validate(createAPSettingsValidator(data), data);
|
|
||||||
saveData();
|
|
||||||
} catch (errors: any) {
|
|
||||||
setFieldErrors(errors);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="provision_mode"
|
|
||||||
label="Provide Access Point…"
|
|
||||||
value={data.provision_mode}
|
|
||||||
fullWidth
|
|
||||||
select
|
|
||||||
variant="outlined"
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
>
|
|
||||||
<MenuItem value={APProvisionMode.AP_MODE_ALWAYS}>Always</MenuItem>
|
|
||||||
<MenuItem value={APProvisionMode.AP_MODE_DISCONNECTED}>When WiFi Disconnected</MenuItem>
|
|
||||||
<MenuItem value={APProvisionMode.AP_NEVER}>Never</MenuItem>
|
|
||||||
</ValidatedTextField>
|
|
||||||
{
|
|
||||||
isAPEnabled(data) &&
|
|
||||||
<>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="ssid"
|
|
||||||
label="Access Point SSID"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.ssid}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
<ValidatedPasswordField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="password"
|
|
||||||
label="Access Point Password"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.password}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="channel"
|
|
||||||
label="Preferred Channel"
|
|
||||||
value={numberValue(data.channel)}
|
|
||||||
fullWidth
|
|
||||||
select
|
|
||||||
type="number"
|
|
||||||
variant="outlined"
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
>
|
|
||||||
{
|
|
||||||
range(1, 14).map((i) => <MenuItem key={i} value={i}>{i}</MenuItem>)
|
|
||||||
}
|
|
||||||
</ValidatedTextField>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
name="ssid_hidden"
|
|
||||||
checked={data.ssid_hidden}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Hide SSID?"
|
|
||||||
/>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="max_clients"
|
|
||||||
label="Max Clients"
|
|
||||||
value={numberValue(data.max_clients)}
|
|
||||||
fullWidth
|
|
||||||
select
|
|
||||||
type="number"
|
|
||||||
variant="outlined"
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
>
|
|
||||||
{
|
|
||||||
range(1, 9).map((i) => <MenuItem key={i} value={i}>{i}</MenuItem>)
|
|
||||||
}
|
|
||||||
</ValidatedTextField>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="local_ip"
|
|
||||||
label="Local IP"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.local_ip}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="gateway_ip"
|
|
||||||
label="Gateway"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.gateway_ip}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="subnet_mask"
|
|
||||||
label="Subnet"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.subnet_mask}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
<ButtonRow mt={1}>
|
|
||||||
<Button startIcon={<SaveIcon />} disabled={saving} variant="contained" color="primary" type="submit" onClick={validateAndSubmit}>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionContent title='Access Point Settings' titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default APSettingsForm;
|
|
@ -1,105 +0,0 @@
|
|||||||
import { FC } from "react";
|
|
||||||
|
|
||||||
import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, Theme, useTheme } from "@mui/material";
|
|
||||||
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
|
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
|
||||||
import ComputerIcon from '@mui/icons-material/Computer';
|
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
||||||
|
|
||||||
import * as APApi from "../../api/ap";
|
|
||||||
import { APNetworkStatus, APStatus } from "../../types";
|
|
||||||
import { ButtonRow, FormLoader, SectionContent } from "../../components";
|
|
||||||
import { useRest } from "../../utils";
|
|
||||||
|
|
||||||
export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
|
|
||||||
switch (status) {
|
|
||||||
case APNetworkStatus.ACTIVE:
|
|
||||||
return theme.palette.success.main;
|
|
||||||
case APNetworkStatus.INACTIVE:
|
|
||||||
return theme.palette.info.main;
|
|
||||||
case APNetworkStatus.LINGERING:
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
default:
|
|
||||||
return theme.palette.warning.main;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const apStatus = ({ status }: APStatus) => {
|
|
||||||
switch (status) {
|
|
||||||
case APNetworkStatus.ACTIVE:
|
|
||||||
return "Active";
|
|
||||||
case APNetworkStatus.INACTIVE:
|
|
||||||
return "Inactive";
|
|
||||||
case APNetworkStatus.LINGERING:
|
|
||||||
return "Lingering until idle";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const APStatusForm: FC = () => {
|
|
||||||
const { loadData, data, errorMessage } = useRest<APStatus>({ read: APApi.readAPStatus });
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const content = () => {
|
|
||||||
if (!data) {
|
|
||||||
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar sx={{ bgcolor: apStatusHighlight(data, theme) }}>
|
|
||||||
<SettingsInputAntennaIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="Status" secondary={apStatus(data)} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>IP</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="IP Address" secondary={data.ip_address} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<DeviceHubIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="MAC Address" secondary={data.mac_address} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
<ListItem>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar>
|
|
||||||
<ComputerIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary="AP Clients" secondary={data.station_num} />
|
|
||||||
</ListItem>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
</List>
|
|
||||||
<ButtonRow pt={1}>
|
|
||||||
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={loadData}>
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionContent title='Access Point Status' titleGutter>
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default APStatusForm;
|
|
@ -6,10 +6,11 @@ import { Tab } from '@mui/material';
|
|||||||
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from '../../components';
|
import { RequireAdmin, RouterTabs, useLayoutTitle, useRouterTab } from '../../components';
|
||||||
import { AuthenticatedContext } from '../../contexts/authentication';
|
import { AuthenticatedContext } from '../../contexts/authentication';
|
||||||
|
|
||||||
import APStatusForm from './APStatusForm';
|
import IntercomStatusForm from './IntercomStatusForm';
|
||||||
import APSettingsForm from './APSettingsForm';
|
import IntercomJournalForm from './IntercomJournalForm';
|
||||||
|
import IntercomSettingsForm from './IntercomSettingsForm';
|
||||||
|
|
||||||
const AccessPoint: FC = () => {
|
const Intercom: FC = () => {
|
||||||
useLayoutTitle("Access Point");
|
useLayoutTitle("Access Point");
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
const authenticatedContext = useContext(AuthenticatedContext);
|
||||||
@ -19,15 +20,17 @@ const AccessPoint: FC = () => {
|
|||||||
<>
|
<>
|
||||||
<RouterTabs value={routerTab}>
|
<RouterTabs value={routerTab}>
|
||||||
<Tab value="status" label="Intercom Status" />
|
<Tab value="status" label="Intercom Status" />
|
||||||
|
<Tab value="journal" label="Intercom Journal" />
|
||||||
<Tab value="settings" label="Intercom Settings" disabled={!authenticatedContext.me.admin} />
|
<Tab value="settings" label="Intercom Settings" disabled={!authenticatedContext.me.admin} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="status" element={<APStatusForm />} />
|
<Route path="status" element={<IntercomStatusForm />} />
|
||||||
|
<Route path="journal" element={<IntercomJournalForm />} />
|
||||||
<Route
|
<Route
|
||||||
path="settings"
|
path="settings"
|
||||||
element={
|
element={
|
||||||
<RequireAdmin>
|
<RequireAdmin>
|
||||||
<APSettingsForm />
|
<IntercomSettingsForm />
|
||||||
</RequireAdmin>
|
</RequireAdmin>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -38,4 +41,4 @@ const AccessPoint: FC = () => {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccessPoint;
|
export default Intercom;
|
63
interface/src/framework/intercom/IntercomJournalForm.tsx
Normal file
63
interface/src/framework/intercom/IntercomJournalForm.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
import { Button, Divider, List, Paper,
|
||||||
|
Table, TableBody, TableCell, TableContainer,
|
||||||
|
TableHead, TableRow, useTheme } from "@mui/material";
|
||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
|
||||||
|
import * as IntercomApi from "../../api/intercom";
|
||||||
|
import { IntercomJournal } from "../../types";
|
||||||
|
import { ButtonRow, FormLoader, SectionContent } from "../../components";
|
||||||
|
import { useRest } from "../../utils";
|
||||||
|
|
||||||
|
const IntercomJournalForm: FC = () => {
|
||||||
|
const { loadData, data, errorMessage } = useRest<IntercomJournal>({ read: IntercomApi.readIntercomJournal });
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Date</TableCell>
|
||||||
|
<TableCell>Apartment</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{data.notes.map((note, index) => (
|
||||||
|
<TableRow key={index}>
|
||||||
|
<TableCell>{note.createdAt}</TableCell>
|
||||||
|
<TableCell>{note.appartment}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
</List>
|
||||||
|
<ButtonRow pt={1}>
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={loadData}>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContent title='Intercom Journal' titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IntercomJournalForm;
|
83
interface/src/framework/intercom/IntercomSettingsForm.tsx
Normal file
83
interface/src/framework/intercom/IntercomSettingsForm.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { FC, useState } from 'react';
|
||||||
|
import { ValidateFieldsError } from 'async-validator';
|
||||||
|
|
||||||
|
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||||
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
|
||||||
|
import * as IntercomApi from "../../api/intercom";
|
||||||
|
import { IntercomSettings } from '../../types';
|
||||||
|
import { ButtonRow, FormLoader, SectionContent, ValidatedTextField } from '../../components';
|
||||||
|
import { createIntercomSettingsValidator, validate } from '../../validators';
|
||||||
|
import { updateValue, useRest } from '../../utils';
|
||||||
|
|
||||||
|
const IntercomSettingsForm: FC = () => {
|
||||||
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
const {
|
||||||
|
loadData, saving, data, setData, saveData, errorMessage
|
||||||
|
} = useRest<IntercomSettings>({ read: IntercomApi.readIntercomSettings, update: IntercomApi.updateIntercomSettings });
|
||||||
|
|
||||||
|
const updateFormValue = updateValue(setData);
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateAndSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setFieldErrors(undefined);
|
||||||
|
await validate(createIntercomSettingsValidator(data), data);
|
||||||
|
saveData();
|
||||||
|
} catch (errors: any) {
|
||||||
|
setFieldErrors(errors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors}
|
||||||
|
name="kmnModel"
|
||||||
|
label="KMN Model"
|
||||||
|
value={data.kmnModel}
|
||||||
|
fullWidth
|
||||||
|
select
|
||||||
|
variant="outlined"
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
data.kmnModelList.map((item) => (
|
||||||
|
<MenuItem key={item} value={item}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ValidatedTextField>
|
||||||
|
<ValidatedTextField
|
||||||
|
fieldErrors={fieldErrors}
|
||||||
|
name="firstAppartment"
|
||||||
|
label="First Appartment"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.firstAppartment}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
<ButtonRow mt={1}>
|
||||||
|
<Button startIcon={<SaveIcon />} disabled={saving} variant="contained" color="primary" type="submit" onClick={validateAndSubmit}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContent title='Intercom Settings' titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IntercomSettingsForm;
|
165
interface/src/framework/intercom/IntercomStatusForm.tsx
Normal file
165
interface/src/framework/intercom/IntercomStatusForm.tsx
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
import { Avatar, Box, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, Theme, useTheme } from "@mui/material";
|
||||||
|
import PowerIcon from '@mui/icons-material/Power';
|
||||||
|
import PowerOffIcon from '@mui/icons-material/PowerOff';
|
||||||
|
import UpdateIcon from '@mui/icons-material/Update';
|
||||||
|
import DoorFrontIcon from '@mui/icons-material/DoorFront';
|
||||||
|
import MeetingRoomIcon from '@mui/icons-material/MeetingRoom';
|
||||||
|
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
|
||||||
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||||
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
|
||||||
|
import * as IntercomApi from "../../api/intercom";
|
||||||
|
import { DoorStatus, IntercomConnectionStatus, IntercomStatus, SwitchDoorType } from "../../types";
|
||||||
|
import { ButtonRow, FormLoader, SectionContent } from "../../components";
|
||||||
|
import { useRest } from "../../utils";
|
||||||
|
import {useSnackbar} from "notistack";
|
||||||
|
|
||||||
|
export const intercomStatusHighlight = ({ status }: IntercomStatus, theme: Theme) => {
|
||||||
|
switch (status) {
|
||||||
|
case IntercomConnectionStatus.CONNECTED:
|
||||||
|
return theme.palette.success.main;
|
||||||
|
case IntercomConnectionStatus.NOT_CONNECTED:
|
||||||
|
return theme.palette.info.main;
|
||||||
|
case IntercomConnectionStatus.RECEIVING_DATA:
|
||||||
|
return theme.palette.success.main;
|
||||||
|
default:
|
||||||
|
return theme.palette.warning.main;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const intercomStatusIcon = (status: IntercomConnectionStatus) => {
|
||||||
|
switch (status) {
|
||||||
|
case IntercomConnectionStatus.CONNECTED:
|
||||||
|
return <PowerIcon />;
|
||||||
|
case IntercomConnectionStatus.NOT_CONNECTED:
|
||||||
|
return <PowerOffIcon />;
|
||||||
|
case IntercomConnectionStatus.RECEIVING_DATA:
|
||||||
|
return <UpdateIcon />;
|
||||||
|
default:
|
||||||
|
return <QuestionMarkIcon />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doorStatusIcon = (status: DoorStatus) => {
|
||||||
|
switch (status) {
|
||||||
|
case DoorStatus.CLOSED:
|
||||||
|
return <DoorFrontIcon />;
|
||||||
|
case DoorStatus.OPENED:
|
||||||
|
return <MeetingRoomIcon />;
|
||||||
|
default:
|
||||||
|
return <QuestionMarkIcon />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const intercomStatus = ({ status }: IntercomStatus) => {
|
||||||
|
switch (status) {
|
||||||
|
case IntercomConnectionStatus.CONNECTED:
|
||||||
|
return "Active";
|
||||||
|
case IntercomConnectionStatus.NOT_CONNECTED:
|
||||||
|
return "Inactive";
|
||||||
|
case IntercomConnectionStatus.RECEIVING_DATA:
|
||||||
|
return "Receiving data";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doorStatus = (status: DoorStatus) => {
|
||||||
|
switch (status) {
|
||||||
|
case DoorStatus.OPENED:
|
||||||
|
return "Opened";
|
||||||
|
case DoorStatus.CLOSED:
|
||||||
|
return "Closed";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const openDoor = () => {
|
||||||
|
IntercomApi.switchDoor({
|
||||||
|
type: SwitchDoorType.JOGGING,
|
||||||
|
status: DoorStatus.OPENED,
|
||||||
|
time: 1
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
enqueueSnackbar("Update successful", { variant: 'success' });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const IntercomStatusForm: FC = () => {
|
||||||
|
const { loadData, data, errorMessage } = useRest<IntercomStatus>({ read: IntercomApi.readIntercomStatus });
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const content = () => {
|
||||||
|
if (!data) {
|
||||||
|
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar sx={{ bgcolor: intercomStatusHighlight(data, theme) }}>
|
||||||
|
{intercomStatusIcon(data.status)}
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Status" secondary={intercomStatus(data)} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar>
|
||||||
|
<NotificationsIcon />
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Last Called Number" secondary={data.lastCalledNumber} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
<ListItem>
|
||||||
|
<ListItemAvatar>
|
||||||
|
<Avatar>
|
||||||
|
{doorStatusIcon(data.doorStatus)}
|
||||||
|
</Avatar>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary="Door Status" secondary={doorStatus(data.doorStatus)} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Box display="flex" flexWrap="wrap">
|
||||||
|
<Box flexGrow={1}>
|
||||||
|
<ButtonRow mt={1}>
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={loadData}>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
|
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||||
|
<ButtonRow mt={1}>
|
||||||
|
<Button startIcon={<MeetingRoomIcon />} variant="contained" color="info" onClick={openDoor}>
|
||||||
|
Open door
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContent title='Intercom Status' titleGutter>
|
||||||
|
{content()}
|
||||||
|
</SectionContent>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IntercomStatusForm;
|
@ -1,18 +1,15 @@
|
|||||||
import { FC, useContext, useEffect, useState } from 'react';
|
import { FC, useContext, useEffect, useState } from 'react';
|
||||||
import { ValidateFieldsError } from 'async-validator';
|
import { ValidateFieldsError } from 'async-validator';
|
||||||
|
|
||||||
import { Avatar, Button, Checkbox, IconButton, List, ListItem, ListItemAvatar, ListItemSecondaryAction, ListItemText } from '@mui/material';
|
import { Button, Checkbox } from '@mui/material';
|
||||||
import LockOpenIcon from '@mui/icons-material/LockOpen';
|
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
|
||||||
import * as NetworkApi from "../../api/network";
|
import * as NetworkApi from "../../api/network";
|
||||||
import { NetworkSettings } from '../../types';
|
import { NetworkSettings } from '../../types';
|
||||||
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedPasswordField, ValidatedTextField } from '../../components';
|
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedTextField } from '../../components';
|
||||||
import { validate, createNetworkSettingsValidator } from '../../validators';
|
import { validate, createNetworkSettingsValidator } from '../../validators';
|
||||||
import { updateValue, useRest } from '../../utils';
|
import { updateValue, useRest } from '../../utils';
|
||||||
|
|
||||||
import { isNetworkOpen, networkSecurityMode } from './NetworkSelector';
|
|
||||||
import { NetworkConnectionContext } from './NetworkConnectionContext';
|
import { NetworkConnectionContext } from './NetworkConnectionContext';
|
||||||
|
|
||||||
const NetworkSettingsForm: FC = () => {
|
const NetworkSettingsForm: FC = () => {
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
export enum APProvisionMode {
|
|
||||||
AP_MODE_ALWAYS = 0,
|
|
||||||
AP_MODE_DISCONNECTED = 1,
|
|
||||||
AP_NEVER = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum APNetworkStatus {
|
|
||||||
ACTIVE = 0,
|
|
||||||
INACTIVE = 1,
|
|
||||||
LINGERING = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface APStatus {
|
|
||||||
status: APNetworkStatus;
|
|
||||||
ip_address: string;
|
|
||||||
mac_address: string;
|
|
||||||
station_num: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface APSettings {
|
|
||||||
provision_mode: APProvisionMode;
|
|
||||||
ssid: string;
|
|
||||||
password: string;
|
|
||||||
channel: number;
|
|
||||||
ssid_hidden: boolean;
|
|
||||||
max_clients: number;
|
|
||||||
local_ip: string;
|
|
||||||
gateway_ip: string;
|
|
||||||
subnet_mask: string;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
export * from './ap';
|
export * from './intercom';
|
||||||
export * from './features';
|
export * from './features';
|
||||||
export * from './me';
|
export * from './me';
|
||||||
export * from './mqtt';
|
export * from './mqtt';
|
||||||
|
43
interface/src/types/intercom.ts
Normal file
43
interface/src/types/intercom.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
export enum IntercomConnectionStatus {
|
||||||
|
CONNECTED = 0,
|
||||||
|
NOT_CONNECTED = 1,
|
||||||
|
RECEIVING_DATA = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DoorStatus {
|
||||||
|
OPENED = 0,
|
||||||
|
CLOSED = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SwitchDoorType {
|
||||||
|
ON_OFF = 0,
|
||||||
|
JOGGING = 1,
|
||||||
|
DELAY = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntercomStatus {
|
||||||
|
status: IntercomConnectionStatus;
|
||||||
|
lastCalledNumber: number;
|
||||||
|
doorStatus: DoorStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntercomJournalNote {
|
||||||
|
createdAt: string;
|
||||||
|
appartment: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntercomJournal {
|
||||||
|
notes: IntercomJournalNote[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntercomSettings {
|
||||||
|
kmnModel: string;
|
||||||
|
kmnModelList: string[];
|
||||||
|
firstAppartment: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwitchDoorDTO {
|
||||||
|
type: SwitchDoorType;
|
||||||
|
status: DoorStatus;
|
||||||
|
time: number;
|
||||||
|
}
|
@ -17,6 +17,7 @@ export const extractEventValue = (event: React.ChangeEvent<HTMLInputElement>) =>
|
|||||||
|
|
||||||
export const updateValue = <S>(updateEntity: UpdateEntity<S>) => (
|
export const updateValue = <S>(updateEntity: UpdateEntity<S>) => (
|
||||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
console.log(event.target.name);
|
||||||
updateEntity((prevState) => ({
|
updateEntity((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
[event.target.name]: extractEventValue(event)
|
[event.target.name]: extractEventValue(event)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export * from './ap';
|
export * from './intercom';
|
||||||
export * from './authentication';
|
export * from './authentication';
|
||||||
export * from './mqtt';
|
export * from './mqtt';
|
||||||
export * from './ntp';
|
export * from './ntp';
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import Schema from 'async-validator';
|
import Schema from 'async-validator';
|
||||||
|
|
||||||
import { APSettings } from '../types';
|
import { IntercomSettings } from '../types';
|
||||||
import { isAPEnabled } from '../framework/ap/APSettingsForm';
|
|
||||||
|
|
||||||
import { IP_ADDRESS_VALIDATOR } from './shared';
|
import { IP_ADDRESS_VALIDATOR } from './shared';
|
||||||
|
|
||||||
export const createAPSettingsValidator = (apSettings: APSettings) => new Schema({
|
export const createIntercomSettingsValidator = (intercomSettings: IntercomSettings) => new Schema({
|
||||||
provision_mode: { required: true, message: "Please provide a provision mode" },
|
provision_mode: { required: true, message: "Please provide a provision mode" },
|
||||||
...(isAPEnabled(apSettings) && {
|
...({
|
||||||
ssid: [
|
ssid: [
|
||||||
{ required: true, message: "Please provide an SSID" },
|
{ required: true, message: "Please provide an SSID" },
|
||||||
{ type: "string", max: 32, message: "SSID must be 32 characters or less" }
|
{ type: "string", max: 32, message: "SSID must be 32 characters or less" }
|
20457
lib/framework/WWWData.h
20457
lib/framework/WWWData.h
File diff suppressed because it is too large
Load Diff
@ -23,3 +23,4 @@ lib_deps =
|
|||||||
ottowinter/AsyncMqttClient-esphome@^0.8.6
|
ottowinter/AsyncMqttClient-esphome@^0.8.6
|
||||||
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
|
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
|
||||||
bblanchon/ArduinoJson@^6.21.3
|
bblanchon/ArduinoJson@^6.21.3
|
||||||
|
paulstoffregen/Time@^1.6.1
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#include "app/routes.h"
|
#include "app/routes.h"
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "utils/print.h"
|
||||||
|
#include "utils/settings.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
#include "infra/eth.h"
|
#include "infra/eth.h"
|
||||||
#include "infra/httpServer.h"
|
#include "infra/httpServer.h"
|
||||||
#include "infra/mqtt.h"
|
#include "infra/mqtt.h"
|
||||||
#include "infra/relay.h"
|
#include "infra/relay.h"
|
||||||
#include "infra/fs.h"
|
#include "infra/fs.h"
|
||||||
#include "config/config.h"
|
#include "infra/intercom.h"
|
||||||
#include "utils/print.h"
|
#include "domain/intercomJournal.h"
|
||||||
#include "utils/settings.h"
|
|
||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
void handleDoorOpen(AsyncWebServerRequest *request) {
|
void handleDoorOpen(AsyncWebServerRequest *request) {
|
||||||
relayTurnOn();
|
relayTurnOn();
|
||||||
@ -37,15 +39,103 @@ void intercomStatus(AsyncWebServerRequest* request) {
|
|||||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_STATUS_SIZE);
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_STATUS_SIZE);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
root["status"] = 0;
|
root["status"] = (int) getIntercomStatus();
|
||||||
root["ip_address"] = ETH.localIP().toString();
|
root["lastCalledNumber"] = getLastCalledNumber();
|
||||||
root["mac_address"] = ETH.macAddress();
|
root["doorStatus"] = false;
|
||||||
root["station_num"] = WiFi.softAPgetStationNum();
|
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void intercomSettingsRead(AsyncWebServerRequest* request) {
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_SETTINGS_SIZE);
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
|
const size_t CAPACITY = JSON_ARRAY_SIZE(3);
|
||||||
|
StaticJsonDocument<CAPACITY> modelsDoc;
|
||||||
|
|
||||||
|
JsonArray models = modelsDoc.to<JsonArray>();
|
||||||
|
models.add("Vizit");
|
||||||
|
models.add("Cyfral");
|
||||||
|
|
||||||
|
root["kmnModel"] = "Vizit";
|
||||||
|
root["firstAppartment"] = 1;
|
||||||
|
root["kmnModelList"] = models;
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intercomJournalRead(AsyncWebServerRequest* request) {
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_JOURNAL_SIZE);
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
|
const size_t CAPACITY = JSON_ARRAY_SIZE(5*INTERCOM_JOURNAL_FLATS_NUMBER);
|
||||||
|
StaticJsonDocument<CAPACITY> flatsDoc;
|
||||||
|
|
||||||
|
JsonArray flats = flatsDoc.to<JsonArray>();
|
||||||
|
getIntercomJournalAsJson(flats);
|
||||||
|
|
||||||
|
root["notes"] = flats;
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intercomSettingsUpdate(AsyncWebServerRequest *request,
|
||||||
|
uint8_t *data, size_t len, size_t index, size_t total) {
|
||||||
|
DynamicJsonDocument jsonDoc(MAX_INTERCOM_SETTINGS_SIZE);
|
||||||
|
String jsonStr = requestDataToStr(data, len);
|
||||||
|
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
|
||||||
|
|
||||||
|
if (!jsonDoc.is<JsonVariant>()) {
|
||||||
|
request->send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonVariant root = jsonDoc.as<JsonVariant>();
|
||||||
|
|
||||||
|
bool success = writeJsonVariantToFile(INTERCOM_SETTINGS_PATH, root);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
String jsonString;
|
||||||
|
serializeJson(root, jsonString);
|
||||||
|
request->send(200, "application/json", jsonString.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
request->send(500, "text/plain", "Intercom settings not updated");
|
||||||
|
|
||||||
|
configureIntercom(
|
||||||
|
root["kmnModel"].as<String>(),
|
||||||
|
root["firstAppartment"].as<int>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void switchDoor(AsyncWebServerRequest *request,
|
||||||
|
uint8_t *data, size_t len, size_t index, size_t total) {
|
||||||
|
DynamicJsonDocument jsonDoc(MAX_INTERCOM_SWITCH_DOOR_SIZE);
|
||||||
|
String jsonStr = requestDataToStr(data, len);
|
||||||
|
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
|
||||||
|
|
||||||
|
if (!jsonDoc.is<JsonVariant>()) {
|
||||||
|
request->send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonVariant root = jsonDoc.as<JsonVariant>();
|
||||||
|
|
||||||
|
SwitchDoorType switchDoorType = static_cast<SwitchDoorType>(root["type"].as<int>());
|
||||||
|
DoorStatus doorStatus = static_cast<DoorStatus>(root["status"].as<int>());
|
||||||
|
|
||||||
|
switchRelay(
|
||||||
|
switchDoorType,
|
||||||
|
doorStatus,
|
||||||
|
root["time"]
|
||||||
|
);
|
||||||
|
|
||||||
|
request->send(200, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
void networkStatus(AsyncWebServerRequest* request) {
|
void networkStatus(AsyncWebServerRequest* request) {
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NETWORK_STATUS_SIZE);
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NETWORK_STATUS_SIZE);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
@ -244,8 +334,10 @@ void initRoutes() {
|
|||||||
server.on("/api/v1/networkSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, networkSettingsUpdate);
|
server.on("/api/v1/networkSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, networkSettingsUpdate);
|
||||||
|
|
||||||
server.on("/api/v1/intercomStatus", intercomStatus);
|
server.on("/api/v1/intercomStatus", intercomStatus);
|
||||||
//server.on("/api/v1/intercomSettings", HTTP_GET, intercomSettingsRead);
|
server.on("/api/v1/intercomJournal", intercomJournalRead);
|
||||||
//server.on("/api/v1/intercomSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, intercomSettingsUpdate);
|
server.on("/api/v1/intercomSettings", HTTP_GET, intercomSettingsRead);
|
||||||
|
server.on("/api/v1/intercomSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, intercomSettingsUpdate);
|
||||||
|
server.on("/api/v1/switchDoor", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, switchDoor);
|
||||||
|
|
||||||
server.on("/api/v1/mqttStatus", mqttStatus);
|
server.on("/api/v1/mqttStatus", mqttStatus);
|
||||||
server.on("/api/v1/mqttSettings", HTTP_GET, mqttSettingsRead);
|
server.on("/api/v1/mqttSettings", HTTP_GET, mqttSettingsRead);
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#define FLAT_NUMBER_MQTT_TOPIC "flat_number"
|
#define FLAT_NUMBER_MQTT_TOPIC "flat_number"
|
||||||
#define STATE_MQTT_TOPIC "state"
|
#define STATE_MQTT_TOPIC "state"
|
||||||
|
|
||||||
|
#define INTERCOM_JOURNAL_FLATS_NUMBER 100
|
||||||
|
|
||||||
#define LED_PIN 32
|
#define LED_PIN 32
|
||||||
#define DRY_CONT_PIN 15
|
#define DRY_CONT_PIN 15
|
||||||
#define DOOR_SENS_PIN 114
|
#define DOOR_SENS_PIN 114
|
||||||
@ -40,10 +42,14 @@
|
|||||||
#define MAX_NETWORK_STATUS_SIZE 1024
|
#define MAX_NETWORK_STATUS_SIZE 1024
|
||||||
#define MAX_NETWORK_SETTINGS_SIZE 1024
|
#define MAX_NETWORK_SETTINGS_SIZE 1024
|
||||||
#define MAX_INTERCOM_STATUS_SIZE 1024
|
#define MAX_INTERCOM_STATUS_SIZE 1024
|
||||||
|
#define MAX_INTERCOM_JOURNAL_SIZE 1024
|
||||||
|
#define MAX_INTERCOM_SETTINGS_SIZE 1024
|
||||||
|
#define MAX_INTERCOM_SWITCH_DOOR_SIZE 1024
|
||||||
#define MAX_MQTT_STATUS_SIZE 1024
|
#define MAX_MQTT_STATUS_SIZE 1024
|
||||||
#define MAX_MQTT_SETTINGS_SIZE 1024
|
#define MAX_MQTT_SETTINGS_SIZE 1024
|
||||||
#define MAX_ESP_STATUS_SIZE 1024
|
#define MAX_ESP_STATUS_SIZE 1024
|
||||||
|
|
||||||
#define FS_CONFIG_DIRECTORY "/config"
|
#define FS_CONFIG_DIRECTORY "/config"
|
||||||
#define NETWORK_SETTINGS_PATH "/config/networkSettings.json"
|
#define NETWORK_SETTINGS_PATH "/config/networkSettings.json"
|
||||||
|
#define INTERCOM_SETTINGS_PATH "/config/intercomSettings.json"
|
||||||
#define MQTT_SETTINGS_PATH "/config/mqttSettings.json"
|
#define MQTT_SETTINGS_PATH "/config/mqttSettings.json"
|
31
src/domain/intercomJournal.cpp
Normal file
31
src/domain/intercomJournal.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "domain/intercomJournal.h"
|
||||||
|
#include "utils/print.h"
|
||||||
|
|
||||||
|
IntercomJournal _intercomJournal;
|
||||||
|
|
||||||
|
void addFlatToIntercomJournal(unsigned long createdAt, int appartment) {
|
||||||
|
for (int i = INTERCOM_JOURNAL_FLATS_NUMBER - 1; i > 0; --i) {
|
||||||
|
_intercomJournal.notes[i] = _intercomJournal.notes[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
_intercomJournal.notes[0].createdAt = createdAt;
|
||||||
|
_intercomJournal.notes[0].appartment = appartment;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray getIntercomJournalAsJson(JsonArray& flats) {
|
||||||
|
for (int i = 0; i < INTERCOM_JOURNAL_FLATS_NUMBER; ++i) {
|
||||||
|
unsigned long createdAt = _intercomJournal.notes[i].createdAt;
|
||||||
|
int& appartment = _intercomJournal.notes[i].appartment;
|
||||||
|
|
||||||
|
if (createdAt && appartment) {
|
||||||
|
unsigned long timeDifference = timeModule.getTimeDifference(createdAt);
|
||||||
|
String formatedTimeDifference = "\n(" + timeModule.getFormattedTimeAgo(timeDifference) + ")";
|
||||||
|
|
||||||
|
JsonObject flat = flats.createNestedObject();
|
||||||
|
flat["createdAt"] = timeModule.getFormattedTime(createdAt) + formatedTimeDifference;
|
||||||
|
flat["appartment"] = appartment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flats;
|
||||||
|
}
|
19
src/domain/intercomJournal.h
Normal file
19
src/domain/intercomJournal.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "utils/time.h"
|
||||||
|
|
||||||
|
extern Time& timeModule;
|
||||||
|
|
||||||
|
struct Flat {
|
||||||
|
unsigned long createdAt;
|
||||||
|
int appartment;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntercomJournal {
|
||||||
|
Flat notes[INTERCOM_JOURNAL_FLATS_NUMBER];
|
||||||
|
};
|
||||||
|
|
||||||
|
void addFlatToIntercomJournal(unsigned long createdAt, int appartment);
|
||||||
|
JsonArray getIntercomJournalAsJson(JsonArray& flats);
|
@ -1,179 +0,0 @@
|
|||||||
#include "utils/print.h"
|
|
||||||
#include "config/config.h"
|
|
||||||
#include "infra/mqtt.h"
|
|
||||||
#include "infra/led.h"
|
|
||||||
#include "domain/stateMachine.h"
|
|
||||||
|
|
||||||
State currentState = NOT_CONNECTED;
|
|
||||||
int countZeros = 0;
|
|
||||||
int countOnes = 0;
|
|
||||||
|
|
||||||
int previousData = 0;
|
|
||||||
int dataLength = 0;
|
|
||||||
int signalDuration = 0;
|
|
||||||
|
|
||||||
int flat = 0;
|
|
||||||
|
|
||||||
void resetCounters() {
|
|
||||||
countZeros = 0;
|
|
||||||
countOnes = 0;
|
|
||||||
|
|
||||||
previousData = 0;
|
|
||||||
dataLength = 1;
|
|
||||||
signalDuration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeState(char* message) {
|
|
||||||
println(message);
|
|
||||||
publishToMQTT(STATE_MQTT_TOPIC, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void receiveDigit(int data) {
|
|
||||||
if (data != previousData) {
|
|
||||||
if (previousData == HIGH) {
|
|
||||||
//println("AAAA ", dataLength, " ", signalDuration);
|
|
||||||
dataLength++;
|
|
||||||
}
|
|
||||||
signalDuration = 0;
|
|
||||||
} else {
|
|
||||||
signalDuration++;
|
|
||||||
}
|
|
||||||
previousData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void changeState(State state, bool resetCountersFlag=true) {
|
|
||||||
currentState = state;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case NOT_CONNECTED:
|
|
||||||
ledTurnOff();
|
|
||||||
writeState("not connected");
|
|
||||||
break;
|
|
||||||
case CONNECTED:
|
|
||||||
ledTurnOn();
|
|
||||||
writeState("connected");
|
|
||||||
break;
|
|
||||||
case RECEIVING_FIRST_DIGIT:
|
|
||||||
writeState("receiving data");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetCountersFlag)
|
|
||||||
resetCounters();
|
|
||||||
}
|
|
||||||
|
|
||||||
void firstDigitReceived() {
|
|
||||||
println("| 1 data length: ", dataLength);
|
|
||||||
flat = dataLength*10;
|
|
||||||
}
|
|
||||||
|
|
||||||
void secondDigitReceived() {
|
|
||||||
if (dataLength == 11) {
|
|
||||||
dataLength = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
flat += dataLength;
|
|
||||||
flat -= 1;
|
|
||||||
|
|
||||||
if (flat > 100 && flat < 110) {
|
|
||||||
flat -= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
println("| 2 data length: ", dataLength);
|
|
||||||
println("| flat: ", flat);
|
|
||||||
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, flat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateStateMachine(int data) {
|
|
||||||
switch (currentState) {
|
|
||||||
case NOT_CONNECTED:
|
|
||||||
if (data == LOW) {}
|
|
||||||
else if (data == HIGH) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONNECTED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= NOT_CONNECTED_THRESHOLD) {
|
|
||||||
changeState(NOT_CONNECTED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
if (countZeros >= INITIALIZING_CALL_THRESHOLD) {
|
|
||||||
changeState(RECEIVING_FIRST_DIGIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECEIVING_FIRST_DIGIT:
|
|
||||||
receiveDigit(data);
|
|
||||||
|
|
||||||
if (data == LOW) {
|
|
||||||
countOnes = 0;
|
|
||||||
countZeros++;
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countZeros = 0;
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= DATA_RECEIVED_THESHOLD) {
|
|
||||||
firstDigitReceived();
|
|
||||||
changeState(RECEIVING_SECOND_DIGIT);
|
|
||||||
}
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECEIVING_SECOND_DIGIT:
|
|
||||||
receiveDigit(data);
|
|
||||||
|
|
||||||
if (data == LOW) {
|
|
||||||
countOnes = 0;
|
|
||||||
countZeros++;
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countZeros = 0;
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= DATA_RECEIVED_THESHOLD) {
|
|
||||||
secondDigitReceived();
|
|
||||||
changeState(DATA_RECEIVED);
|
|
||||||
}
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DATA_RECEIVED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= CALL_ENDED_THRESHOLD) {
|
|
||||||
changeState(CALL_ENDED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CALL_ENDED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= NOT_CONNECTED_THRESHOLD) {
|
|
||||||
changeState(NOT_CONNECTED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initStateMachine() {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef STATE_MACHINE_H
|
|
||||||
#define STATE_MACHINE_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#define CONNECTED_THRESHOLD 50000
|
|
||||||
#define NOT_CONNECTED_THRESHOLD 50000
|
|
||||||
#define INITIALIZING_CALL_THRESHOLD 45
|
|
||||||
#define DATA_RECEIVED_THESHOLD 5000
|
|
||||||
#define CALL_ENDED_THRESHOLD 10000
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
NOT_CONNECTED,
|
|
||||||
CONNECTED,
|
|
||||||
RECEIVING_FIRST_DIGIT,
|
|
||||||
RECEIVING_SECOND_DIGIT,
|
|
||||||
DATA_RECEIVED,
|
|
||||||
CALL_ENDED
|
|
||||||
};
|
|
||||||
|
|
||||||
void resetCounters();
|
|
||||||
void updateStateMachine(int data);
|
|
||||||
void initStateMachine();
|
|
||||||
|
|
||||||
#endif // STATE_MACHINE_H
|
|
@ -1,148 +0,0 @@
|
|||||||
#include "utils/print.h"
|
|
||||||
#include "config/config.h"
|
|
||||||
#include "infra/mqtt.h"
|
|
||||||
#include "infra/led.h"
|
|
||||||
#include "domain/stateMachine.h"
|
|
||||||
|
|
||||||
State currentState = NOT_CONNECTED;
|
|
||||||
int countZeros = 0;
|
|
||||||
int countOnes = 0;
|
|
||||||
|
|
||||||
int previousData = 0;
|
|
||||||
int dataLength = 0;
|
|
||||||
int signalDuration = 0;
|
|
||||||
|
|
||||||
int flat = 0;
|
|
||||||
|
|
||||||
void resetCounters() {
|
|
||||||
countZeros = 0;
|
|
||||||
countOnes = 0;
|
|
||||||
|
|
||||||
previousData = 0;
|
|
||||||
dataLength = 0;
|
|
||||||
signalDuration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeState(char* message) {
|
|
||||||
println(message);
|
|
||||||
publishToMQTT(STATE_MQTT_TOPIC, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void receiveData(int data) {
|
|
||||||
if (data != previousData) {
|
|
||||||
if (previousData == HIGH) {
|
|
||||||
dataLength++;
|
|
||||||
}
|
|
||||||
signalDuration = 0;
|
|
||||||
} else {
|
|
||||||
signalDuration++;
|
|
||||||
}
|
|
||||||
previousData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void changeState(State state, bool resetCountersFlag=true) {
|
|
||||||
currentState = state;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case NOT_CONNECTED:
|
|
||||||
ledTurnOff();
|
|
||||||
writeState("not connected");
|
|
||||||
break;
|
|
||||||
case CONNECTED:
|
|
||||||
ledTurnOn();
|
|
||||||
writeState("connected");
|
|
||||||
break;
|
|
||||||
case RECEIVING_DATA:
|
|
||||||
writeState("receiving data");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetCountersFlag)
|
|
||||||
resetCounters();
|
|
||||||
}
|
|
||||||
|
|
||||||
void flatReceived() {
|
|
||||||
int flat = dataLength/2;
|
|
||||||
|
|
||||||
if (flat < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
println("| data length: ", dataLength);
|
|
||||||
println("| flat: ", flat);
|
|
||||||
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, flat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateStateMachine(int data) {
|
|
||||||
switch (currentState) {
|
|
||||||
case NOT_CONNECTED:
|
|
||||||
if (data == LOW) {}
|
|
||||||
else if (data == HIGH) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONNECTED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= NOT_CONNECTED_THRESHOLD) {
|
|
||||||
changeState(NOT_CONNECTED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
if (countZeros >= INITIALIZING_CALL_THRESHOLD) {
|
|
||||||
changeState(RECEIVING_DATA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECEIVING_DATA:
|
|
||||||
receiveData(data);
|
|
||||||
|
|
||||||
if (data == LOW) {
|
|
||||||
countOnes = 0;
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= DATA_RECEIVED_THESHOLD) {
|
|
||||||
flatReceived();
|
|
||||||
changeState(DATA_RECEIVED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countZeros = 0;
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DATA_RECEIVED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= CALL_ENDED_THRESHOLD) {
|
|
||||||
changeState(CALL_ENDED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CALL_ENDED:
|
|
||||||
if (data == LOW) {
|
|
||||||
countZeros++;
|
|
||||||
if (countZeros >= NOT_CONNECTED_THRESHOLD) {
|
|
||||||
changeState(NOT_CONNECTED);
|
|
||||||
}
|
|
||||||
} else if (data == HIGH) {
|
|
||||||
countOnes++;
|
|
||||||
if (countOnes >= CONNECTED_THRESHOLD) {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initStateMachine() {
|
|
||||||
changeState(CONNECTED);
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
#ifndef STATE_MACHINE_H
|
|
||||||
#define STATE_MACHINE_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#define CONNECTED_THRESHOLD 50000
|
|
||||||
#define NOT_CONNECTED_THRESHOLD 50000
|
|
||||||
#define INITIALIZING_CALL_THRESHOLD 15000
|
|
||||||
#define DATA_RECEIVED_THESHOLD 30000
|
|
||||||
#define CALL_ENDED_THRESHOLD 10000
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
NOT_CONNECTED,
|
|
||||||
CONNECTED,
|
|
||||||
RECEIVING_DATA,
|
|
||||||
DATA_RECEIVED,
|
|
||||||
CALL_ENDED
|
|
||||||
};
|
|
||||||
|
|
||||||
void resetCounters();
|
|
||||||
void updateStateMachine(int data);
|
|
||||||
void initStateMachine();
|
|
||||||
|
|
||||||
#endif // STATE_MACHINE_H
|
|
@ -1,5 +1,8 @@
|
|||||||
#include "domain/stateMachineController.h"
|
#include "domain/stateMachineController.h"
|
||||||
|
|
||||||
|
StateMachineController StateMachineController::instance;
|
||||||
|
StateMachineController& stateMachineController = StateMachineController::getInstance();
|
||||||
|
|
||||||
StateMachineController::StateMachineController() : _strategy(nullptr) {}
|
StateMachineController::StateMachineController() : _strategy(nullptr) {}
|
||||||
|
|
||||||
void StateMachineController::setStrategy(StateMachineStrategy* newStrategy) {
|
void StateMachineController::setStrategy(StateMachineStrategy* newStrategy) {
|
||||||
@ -20,8 +23,23 @@ void StateMachineController::updateStateMachine(int data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StateMachineController::~StateMachineController() {
|
StateMachineController::~StateMachineController() {
|
||||||
// Cleanup the strategy in the destructor
|
|
||||||
if (_strategy) {
|
if (_strategy) {
|
||||||
delete _strategy;
|
delete _strategy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateMachineController& StateMachineController::getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntercomConnectionStatus StateMachineController::getStatus() {
|
||||||
|
return _strategy->getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
int StateMachineController::getLastCalledNumber() {
|
||||||
|
return _strategy->getFlat();
|
||||||
|
}
|
||||||
|
|
||||||
|
DoorStatus getDoorStatus() {
|
||||||
|
return DoorStatus::CLOSED;
|
||||||
|
}
|
@ -1,14 +1,24 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "infra/relay.h"
|
||||||
#include "domain/stateMachineStrategy.h"
|
#include "domain/stateMachineStrategy.h"
|
||||||
|
|
||||||
class StateMachineController {
|
class StateMachineController {
|
||||||
private:
|
private:
|
||||||
|
static StateMachineController instance;
|
||||||
StateMachineStrategy* _strategy;
|
StateMachineStrategy* _strategy;
|
||||||
|
|
||||||
public:
|
|
||||||
StateMachineController();
|
StateMachineController();
|
||||||
|
StateMachineController(const StateMachineController&) = delete;
|
||||||
|
StateMachineController& operator=(const StateMachineController&) = delete;
|
||||||
|
~StateMachineController();
|
||||||
|
|
||||||
|
public:
|
||||||
void setStrategy(StateMachineStrategy* newStrategy);
|
void setStrategy(StateMachineStrategy* newStrategy);
|
||||||
void updateStateMachine(int data);
|
void updateStateMachine(int data);
|
||||||
~StateMachineController();
|
static StateMachineController& getInstance();
|
||||||
|
|
||||||
|
IntercomConnectionStatus getStatus();
|
||||||
|
int getLastCalledNumber();
|
||||||
|
DoorStatus getDoorStatus();
|
||||||
};
|
};
|
@ -1,8 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
enum class IntercomConnectionStatus { CONNECTED, NOT_CONNECTED, RECEIVING_DATA };
|
||||||
|
|
||||||
class StateMachineStrategy {
|
class StateMachineStrategy {
|
||||||
|
protected:
|
||||||
|
int _flat = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
IntercomConnectionStatus _status = IntercomConnectionStatus::NOT_CONNECTED;
|
||||||
|
|
||||||
virtual void setup() = 0;
|
virtual void setup() = 0;
|
||||||
virtual void updateStateMachine(int data) = 0;
|
virtual void updateStateMachine(int data) = 0;
|
||||||
virtual ~StateMachineStrategy() {}
|
virtual ~StateMachineStrategy() {}
|
||||||
|
|
||||||
|
int getFlat() {
|
||||||
|
return _flat;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntercomConnectionStatus getStatus() {
|
||||||
|
return _status;
|
||||||
|
}
|
||||||
};
|
};
|
@ -1,4 +1,5 @@
|
|||||||
#include "domain/strategies/cyfralStrategy.h"
|
#include "domain/strategies/cyfralStrategy.h"
|
||||||
|
#include "domain/intercomJournal.h"
|
||||||
|
|
||||||
CyfralStrategy::CyfralStrategy() {}
|
CyfralStrategy::CyfralStrategy() {}
|
||||||
|
|
||||||
@ -111,13 +112,16 @@ void CyfralStrategy::_changeState(State state, bool resetCountersFlag) {
|
|||||||
case NOT_CONNECTED:
|
case NOT_CONNECTED:
|
||||||
ledTurnOff();
|
ledTurnOff();
|
||||||
_writeState("not connected");
|
_writeState("not connected");
|
||||||
|
_status = IntercomConnectionStatus::NOT_CONNECTED;
|
||||||
break;
|
break;
|
||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
ledTurnOn();
|
ledTurnOn();
|
||||||
_writeState("connected");
|
_writeState("connected");
|
||||||
|
_status = IntercomConnectionStatus::CONNECTED;
|
||||||
break;
|
break;
|
||||||
case RECEIVING_DATA:
|
case RECEIVING_DATA:
|
||||||
_writeState("receiving data");
|
_writeState("receiving data");
|
||||||
|
_status = IntercomConnectionStatus::RECEIVING_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,14 +130,17 @@ void CyfralStrategy::_changeState(State state, bool resetCountersFlag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CyfralStrategy::_flatReceived() {
|
void CyfralStrategy::_flatReceived() {
|
||||||
int flat = _dataLength/2;
|
_flat = _dataLength/2;
|
||||||
|
|
||||||
if (flat < 1)
|
if (_flat < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
println("| data length: ", _dataLength);
|
println("| data length: ", _dataLength);
|
||||||
println("| flat: ", flat);
|
println("| flat: ", _flat);
|
||||||
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, flat);
|
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, _flat);
|
||||||
|
|
||||||
|
unsigned long createdAt = timeModule.getCurrentTime();
|
||||||
|
addFlatToIntercomJournal(createdAt, _flat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CyfralStrategy::_initStateMachine() {
|
void CyfralStrategy::_initStateMachine() {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include "utils/print.h"
|
#include "utils/print.h"
|
||||||
|
#include "utils/time.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "infra/mqtt.h"
|
#include "infra/mqtt.h"
|
||||||
#include "infra/led.h"
|
#include "infra/led.h"
|
||||||
#include "domain/stateMachineStrategy.h"
|
#include "domain/stateMachineStrategy.h"
|
||||||
|
|
||||||
|
extern Time& timeModule;
|
||||||
|
|
||||||
class CyfralStrategy : public StateMachineStrategy {
|
class CyfralStrategy : public StateMachineStrategy {
|
||||||
private:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "domain/strategies/vizitStrategy.h"
|
#include "domain/strategies/vizitStrategy.h"
|
||||||
|
#include "domain/intercomJournal.h"
|
||||||
|
|
||||||
VizitStrategy::VizitStrategy() {}
|
VizitStrategy::VizitStrategy() {}
|
||||||
|
|
||||||
@ -130,13 +131,16 @@ void VizitStrategy::_changeState(State state, bool resetCountersFlag) {
|
|||||||
case NOT_CONNECTED:
|
case NOT_CONNECTED:
|
||||||
ledTurnOff();
|
ledTurnOff();
|
||||||
_writeState("not connected");
|
_writeState("not connected");
|
||||||
|
_status = IntercomConnectionStatus::NOT_CONNECTED;
|
||||||
break;
|
break;
|
||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
ledTurnOn();
|
ledTurnOn();
|
||||||
_writeState("connected");
|
_writeState("connected");
|
||||||
|
_status = IntercomConnectionStatus::CONNECTED;
|
||||||
break;
|
break;
|
||||||
case RECEIVING_FIRST_DIGIT:
|
case RECEIVING_FIRST_DIGIT:
|
||||||
_writeState("receiving data");
|
_writeState("receiving data");
|
||||||
|
_status = IntercomConnectionStatus::RECEIVING_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +168,9 @@ void VizitStrategy::_secondDigitReceived() {
|
|||||||
println("| 2 data length: ", _dataLength);
|
println("| 2 data length: ", _dataLength);
|
||||||
println("| flat: ", _flat);
|
println("| flat: ", _flat);
|
||||||
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, _flat);
|
publishToMQTT(FLAT_NUMBER_MQTT_TOPIC, _flat);
|
||||||
|
|
||||||
|
unsigned long createdAt = timeModule.getCurrentTime();
|
||||||
|
addFlatToIntercomJournal(createdAt, _flat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VizitStrategy::_initStateMachine() {
|
void VizitStrategy::_initStateMachine() {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include "utils/print.h"
|
#include "utils/print.h"
|
||||||
|
#include "utils/time.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "infra/mqtt.h"
|
#include "infra/mqtt.h"
|
||||||
#include "infra/led.h"
|
#include "infra/led.h"
|
||||||
#include "domain/stateMachineStrategy.h"
|
#include "domain/stateMachineStrategy.h"
|
||||||
|
|
||||||
|
extern Time& timeModule;
|
||||||
|
|
||||||
class VizitStrategy : public StateMachineStrategy {
|
class VizitStrategy : public StateMachineStrategy {
|
||||||
private:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
@ -29,8 +32,6 @@ private:
|
|||||||
int _dataLength = 0;
|
int _dataLength = 0;
|
||||||
int _signalDuration = 0;
|
int _signalDuration = 0;
|
||||||
|
|
||||||
int _flat = 0;
|
|
||||||
|
|
||||||
void _resetCounters();
|
void _resetCounters();
|
||||||
void _writeState(char* message);
|
void _writeState(char* message);
|
||||||
void _receiveDigit(int data);
|
void _receiveDigit(int data);
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
#include "infra/intercom.h"
|
||||||
|
|
||||||
|
extern StateMachineController& stateMachineController;
|
||||||
|
|
||||||
|
void configureIntercom(String kmnModel, int firstAppartment) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IntercomConnectionStatus getIntercomStatus() {
|
||||||
|
return stateMachineController.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLastCalledNumber() {
|
||||||
|
return stateMachineController.getLastCalledNumber();
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include "domain/stateMachineController.h"
|
||||||
|
|
||||||
|
void configureIntercom(String kmnModel, int firstAppartment);
|
||||||
|
IntercomConnectionStatus getIntercomStatus();
|
||||||
|
int getLastCalledNumber();
|
@ -1,4 +1,5 @@
|
|||||||
#include "infra/relay.h"
|
#include "infra/relay.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
void relayTurnOn() {
|
void relayTurnOn() {
|
||||||
digitalWrite(DRY_CONT_PIN, LOW);
|
digitalWrite(DRY_CONT_PIN, LOW);
|
||||||
@ -7,3 +8,32 @@ void relayTurnOn() {
|
|||||||
void relayTurnOff() {
|
void relayTurnOff() {
|
||||||
digitalWrite(DRY_CONT_PIN, HIGH);
|
digitalWrite(DRY_CONT_PIN, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void switchRelay(SwitchDoorType type, DoorStatus status, int time) {
|
||||||
|
switch (type) {
|
||||||
|
case SwitchDoorType::ON_OFF:
|
||||||
|
if (status == DoorStatus::OPENED) {
|
||||||
|
relayTurnOn();
|
||||||
|
} else {
|
||||||
|
relayTurnOff();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SwitchDoorType::JOGGING:
|
||||||
|
if (status == DoorStatus::OPENED) {
|
||||||
|
relayTurnOn();
|
||||||
|
delay(secondsToMilliseconds(time));
|
||||||
|
relayTurnOff();
|
||||||
|
} else {
|
||||||
|
relayTurnOff();
|
||||||
|
delay(secondsToMilliseconds(time));
|
||||||
|
relayTurnOn();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SwitchDoorType::DELAY:
|
||||||
|
delay(secondsToMilliseconds(time));
|
||||||
|
relayTurnOn();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
@ -2,5 +2,9 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
|
enum class SwitchDoorType { ON_OFF, JOGGING, DELAY };
|
||||||
|
enum class DoorStatus { OPENED, CLOSED };
|
||||||
|
|
||||||
void relayTurnOn();
|
void relayTurnOn();
|
||||||
void relayTurnOff();
|
void relayTurnOff();
|
||||||
|
void switchRelay(SwitchDoorType type, DoorStatus status, int time);
|
@ -30,7 +30,7 @@ bool flag = false;
|
|||||||
int zeros = 0;
|
int zeros = 0;
|
||||||
int ones = 0;
|
int ones = 0;
|
||||||
|
|
||||||
StateMachineController controller;
|
extern StateMachineController& stateMachineController;
|
||||||
CyfralStrategy strategy;
|
CyfralStrategy strategy;
|
||||||
|
|
||||||
void IRAM_ATTR one() {
|
void IRAM_ATTR one() {
|
||||||
@ -72,13 +72,13 @@ void setup() {
|
|||||||
initRoutes();
|
initRoutes();
|
||||||
initHttpServer();
|
initHttpServer();
|
||||||
|
|
||||||
controller.setStrategy(&strategy);
|
stateMachineController.setStrategy(&strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (lastMicros < micros()) {
|
if (lastMicros < micros()) {
|
||||||
data = digitalRead(DATA_PIN);
|
data = digitalRead(DATA_PIN);
|
||||||
controller.updateStateMachine(data);
|
stateMachineController.updateStateMachine(data);
|
||||||
|
|
||||||
if (PRINT_RAW_SIGNAL_FLAG)
|
if (PRINT_RAW_SIGNAL_FLAG)
|
||||||
printf("{}", data);
|
printf("{}", data);
|
||||||
|
54
src/utils/time.cpp
Normal file
54
src/utils/time.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <TimeLib.h>
|
||||||
|
#include "utils/time.h"
|
||||||
|
|
||||||
|
Time& timeModule = Time::getInstance();
|
||||||
|
|
||||||
|
Time::Time() {
|
||||||
|
initialTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Time& Time::getInstance() {
|
||||||
|
static Time instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Time::setCurrentTime(unsigned long currentTimeMillis) {
|
||||||
|
initialTime = currentTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long Time::getCurrentTime() const {
|
||||||
|
return millis() + initialTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long Time::getTimeDifference(unsigned long otherTimeMillis) const {
|
||||||
|
return millis() - otherTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Time::getFormattedTime(unsigned long timeMillis) const {
|
||||||
|
time_t currentTime = timeMillis / 1000;
|
||||||
|
|
||||||
|
struct tm *timeStruct = gmtime(¤tTime);
|
||||||
|
|
||||||
|
char buffer[20];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
timeStruct->tm_year + 1900, timeStruct->tm_mon + 1, timeStruct->tm_mday,
|
||||||
|
timeStruct->tm_hour, timeStruct->tm_min, timeStruct->tm_sec);
|
||||||
|
|
||||||
|
return String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Time::getFormattedTimeAgo(unsigned long timeMillis) const {
|
||||||
|
time_t currentTime = timeMillis / 1000;
|
||||||
|
|
||||||
|
struct tm *timeStruct = gmtime(¤tTime);
|
||||||
|
|
||||||
|
if (timeStruct->tm_yday >= 0) {
|
||||||
|
return String(timeStruct->tm_yday) + " days ago";
|
||||||
|
} else if (timeStruct->tm_hour > 0) {
|
||||||
|
return String(timeStruct->tm_hour) + " hours ago";
|
||||||
|
} else if (timeStruct->tm_min > 0) {
|
||||||
|
return String(timeStruct->tm_min) + " minutes ago";
|
||||||
|
} else {
|
||||||
|
return String(timeStruct->tm_sec) + " seconds ago";
|
||||||
|
}
|
||||||
|
}
|
17
src/utils/time.h
Normal file
17
src/utils/time.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class Time {
|
||||||
|
public:
|
||||||
|
static Time& getInstance();
|
||||||
|
void setCurrentTime(unsigned long currentTimeMillis);
|
||||||
|
unsigned long getCurrentTime() const;
|
||||||
|
unsigned long getTimeDifference(unsigned long otherTimeMillis) const;
|
||||||
|
String getFormattedTime(unsigned long timeMillis) const;
|
||||||
|
String getFormattedTimeAgo(unsigned long timeMillis) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Time();
|
||||||
|
unsigned long initialTime;
|
||||||
|
};
|
@ -9,3 +9,7 @@ String requestDataToStr(uint8_t *data, size_t len) {
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long secondsToMilliseconds(int seconds) {
|
||||||
|
return static_cast<unsigned long>(seconds) * 1000;
|
||||||
|
}
|
@ -3,3 +3,4 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
String requestDataToStr(uint8_t *data, size_t len);
|
String requestDataToStr(uint8_t *data, size_t len);
|
||||||
|
unsigned long secondsToMilliseconds(int seconds);
|
Loading…
x
Reference in New Issue
Block a user