chore: Enforce a few house rules via eslint (#2539)
This commit is contained in:
committed by
dermotduffy
parent
5572ec728e
commit
921d45e577
@@ -9,13 +9,13 @@ export class EffectAction extends AdvancedCameraCardAction<EffectActionConfig> {
|
||||
const action = this._getAction();
|
||||
switch (action.effect_action) {
|
||||
case 'start':
|
||||
api.getEffectsManager().startEffect(action.effect);
|
||||
void api.getEffectsManager().startEffect(action.effect);
|
||||
break;
|
||||
case 'stop':
|
||||
api.getEffectsManager().stopEffect(action.effect);
|
||||
break;
|
||||
case 'toggle':
|
||||
api.getEffectsManager().toggleEffect(action.effect);
|
||||
void api.getEffectsManager().toggleEffect(action.effect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class PTZControlsAction extends AdvancedCameraCardAction<PTZControlsActio
|
||||
|
||||
// If `enabled` is explicit, use it. If only `type` is being changed, leave
|
||||
// `enabled` untouched (undefined = no change). Otherwise (neither set),
|
||||
// toggle the current enabled value — this is the menu-button show/hide use
|
||||
// toggle the current enabled value -- this is the menu-button show/hide use
|
||||
// case.
|
||||
const enabled =
|
||||
action.enabled ??
|
||||
|
||||
@@ -28,9 +28,8 @@ export class PTZMultiAction extends AdvancedCameraCardAction<PTZMultiActionConfi
|
||||
return;
|
||||
}
|
||||
|
||||
(type === 'ptz'
|
||||
? this._toPTZAction(targetID)
|
||||
: this._toPTZDigitalAction(targetID)
|
||||
void (
|
||||
type === 'ptz' ? this._toPTZAction(targetID) : this._toPTZDigitalAction(targetID)
|
||||
).execute(api);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,6 @@ export class AutomationsManager {
|
||||
--this._nestedAutomationExecutions;
|
||||
}
|
||||
};
|
||||
runActions(automation.actions);
|
||||
void runActions(automation.actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ export class CallManager {
|
||||
restoreView &&
|
||||
(previousView.view !== 'live' || previousView.camera !== call.cameraID)
|
||||
) {
|
||||
viewManager.setViewByParametersWithExistingQuery({
|
||||
void viewManager.setViewByParametersWithExistingQuery({
|
||||
baseView: previousView,
|
||||
force: true,
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ export abstract class GeneratedTone implements Tone {
|
||||
// a stopped tone can never re-arm its loop.
|
||||
protected _scheduleNext(intervalSeconds: number): void {
|
||||
/* istanbul ignore next: defensive guard against a subclass calling
|
||||
_scheduleNext after stop() — JS single-threading makes this unreachable
|
||||
_scheduleNext after stop() -- JS single-threading makes this unreachable
|
||||
from the existing subclasses -- @preserve */
|
||||
if (!this._context) {
|
||||
return;
|
||||
|
||||
@@ -79,8 +79,8 @@ export class CardElementManager {
|
||||
// These initializers are called when the config is updated, but on initial
|
||||
// creation of the card hass is not yet available when the config is first
|
||||
// loaded.
|
||||
this._api.getDefaultManager().initialize();
|
||||
this._api.getMediaPlayerManager().initialize();
|
||||
void this._api.getDefaultManager().initialize();
|
||||
void this._api.getMediaPlayerManager().initialize();
|
||||
|
||||
this._api
|
||||
.getHASSManager()
|
||||
@@ -184,7 +184,7 @@ export class CardElementManager {
|
||||
this._api.getFullscreenManager().disconnect();
|
||||
this._api.getPIPManager().uninitialize();
|
||||
this._api.getKeyboardStateManager().uninitialize();
|
||||
this._api.getActionsManager().uninitialize();
|
||||
void this._api.getActionsManager().uninitialize();
|
||||
this._api.getInteractionManager().uninitialize();
|
||||
this._api.getDefaultManager().uninitialize();
|
||||
this._api.getHASSManager().getStateWatcher()?.unsubscribe(this.update);
|
||||
@@ -200,7 +200,7 @@ export class CardElementManager {
|
||||
this._api
|
||||
.getInitializationManager()
|
||||
.uninitialize(InitializationAspect.INITIAL_TRIGGER);
|
||||
this._api.getCameraManager().destroy();
|
||||
void this._api.getCameraManager().destroy();
|
||||
|
||||
this._element.removeEventListener(
|
||||
'mousemove',
|
||||
|
||||
@@ -154,7 +154,7 @@ export class ConfigManager {
|
||||
// automations, which destroys associated ConditionsManagers. If a condition
|
||||
// transition (e.g. microphone connect) triggers both a user automation and
|
||||
// an unrelated override, an unconditional reload would delete the
|
||||
// automation mid-transition — the freshly created replacement has no prior
|
||||
// automation mid-transition -- the freshly created replacement has no prior
|
||||
// state, treats the current condition as its baseline, and never fires the
|
||||
// action.
|
||||
const runIfChanged = <T>(
|
||||
@@ -189,7 +189,7 @@ export class ConfigManager {
|
||||
(config) => [config.cameras, config.cameras_global],
|
||||
() => {
|
||||
this._api.getInitializationManager().uninitialize(InitializationAspect.CAMERAS);
|
||||
this._api.getCameraManager().destroy();
|
||||
void this._api.getCameraManager().destroy();
|
||||
},
|
||||
true,
|
||||
);
|
||||
@@ -203,7 +203,7 @@ export class ConfigManager {
|
||||
true,
|
||||
);
|
||||
|
||||
/* async */ this._initializeBackgroundAndUpdate(previousConfig);
|
||||
void this._initializeBackgroundAndUpdate(previousConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,7 +92,7 @@ export class OverridesManager {
|
||||
|
||||
const parseResult = advancedCameraCardConfigSchema.safeParse(output);
|
||||
if (!parseResult.success) {
|
||||
// Surface one co-located failure object per Zod issue — path, the value
|
||||
// Surface one co-located failure object per Zod issue -- path, the value
|
||||
// the user actually wrote, and the most informative "expected" field for
|
||||
// this issue code. Avoids dumping the full merged config (which is
|
||||
// mostly schema defaults the user never wrote) and keeps the reader from
|
||||
|
||||
@@ -166,7 +166,7 @@ export class EffectsManager implements EffectsManagerInterface {
|
||||
private _startPendingEffects(): void {
|
||||
for (const [name, options] of this._pendingEffects.entries()) {
|
||||
this._pendingEffects.delete(name);
|
||||
this._startEffect(name, options);
|
||||
void this._startEffect(name, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,12 @@ export class ScreenfullFullScreenProvider
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
screenfull.request(this._api.getCardElementManager().getElement());
|
||||
// A denied request (or an exit when not in fullscreen) leaves the UI
|
||||
// consistent: the 'change' handler only fires on a real transition.
|
||||
// Nothing to act on.
|
||||
screenfull.request(this._api.getCardElementManager().getElement()).catch(() => {});
|
||||
} else {
|
||||
screenfull.exit();
|
||||
screenfull.exit().catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,10 @@ export class WebkitFullScreenProvider
|
||||
// clicking the 'X' which then fires this event). That's probably the rare
|
||||
// case though.
|
||||
this._playTimer.start(WEBKIT_PLAY_SECONDS, () => {
|
||||
this._getVideoElement()?.play();
|
||||
// Best-effort resume after a fullscreen exit.
|
||||
this._getVideoElement()
|
||||
?.play()
|
||||
.catch(() => {});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export class HASSManager implements HASSManagerReadonlyInterface {
|
||||
);
|
||||
|
||||
this._api.getInitializationManager().uninitialize(InitializationAspect.CAMERAS);
|
||||
this._api.getCameraManager().destroy();
|
||||
void this._api.getCameraManager().destroy();
|
||||
this._api.getInitializationManager().uninitialize(InitializationAspect.VIEW);
|
||||
this._api
|
||||
.getInitializationManager()
|
||||
|
||||
@@ -80,7 +80,7 @@ export class InitializationManager {
|
||||
if (!this._shouldInitializeMandatory()) {
|
||||
return;
|
||||
}
|
||||
/* async */ this.initializeMandatory();
|
||||
void this.initializeMandatory();
|
||||
}
|
||||
|
||||
private _shouldInitializeMandatory(): boolean {
|
||||
|
||||
@@ -29,7 +29,7 @@ export class IssueManager {
|
||||
private _suspended = false;
|
||||
|
||||
// Reentrancy guard: evaluate() calls setState() on the condition state
|
||||
// manager, which fires listeners synchronously — including the one
|
||||
// manager, which fires listeners synchronously -- including the one
|
||||
// registered in this constructor. Without this guard, detectDynamic()
|
||||
// and presence computation would run twice per evaluation.
|
||||
private _evaluating = false;
|
||||
@@ -165,16 +165,14 @@ export class IssueManager {
|
||||
// normal re-evaluation (on any condition-state change).
|
||||
//
|
||||
// `initialized: true` in the change payload means mandatory initialization
|
||||
// just finished — see InitializationManager._initializeMandatory. That's
|
||||
// just finished -- see InitializationManager._initializeMandatory. That's
|
||||
// also the earliest point at which the full HASS object is guaranteed
|
||||
// ready for websocket calls (e.g. LegacyResourceIssue's lovelace/resources
|
||||
// fetch). Because `initialized` is latched (its comment notes it never
|
||||
// changes again), this block fires exactly once per IssueManager life.
|
||||
private _onStateChange(change: ConditionStateChange): void {
|
||||
if (change.change.initialized === true && change.new.hass) {
|
||||
/* async */ this._stateManager
|
||||
.detectStatic(change.new.hass)
|
||||
.then(() => this.evaluate());
|
||||
void this._stateManager.detectStatic(change.new.hass).then(() => this.evaluate());
|
||||
}
|
||||
this.evaluate();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ConnectionIssue implements Issue {
|
||||
private _state: ConnectionState = 'ready';
|
||||
|
||||
public detectDynamic(state: ConditionState): void {
|
||||
// Before HASS is ever provided, leave state untouched — undefined hass is
|
||||
// Before HASS is ever provided, leave state untouched -- undefined hass is
|
||||
// not a disconnection, just "not yet initialized".
|
||||
if (state.hass === undefined) {
|
||||
return;
|
||||
|
||||
@@ -44,7 +44,7 @@ export class InitializationIssue extends AbstractErrorIssue {
|
||||
// resources (WebSocket subscriptions, listeners) before the CAMERAS
|
||||
// init aspect replaces the instance via createCameraManager().
|
||||
this._api.getInitializationManager().uninitializeMandatory();
|
||||
this._api.getCameraManager().destroy();
|
||||
void this._api.getCameraManager().destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class MediaLoadIssue implements Issue {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Explicit trigger — called when a component fires an issue:trigger event.
|
||||
// Explicit trigger -- called when a component fires an issue:trigger event.
|
||||
// =========================================================================
|
||||
|
||||
public trigger(context: IssueTriggerContext['media_load']): void {
|
||||
@@ -45,7 +45,7 @@ export class MediaLoadIssue implements Issue {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Detection — called by the manager on every state change.
|
||||
// Detection -- called by the manager on every state change.
|
||||
// =========================================================================
|
||||
|
||||
public detectDynamic(state: ConditionState): void {
|
||||
@@ -62,7 +62,7 @@ export class MediaLoadIssue implements Issue {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// State queries — called by the manager to read current state.
|
||||
// State queries -- called by the manager to read current state.
|
||||
// =========================================================================
|
||||
|
||||
public hasIssue(): boolean {
|
||||
@@ -113,7 +113,7 @@ export class MediaLoadIssue implements Issue {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Retry — called by the manager to schedule a media reload.
|
||||
// Retry -- called by the manager to schedule a media reload.
|
||||
// =========================================================================
|
||||
|
||||
public needsRetry(): boolean {
|
||||
@@ -144,7 +144,7 @@ export class MediaLoadIssue implements Issue {
|
||||
// re-attempts loading underneath. If the retry succeeds,
|
||||
// _handleMediaLoaded will clear everything when media:loaded fires. If
|
||||
// it fails silently (e.g. bogus stream name), the error stays visible
|
||||
// immediately — no new 10s grace period.
|
||||
// immediately -- no new 10s grace period.
|
||||
this._api.getViewManager().setViewWithMergedContext({ mediaEpoch });
|
||||
return false;
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export class MediaLoadIssue implements Issue {
|
||||
this._timerTargetID = targetID;
|
||||
this._timer.start(MEDIA_LOADING_TIMEOUT_SECONDS, () => {
|
||||
// Record the error on timeout so retry() knows which epoch to bump.
|
||||
// targetID is guaranteed non-null here — the null case bails at the
|
||||
// targetID is guaranteed non-null here -- the null case bails at the
|
||||
// top of _handleMediaNotLoaded.
|
||||
this._erroredTargetIDs.add(targetID);
|
||||
this._activate();
|
||||
|
||||
@@ -31,7 +31,7 @@ export class MediaQueryIssue extends AbstractErrorIssue {
|
||||
return false;
|
||||
}
|
||||
this._error = null;
|
||||
this._api.getViewManager().setViewByParametersWithNewQuery();
|
||||
void this._api.getViewManager().setViewByParametersWithNewQuery();
|
||||
|
||||
// Exclusive retry. No other issue should attempt to retry until the next
|
||||
// evaluation cycle, when we'll know if this was successful.
|
||||
|
||||
@@ -3,7 +3,7 @@ import { summarizeNotification } from '../../components-lib/notification/summari
|
||||
import { ConditionState } from '../../condition-trigger/conditions/types';
|
||||
import { Notification } from '../../config/schema/actions/types';
|
||||
import { HomeAssistant } from '../../ha/types';
|
||||
import { isTruthy } from '../../utils/basic';
|
||||
import { errorToConsole, isTruthy } from '../../utils/basic';
|
||||
import {
|
||||
Issue,
|
||||
IssueDescription,
|
||||
@@ -27,12 +27,18 @@ export class IssueStateManager implements IssueReadOnlyState {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Detection — static (one-shot on init) and dynamic (on every state change).
|
||||
// Detection -- static (one-shot on init) and dynamic (on every state change).
|
||||
// =========================================================================
|
||||
|
||||
public async detectStatic(hass: HomeAssistant): Promise<void> {
|
||||
for (const issue of this._issues.values()) {
|
||||
await issue.detectStatic?.(hass);
|
||||
try {
|
||||
await issue.detectStatic?.(hass);
|
||||
} catch (e) {
|
||||
// Isolate one issue's detection failure so it cannot abort detection
|
||||
// for the rest; log so the cause is visible.
|
||||
errorToConsole(e as Error);
|
||||
}
|
||||
this._logIfNew(issue);
|
||||
}
|
||||
}
|
||||
@@ -57,7 +63,7 @@ export class IssueStateManager implements IssueReadOnlyState {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Queries — read active issue state.
|
||||
// Queries -- read active issue state.
|
||||
// =========================================================================
|
||||
|
||||
public getFullCardIssue(): IssueDescription | null {
|
||||
|
||||
@@ -28,11 +28,11 @@ export interface KeyedIssueDescription {
|
||||
}
|
||||
|
||||
// Map of currently active issues keyed by IssueKey, with each entry's value
|
||||
// being the issue's current rendered description. Stored as a Map (not just
|
||||
// a Set of keys) so that sub-state changes within an issue — e.g.
|
||||
// ConnectionIssue swapping between 'lost' and 'starting' — are reflected as
|
||||
// real value-level diffs to the condition state, triggering re-renders and
|
||||
// any user-defined conditions that depend on issue state.
|
||||
// being the issue's current rendered description. Stored as a Map (not just a
|
||||
// Set of keys) so that sub-state changes within an issue -- e.g.
|
||||
// ConnectionIssue swapping between 'lost' and 'starting' -- are reflected as
|
||||
// real value-level diffs to the condition state, triggering re-renders and any
|
||||
// user-defined conditions that depend on issue state.
|
||||
export type IssuePresence = Map<IssueKey, IssueDescription>;
|
||||
export interface IssueReadOnlyState {
|
||||
hasFullCardIssue(): boolean;
|
||||
@@ -80,7 +80,7 @@ export interface Issue {
|
||||
// loop (exclusive), false to allow subsequent issues to also retry.
|
||||
retry?(): boolean;
|
||||
|
||||
// Optional user-initiated fix. Not called by the issue infrastructure —
|
||||
// Optional user-initiated fix. Not called by the issue infrastructure --
|
||||
// callers (e.g. notification control actions) invoke this directly.
|
||||
fix?(hass: HomeAssistant): Promise<boolean>;
|
||||
|
||||
@@ -92,11 +92,11 @@ export interface Issue {
|
||||
|
||||
// Called when the card is detached. Issues with age-based timers (e.g.
|
||||
// loading-timeout timers) must stop them here so that time spent offscreen
|
||||
// doesn't count against the user. Must preserve already-active issue state
|
||||
// — a full-card issue visible at detach should still be visible on
|
||||
// reattach. No `resume` hook: IssueManager.resume() triggers a normal
|
||||
// evaluate(), so any timer that should restart is re-armed via
|
||||
// detectDynamic against the current condition state.
|
||||
// doesn't count against the user. Must preserve already-active issue state --
|
||||
// a full-card issue visible at detach should still be visible on reattach. No
|
||||
// `resume` hook: IssueManager.resume() triggers a normal evaluate(), so any
|
||||
// timer that should restart is re-armed via detectDynamic against the current
|
||||
// condition state.
|
||||
suspend?(): void;
|
||||
|
||||
// Release external resources (e.g. a listener registered on another manager)
|
||||
|
||||
@@ -10,8 +10,8 @@ import type { LockPolicy } from './types';
|
||||
// - Stream-stopping / re-init actions: pause, reload, and casting (which
|
||||
// rehosts the stream to a media player).
|
||||
//
|
||||
// `call_start` is intentionally absent — it's the entry into the lock.
|
||||
// `call_end` is also absent — it dispatches via `setViewByParameters({ force:
|
||||
// `call_start` is intentionally absent -- it's the entry into the lock.
|
||||
// `call_end` is also absent -- it dispatches via `setViewByParameters({ force:
|
||||
// true })` to bypass the lock, so listing it here would be redundant.
|
||||
const CALL_DISRUPTIVE_ACTIONS: ReadonlySet<string> = new Set([
|
||||
// View / camera / substream changes.
|
||||
|
||||
@@ -27,7 +27,7 @@ export class MediaLoadedInfoManager {
|
||||
// never cleared by `clear` / `_clearTarget`, only by `initialize`.
|
||||
private _lastKnown: Map<string, MediaLoadedInfo> = new Map();
|
||||
|
||||
// The currently "active" target — the one whose info drives condition state
|
||||
// The currently "active" target -- the one whose info drives condition state
|
||||
// and card-level side effects. Driven by ViewManager on every view change.
|
||||
private _selected: string | null = null;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ interface QueryStringViewIntent {
|
||||
|
||||
// The substream change to apply alongside the view. Tri-state:
|
||||
// - `undefined`: no substream URL action present, no modifier issued.
|
||||
// - `string`: `substream_on=X` — engage stream X.
|
||||
// - `null`: `substream_off` — explicitly clear the override.
|
||||
// - `string`: `substream_on=X` -- engage stream X.
|
||||
// - `null`: `substream_off` -- explicitly clear the override.
|
||||
stream?: string | null;
|
||||
};
|
||||
other?: AdvancedCameraCardCustomActionConfig[];
|
||||
|
||||
@@ -126,7 +126,7 @@ export class ViewManager implements ViewManagerInterface {
|
||||
...options,
|
||||
});
|
||||
// A non-throwing factory call clears any prior view_incompatible /
|
||||
// media_query state — ensures a previously-dismissed mid-session popup
|
||||
// media_query state -- ensures a previously-dismissed mid-session popup
|
||||
// does not linger invisibly and re-pop on the next evaluation cycle,
|
||||
// and that a stale media_query failure from an abandoned gallery /
|
||||
// viewer doesn't follow the user into an unrelated view.
|
||||
@@ -361,7 +361,7 @@ export class ViewManager implements ViewManagerInterface {
|
||||
if (!this._api.getQueryStringManager().hasViewRelatedActionsToRun()) {
|
||||
// This is not awaited to allow the initialization to complete before the
|
||||
// query is answered.
|
||||
this.setViewDefaultWithNewQuery({ failSafe: true });
|
||||
void this.setViewDefaultWithNewQuery({ failSafe: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user