Generated device triggers from Homey Script

Alright, alright, the title is a bit of a click-bait.
BUT, I was able to create a Homey Script that generates triggers. The script generates an advanced Flow, which in turn has generates trigger cards.

This is where my question started

I think it’s not the cleanest way to work, but I did reach my goal. And that was to generate a triggers for devices. This way, if you have about 15 settings, you don’t have to manually click and create the flow. Why? If I have 15 scenes and I change my logic I might have to recreate 15 scenes * 4 triggers = 60 cards… Yeah, no way.

This way I can rethink my logic and re-generate the whole flow. I would still have preferred to have this all in HS but hey, it’s a start.

This is the generated flow:

And this is part of 3 Homey Scripts for the actions.

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;

const cardDistXBase = 0;
const cardDistX = 400;
const cardDistYBase = 100;
const cardDistY = 120;

const scriptID = '58fbd2be-15ac-4cac-81ce-61a8ff9064c5';

const globalSettings = settings['global'];
let flowObject = {
  advancedflow: {
    "name": "motion_alarm_test",
    "enabled": false,
    "cards": {}
  }
}

const addMotionTriggerCard = (sceneIndex, sceneKey, scene, sensorID, command) => {
  flowObject.advancedflow.cards[`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-${sensorID}-${command}`] = {
    ownerUri: `homey:device:${sensorID}`,
    id: `homey:device:${sensorID}:alarm_motion_${(command === 'on').toString()}`,
    type: 'trigger',
    x: cardDistXBase + (colIndex * cardDistX),
    y: cardDistYBase + (rowIndex * cardDistY),
    outputSuccess: [`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-homeyscript-${command}`]
  }
}
const addMotionThenCardScript = (sceneIndex, sceneKey, scene, command) => {
  flowObject.advancedflow.cards[`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-homeyscript-${command}`] = {
    ownerUri: 'homey:app:com.athom.homeyscript',
    id: 'homey:app:com.athom.homeyscript:runWithArg',
    args: {
      script: {
        id: scriptID,
        name: 'light-motion-sensors'
      },
      argument: `{"scene":"${sceneKey}","command":"${command}"}`
    },
    type: 'action',
    x: cardDistXBase + (colIndex * cardDistX),
    y: cardDistYBase + (rowIndex * cardDistY),
  }
}

const addButtonTriggerCard = (sceneIndex, sceneKey, scene, deviceID, button) => {
  let buttonID = null;
  switch (button) {
    case 's2':
      buttonID = '26'
      break;
    case 's1':
    default:
      buttonID = '16'
      break;
  }
  flowObject.advancedflow.cards[`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-${deviceID}-${button}`] = {
    ownerUri: `homey:device:${deviceID}`,
    id: `homey:device:${deviceID}:FGD-212_momentary`,
    args: { scene: buttonID },
    type: 'trigger',
    x: cardDistXBase + (colIndex * cardDistX),
    y: cardDistYBase + (rowIndex * cardDistY),
    outputSuccess: [`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-${deviceID}-is-on`]
  }
}

const addButtonConditionCard = (sceneIndex, sceneKey, scene, deviceID, command) => {
 
  flowObject.advancedflow.cards[`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-${deviceID}-is-on`] = {
    ownerUri: `homey:device:${deviceID}`,
    id: `homey:device:${deviceID}:on`,
    type: 'condition',
    x: cardDistXBase + (colIndex * cardDistX),
    y: cardDistYBase + (rowIndex * cardDistY),
    outputFalse: [`${sceneIndex}-${sceneKey.toLowerCase().replaceAll(' ', '-')}-homeyscript-${command}`]
  }

}


const addMotionFlows = (sceneIndex, sceneKey, scene, command) => {
  let sensorCount = 0;
  for (const [sensorKey, sensorID] of Object.entries(scene.multisensors)) {
    addMotionTriggerCard(sceneIndex, sceneKey, scene, sensorID, command)
    if (sensorCount === scene.multisensors.length - 1) {
      colIndex++;
      addMotionThenCardScript(sceneIndex, sceneKey, scene, command)
    }
    rowIndex++;
    sensorCount++;
  }
}

