call scheduler immediate from dashboard

This commit is contained in:
proddy
2026-06-07 12:37:34 +02:00
parent cbfebabfa3
commit cfcc84d0c4
18 changed files with 195 additions and 124 deletions

View File

@@ -7,6 +7,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import HelpOutlineIcon from '@mui/icons-material/HelpOutlined'; import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
import { import {
@@ -75,24 +76,25 @@ const Dashboard = memo(() => {
{ {
immediate: false immediate: false
} }
); )
.onSuccess(() => {
toast.success(LL.WRITE_CMD_SENT());
})
.onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const deviceValueDialogSave = async (devicevalue: DeviceValue) => { const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
if (!selectedDashboardItem) { if (!selectedDashboardItem) {
return; return;
} }
const id = selectedDashboardItem.parentNode.id; // this is the parent ID // skip if we're executing an immediate schedule
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v }) if (devicevalue.v !== undefined) {
.then(() => { const id = selectedDashboardItem.parentNode.id; // this is the parent ID
toast.success(LL.WRITE_CMD_SENT()); await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v });
}) }
.catch((error: Error) => { setDeviceValueDialogOpen(false);
toast.error(error.message); setSelectedDashboardItem(undefined);
})
.finally(() => {
setDeviceValueDialogOpen(false);
setSelectedDashboardItem(undefined);
});
}; };
const dashboard_theme = useTheme({ const dashboard_theme = useTheme({
@@ -210,7 +212,12 @@ const Dashboard = memo(() => {
(parseInt(id.slice(0, 2), 16) & mask) === mask; (parseInt(id.slice(0, 2), 16) & mask) === mask;
const editDashboardValue = (di: DashboardItem) => { const editDashboardValue = (di: DashboardItem) => {
if (me.admin && di.dv?.c) { // don't execute on parent nodes
if (!me.admin || di.id <= 99) {
return;
}
if (di.dv?.c) {
setSelectedDashboardItem(di); setSelectedDashboardItem(di);
setDeviceValueDialogOpen(true); setDeviceValueDialogOpen(true);
} }
@@ -321,7 +328,19 @@ const Dashboard = memo(() => {
<Cell> <Cell>
{me.admin && {me.admin &&
di.dv?.c && di.dv?.c &&
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && ( !hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) &&
(di.dv.v === '' || di.dv.v === undefined ? (
<IconButton
size="small"
aria-label={LL.RUN_COMMAND()}
onClick={() => editDashboardValue(di)}
>
<PlayArrowIcon
color="primary"
sx={{ fontSize: 16 }}
/>
</IconButton>
) : (
<IconButton <IconButton
size="small" size="small"
aria-label={ aria-label={
@@ -334,7 +353,7 @@ const Dashboard = memo(() => {
sx={{ fontSize: 16 }} sx={{ fontSize: 16 }}
/> />
</IconButton> </IconButton>
)} ))}
</Cell> </Cell>
</> </>
) : ( ) : (

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
@@ -18,7 +19,9 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import { callAction } from '@/api/app';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import { useRequest } from 'alova/client';
import type Schema from 'async-validator'; import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator'; import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
@@ -61,10 +64,25 @@ const DevicesDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const save = async () => { const { send: executeSchedule } = useRequest(
(id: string) => callAction({ action: 'executeSchedule', param: id }),
{ immediate: false }
)
.onSuccess(() => {
toast.success(LL.EXECUTE_SCHEDULE_SENT());
})
.onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const doAction = async () => {
try { try {
setFieldErrors(undefined); setFieldErrors(undefined);
await validate(validator, editItem); if (editItem.v === undefined && editItem.c !== undefined) {
await executeSchedule(editItem.c);
} else {
await validate(validator, editItem);
}
onSave(editItem); onSave(editItem);
} catch (error) { } catch (error) {
setFieldErrors((error as ValidationError).fieldErrors); setFieldErrors((error as ValidationError).fieldErrors);
@@ -100,9 +118,14 @@ const DevicesDialog = ({
return undefined; return undefined;
}; };
const isCommand = selectedItem.v === '' && selectedItem.c; const isCommand =
(selectedItem.v === '' || selectedItem.v === undefined) &&
Boolean(selectedItem.c);
const isSchedulerImmediate = selectedItem.v === undefined;
const dialogTitle = isCommand const dialogTitle = isCommand
? LL.RUN_COMMAND() ? isSchedulerImmediate
? LL.EXECUTE() + ' ' + LL.SCHEDULE(0)
: LL.RUN_COMMAND()
: writeable : writeable
? LL.CHANGE_VALUE() ? LL.CHANGE_VALUE()
: LL.VALUE(0); : LL.VALUE(0);
@@ -118,67 +141,69 @@ const DevicesDialog = ({
<Typography sx={{ mb: 2 }} color="warning" variant="body2"> <Typography sx={{ mb: 2 }} color="warning" variant="body2">
{editItem.id.slice(2)} {editItem.id.slice(2)}
</Typography> </Typography>
<Grid container> {!isSchedulerImmediate && (
<Grid size={12}> <Grid container>
{editItem.l ? ( <Grid size={12}>
<TextField {editItem.l ? (
name="v" <TextField
value={editItem.v} name="v"
aria-label={valueLabel} value={editItem.v}
disabled={!writeable} aria-label={valueLabel}
sx={{ width: '30ch' }} disabled={!writeable}
select sx={{ width: '30ch' }}
onChange={updateFormValue} select
> onChange={updateFormValue}
{editItem.l.map((val) => ( >
<MenuItem value={val} key={val}> {editItem.l.map((val) => (
{val} <MenuItem value={val} key={val}>
</MenuItem> {val}
))} </MenuItem>
</TextField> ))}
) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? ( </TextField>
<ValidatedTextField ) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? (
fieldErrors={fieldErrors || {}} <ValidatedTextField
name="v" fieldErrors={fieldErrors || {}}
label={valueLabel} name="v"
value={numberValue(Math.round((editItem.v as number) * 10) / 10)} label={valueLabel}
autoFocus value={numberValue(Math.round((editItem.v as number) * 10) / 10)}
disabled={!writeable} autoFocus
type="number" disabled={!writeable}
sx={{ width: '30ch' }} type="number"
onChange={updateFormValue} sx={{ width: '30ch' }}
slotProps={{ onChange={updateFormValue}
htmlInput: editItem.s slotProps={{
? { min: editItem.m, max: editItem.x, step: editItem.s } htmlInput: editItem.s
: {}, ? { min: editItem.m, max: editItem.x, step: editItem.s }
input: { : {},
startAdornment: ( input: {
<InputAdornment position="start"> startAdornment: (
{setUom(editItem.u)} <InputAdornment position="start">
</InputAdornment> {setUom(editItem.u)}
) </InputAdornment>
} )
}} }
/> }}
) : ( />
<ValidatedTextField ) : (
fieldErrors={fieldErrors || {}} <ValidatedTextField
name="v" fieldErrors={fieldErrors || {}}
label={valueLabel} name="v"
value={editItem.v} label={valueLabel}
disabled={!writeable} value={editItem.v}
sx={{ width: '30ch' }} disabled={!writeable}
multiline={!editItem.u} sx={{ width: '30ch' }}
onChange={updateFormValue} multiline={!editItem.u}
/> onChange={updateFormValue}
/>
)}
</Grid>
{writeable && helperText && (
<Grid>
<FormHelperText>{helperText}</FormHelperText>
</Grid>
)} )}
</Grid> </Grid>
{writeable && helperText && ( )}
<Grid>
<FormHelperText>{helperText}</FormHelperText>
</Grid>
)}
</Grid>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@@ -202,7 +227,7 @@ const DevicesDialog = ({
<Button <Button
startIcon={<WarningIcon color="warning" />} startIcon={<WarningIcon color="warning" />}
variant="outlined" variant="outlined"
onClick={save} onClick={doAction}
color="primary" color="primary"
> >
{buttonLabel} {buttonLabel}

View File

@@ -134,9 +134,13 @@ const SchedulerDialog = ({
const { send: executeSchedule } = useRequest( const { send: executeSchedule } = useRequest(
(id: string) => callAction({ action: 'executeSchedule', param: id }), (id: string) => callAction({ action: 'executeSchedule', param: id }),
{ immediate: false } { immediate: false }
).onError((error) => { )
toast.error(String(error.error?.message || 'An error occurred')); .onSuccess(() => {
}); toast.success(LL.EXECUTE_SCHEDULE_SENT());
})
.onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const execute = async () => { const execute = async () => {
await executeSchedule(editItem.name); await executeSchedule(editItem.name);

View File

@@ -364,7 +364,8 @@ const cz: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.', UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?', WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?',
TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán', TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán',
SYSTEM_NAME: 'Název systému' SYSTEM_NAME: 'Název systému',
EXECUTE_SCHEDULE_SENT: 'Plán byl úspěšně proveden'
}; };
export default cz; export default cz;

View File

@@ -364,7 +364,8 @@ const de: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.', UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?', WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?',
TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet', TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet',
SYSTEM_NAME: 'Systemname' SYSTEM_NAME: 'Systemname',
EXECUTE_SCHEDULE_SENT: 'Zeitplan erfolgreich ausgeführt'
}; };
export default de; export default de;

View File

@@ -364,7 +364,8 @@ const en: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.', UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?', WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?',
TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully', TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully',
SYSTEM_NAME: 'System Name' SYSTEM_NAME: 'System Name',
EXECUTE_SCHEDULE_SENT: 'Schedule executed successfully'
}; };
export default en; export default en;

View File

@@ -364,7 +364,8 @@ const fr: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.', UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?', WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?',
TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès', TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès',
SYSTEM_NAME: 'Nom du système' SYSTEM_NAME: 'Nom du système',
EXECUTE_SCHEDULE_SENT: 'Planlegger exécuté avec succès'
}; };
export default fr; export default fr;

View File

@@ -364,7 +364,8 @@ const it: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.', UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?', WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?',
TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo', TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo',
SYSTEM_NAME: 'Nome del sistema' SYSTEM_NAME: 'Nome del sistema',
EXECUTE_SCHEDULE_SENT: 'Programma eseguito con successo'
}; };
export default it; export default it;

View File

@@ -364,7 +364,8 @@ const nl: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.', UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?', WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?',
TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol', TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol',
SYSTEM_NAME: 'Systeemnaam' SYSTEM_NAME: 'Systeemnaam',
EXECUTE_SCHEDULE_SENT: 'Planlegger uitgevoerd succesvol'
}; };
export default nl; export default nl;

View File

@@ -364,7 +364,8 @@ const no: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.', UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?', WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?',
TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt', TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt',
SYSTEM_NAME: 'Systemnavn' SYSTEM_NAME: 'Systemnavn',
EXECUTE_SCHEDULE_SENT: 'Planlegger utført suksessfullt'
}; };
export default no; export default no;

View File

@@ -364,7 +364,8 @@ const pl: BaseTranslation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.', UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?', WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?',
TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie', TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie',
SYSTEM_NAME: 'Nazwa systemu' SYSTEM_NAME: 'Nazwa systemu',
EXECUTE_SCHEDULE_SENT: 'Harmonogram wykonany pomyślnie'
}; };
export default pl; export default pl;

View File

@@ -364,7 +364,8 @@ const sk: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.', UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?', WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?',
TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný', TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný',
SYSTEM_NAME: 'Názov systému' SYSTEM_NAME: 'Názov systému',
EXECUTE_SCHEDULE_SENT: 'Plán bol úspešne vykonaný'
}; };
export default sk; export default sk;

View File

@@ -364,7 +364,8 @@ const sv: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.', UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?', WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?',
TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades', TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades',
SYSTEM_NAME: 'Systemnamn' SYSTEM_NAME: 'Systemnamn',
EXECUTE_SCHEDULE_SENT: 'Schema utfört'
}; };
export default sv; export default sv;

View File

@@ -364,7 +364,8 @@ const tr: Translation = {
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.', UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?', WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?',
TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi', TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi',
SYSTEM_NAME: 'Sistem Adı' SYSTEM_NAME: 'Sistem Adı',
EXECUTE_SCHEDULE_SENT: 'Zamanlama başarıyla uygulandı'
}; };
export default tr; export default tr;

View File

@@ -15,7 +15,7 @@ const headers = {
// EMS-ESP Application Settings // EMS-ESP Application Settings
let settings = { let settings = {
system_name: '', system_name: 'standalone',
locale: 'en', locale: 'en',
tx_mode: 1, tx_mode: 1,
ems_bus_id: 11, ems_bus_id: 11,
@@ -230,6 +230,21 @@ let countWifiScanPoll = 0; // wifi network scan
let countHardwarePoll = 0; // for during an upload let countHardwarePoll = 0; // for during an upload
// DeviceTypes // DeviceTypes
const enum ScheduleFlag {
SCHEDULE_SUN = 1,
SCHEDULE_MON = 2,
SCHEDULE_TUE = 4,
SCHEDULE_WED = 8,
SCHEDULE_THU = 16,
SCHEDULE_FRI = 32,
SCHEDULE_SAT = 64,
SCHEDULE_DAY = 0,
SCHEDULE_TIMER = 128,
SCHEDULE_ONCHANGE = 129,
SCHEDULE_CONDITION = 130,
SCHEDULE_IMMEDIATE = 132
}
const enum DeviceType { const enum DeviceType {
SYSTEM = 0, SYSTEM = 0,
TEMPERATURESENSOR, TEMPERATURESENSOR,
@@ -4165,7 +4180,7 @@ let emsesp_schedule = {
{ {
id: 4, id: 4,
active: false, active: false,
flags: 1, flags: ScheduleFlag.SCHEDULE_TIMER,
time: '04:00', time: '04:00',
cmd: 'system/restart', cmd: 'system/restart',
value: '', value: '',
@@ -4174,7 +4189,7 @@ let emsesp_schedule = {
{ {
id: 5, id: 5,
active: false, active: false,
flags: 130, flags: ScheduleFlag.SCHEDULE_CONDITION,
time: 'system/network info/rssi < -70', time: 'system/network info/rssi < -70',
cmd: 'system/restart', cmd: 'system/restart',
value: '', value: '',
@@ -4183,7 +4198,7 @@ let emsesp_schedule = {
{ {
id: 6, id: 6,
active: false, active: false,
flags: 129, flags: ScheduleFlag.SCHEDULE_ONCHANGE,
time: 'boiler/outdoortemp', time: 'boiler/outdoortemp',
cmd: 'boiler/selflowtemp', cmd: 'boiler/selflowtemp',
value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3', value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3',
@@ -4192,11 +4207,11 @@ let emsesp_schedule = {
{ {
id: 7, id: 7,
active: false, active: false,
flags: 132, flags: ScheduleFlag.SCHEDULE_IMMEDIATE,
time: '', time: '',
cmd: 'system/message', cmd: 'system/message',
value: '"hello world"', value: '"hello world"',
name: '' // empty name: 'send_message'
} }
] ]
}; };
@@ -4757,9 +4772,13 @@ router
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index, id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
dv: { dv: {
id: '00' + item.name, id: '00' + item.name,
v: item.active ? 'on' : 'off',
c: item.name, c: item.name,
l: ['off', 'on'] ...(item.flags === ScheduleFlag.SCHEDULE_IMMEDIATE
? {}
: {
v: item.active ? 'on' : 'off',
l: ['off', 'on']
})
} }
})); }));
dashboard_object = { dashboard_object = {

View File

@@ -478,8 +478,8 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
} }
} }
// show scheduler, with name, on/off, unless it's of type SCHEDULE_IMMEDIATE // show scheduler items
if (EMSESP::webSchedulerService.count_entities(true)) { if (EMSESP::webSchedulerService.count_entities()) {
JsonObject obj = nodes.add<JsonObject>(); JsonObject obj = nodes.add<JsonObject>();
obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id obj["id"] = EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID; // it's unique id
obj["t"] = EMSdevice::DeviceType::SCHEDULER; // device type number obj["t"] = EMSdevice::DeviceType::SCHEDULER; // device type number
@@ -488,20 +488,21 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) { EMSESP::webSchedulerService.read([&](const WebScheduler & webScheduler) {
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) { for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
continue;
}
JsonObject node = nodes.add<JsonObject>(); JsonObject node = nodes.add<JsonObject>();
node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++; node["id"] = (EMSdevice::DeviceTypeUniqueID::SCHEDULER_UID * 100) + count++;
JsonObject dv = node["dv"].to<JsonObject>(); JsonObject dv = node["dv"].to<JsonObject>();
dv["id"] = std::string("00") + scheduleItem.name; dv["id"] = std::string("00") + scheduleItem.name;
dv["c"] = scheduleItem.name; dv["c"] = scheduleItem.name;
char s[12];
dv["v"] = Helpers::render_boolean(s, scheduleItem.active, true); // for immediate schedules, we don't show the active/inactive state or on/off options
JsonArray l = dv["l"].to<JsonArray>(); if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
l.add(Helpers::render_boolean(s, false, true)); char s[12];
l.add(Helpers::render_boolean(s, true, true)); dv["v"] = Helpers::render_boolean(s, scheduleItem.active, true);
JsonArray l = dv["l"].to<JsonArray>();
l.add(Helpers::render_boolean(s, false, true)); // False option
l.add(Helpers::render_boolean(s, true, true)); // True option
}
} }
}); });
} }

View File

@@ -332,17 +332,9 @@ void WebSchedulerService::publish(const bool force) {
} }
} }
// count number of entries, default: only named items // count number of scheduler entries
// if exclude_immediate is true, include those that are of type SCHEDULEFLAG_SCHEDULE_IMMEDIATE uint8_t WebSchedulerService::count_entities() {
uint8_t WebSchedulerService::count_entities(bool exclude_immediate) { return static_cast<uint8_t>(scheduleItems_ ? scheduleItems_->size() : 0);
uint8_t count = 0;
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
// count all except SCHEDULE_IMMEDIATE if exclude_immediate is true, else count all
if (!exclude_immediate || scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
count++;
}
}
return count;
} }
// execute scheduled command // execute scheduled command

View File

@@ -85,7 +85,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
void ha_reset() { void ha_reset() {
ha_configdone_ = false; ha_configdone_ = false;
} }
uint8_t count_entities(bool exclude_immediate = false); uint8_t count_entities();
bool onChange(const char * cmd); bool onChange(const char * cmd);
bool executeSchedule(const char * name); bool executeSchedule(const char * name);