iobroker.lightcontrol
Version:
Light control of lamps from various manufacturers
553 lines (487 loc) • 16.8 kB
JavaScript
;
const helper = require("./helper");
const { DeviceTemplate, DeviceAllTemplate } = require(`./groupTemplates`);
const lightHandling = require("./lightHandling");
const {
TestTemplateLamps,
TestTemplateMotionSensors,
TestTemplateLuxSensors,
TestTemplatePresence,
} = require(`./testTemplates`);
/**
* State create, extend objects and subscribe states
* @param adapter
*/
async function Init(adapter) {
adapter.writeLog(`Init is starting...`, "info");
if (adapter.DevMode) await TestStatesCreate(adapter);
await GlobalLuxHandling(adapter);
await GlobalPresenceHandling(adapter);
await StatesCreate(adapter);
const latlng = await adapter.GetSystemData();
if (latlng) lightHandling.AdaptiveCt(adapter);
adapter.writeLog(`Init finished.`, "info");
adapter.writeLog(`Init => Created LightGroups Memory: ${JSON.stringify(adapter.LightGroups)}`);
}
/**
* Create LightGroups Object
* @description Creates Object LightGroups from system.config array
*/
async function CreateLightGroupsObject(adapter) {
try {
if (adapter.Settings.LightGroups && adapter.Settings.LightGroups.length) {
adapter.writeLog(`[ CreateLightGroupsObject ] LightGroups are defined in instance settings`);
const regex = /^[a-zA-Z0-9_-]*$/; // Regulärer Ausdruck zur Überprüfung von erlaubten Zeichen
adapter.Settings.LightGroups.forEach(({ Group, GroupLuxSensor }) => {
if (!regex.test(Group)) {
// Überprüfen, ob "Group" nur erlaubte Zeichen enthält
adapter.writeLog(
`[ CreateLightGroupsObject ] Group "${Group}" contains invalid characters. Please update the group name in instance setting. Skipping...`,
"warn",
);
return; // Überspringen des Loops, wenn "Group" ungültige Zeichen enthält
}
adapter.LightGroups[Group] = {
description: Group,
LuxSensor: GroupLuxSensor,
lights: [],
sensors: [],
};
});
adapter.writeLog(`[ CreateLightGroupsObject ] LightGroups: ${JSON.stringify(adapter.LightGroups)}`);
} else {
adapter.writeLog(`[ CreateLightGroupsObject ] No LightGroups defined in instance settings!`, "warn");
}
} catch (error) {
adapter.errorHandling(error, "CreateLightGroupsObject");
}
}
/**
* GlobalLuxHandling
* @description If a global lux sensor has been defined, its value is written to the global variable and the state is subscribed.
* @param adapter Adapter-Class
*/
async function GlobalLuxHandling(adapter) {
try {
const { Settings } = adapter;
const { GlobalLuxSensor } = Settings;
if (!GlobalLuxSensor) {
adapter.ActualGenericLux = 0;
return;
}
const actualGenericLux = await adapter.getForeignStateAsync(GlobalLuxSensor);
if (!actualGenericLux || actualGenericLux.val === "") {
adapter.ActualGenericLux = 0;
return;
}
adapter.ActualGenericLux = actualGenericLux.val;
if (typeof adapter.ActualGenericLux !== "number") {
adapter.log.warn(
`[ GlobalLuxHandling ] ActualGenericLux ObjectID "${GlobalLuxSensor}" is not a number: ${adapter.ActualGenericLux}`,
);
return;
}
await adapter.subscribeForeignStatesAsync(GlobalLuxSensor);
adapter.LuxSensors.push(GlobalLuxSensor);
} catch (error) {
adapter.errorHandling(error, "GlobalLuxHandling");
}
}
/**
* DoAllTheSensorThings
* @param adapter Adapter-Class
* @param {string} Group Group of Lightgroups eg. LivingRoom, Children1,...
*/
async function DoAllTheMotionSensorThings(adapter, Group) {
try {
const LightGroups = adapter.LightGroups;
adapter.writeLog(`[ DoAllTheMotionSensorThings ] Reaching, Group = "${Group}`);
for (const sensor of LightGroups[Group].sensors) {
const _motionState = await adapter.getForeignStateAsync(sensor.oid);
if (_motionState) {
sensor.isMotion = _motionState.val == sensor.motionVal;
adapter.log.debug(
`[ DoAllTheMotionSensorThings ] Group="${Group}" SensorID="${sensor.oid}" MotionVal="${sensor.isMotion}"`,
);
await adapter.subscribeForeignStatesAsync(sensor.oid);
adapter.MotionSensors.push(sensor.oid);
} else {
adapter.log.debug(
`[ DoAllTheMotionSensorThings ] Group="${Group}" ${sensor.oid} has no data, skipping subscribe`,
);
}
}
} catch (error) {
adapter.errorHandling(error, "DoAllTheMotionSensorThings");
}
}
/**
* DoAllTheLuxSensorThings
* @description Read the current lux value per group. However, if no individual lux sensor has been defined, a global lux sensor is assigned to the group.
* @param adapter Adapter-Class
* @param {string} Group Group of Lightgroups eg. LivingRoom, Children1,...
*/
async function DoAllTheLuxSensorThings(adapter, Group) {
const LightGroups = adapter.LightGroups;
try {
const luxSensor = LightGroups[Group].LuxSensor || adapter.Settings.GlobalLuxSensor;
LightGroups[Group].actualLux = 0;
if (luxSensor) {
if (luxSensor === adapter.Settings.GlobalLuxSensor) {
LightGroups[Group].actualLux = adapter.ActualGenericLux ?? null;
LightGroups[Group].LuxSensor = luxSensor;
adapter.writeLog(`[ DoAllTheLuxSensorThings ] Group "${Group}" using global luxsensor.`);
} else {
const individualLux = await adapter.getForeignStateAsync(luxSensor);
if (individualLux !== null) {
const individualLuxVal = individualLux.val;
if (typeof individualLuxVal === "number" && individualLuxVal.toString().length !== 0) {
LightGroups[Group].actualLux = individualLuxVal;
await adapter.subscribeForeignStatesAsync(luxSensor);
adapter.LuxSensors.push(luxSensor);
adapter.writeLog(
`[ DoAllTheLuxSensorThings ] Group="${Group}" using individual luxsensor "${luxSensor}", value is: ${LightGroups[Group].actualLux}`,
);
} else {
adapter.writeLog(
`[ DoAllTheLuxSensorThings ] Group="${Group}" using individual luxsensor "${luxSensor}" which is not a number or it is empty!!!`,
"warn",
);
}
}
}
} else {
LightGroups[Group].actualLux = 0;
adapter.writeLog(
`[ DoAllTheLuxSensorThings ] No Luxsensor for Group="${Group}" defined, set actualLux = 0, skip handling`,
);
}
} catch (error) {
adapter.errorHandling(error, "DoAllTheLuxSensorThings");
}
}
/**
* GlobalPresenceHandling
* @param adapter
*/
async function GlobalPresenceHandling(adapter) {
try {
if (adapter.Settings.PresenceCountDp) {
adapter.writeLog(`[ GlobalPresenceHandling ] PresenceCounteDp=${adapter.Settings.PresenceCountDp}`);
adapter.ActualPresenceCount.newVal = (
await adapter.getForeignStateAsync(adapter.Settings.PresenceCountDp)
).val;
if (typeof adapter.ActualPresenceCount.newVal === "number") {
//Subscribe PresenceCountDp
await adapter.subscribeForeignStatesAsync(adapter.Settings.PresenceCountDp);
} else {
adapter.writeLog(
`[ GlobalPresenceHandling ] PresenceCounterDp=${adapter.Settings.PresenceCountDp} is not type="number"!`,
"warn",
);
}
}
if (adapter.Settings.IsPresenceDp) {
adapter.writeLog(`[ GlobalPresenceHandling ] IsPresenceDp=${adapter.Settings.IsPresenceDp}`);
adapter.ActualPresence = (await adapter.getForeignStateAsync(adapter.Settings.IsPresenceDp)).val;
if (typeof adapter.ActualPresence === "boolean") {
//Subscribe IsPresenceDp
await adapter.subscribeForeignStatesAsync(adapter.Settings.IsPresenceDp);
} else {
adapter.writeLog(
`[ GlobalPresenceHandling ] isPresenceDp=${adapter.Settings.IsPresenceDp} is not type="boolean"!`,
"warn",
);
}
}
adapter.ActualPresence = adapter.ActualPresenceCount.newVal == 0 ? false : true;
} catch (error) {
adapter.errorHandling(error, "GlobalPresenceHandling");
}
}
/**
* State create, extend objects and subscribe states
* @param adapter
*/
async function StatesCreate(adapter) {
adapter.log.debug("[ StatesCreate ] Start Creating devices...");
const LightGroups = adapter.LightGroups;
const keepStates = [];
const keepChannels = [];
const keepDevices = [];
keepStates.push("info.connection");
keepChannels.push("info");
/** @type {ioBroker.State} */
let state;
//Loop LightGroups and create devices
for (const Group in LightGroups) {
if (Group === "All") continue;
//Create device if not exist
await CreateDevice(adapter, Group, Group);
keepDevices.push(Group);
await DoAllTheMotionSensorThings(adapter, Group);
await DoAllTheLuxSensorThings(adapter, Group);
try {
for (const prop1 in DeviceTemplate) {
const dp = Group + "." + prop1;
// Check for second layer
if (typeof DeviceTemplate[prop1].name == "undefined") {
//Create channel if not exist
await CreateChannel(adapter, dp, Group + " " + prop1);
keepChannels.push(dp);
for (const key in DeviceTemplate[prop1]) {
const dp = Group + "." + prop1 + "." + key;
const common = DeviceTemplate[prop1][key];
//create datapoint
await CreateStates(adapter, dp, common);
keepStates.push(dp);
//Get value of state and set value to LightGroup
state = await adapter.getStateAsync(dp);
if (state != null) {
await helper.SetValueToObject(LightGroups, dp, state.val);
} else {
adapter.writeLog(`[ StatesCreate ] State: "${dp}" is NULL or undefined!`, "warn");
}
// Subscribe on state changes if writable
common.write && (await adapter.subscribeStatesAsync(dp));
}
} else {
const common = DeviceTemplate[prop1];
//create datapoint
await CreateStates(adapter, dp, common);
keepStates.push(dp);
//Get value of state and set value to LightGroup
state = await adapter.getStateAsync(dp);
if (prop1 === "power") {
adapter.writeLog(
`[ StatesCreate ] Group="${Group}", Prop1="${prop1}", powerNewVal="${state.val}"`,
);
await helper.SetValueToObject(LightGroups[Group], "powerNewVal", state.val);
}
if (state !== null) {
await helper.SetValueToObject(LightGroups, dp, state.val);
} else {
adapter.writeLog(`[ StatesCreate ] State: "${dp}" is NULL or undefined!`, "warn");
}
// Subscribe on state changes if writable
common.write && (await adapter.subscribeStatesAsync(dp));
}
}
await helper.SetValueToObject(
LightGroups[Group],
["autoOnLux.dailyLockCounter", "autoOffLux.dailyLockCounter"],
0,
);
} catch (error) {
adapter.errorHandling(error, "StateCreate");
}
}
//Create All-Channel if not exists
await CreateDevice(adapter, "All", "Controll all groups together");
keepDevices.push("All");
for (const prop1 in DeviceAllTemplate) {
try {
const dp = "All." + prop1;
const common = DeviceAllTemplate[prop1];
await CreateStates(adapter, dp, common);
keepStates.push(dp);
//Get value of state and set value to LightGroup
const state = await adapter.getStateAsync(dp);
if (prop1 === "power") {
adapter.writeLog(`[ StateCreate ] Group="All", Prop1="${prop1}", powerNewVal="${state.val}"`);
await helper.SetValueToObject(LightGroups, "All.powerNewVal", state.val);
}
if (state != null) {
await helper.SetValueToObject(LightGroups, dp, state.val);
} else {
adapter.writeLog(`[ StateCreate ] State: "${dp}" is NULL or undefined!`, "warn");
}
// Subscribe on state changes if writable
common.write && (await adapter.subscribeStatesAsync(dp));
} catch (error) {
adapter.errorHandling(error, "StateCreate");
}
}
// Delete non existent states, channels and devices
const allStates = [];
const allChannels = [];
const allDevices = [];
const objects = await adapter.getAdapterObjectsAsync();
for (const o in objects) {
const parts = o.split(".");
if (parts[2] != "info") {
const id = await helper.removeNamespace(adapter, objects[o]._id);
if (objects[o].type == "state") {
allStates.push(id);
} else if (objects[o].type == "channel") {
allChannels.push(id);
} else if (objects[o].type == "device") {
allDevices.push(id);
}
}
}
try {
for (let i = 0; i < allStates.length; i++) {
const id = allStates[i];
if (keepStates.indexOf(id) === -1) {
adapter.delObject(id, { recursive: true }, () => {
adapter.writeLog("[ StateCreate ] State deleted " + id);
});
}
}
for (let i = 0; i < allChannels.length; i++) {
const id = allChannels[i];
if (keepChannels.indexOf(id) === -1) {
adapter.delObject(id, { recursive: true }, () => {
adapter.writeLog("[ StateCreate ] Channel deleted" + id);
});
}
}
for (let i = 0; i < allDevices.length; i++) {
const id = allDevices[i];
if (keepDevices.indexOf(id) === -1) {
adapter.delObject(id, { recursive: true }, () => {
adapter.writeLog("[ StateCreate ] Devices deleted" + id);
});
}
}
} catch (error) {
adapter.errorHandling(error, "StateCreate / Delete");
}
return LightGroups;
}
/**
* clean Dev_Mode userdata
*/
async function CleanUserData(adapter) {
const userdata = "0_userdata.0.lightcontrol_DEV.";
try {
adapter.delObject(userdata, { recursive: true }, () => {
adapter.writeLog("Test objects deleted" + userdata);
});
} catch (error) {
adapter.errorHandling(error, "CleanUserData");
}
}
/**
* State create, extend objects and subscribe states
*/
async function TestStatesCreate(adapter) {
adapter.log.debug("Creating Test devices...");
const userdata = "0_userdata.0.lightcontrol_DEV.";
try {
//Loop TestLamps and create datapoints to 0_userdata.0
for (const Lamp in TestTemplateLamps) {
//Create Lamp if not exist
await adapter.setForeignObjectNotExistsAsync(userdata + "Lamps." + Lamp, {
type: "channel",
common: { name: Lamp },
native: {},
});
for (const prop1 in TestTemplateLamps[Lamp]) {
const common = TestTemplateLamps[Lamp][prop1];
const dp = userdata + "Lamps." + Lamp + "." + prop1;
await CreateStates(adapter, dp, common, true);
}
}
//Loop Test Motion Sensors and create datapoints to 0_userdata.0
for (const MotionSensor in TestTemplateMotionSensors) {
const common = TestTemplateMotionSensors[MotionSensor];
const dp = userdata + "MotionSensors." + MotionSensor;
await CreateStates(adapter, dp, common, true);
}
//Loop Test Lux Sensors and create datapoints to 0_userdata.0
for (const LuxSensor in TestTemplateLuxSensors) {
const common = TestTemplateLuxSensors[LuxSensor];
const dp = userdata + "LuxSensors." + LuxSensor;
await CreateStates(adapter, dp, common, true);
}
//Loop Test Presence and create datapoints to 0_userdata.0
for (const Presence in TestTemplatePresence) {
const common = TestTemplatePresence[Presence];
const dp = userdata + "Presence." + Presence;
await CreateStates(adapter, dp, common, true);
}
} catch (error) {
adapter.errorHandling(error, "TestStatesCreate");
}
}
/**
* Create datapoint and extend datapoints
* @author Schmakus
* @async
* @param {Object} adapter
* @param {string} dp path to datapoint
* @param {ioBroker.StateCommon} common type of datapoint, e.g. string, number, boolean, ...
* @param {boolean} [foreign = false] set adapter states = false; set foreign states = true
*/
async function CreateStates(adapter, dp, common, foreign) {
try {
const obj = !foreign ? await adapter.getObjectAsync(dp) : await adapter.getForeignObjectAsync(dp);
if (!obj) {
const obj = {
type: "state",
common: common,
native: {},
};
await (foreign ? adapter.setForeignObjectAsync(dp, obj) : adapter.setObjectAsync(dp, obj));
adapter.writeLog(`[ CreateStates ] State: ${dp} created.`);
} else {
if (JSON.stringify(obj.common) !== JSON.stringify(common) || !("native" in obj)) {
obj.common = common;
obj.native = obj.native ?? {};
await (foreign ? adapter.setForeignObjectAsync(dp, obj) : adapter.setObjectAsync(dp, obj));
}
}
} catch (error) {
adapter.errorHandling(error, "CreateStates");
}
}
/**
* Create channel and extend
* @author Schmakus
* @async
* @param {Object} adapter
* @param {string} dp path to datapoint
* @param {string} name name of the channel
*/
async function CreateChannel(adapter, dp, name) {
try {
await adapter.extendObjectAsync(dp, {
type: "channel",
common: {
name: name,
},
native: {},
});
} catch (error) {
adapter.errorHandling(error, "CreateChannel");
}
}
/**
* Create device and extend
* @author Schmakus
* @async
* @param adapter
* @param {string} dp path to datapoint
* @param {string} name name of the channel
*/
async function CreateDevice(adapter, dp, name) {
try {
await adapter.extendObjectAsync(dp, {
type: "device",
common: {
name: name,
},
native: {},
});
} catch (error) {
adapter.errorHandling(error, "CreateDevice");
}
}
module.exports = {
Init,
CleanUserData,
DoAllTheMotionSensorThings,
CreateLightGroupsObject,
};