homebridge-virtual-accessories
Version:
Virtual HomeKit accessories for Homebridge.
102 lines • 5.35 kB
JavaScript
import { Trigger } from './trigger.js';
import { Sensor } from '../sensor.js';
import { Utils } from '../../utils/utils.js';
import { Cron } from 'croner';
import { DateTimeFormatter, LocalDateTime, ZonedDateTime, ZoneId } from '@js-joda/core';
import '@js-joda/timezone';
/**
* CronTrigger - Trigger implementation
*/
export class CronTrigger extends Trigger {
cronJob;
constructor(sensor, name) {
super(sensor, name);
const triggerConfig = this.sensorConfig.cronTrigger;
if (triggerConfig.isDisabled) {
this.log.info(`[${this.sensorConfig.accessoryName}] Cron trigger is disabled`);
return;
}
if (triggerConfig.disableTriggerEventLogging) {
this.log.info(`[${this.sensorConfig.accessoryName}] Cron trigger event logging is disabled. Sensor state changes will not be displayed in the logs`);
}
// Hardcode reset delay
const resetDelayMillis = 3 * 1000; // 3 second reset delay
// System settings - date/time formatting - timezone
const timezone = (triggerConfig.zoneId !== undefined) ? triggerConfig.zoneId : Intl.DateTimeFormat().resolvedOptions().timeZone;
this.log.debug(`[${this.sensorConfig.accessoryName}] Setting timezone to '${timezone}'`);
const zoneId = ZoneId.of(timezone);
this.log.debug(`[${this.sensorConfig.accessoryName}] Setting ZoneId to '${zoneId}'`);
const cronStart = this.getZonedDateTime(triggerConfig.startDateTime, zoneId);
const cronEnd = this.getZonedDateTime(triggerConfig.endDateTime, zoneId);
this.log.debug(`[${this.sensorConfig.accessoryName}] Start time: '${cronStart?.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}'`);
this.log.debug(`[${this.sensorConfig.accessoryName}] End time: '${cronEnd?.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}'`);
this.log.debug(`[${this.sensorConfig.accessoryName}] Now time: '${Utils.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}'`);
// If we're past the end date, don't even bother starting up the cron job
if (cronEnd && Utils.now().isAfter(cronEnd)) {
this.log.info(`[${this.sensorConfig.accessoryName}] After cron end: '${triggerConfig.endDateTime}'. Not setting up cron job`);
return;
// eslint-disable-next-line brace-style
}
else if (cronStart && (Utils.now().isEqual(cronStart) || Utils.now().isBefore(cronStart))) {
this.log.info(`[${this.sensorConfig.accessoryName}] Before cron start: '${triggerConfig.startDateTime}'. Waiting for start time`);
}
let firstTrigger = true;
this.cronJob = new Cron(triggerConfig.pattern, {
name: `Schedule Cron Job (${this.sensorConfig.accessoryName})`,
startAt: this.includeStartTime(triggerConfig.startDateTime),
stopAt: this.includeEndTime(triggerConfig.endDateTime),
timezone: timezone,
}, (async () => {
if (firstTrigger) {
this.log.info(`[${this.sensorConfig.accessoryName}] Starting cron job`);
firstTrigger = false;
}
this.log.debug(`[${this.sensorConfig.accessoryName}] Matched cron pattern '${triggerConfig.pattern}'. Triggering sensor`);
sensor.triggerSensorState(Sensor.TRIGGERED, this, triggerConfig.disableTriggerEventLogging);
await Utils.delay(resetDelayMillis);
sensor.triggerSensorState(Sensor.NORMAL, this, triggerConfig.disableTriggerEventLogging);
if (!this.cronJob.nextRun()) {
this.log.info(`[${this.sensorConfig.accessoryName}] Stopping cron job`);
}
}));
this.displayNextRun(this.cronJob);
}
/**
* Private methods
*/
displayNextRun(cronJob) {
let nextRunTimestamp = cronJob.nextRun()?.toISOString();
nextRunTimestamp = (nextRunTimestamp === undefined) ?
'None scheduled' :
`${nextRunTimestamp.split('.')[0]} (count: ${cronJob.options.maxRuns})`;
this.log.debug(`[${this.sensorConfig.accessoryName}] Next ${cronJob.name} run: ${nextRunTimestamp}`);
}
getZonedDateTime(datetime, zoneId) {
if (datetime === undefined) {
return undefined;
}
const localDateTimeLength = 'yyyy:MM:ddThh:mm:ss'.length;
const localDatetime = datetime.substring(0, localDateTimeLength);
const zonedDateTime = ZonedDateTime.of(LocalDateTime.parse(localDatetime), zoneId);
return zonedDateTime;
}
// The cron implementation is exclusive of start and end times
// To include start time, setting start time to one second earlier
// To include end time, setting end time to one second later
includeStartTime(startTime) {
return this.adjustTime(startTime, -1);
}
includeEndTime(endTime) {
return this.adjustTime(endTime, 1);
}
adjustTime(datetime, adjusmentSeconds) {
if (datetime === undefined) {
return undefined;
}
let localDateTime = LocalDateTime.parse(datetime);
localDateTime = localDateTime.plusSeconds(adjusmentSeconds);
return localDateTime.format(DateTimeFormatter.ISO_DATE_TIME);
}
}
export const dynamicTrigger = CronTrigger;
//# sourceMappingURL=triggerCron.js.map