Jump to content

Recommended Posts

Posted

I've managed to find the two example scripts and have all Blu events logging to console and now I'm trying to modify the scene from this script

 

https://github.com/ALLTERCO/shelly-script-examples/blob/main/ble-events-handler.js

 

using the example for publishing to mqtt from inside the same script and from this script

 

https://github.com/ALLTERCO/shelly-script-examples/blob/main/ble-events-handler.js

 

but it's throwing an error

 

Uncaught SyntaxError: Got EOF expected '}'

 

anyone know what I've mixed in the wrong order?

 

/** SCENE START 3 - Shelly BLU Scanner example **/ { // when event name is shelly-blu conditions: { event: "shelly-blu", },

 

  action: function (data) {

    MQTT.publish(

      "shellybridge/devices",

      JSON.stringify(data),

    );

  },

},

/** SCENE END 3 **/

Posted

After hours of faff I've traced the issue to an erroneous comma, I've also had contact with a Shelly staff member who has confirmed they will be releasing a specific script for this shortly and I will post again here with details once received.

 

it should have been

 

/** SCENE START 3 - Shelly BLU Scanner example **/ { // when event name is shelly-blu conditions: { event: "shelly-blu", },



  action: function (data) {

    MQTT.publish(

      "shellybridge/devices",

      JSON.stringify(data)

    );

  },

},

/** SCENE END 3 **/

I've also included the full script below which outputs every Blue event via MQTT

 

/**
 * The following example will show how to handle events from Shelly BLU devices what are emited from `ble-shelly-blu.js` example.
 * This script CAN'T be used standone for BLE events. You need the `ble-shelly-blu.js` example running in order to use this 
 * example for automations
 * 
 * The `CONFIG` object contains a scenes property which is an array of scene objects.
 * Each scene object consists of two properties: `conditions` and `action`. The `conditions` property
 * defines the conditions under which the scene should be triggered, and the `action`
 * property defines the function to be executed when the conditions are met.
 *
 * The `conditions` are defined as key-value pairs.
 * These keys correspond to specific data values received with the event.
 * The values associated with these keys can be either a direct value, an object specifying a comparison, or a function that
 * must return boolean value.
 * The `conditions` value supports various types: 
 * - key: value pair, where the key is fetched from the received data and must equal to the target value
 * - function that receives the current value and must return boolean
 * - object with `compare` and `value` keys:
 * Where `compare` supports the following methods: 
 * - "==" -> both values are the same
 * - "<" -> the current value is less than the target value
 * - ">" -> the current value is bigger than the target value
 * - "~=" -> the rounded value of both values are the same
 * - "!=" -> both values are different types
 * - "in" -> (supplied value must be array) the current value is IN the array
 * - "notin" -> (supplied value must be array) the current value is NOT IN the array
 *
 * The `action` property defines a function that receives event's data as an input. You can write custom code within this function to
 * perform specific actions.
 * 
 * Some examples can be seen bellow.
 *
 * Event name for the `ble-shelly-blu.js` example is `shelly-blu`.
 */

/****************** START CHANGE ******************/
let CONFIG = {
  // List of scenes
  scenes: [
    /** SCENE START 1 - Shelly BLU example **/
   
    /** SCENE END 1 **/

    /** SCENE START 2 - Shelly BLU Door/Window example **/
    
    /** SCENE END 2 **/

    /** SCENE START 3 - Shelly BLU Scanner example **/
    {
      // when event name is `shelly-blu`
      conditions: {
        event: "shelly-blu",
      },

      action: function (data) {
        MQTT.publish(
          "shellies/" + data["address"],
          JSON.stringify(data)
        );
      },
    },
    /** SCENE END 3 **/
  ],

  //When set to true, debug messages will be logged to the console
  debug: false,
};
/****************** STOP CHANGE ******************/

// Logs the provided message with an optional prefix to the console
function logger(message, prefix) {
  //exit if the debug isn't enabled
  if (!CONFIG.debug) {
    return;
  }

  let finalText = "";

  //if the message is list loop over it
  if (Array.isArray(message)) {
    for (let i = 0; i < message.length; i++) {
      finalText = finalText + " " + JSON.stringify(message[i]);
    }
  } else {
    finalText = JSON.stringify(message);
  }

  //the prefix must be string
  if (typeof prefix !== "string") {
    prefix = "";
  } else {
    prefix = prefix + ":";
  }

  //log the result
  console.log(prefix, finalText);
}

