Jump to content
Gen2 Devices: FW Update Required / Gen2 Geräte: FW-Update erforderlich ×

Script with the use of chatgpt


Recommended Posts

  • Members
Posted (edited)

I am using two BLU Motion sensors to automate a Shelly 1PM Gen3 and control a relay connected to an air conditioner. The goal is to disconnect the relay after a specific period (currently set to 5 minutes but will be extended to 30 minutes) if no movement is detected during that time. Additionally, I have set a schedule to disable the automation at night (between 10 PM and 8 AM) and during weekends.

As I am not an expert in creating scripts, I utilized some templates from the library and the help of ChatGPT to achieve a working solution. This is still a work in progress, as I plan to add more sensors in the future (such as window and motion sensors). The schedule functionality has not been fully tested yet due to the time consuming nature of following the actual schedule.

 


/******************* START CHANGE HERE *******************/
let CONFIG = {
  debug: false,
  active: false,
  allowedMacAddresses: [
    'E8:E0:7E:D0:AE:C3', 'B0:C7:DE:C4:D7:36'
  ],

  // To store the state of motion from each sensor
  motionState: {
    'E8:E0:7E:D0:AE:C3': false,
    'B0:C7:DE:C4:D7:36': false
  },

  // Timer for turning off the relay
  offTimer: null,

  // Define the start and end times of the interval (in minutes)
  intervalStartFriday: (5 * 24 * 60) + (15 * 60), // Friday 15:00
  intervalEndSaturday: (6 * 24 * 60) + (21 * 60), // Saturday 21:00
  nightlyIntervalStart: 22 * 60, // 22:00
  nightlyIntervalEnd: 8 * 60, // 08:00

  isWithinBlockedInterval: function() {
    let now = new Date();
    let day = now.getDay(); // 0 = Sunday, 1 = Monday, ..., 5 = Friday, 6 = Saturday
    let hour = now.getHours();
    let minute = now.getMinutes();
    let currentTime = (day * 24 * 60) + (hour * 60) + minute;

    // Check if the current time is within the blocked interval on Friday to Saturday
    let withinWeekendBlockedInterval = currentTime >= CONFIG.intervalStartFriday && currentTime <= CONFIG.intervalEndSaturday;

    // Check if the current time is within the nightly blocked interval
    let withinNightlyBlockedInterval = (hour >= 22 && hour < 24) || (hour >= 0 && hour < 8);

    return withinWeekendBlockedInterval || withinNightlyBlockedInterval;
  },

  motionHandler: function (motion, eventData) {
    // Update the motion state of the corresponding sensor
    CONFIG.motionState[eventData.address.toLowerCase()] = motion;

    // Check if there is any motion detected by either sensor
    let anyMotion = CONFIG.motionState['e8:e0:7e:d0:ae:c3'] || CONFIG.motionState['b0:c7:de:c4:d7:36'];

    // If any motion is detected, clear the timer
    if (anyMotion) {
      if (CONFIG.offTimer) {
        Timer.clear(CONFIG.offTimer);
        CONFIG.offTimer = null;
      }
    } else {
      // If no motion is detected from both sensors, set a timer to turn off the relay after 5 minutes
      if (!CONFIG.offTimer && !CONFIG.isWithinBlockedInterval()) {
        CONFIG.offTimer = Timer.set(300000, false, function () {
          Shelly.call("Switch.Set", { id: 0, on: false }, function (result) {
            console.log("Relay turned off due to no motion:", JSON.stringify(result));
          });
          CONFIG.offTimer = null; // Clear the timer reference
        });
      }
    }
    console.log("Motion detected from", eventData.address, ":", motion);
  },

  illuminanceHandler: function (illuminance, eventData) {
    let topic = eventData.address + "/illuminance";
    MQTT.publish(topic, String(illuminance));
  },

  onStatusUpdate: function (eventData) {
    // Do nothing at the moment.
  }
};
/******************* STOP CHANGE HERE *******************/

