mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
Add persistent auto-off option for smart plugs (#826)
Auto-off now has a "Keep Enabled" toggle that keeps it active between prints instead of disabling after each use (one-shot). Useful for HA accessories like BentoBox filters that should always power off after prints. Default behavior (one-shot) is unchanged.
This commit is contained in:
@@ -43,6 +43,7 @@ const createMockPlug = (overrides: Partial<SmartPlug> = {}): SmartPlug => ({
|
||||
enabled: true,
|
||||
auto_on: true,
|
||||
auto_off: true,
|
||||
auto_off_persistent: false,
|
||||
off_delay_mode: 'time',
|
||||
off_delay_minutes: 5,
|
||||
off_temp_threshold: 70,
|
||||
@@ -241,6 +242,51 @@ describe('SmartPlugCard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('persistent auto-off', () => {
|
||||
it('shows Keep Enabled toggle when auto_off is enabled', async () => {
|
||||
const user = userEvent.setup();
|
||||
const plug = createMockPlug({ auto_off: true, auto_off_persistent: false });
|
||||
render(<SmartPlugCard plug={plug} onEdit={mockOnEdit} />);
|
||||
|
||||
await user.click(screen.getByText('Automation Settings'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keep Enabled')).toBeInTheDocument();
|
||||
expect(screen.getByText('Stay enabled between prints instead of one-shot')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show Keep Enabled toggle when auto_off is disabled', async () => {
|
||||
const user = userEvent.setup();
|
||||
const plug = createMockPlug({ auto_off: false });
|
||||
render(<SmartPlugCard plug={plug} onEdit={mockOnEdit} />);
|
||||
|
||||
await user.click(screen.getByText('Automation Settings'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Keep Enabled')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows Keep Enabled toggle for HA plugs with auto_off enabled', async () => {
|
||||
const user = userEvent.setup();
|
||||
const plug = createMockPlug({
|
||||
plug_type: 'homeassistant',
|
||||
ip_address: null,
|
||||
ha_entity_id: 'switch.bentobox_filter',
|
||||
auto_off: true,
|
||||
auto_off_persistent: true,
|
||||
});
|
||||
render(<SmartPlugCard plug={plug} onEdit={mockOnEdit} />);
|
||||
|
||||
await user.click(screen.getByText('Automation Settings'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keep Enabled')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled state', () => {
|
||||
it('renders plug even when disabled', () => {
|
||||
const plug = createMockPlug({ enabled: false });
|
||||
|
||||
@@ -1074,6 +1074,7 @@ export interface SmartPlug {
|
||||
enabled: boolean;
|
||||
auto_on: boolean;
|
||||
auto_off: boolean;
|
||||
auto_off_persistent: boolean;
|
||||
off_delay_mode: 'time' | 'temperature';
|
||||
off_delay_minutes: number;
|
||||
off_temp_threshold: number;
|
||||
@@ -1128,6 +1129,7 @@ export interface SmartPlugCreate {
|
||||
enabled?: boolean;
|
||||
auto_on?: boolean;
|
||||
auto_off?: boolean;
|
||||
auto_off_persistent?: boolean;
|
||||
off_delay_mode?: 'time' | 'temperature';
|
||||
off_delay_minutes?: number;
|
||||
off_temp_threshold?: number;
|
||||
@@ -1174,6 +1176,7 @@ export interface SmartPlugUpdate {
|
||||
enabled?: boolean;
|
||||
auto_on?: boolean;
|
||||
auto_off?: boolean;
|
||||
auto_off_persistent?: boolean;
|
||||
off_delay_mode?: 'time' | 'temperature';
|
||||
off_delay_minutes?: number;
|
||||
off_temp_threshold?: number;
|
||||
|
||||
@@ -361,6 +361,25 @@ export function SmartPlugCard({ plug, onEdit }: SmartPlugCardProps) {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Auto Off Persistent */}
|
||||
{plug.auto_off && (
|
||||
<div className="flex items-center justify-between pl-4 border-l-2 border-bambu-dark-tertiary">
|
||||
<div>
|
||||
<p className="text-sm text-white">{t('smartPlugs.autoOffPersistent')}</p>
|
||||
<p className="text-xs text-bambu-gray">{t('smartPlugs.autoOffPersistentDescription')}</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={plug.auto_off_persistent}
|
||||
onChange={(e) => updateMutation.mutate({ auto_off_persistent: e.target.checked })}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-9 h-5 bg-bambu-dark-tertiary peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-bambu-green"></div>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delay Mode */}
|
||||
{plug.auto_off && (
|
||||
<div className="space-y-3 pl-4 border-l-2 border-bambu-dark-tertiary">
|
||||
|
||||
@@ -3671,6 +3671,8 @@ export default {
|
||||
autoOnDescription: 'Einschalten wenn Druck startet',
|
||||
autoOff: 'Auto Aus',
|
||||
autoOffDescription: 'Ausschalten wenn Druck abgeschlossen (einmalig)',
|
||||
autoOffPersistent: 'Aktiviert lassen',
|
||||
autoOffPersistentDescription: 'Zwischen Drucken aktiviert bleiben statt einmalig',
|
||||
turnOffDelayMode: 'Ausschaltverzögerungsmodus',
|
||||
time: 'Zeit',
|
||||
temp: 'Temp',
|
||||
|
||||
@@ -3676,6 +3676,8 @@ export default {
|
||||
autoOnDescription: 'Turn on when print starts',
|
||||
autoOff: 'Auto Off',
|
||||
autoOffDescription: 'Turn off when print completes (one-shot)',
|
||||
autoOffPersistent: 'Keep Enabled',
|
||||
autoOffPersistentDescription: 'Stay enabled between prints instead of one-shot',
|
||||
turnOffDelayMode: 'Turn Off Delay Mode',
|
||||
time: 'Time',
|
||||
temp: 'Temp',
|
||||
|
||||
@@ -3663,6 +3663,8 @@ export default {
|
||||
autoOnDescription: 'Allumer au démarrage de l\'impression',
|
||||
autoOff: 'Auto Off',
|
||||
autoOffDescription: 'Éteindre à la fin de l\'impression (unique)',
|
||||
autoOffPersistent: 'Garder activé',
|
||||
autoOffPersistentDescription: 'Rester activé entre les impressions au lieu d\'une seule fois',
|
||||
turnOffDelayMode: 'Mode de délai d\'extinction',
|
||||
time: 'Temps',
|
||||
temp: 'Temp',
|
||||
|
||||
@@ -3662,6 +3662,8 @@ export default {
|
||||
autoOnDescription: 'Accendi quando inizia la stampa',
|
||||
autoOff: 'Auto Off',
|
||||
autoOffDescription: 'Spegni quando la stampa è completata (una tantum)',
|
||||
autoOffPersistent: 'Mantieni attivo',
|
||||
autoOffPersistentDescription: 'Resta attivo tra le stampe invece di una tantum',
|
||||
turnOffDelayMode: 'Modalità ritardo spegnimento',
|
||||
time: 'Tempo',
|
||||
temp: 'Temp',
|
||||
|
||||
@@ -3675,6 +3675,8 @@ export default {
|
||||
autoOnDescription: '印刷開始時にオンにする',
|
||||
autoOff: '自動オフ',
|
||||
autoOffDescription: '印刷完了時にオフにする(ワンショット)',
|
||||
autoOffPersistent: '有効のまま維持',
|
||||
autoOffPersistentDescription: 'ワンショットではなく印刷間で有効のまま維持',
|
||||
turnOffDelayMode: 'オフ遅延モード',
|
||||
time: '時間',
|
||||
temp: '温度',
|
||||
|
||||
@@ -3662,6 +3662,8 @@ export default {
|
||||
autoOnDescription: 'Ligar quando a impressão iniciar',
|
||||
autoOff: 'Auto Desligar',
|
||||
autoOffDescription: 'Desligar quando a impressão terminar (única vez)',
|
||||
autoOffPersistent: 'Manter ativado',
|
||||
autoOffPersistentDescription: 'Permanecer ativado entre impressões em vez de única vez',
|
||||
turnOffDelayMode: 'Modo de atraso para desligar',
|
||||
time: 'Tempo',
|
||||
temp: 'Temp',
|
||||
|
||||
@@ -3662,6 +3662,8 @@ export default {
|
||||
autoOnDescription: '打印开始时开启',
|
||||
autoOff: '自动关闭',
|
||||
autoOffDescription: '打印完成时关闭(一次性)',
|
||||
autoOffPersistent: '保持启用',
|
||||
autoOffPersistentDescription: '在打印之间保持启用而非一次性',
|
||||
turnOffDelayMode: '关闭延迟模式',
|
||||
time: '时间',
|
||||
temp: '温度',
|
||||
|
||||
Reference in New Issue
Block a user