Skip to content

Commit

Permalink
refactor(poll-state): Convert controller to typescript
Browse files Browse the repository at this point in the history
feat(poll-state): Add output properties

BREAKING CHANGE: drop support for entity.timeSinceChanged
  • Loading branch information
zachowj committed Sep 24, 2023
1 parent 6ae4b25 commit 13de0d7
Show file tree
Hide file tree
Showing 13 changed files with 547 additions and 293 deletions.
25 changes: 7 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@
"lowdb": "1.0.0",
"mustache": "4.2.0",
"selectn": "1.1.2",
"slugify": "1.6.6",
"time-ago": "0.2.1",
"slugify": "1.6.5",
"timestring": "7.0.0",
"ws": "8.14.1"
},
Expand Down
2 changes: 2 additions & 0 deletions src/editor/exposenode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function init(n: HassNodeProperties) {
break;
case NodeType.EventsAll:
case NodeType.EventsState:
case NodeType.PollState:
case NodeType.Tag:
case NodeType.Zone:
case NodeType.Time:
Expand Down Expand Up @@ -125,6 +126,7 @@ function render() {
renderAlert(type);
}
break;
case NodeType.PollState:
case NodeType.Tag:
case NodeType.Zone:
case NodeType.Time:
Expand Down
1 change: 1 addition & 0 deletions src/editor/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function versionCheckOnEditPrepare(
const exposedEventNodes: NodeType[] = [
NodeType.EventsAll,
NodeType.EventsState,
NodeType.PollState,
NodeType.Tag,
NodeType.Time,
NodeType.Zone,
Expand Down
144 changes: 144 additions & 0 deletions src/nodes/poll-state/PollStateController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import ExposeAsController, {
ExposeAsControllerConstructor,
} from '../../common/controllers/EposeAsController';
import ConfigError from '../../common/errors/ConfigError';
import ComparatorService from '../../common/services/ComparatorService';
import TransformState, { TransformType } from '../../common/TransformState';
import { TypedInputTypes } from '../../const';
import { RED } from '../../globals';
import { getTimeInMilliseconds } from '../../helpers/utils';
import { HassEntity } from '../../types/home-assistant';
import { NodeMessage } from '../../types/nodes';
import { PollStateNode } from '.';

interface PollStateNodeConstructor
extends ExposeAsControllerConstructor<PollStateNode> {
comparatorService: ComparatorService;
transformState: TransformState;
}

export default class PollStateController extends ExposeAsController<PollStateNode> {
#comparatorService: ComparatorService;
#timer: NodeJS.Timeout | undefined;
#transformState: TransformState;
#updateinterval?: number;

constructor(props: PollStateNodeConstructor) {
super(props);
this.#comparatorService = props.comparatorService;
this.#transformState = props.transformState;
}

#getInterval() {
let interval = this.node.config.updateInterval || '0';
if (this.node.config.updateIntervalType === TypedInputTypes.JSONata) {
interval = this.jsonataService.evaluate(interval);
}

const intervalMs = getTimeInMilliseconds(
Number(interval),
this.node.config.updateIntervalUnits
);
if (isNaN(intervalMs)) {
throw new ConfigError([
'poll-state.error.interval_not_a_number',
{ interval },
]);
}

return Number(intervalMs);
}

protected onClose(): void {
if (this.#timer) {
clearInterval(this.#timer);
this.#timer = undefined;
}
}

protected getNodeEntityId() {
return this.node.config.entityId;
}

public onTimer(triggered = false) {
if (this.isEnabled === false) {
return;
}

const entity = this.homeAssistant.websocket.getStates(
this.node.config.entityId
) as HassEntity;
if (!entity) {
throw new ConfigError([
'poll-state.error.entity_id_not_found',
{ entity_id: this.node.config.entityId },
]);
}

entity.timeSinceChangedMs =
Date.now() - new Date(entity.last_changed).getTime();

if (this.node.config.stateType !== TransformType.String) {
// Convert and save original state if needed
entity.original_state = entity.state as string;
entity.state = this.#transformState.transform(
this.node.config.stateType as TransformType,
entity.state as string
);
}

const isIfState = this.#comparatorService.getComparatorResult(
this.node.config.ifStateOperator,
this.node.config.ifState,
entity.state,
this.node.config.ifStateType,
{
entity,
}
);

const statusMessage = `${entity.state}${
triggered === true
? ` (${RED._('home-assistant.status.triggered')})`
: ''
}`;

const message: NodeMessage = {};
this.setCustomOutputs(this.node.config.outputProperties, message, {
config: this.node.config,
entity,
entityState: entity.state,
triggerId: this.node.config.entityId,
});

// Check 'if state' and send to correct output
if (this.node.config.ifState && !isIfState) {
this.status.setFailed(statusMessage);
this.node.send([null, message]);
return;
}

this.status.setSuccess(statusMessage);
this.node.send([message, null]);
}

public onIntervalUpdate() {
const interval = this.#getInterval();
// create new timer if interval changed
if (interval !== this.#updateinterval) {
clearInterval(this.#timer);
this.#updateinterval = interval;
this.#timer = setInterval(() => {
try {
this.onTimer();
} catch (e) {
this.node.error(e);
}
}, this.#updateinterval);
}
}

public onTriggered() {
this.onTimer(true);
}
}
Loading

0 comments on commit 13de0d7

Please sign in to comment.