iobroker.schedule-switcher
Version:
Switch states over scheduler
456 lines (455 loc) • 18.5 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var MessageService_exports = {};
__export(MessageService_exports, {
MessageService: () => MessageService
});
module.exports = __toCommonJS(MessageService_exports);
var import_suncalc = require("suncalc");
var import_OnOffSchedule = require("../schedules/OnOffSchedule");
var import_AstroTime = require("../triggers/AstroTime");
var import_AstroTriggerBuilder = require("../triggers/AstroTriggerBuilder");
var import_TimeTriggerBuilder = require("../triggers/TimeTriggerBuilder");
var import_Weekday = require("../triggers/Weekday");
class MessageService {
/**
* Messages
*
* @param stateService Nothing
* @param scheduleIdToSchedule Nothing
* @param createOnOffScheduleSerializer Nothing
* @param adapter Nothing
* @param coordinate Nothing
* @param validation Nothing
* @param html Nothing
*/
constructor(stateService, scheduleIdToSchedule, createOnOffScheduleSerializer, adapter, coordinate, validation, html) {
this.stateService = stateService;
this.scheduleIdToSchedule = scheduleIdToSchedule;
this.createOnOffScheduleSerializer = createOnOffScheduleSerializer;
this.adapter = adapter;
this.coordinate = coordinate;
this.validation = validation;
this.html = html;
this.adapter = adapter;
this.triggerTimeout = void 0;
this.validation = validation;
this.html = html;
}
currentMessage = null;
triggerTimeout;
/**
* @param message ioBroker.Message
*/
async handleMessage(message) {
if (this.currentMessage) {
this.triggerTimeout = this.adapter.setTimeout(async () => {
await this.handleMessage(message);
this.triggerTimeout = void 0;
}, 50);
return;
}
this.currentMessage = message;
const data = message.message;
if (message.command === "change-view-dataId") {
await this.updateViews(data);
this.adapter.log.debug(`Finished message ${message.command}`);
this.currentMessage = null;
return;
}
this.adapter.log.debug(`Received ${message.command}`);
this.adapter.log.debug(JSON.stringify(message.message));
const schedule = this.scheduleIdToSchedule.get(data.dataId);
let active = false;
if (!schedule) {
this.adapter.log.error(`No schedule found for state ${data.dataId}`);
this.currentMessage = null;
return;
}
switch (message.command) {
case "add-trigger":
await this.addTrigger(schedule, data);
await this.validation.setActionTime(this.coordinate);
await this.setCountTrigger();
break;
case "add-one-time-trigger":
await this.addOneTimeTrigger(schedule, data);
await this.validation.setActionTime(this.coordinate);
await this.setCountTrigger();
break;
case "update-one-time-trigger":
await this.updateOneTimeTrigger(schedule, data.trigger, data.dataId);
await this.validation.setActionTime(this.coordinate);
break;
case "update-trigger":
if (data.trigger && data.trigger.type === "AstroTrigger") {
data.trigger.todayTrigger = await this.nextDate(data.trigger);
}
await this.updateTrigger(schedule, data.trigger, data.dataId);
await this.validation.setActionTime(this.coordinate);
break;
case "delete-trigger":
schedule.removeTrigger(data.triggerId);
await this.validation.setActionTime(this.coordinate);
await this.setCountTrigger();
break;
case "change-name":
if (data.name == null) {
this.adapter.log.error(`The name cannot be null`);
return;
}
schedule.setName(data.name);
await this.changeName(data);
break;
case "enable-schedule":
await this.html.changeEnabled(data.dataId, true);
schedule.setEnabled(true);
await this.stateService.setState(this.getEnabledIdFromScheduleId(data.dataId), true);
await this.setCountTrigger();
break;
case "disable-schedule":
schedule.setEnabled(false);
await this.html.changeEnabled(data.dataId, false);
await this.stateService.setState(this.getEnabledIdFromScheduleId(data.dataId), false);
await this.setCountTrigger();
break;
case "change-switched-values":
await this.changeOnOffSchedulesSwitchedValues(schedule, data);
break;
case "change-switched-ids":
await this.changeOnOffSchedulesSwitchedIds(schedule, data.stateIds);
await this.setCountTrigger();
break;
case "change-active":
active = true;
break;
default:
this.adapter.log.error("Unknown command received");
this.currentMessage = null;
return;
}
if (schedule instanceof import_OnOffSchedule.OnOffSchedule) {
let saveTrigger;
saveTrigger = (await this.createOnOffScheduleSerializer(data.dataId)).serialize(schedule);
if (active) {
const actual_trigger = JSON.parse(saveTrigger);
actual_trigger.active = data.active;
saveTrigger = JSON.stringify(actual_trigger);
}
await this.stateService.setState(data.dataId, saveTrigger);
await this.html.changeTrigger(data.dataId, saveTrigger);
} else {
this.adapter.log.error("Cannot update schedule state after message, no serializer found for schedule");
return;
}
this.adapter.log.debug(`Finished message ${message.command}`);
this.currentMessage = null;
}
async changeName(data) {
const state = data == null ? void 0 : data.dataId.split(".");
await this.stateService.extendObject(`onoff.${state[3]}`, { common: { name: data == null ? void 0 : data.name } });
await this.stateService.extendObject(`onoff.${state[3]}.data`, { common: { name: data == null ? void 0 : data.name } });
}
getEnabledIdFromScheduleId(scheduleId) {
return scheduleId.replace("data", "enabled");
}
/**
* Counter trigger
*/
async setCountTrigger() {
var _a;
let count = 0;
for (const id of this.scheduleIdToSchedule.keys()) {
try {
const len = (_a = this.scheduleIdToSchedule.get(id)) == null ? void 0 : _a.getTriggers().length;
count += len != null ? len : 0;
} catch (e) {
this.adapter.log.debug(`scheduleIdToSchedule: ${e}`);
}
}
await this.adapter.setState("counterTrigger", count, true);
}
nextDate(data) {
const next = (0, import_suncalc.getTimes)(/* @__PURE__ */ new Date(), this.coordinate.getLatitude(), this.coordinate.getLongitude());
let astro;
if (data.astroTime === "sunset") {
astro = next.sunset;
} else if (data.astroTime === "sunrise") {
astro = next.sunrise;
} else {
astro = next.solarNoon;
}
new Date(astro.getTime()).setMinutes(new Date(astro.getTime()).getMinutes() + data.shiftInMinutes);
return { hour: astro.getHours(), minute: astro.getMinutes(), weekday: astro.getDay(), date: astro };
}
async addTrigger(schedule, data) {
const state = data == null ? void 0 : data.dataId.split(".");
let triggerBuilder;
if (data.triggerType === "TimeTrigger") {
this.adapter.log.debug("Wants TimeTrigger");
triggerBuilder = new import_TimeTriggerBuilder.TimeTriggerBuilder().setHour(0).setMinute(0).setObjectId(parseInt(state[3])).setTodayTrigger({});
} else if (data.triggerType === "AstroTrigger") {
this.adapter.log.debug("Wants AstroTrigger");
triggerBuilder = new import_AstroTriggerBuilder.AstroTriggerBuilder().setAstroTime(import_AstroTime.AstroTime.Sunrise).setShift(0).setObjectId(parseInt(state[3])).setTodayTrigger(await this.nextDate({ astroTime: "sunrise", shiftInMinutes: 0 }));
} else {
this.adapter.log.error(`Cannot add trigger of type ${data.triggerType}`);
return;
}
triggerBuilder.setWeekdays(import_Weekday.AllWeekdays).setId(this.getNextTriggerId(schedule.getTriggers()));
if (data.actionType === "OnOffStateAction" && schedule instanceof import_OnOffSchedule.OnOffSchedule) {
this.adapter.log.debug("Wants OnOffStateAction");
triggerBuilder.setAction(schedule.getOnAction());
} else {
this.adapter.log.error(`Cannot add trigger with action of type ${data.actionType}`);
return;
}
schedule.addTrigger(triggerBuilder.build());
}
async updateOneTimeTrigger(schedule, triggerString, dataId) {
let updated;
if (isNaN(new Date(triggerString.date).getTime())) {
this.adapter.log.warn(`Wrong OneTimeDate ${triggerString.date} in ${dataId}`);
triggerString.date = (/* @__PURE__ */ new Date()).toISOString();
}
if (triggerString.timedate == null || typeof triggerString.timedate !== "boolean") {
this.adapter.log.warn(`Wrong timedate ${triggerString.timedate} in ${dataId}`);
triggerString.timedate = true;
}
if (schedule instanceof import_OnOffSchedule.OnOffSchedule) {
updated = (await this.createOnOffScheduleSerializer(dataId)).getTriggerSerializer(schedule).deserialize(JSON.stringify(triggerString));
} else {
this.adapter.log.error(`Can not deserialize trigger for schedule of type ${typeof schedule}`);
return;
}
schedule.updateTrigger(updated);
}
async addOneTimeTrigger(schedule, data) {
const t = JSON.parse(data.trigger);
const id = data.dataId.split(".");
t.id = this.getNextTriggerId(schedule.getTriggers());
t.objectId = parseInt(id[3]);
if (isNaN(new Date(t.date).getTime())) {
this.adapter.log.warn(`Wrong OneTimeDate ${t.date} in ${id}`);
t.date = (/* @__PURE__ */ new Date()).toISOString();
}
if (t.timedate == null || typeof t.timedate !== "boolean") {
this.adapter.log.warn(`Wrong timedate ${t.timedate} in ${id}`);
t.timedate = true;
}
const trigger = (await this.createOnOffScheduleSerializer(data.dataId)).getTriggerSerializer(schedule).deserialize(JSON.stringify(t));
schedule.addTrigger(trigger);
}
async updateTrigger(schedule, triggerString, dataId) {
let updated;
await this.validation.validation(dataId, triggerString, true);
if (schedule instanceof import_OnOffSchedule.OnOffSchedule && typeof triggerString === "object" && Object.keys(triggerString).length > 0) {
updated = (await this.createOnOffScheduleSerializer(dataId)).getTriggerSerializer(schedule).deserialize(JSON.stringify(triggerString));
} else {
this.adapter.log.error(`Can not deserialize trigger for schedule of type ${typeof schedule}`);
return;
}
schedule.updateTrigger(updated);
}
async updateViews(data) {
if (data) {
if (data.newId && data.newId.endsWith(".data")) {
const path = `${data.newId.replace(".data", ".views")}`;
const pathSplit = path.split(".");
const id = parseInt(pathSplit[3]);
if (!isNaN(id)) {
const valView = await this.stateService.getState(path);
if (valView != null) {
const newView = typeof valView === "string" ? JSON.parse(valView) : valView;
if (newView && newView[data.namespace] && newView[data.namespace][data.prefix]) {
newView[data.namespace][data.prefix][data.widgetId] = data;
} else {
newView[data.namespace] = {};
newView[data.namespace][data.prefix] = {};
newView[data.namespace][data.prefix][data.widgetId] = data;
}
await this.stateService.setState(path, JSON.stringify(newView));
}
}
}
if (data.oldId && data.oldId.endsWith(".data")) {
const oldPath = `${data.oldId.replace(".data", ".views")}`;
const oldPathSplit = oldPath.split(".");
const id = parseInt(oldPathSplit[3]);
if (!isNaN(id)) {
const valOldView = await this.stateService.getState(oldPath);
if (valOldView != null) {
const oldView = typeof valOldView === "string" ? JSON.parse(valOldView) : valOldView;
if (oldView && oldView[data.namespace] && oldView[data.namespace][data.prefix] && oldView[data.namespace][data.prefix][data.widgetId]) {
if (Object.keys(oldView[data.namespace]).length === 1) {
delete oldView[data.namespace];
} else if (Object.keys(oldView[data.namespace][data.prefix]).length === 1) {
delete oldView[data.namespace][data.prefix];
} else {
delete oldView[data.namespace][data.prefix][data.widgetId];
}
await this.stateService.setState(oldPath, JSON.stringify(oldView));
}
}
}
}
}
}
async changeOnOffSchedulesSwitchedValues(schedule, data) {
if (!(schedule instanceof import_OnOffSchedule.OnOffSchedule)) {
this.adapter.log.error(`Cannot change switched values when schedule type is not OnOffSchedule`);
return;
}
const states = schedule.getOnAction().getIdsOfStatesToSet();
if (states && typeof states === "object") {
const type = data.valueType;
for (const stateId of states) {
if (!stateId) {
continue;
}
const check = await this.adapter.getForeignObjectAsync(stateId);
if (!check || check.type != "state") {
this.adapter.log.error(`StateId ${stateId} is null/undefined or not state`);
return;
}
if (!check.common || !check.common.type) {
this.adapter.log.error(`Missing type ${JSON.stringify(check)} of ${stateId} !!!}`);
return;
}
if (check.common && check.common.type == "mixed") {
continue;
}
if (check.common && check.common.type != type) {
this.adapter.log.warn(
`The type ${check.common.type} of ${stateId} is incorrect!!! Type in VIS settings - ${type}`
);
}
}
} else {
this.adapter.log.debug(`StateIds is not an object`);
}
if (schedule.getOnAction().getValueType() === data.valueType && data.valueType === "boolean") {
this.adapter.log.debug("Catch no boolean change!!");
return;
}
if (data.valueType === "boolean") {
if (data.onValue != null) {
delete data.onValue;
}
if (data.offValue != null) {
delete data.offValue;
}
}
if (data.valueType === "number") {
if (!data.onValue || typeof data.onValue !== "number" && isNaN(Number.parseFloat(data.onValue))) {
data.onValue = 0;
}
if (!data.offValue || typeof data.offValue !== "number" && isNaN(Number.parseFloat(data.offValue))) {
data.offValue = 0;
}
}
if (data.valueType === "string") {
if (!data.onValue || typeof data.onValue !== "string") {
data.onValue = data.onValue.toString();
}
if (!data.offValue || typeof data.offValue !== "string") {
data.offValue = data.offValue.toString();
}
}
schedule.setOnAction(this.changeSwitchedValueOfOnOffScheduleAction(schedule.getOnAction(), data));
schedule.setOffAction(this.changeSwitchedValueOfOnOffScheduleAction(schedule.getOffAction(), data));
return;
}
async changeOnOffSchedulesSwitchedIds(schedule, stateIds) {
if (!(schedule instanceof import_OnOffSchedule.OnOffSchedule)) {
this.adapter.log.error(`Cannot change switched ids when schedule type is not OnOffSchedule`);
return;
}
if (typeof stateIds === "object") {
const type = schedule.getOnAction().getValueType();
for (const stateId of stateIds) {
if (!stateId) {
continue;
}
const check = await this.adapter.getForeignObjectAsync(stateId);
if (!check || check.type != "state") {
this.adapter.log.error(`StateId ${stateId} is null/undefined or not state`);
return;
}
if (!check.common || !check.common.type) {
this.adapter.log.error(`Missing type ${JSON.stringify(check)} of ${stateId} !!!}`);
return;
}
if (check.common && check.common.type == "mixed") {
continue;
}
if (check.common && check.common.type != type) {
this.adapter.log.warn(
`The type ${check.common.type} of ${stateId} is incorrect!!! Type in VIS settings - ${type}`
);
}
}
} else {
this.adapter.log.warn(`StateIds is not an object`);
return;
}
schedule.getOnAction().setIdsOfStatesToSet(stateIds);
schedule.getOffAction().setIdsOfStatesToSet(stateIds);
return;
}
changeSwitchedValueOfOnOffScheduleAction(action, data) {
switch (data.valueType) {
case "boolean":
return action.toBooleanValueType();
break;
case "number":
return action.toNumberValueType(data.onValue, data.offValue);
break;
case "string":
return action.toStringValueType(data.onValue, data.offValue);
break;
default:
throw new Error(`Value Type ${data.valueType} not supported`);
}
}
/**
* Destroy all triggers
*/
destroy() {
this.triggerTimeout && this.adapter.clearTimeout(this.triggerTimeout);
this.triggerTimeout = null;
return Promise.resolve(true);
}
getNextTriggerId(current) {
const numbers = current.map((t) => t.getId()).map((id) => Number.parseInt(id, 10)).filter((id) => !Number.isNaN(id)).sort((a, b) => a - b);
let newId = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > newId) {
break;
} else {
newId++;
}
}
return newId.toString();
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MessageService
});
//# sourceMappingURL=MessageService.js.map