So, I’m a few stages further now. This is my current approach. This is my first flow in combination with my first Homey Script. So any feedback is welcome!
This is the flow. I’m still bummed out that I can’t add trigger listeners to HS. Now I have to add 30 triggers when using 10 scenes. But ay, let’s move on.
_settings-motion-lights.js // still have to play with the lux settings and add device ids
return {
"global": {
"enabledID": "48726aca-1784-4328-9fd0-2ee6b9d33e4e" // virtual device to enable/disable global motion sensor use
},
"Keuken": {
"enabled": true,
"multisensors": [
"9d4348de-c1c3-4341-a6e7-7f33c20b63dd",//"keuken multisensor",
"319d966f-638e-44b0-9f76-7610ced72ee9",//"bijkeuken multisensor"
],
"min_lux": 0,
"max_lux": 200,
"lights": [
"94254ea1-4e87-4763-991d-6eedc52a36aa",//"keuken lamp",
"433639e7-bc41-4a2a-a701-eb383da1ea19",//"bijkeuken lamp"
],
"dim_min": 1,
"dim_max": 10,
"on_time": 1,
"debouce_time": 1,
},
"Gang Beneden": {
"enabled": true,
"multisensors": [
"gang beneden multisensor"// TODO: add device ID
],
"min_lux": 0,
"max_lux": 200,
"lights": [
"gang beneden lamp"// TODO: add device ID
],
"dim_min": 1,
"dim_max": 15,
"on_time": 5,
"debouce_time": 1,
},
"Gang Boven": {
"enabled": true,
"multisensors": [
"gang boven multisensor voor",// TODO: add device ID
"gang boven multisensor achter"// TODO: add device ID
],
"min_lux": 0,
"max_lux": 200,
"lights": [
"gang boven lamp"// TODO: add device ID
],
"dim_min": 1,
"dim_max": 15,
"on_time": 5,
"debouce_time": 1,
},
"Toilet": {
"enabled": true,
"multisensors": [
"toilet multisensor"// TODO: add device ID
],
"min_lux": 0,
"max_lux": 300,
"lights": [
"gang boven lamp"// TODO: add device ID
],
"dim_min": 1,
"dim_max": 40,
"on_time": 15,
"debouce_time": 1,
}
}
light-motion-sensors.js
// dev settings
const debug = true;
const exec = false;
const skipDebounce = false;
// process arguments
let arguments = JSON.parse(typeof args[0] !== 'undefined' ? args[0] : '{"scene":"Keuken","command":"on"}');
// let arguments = JSON.parse(typeof args[0] !== 'undefined' ? args[0] : '{"scene":"Keuken","command":"off"}');
// let arguments = JSON.parse(typeof args[0] !== 'undefined' ? args[0] : '{"scene":"Keuken","command":"off_by_user"}');
// There must be a better way to import a script, right? why no import or require?
let settings = await Homey.apps.getApp({ id: 'com.athom.homeyscript' })
.then(homeyScript => { return homeyScript.apiPost('script/11e33a58-bc05-4110-993e-da8759dec050/run'); })
settings = settings.returns;
let sceneSettings = settings[arguments.scene];
sceneSettings.timestampKey = `user_light_off_timestamp_${arguments.scene.toLowerCase()}`
const globalSettings = settings['global'];
const motionsensortAreEnabled = true;//await getEnabledSetting(globalSettings);
const writeOnTimeLine = async (message) => {
await Homey.flow.runFlowCardAction({
uri: "homey:manager:notifications:create_notification",
id: "homey:manager:notifications:create_notification",
args: {
text: JSON.stringify(message)
},
});
}
const map = (value, oldRange, newRange) => {
var newValue = (value - oldRange[0]) * (newRange[1] - newRange[0]) / (oldRange[1] - oldRange[0]) + newRange[0];
return Math.min(Math.max(newValue, newRange[0]), newRange[1]);
}
const getDeviceByID = async (deviceID) => {
return await Homey.devices.getDevice({ $skipCache: false, id: deviceID });
}
const getCapability = async (device, capability) => {
return await device.capabilitiesObj[capability].value;
}
const setCapability = async (device, capability, value) => {
return await device.setCapabilityValue(capability, value).catch(this.error);
}
const getCapabilityByDeviceID = async (deviceID, capability) => {
return await getCapability(await getDeviceByID(deviceID), capability)
}
const setCapabilityByDeviceID = async (deviceID, capability, value) => {
return await setCapability(await getDeviceByID(deviceID), capability, value)
}
const getEnabledSetting = async (globalSettings) => {
return await getCapabilityByDeviceID(globalSettings.enabledID, 'onoff')
}
const getDeviceLux = async (device) => {
return await getCapability(device, 'measure_luminance');
}
const turnOn = async () => {
if (settings && motionsensortAreEnabled && sceneSettings && sceneSettings.enabled) {
const lastUserOffTimestamp = global.get(sceneSettings.timestampKey);
const currentTimestamp = Date.now();
const debounceTimestamp = (lastUserOffTimestamp + (sceneSettings.on_time * 1000 * 60));
if (currentTimestamp > debounceTimestamp || skipDebounce) {
let multisensors = [];
for await (const multisensorID of sceneSettings.multisensors) {
multisensors.push(await getDeviceByID(multisensorID))
}
const curLux = await getDeviceLux(multisensors[0]);
const dimLevel = Math.round(map(
curLux,
[sceneSettings.min_lux, sceneSettings.max_lux],
[sceneSettings.dim_min, sceneSettings.dim_max]
));
for (const lightID of sceneSettings.lights) {
const lightIsOn = await getCapabilityByDeviceID(lightID, 'onoff')
if (!lightIsOn) {
// if dim_min === 100 then the light is only on/off, so not dimmable
if (sceneSettings.dim_min < 100) {
if (debug) {
const message = `Dim Light: ${arguments.scene}`;
log(message)
writeOnTimeLine(message)
}
if (exec) {
setCapabilityByDeviceID(lightID, 'dim', dimLevel)
}
} else {
if (debug) {
const message = `Switch on Light: ${arguments.scene}`;
log(message)
writeOnTimeLine(message)
}
if (exec) {
setCapabilityByDeviceID(lightID, 'onoff', true)
}
}
}
}
} else {
if (debug) {
const message = `${arguments.scene} motionsensor was tried to turn on within debounce time. Skipping action.`;
log(message)
writeOnTimeLine(message)
}
}
}
}
const turnOff = async () => {
if (settings && motionsensortAreEnabled && sceneSettings && sceneSettings.enabled) {
await wait(sceneSettings.time_on * 60);
let allMotionAlarmsAreOff = true;
for await (const multisensorID of sceneSettings.multisensors) {
const motionAlarmIsOn = await getCapabilityByDeviceID(multisensorID, 'alarm_motion')
if (motionAlarmIsOn) {
allMotionAlarmsAreOff = false
}
}
if (allMotionAlarmsAreOff) {
for (const lightID of sceneSettings.lights) {
if (exec) {
setCapabilityByDeviceID(lightID, 'onoff', true)
}
if (debug) {
const message = `${arguments.scene} motionsensor waited and turned off light.`;
log(message)
writeOnTimeLine(message)
}
}
}
}
}
const userOff = async () => {
if (settings && sceneSettings && sceneSettings.enabled) {
global.set(sceneSettings.timestampKey, Date.now());
}
}
switch (arguments.command) {
case 'on':
turnOn();
break;
case 'off':
turnOff();
break;
case 'off_by_user':
userOff();
break
}