let ALLTERCO_MFD_ID_STR = "0ba9";
let BTHOME_SVC_ID_STR = "fcd2";

let uint8 = 0;
let int8 = 1;
let uint16 = 2;
let int16 = 3;
let uint24 = 4;
let int24 = 5;

function logger(message, prefix) {
  if (!CONFIG.debug) {
    return;
  }

  let finalText = "";

  if (Array.isArray(message)) {
    for (let i = 0; i < message.length; i++) {
      finalText = finalText + " " + JSON.stringify(message[i]);
    }
  } else {
    finalText = JSON.stringify(message);
  }

  if (typeof prefix !== "string") {
    prefix = "";
  } else {
    prefix = prefix + ":";
  }

  console.log(prefix, finalText);
}

let BTH = {};
BTH[0x00] = { n: "pid", t: uint8 };
BTH[0x01] = { n: "battery", t: uint8, u: "%" };
BTH[0x02] = { n: "temperature", t: int16, f: 0.01, u: "tC" };
BTH[0x03] = { n: "humidity", t: uint16, f: 0.01, u: "%" };
BTH[0x05] = { n: "illuminance", t: uint24, f: 0.01 };
BTH[0x21] = { n: "motion", t: uint8 };
BTH[0x2d] = { n: "window", t: uint8 };
BTH[0x3a] = { n: "button", t: uint8 };
BTH[0x3f] = { n: "rotation", t: int16, f: 0.1 };

function getByteSize(type) {
  if (type === uint8 || type === int8) return 1;
  if (type === uint16 || type === int16) return 2;
  if (type === uint24 || type === int24) return 3;
  return 255;
}

let BTHomeDecoder = {
  utoi: function (num, bitsz) {
    let mask = 1 << (bitsz - 1);
    return num & mask ? num - (1 << bitsz) : num;
  },
  getUInt8: function (buffer) {
    return buffer.at(0);
  },
  getInt8: function (buffer) {
    return this.utoi(this.getUInt8(buffer), 8);
  },
  getUInt16LE: function (buffer) {
    return 0xffff & ((buffer.at(1) << 8) | buffer.at(0));
  },
  getInt16LE: function (buffer) {
    return this.utoi(this.getUInt16LE(buffer), 16);
  },
  getUInt24LE: function (buffer) {
    return (
      0x00ffffff & ((buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0))
    );
  },
  getInt24LE: function (buffer) {
    return this.utoi(this.getUInt24LE(buffer), 24);
  },
  getBufValue: function (type, buffer) {
    if (buffer.length < getByteSize(type)) return null;
    let res = null;
    if (type === uint8) res = this.getUInt8(buffer);
    if (type === int8) res = this.getInt8(buffer);
    if (type === uint16) res = this.getUInt16LE(buffer);
    if (type === int16) res = this.getInt16LE(buffer);
    if (type === uint24) res = this.getUInt24LE(buffer);
    if (type === int24) res = this.getInt24LE(buffer);
    return res;
  },

  unpack: function (buffer) {
    if (typeof buffer !== "string" || buffer.length === 0) return null;
    let result = {};
    let _dib = buffer.at(0);
    result["encryption"] = _dib & 0x1 ? true : false;
    result["BTHome_version"] = _dib >> 5;
    if (result["BTHome_version"] !== 2) return null;
    if (result["encryption"]) return result;
    buffer = buffer.slice(1);

    let _bth;
    let _value;
    while (buffer.length > 0) {
      _bth = BTH[buffer.at(0)];
      if (typeof _bth === "undefined") {
        logger("unknown type", "BTH");
        break;
      }
      buffer = buffer.slice(1);
      _value = this.getBufValue(_bth.t, buffer);
      if (_value === null) break;
      if (typeof _bth.f !== "undefined") _value = _value * _bth.f;
      result[_bth.n] = _value;
      buffer = buffer.slice(getByteSize(_bth.t));
    }
    return result;
  },
};