const addButtonFlows = (sceneIndex, sceneKey, scene, command) => {
  let lightCount = 0;
  for (const [lightKey, deviceID] of Object.entries(scene.lights)) {

    addButtonTriggerCard(sceneIndex, sceneKey, scene, deviceID, 's1')
    rowIndex++;
    
    addButtonTriggerCard(sceneIndex, sceneKey, scene, deviceID, 's2')
    colIndex++;

    addButtonConditionCard  (sceneIndex, sceneKey, scene, deviceID, command);

    if (lightCount === scene.multisensors.length - 1) {
      colIndex++;
      addMotionThenCardScript(sceneIndex, sceneKey, scene, command)
    }
    colIndex--;
    rowIndex++;
    lightCount++;
  }
}


let sceneIndex = 0;
let rowIndex = 0;
let colIndex = 0;
const createCards = () => {

  for (const [sceneKey, scene] of Object.entries(settings.scenes)) {
    colIndex = 0;
    if (scene.enabled || true) {
      addMotionFlows(sceneIndex, sceneKey, scene, 'on')
    }
    sceneIndex++;
  }
  sceneIndex = 0;
  rowIndex = 0;
  for (const [sceneKey, scene] of Object.entries(settings.scenes)) {
    colIndex = 2;
    if (scene.enabled || true) {
      addMotionFlows(sceneIndex, sceneKey, scene, 'off')
    }
    sceneIndex++;
  }

  sceneIndex = 0;
  rowIndex = 0;
  for (const [sceneKey, scene] of Object.entries(settings.scenes)) {
    colIndex = 4;
    if (scene.enabled || true) {
      addButtonFlows(sceneIndex, sceneKey, scene, 'off_by_user')
    }
    sceneIndex++;
  }
}
createCards();


let advancedFlowId = null;
const flows = await Homey.flow.getAdvancedFlows()
for (const flow of Object.values(flows)) {
  if (flow.name === flowObject.advancedflow.name) {
    advancedFlowId = flow.id;
  }
}

let result = false;
if (advancedFlowId) {
  flowObject.id = advancedFlowId;
  result = await Homey.flow.updateAdvancedFlow(flowObject)
} else {
  result = await Homey.flow.createAdvancedFlow(flowObject)
}

Yes, it’s messy as hell. But that’s best I could do in my lunch-break :slight_smile:
I’ll clean up the code later.

The input is this script:

return {
  "global": {
    "enabledID": "48726aca-1784-4328-9fd0-2ee6b9d33e4e" // virtual device to enable/disable global motion sensor use
  },
  "scenes": {
    "Keuken": {
      "enabled": true,
      "multisensors": [
        "9d4348de-c1c3-4341-a6e7-7f33c20b63dd",//"keuken multisensor",
        "319d966f-638e-44b0-9f76-7610ced72ee9",//"bijkeuken multisensor"
      ],
      "min_lux": 0,
      "max_lux": 20,
      "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": [
        "f996edfb-06af-4f14-be03-fd9288645ded",//"gang beneden multisensor"// TODO: add device ID
      ],
      "min_lux": 0,
      "max_lux": 10,
      "lights": [
        "06b25af3-c93e-46d6-b1d5-fa7e6a0f18ee",//"gang beneden lamp"// TODO: add device ID
      ],
      "dim_min": 1,
      "dim_max": 15,
      "on_time": 1,
      "debouce_time": 1,
    },
    "Gang Boven": {
      "enabled": false,
      "multisensors": [
        "5823806e-2fd7-4eae-b88f-6e4ea5957f95",//"gang boven multisensor voor",// TODO: add device ID
      ],
      "min_lux": 0,
      "max_lux": 10,
      "lights": [
        "ce713301-f469-484c-b455-196c20063dab",//"gang boven lamp"// TODO: add device ID
      ],
      "dim_min": 1,
      "dim_max": 3,
      "on_time": 1,
      "debouce_time": 1,
    },
    "Toilet": {
      "enabled": true,
      "multisensors": [
        "83e6b629-076c-4be6-b2d2-a633374f9b97",//"toilet multisensor"// TODO: add device ID
      ],
      "min_lux": 0,
      "max_lux": 20,
      "lights": [
        "4d8f4ec9-4540-411e-ad94-5eeaa689420f",//"gang boven lamp"// TODO: add device ID
      ],
      "dim_min": 1,
      "dim_max": 5,
      "on_time": 10,
      "debouce_time": 1,
    }
  }
}
1 Like

Hi Tim, how did you read the scriptid. I cannot find any id for scripts.