Use custom websocket to avoid needing admin privileges.

This commit is contained in:
Dermot Duffy
2024-08-24 20:00:00 -07:00
parent 5a1d08f3ea
commit 429dd90972
47 changed files with 1219 additions and 874 deletions
+57
View File
@@ -0,0 +1,57 @@
import { localize } from '../../localize/localize';
import { ExtendedHomeAssistant } from '../../types';
import { hasHAConnectionStateChanged } from '../../utils/ha';
import { CardHASSAPI } from '../types';
import { StateWatcher, StateWatcherSubscriptionInterface } from './state-watcher';
export class HASSManager {
protected _hass: ExtendedHomeAssistant | null = null;
protected _api: CardHASSAPI;
protected _stateWatcher: StateWatcher = new StateWatcher();
constructor(api: CardHASSAPI) {
this._api = api;
}
public getHASS(): ExtendedHomeAssistant | null {
return this._hass;
}
public getStateWatcher(): StateWatcherSubscriptionInterface {
return this._stateWatcher;
}
public setHASS(hass?: ExtendedHomeAssistant | null): void {
if (hasHAConnectionStateChanged(this._hass, hass)) {
if (!hass?.connected) {
this._api.getMessageManager().setMessageIfHigherPriority({
message: localize('error.reconnecting'),
icon: 'mdi:lan-disconnect',
type: 'connection',
dotdotdot: true,
});
} else {
this._api.getMessageManager().resetType('connection');
}
}
if (!hass) {
return;
}
const oldHass = this._hass;
this._hass = hass;
if (this._api.getConditionsManager().hasHAStateConditions()) {
this._api.getConditionsManager().setState({
state: this._hass.states,
user: this._hass.user,
});
}
// Dark mode may depend on HASS.
this._api.getStyleManager().setLightOrDarkMode();
this._stateWatcher.setHASS(oldHass, hass);
}
}
+51
View File
@@ -0,0 +1,51 @@
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
import { getHassDifferences, HassStateDifference } from '../../utils/ha';
type StateWatcherCallback = (difference: HassStateDifference) => void;
export interface StateWatcherSubscriptionInterface {
subscribe(callback: StateWatcherCallback, entityIDs: string[]): void;
unsubscribe(callback: StateWatcherCallback): void;
}
export class StateWatcher implements StateWatcherSubscriptionInterface {
protected _watcherCallbacks = new Map<StateWatcherCallback, string[]>();
public setHASS(oldHass: HomeAssistant | null, hass: HomeAssistant): void {
if (!oldHass) {
return;
}
for (const [callback, entityIDs] of this._watcherCallbacks.entries()) {
const differences = getHassDifferences(hass, oldHass, entityIDs, {
stateOnly: true,
firstOnly: true,
});
if (differences.length) {
callback(differences[0]);
}
}
}
/**
* Calls callback when the state of any of the entities changes. The callback is
* called with the state difference of the first entity that changed.
* @param callback The callback.
* @param entityIDs An array of entity IDs to watch.
*/
public subscribe(callback: StateWatcherCallback, entityIDs: string[]): boolean {
if (!entityIDs.length) {
return false;
}
if (this._watcherCallbacks.has(callback)) {
this._watcherCallbacks.get(callback)?.push(...entityIDs);
} else {
this._watcherCallbacks.set(callback, entityIDs);
}
return true;
}
public unsubscribe(callback: StateWatcherCallback): void {
this._watcherCallbacks.delete(callback);
}
}