// Scene Manager object
let SceneManager = {
  scenes: [],

  setScenes: function (scenes) {
    this.scenes = scenes;
  },

  // Process new data and check if any scenes should be executed
  onNewData: function (data) {
    logger(["New data received", JSON.stringify(data)], "Info");
    for (let sceneIndex = 0; sceneIndex < this.scenes.length; sceneIndex++) {
      logger(
        ["Validating conditions for scene with index=", sceneIndex],
        "Info"
      );
      if (this.validateConditionsForScene(sceneIndex, data)) {
        logger(
          ["Conditions are valid for scene with index=", sceneIndex],
          "Info"
        );
        this.executeScene(sceneIndex, data);
      } else {
        logger(
          ["Conditions are invalid for scene with index=", sceneIndex],
          "Info"
        );
      }
    }
  },

  // Event handler for handling events from the device
  eventHandler: function (eventData, sceneEventObject) {
    let info = eventData.info;
    if (typeof info !== "object") {
      console.log("ERROR: ");
      logger("Can't find the info object", "Error");

      return;
    }

    if (typeof info.data === "object") {
      for (let key in info.data) {
        info[key] = info.data[key];
      }

      info.data = undefined;
    }

    sceneEventObject.onNewData(info);
  },

  // Check if the conditions are met
  checkCondition: function (compFunc, currValue, compValue) {
    if (
      typeof currValue === "undefined" ||
      typeof compValue === "undefined" ||
      typeof compFunc === "undefined"
    ) {
      return false;
    }

    if (typeof compFunc === "string") {
      if(compFunc in this.compFuncList) {
        compFunc = this.compFuncList[compFunc];
      }
      else {
        logger(["Unknown comapre function", compFunc], "Error");
      }
    }

    if (typeof compFunc === "function") {
      return compFunc(currValue, compValue);
    }

    return false;
  },

  // Validate conditions for a specific scene based on the received data
  validateConditionsForScene: function (sceneIndex, receivedData) {
    if (
      typeof sceneIndex !== "number" ||
      sceneIndex < 0 ||
      sceneIndex >= this.scenes.length
    ) {
      return false;
    }

    let conditions = this.scenes[sceneIndex].conditions;
    if (typeof conditions === "undefined") {
      return false;
    }

    for (let condKey in conditions) {
      let condData = conditions[condKey];
      let currValue = receivedData[condKey];
      let compValue = condData;
      let compFunc = condData;

      if (typeof condData === "object") {
        compValue = condData.value;
        compFunc = condData.compare;
      } else if (typeof condData !== "function") {
        compFunc = "==";
      }

      if (!this.checkCondition(compFunc, currValue, compValue)) {
        logger(
          ["Checking failed for", condKey, "in scene with index=", sceneIndex],
          "Info"
        );
        return false;
      }
    }

    return true;
  },

  // Execute the action for a specific scene
  executeScene: function (sceneIndex, data) {
    if (
      typeof sceneIndex !== "number" ||
      sceneIndex < 0 ||
      sceneIndex >= this.scenes.length
    ) {
      return;
    }

    let func = this.scenes[sceneIndex].action;
    if (typeof func === "function") {
      logger(["Executing action for scene with index=", sceneIndex], "Info");
      func(data);
    }
  },

  // Comparison functions used for validating conditions
  compFuncList: {
    "==": function (currValue, compValue) {
      if (typeof currValue !== typeof compValue) {
        return false;
      }

      return currValue === compValue;
    },
    "~=": function (currValue, compValue) {
      if (typeof currValue !== "number" || typeof compValue !== "number") {
        return false;
      }

      return Math.round(currValue) === Math.round(compValue);
    },
    ">": function (currValue, compValue) {
      if (typeof currValue !== "number" || typeof compValue !== "number") {
        return false;
      }

      return currValue > compValue;
    },
    "<": function (currValue, compValue) {
      if (typeof currValue !== "number" || typeof compValue !== "number") {
        return false;
      }

      return currValue < compValue;
    },
    "!=": function (currValue, compValue) {
      return !this.compFuncList["=="](currValue, compValue);
    },
    "in": function (currValue, compValue) {
      if (
        typeof currValue !== "undefined" &&
        typeof compValue !== "undefined" &&
        !Array.isArray(compValue)
      ) {
        return false;
      }

      return currValue in compValue;
    },
    "notin": function (currValue, compValue) {
      return !this.compFuncList["in"](currValue, compValue);
    },
  },
};

// Initialize function for the scene manager and register the event handler
function init() {
  SceneManager.setScenes(CONFIG.scenes);
  Shelly.addEventHandler(SceneManager.eventHandler, SceneManager);
  logger("Scene Manager successfully started", "Info");
}

init();

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...