function onReceivedPacket (data) {
  if(CONFIG._processedMacAddresses !== null) { 
    if(CONFIG._processedMacAddresses.indexOf(data.address) < 0) {
      logger(["Received event from", data.address, "outside of the allowed addresses"], "Info");
      return;
    }
  }

  if (
    typeof CONFIG.motionHandler === "function" &&
    typeof data.motion !== "undefined"
  ) {
    CONFIG.motionHandler(data.motion === 1, data);
    logger("Motion handler called", "Info");
  }

  if (
    typeof CONFIG.illuminanceHandler === "function" &&
    typeof data.illuminance !== "undefined"
  ) {
    CONFIG.illuminanceHandler(data.illuminance, data);
    logger("Illuminance handler called", "Info");
  }

  if (typeof CONFIG.onStatusUpdate === "function") {
    CONFIG.onStatusUpdate(data);
    logger("New status update", "Info");
  }
}

let lastPacketId = 0x100;

function BLEScanCallback(event, result) {
  if (event !== BLE.Scanner.SCAN_RESULT) {
    return;
  }

  if (
    typeof result.service_data === "undefined" ||
    typeof result.service_data[BTHOME_SVC_ID_STR] === "undefined"
  ) {
    return;
  }

  let unpackedData = BTHomeDecoder.unpack(
    result.service_data[BTHOME_SVC_ID_STR]
  );

  if (
    unpackedData === null ||
    typeof unpackedData === "undefined" ||
    unpackedData["encryption"]
  ) {
    logger("Encrypted devices are not supported", "Error");
    return;
  }

  if (lastPacketId === unpackedData.pid) {
    return;
  }

  lastPacketId = unpackedData.pid;

  unpackedData.rssi = result.rssi;
  unpackedData.address = result.addr;

  onReceivedPacket(unpackedData);
}

function init() {
  if (typeof CONFIG === "undefined") {
    console.log("Error: Undefined config");
    return;
  }

  let BLEConfig = Shelly.getComponentConfig("ble");

  if (!BLEConfig.enable) {
    console.log(
      "Error: The Bluetooth is not enabled, please enable it from settings"
    );
    return;
  }

  if (BLE.Scanner.isRunning()) {
    console.log("Info: The BLE gateway is running, the BLE scan configuration is managed by the device");
  }
  else {
    let bleScanner = BLE.Scanner.Start({
        duration_ms: BLE.Scanner.INFINITE_SCAN,
        active: CONFIG.active
    });

    if(!bleScanner) {
      console.log("Error: Can not start new scanner");
    }
  }

  if (
    typeof CONFIG.allowedMacAddresses !== "undefined"
  ) {
    if(CONFIG.allowedMacAddresses !== null) {
      CONFIG._processedMacAddresses = 
        CONFIG
          .allowedMacAddresses
          .map(function (mac) { return mac.toLowerCase(); })
          .filter(function (value, index, array) { return array.indexOf(value) === index; })
    }
    else {
      CONFIG._processedMacAddresses = null;
    }
  }

  BLE.Scanner.Subscribe(BLEScanCallback);
}
init();

 

Key functionalities:

  1. Motion Detection: Monitors motion from specified BLE sensors. If motion is detected, it keeps the relay on. If no motion is detected for 5 minutes and it's not within blocked time intervals, it turns the relay off.

  2. Blocked Time Intervals: Prevents the relay from being turned off during specified times:

    • Friday 15:00 to Saturday 21:00
    • Nightly from 22:00 to 08:00
  3. BLE Scanning: Continuously scans for BLE devices, processes data from allowed MAC addresses, and handles motion and illuminance events.

  4. Logging: Provides optional debug logging for detailed information about the script’s operation.

Edited by Zyrkonim
  • Like 1
Link to comment
Share on other sites

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...