Skip to content
Aleksandar Nikolikj edited this page Sep 28, 2021 · 5 revisions

Intro

Thanks to https://github.com/keefo/NeewerLite and esp32-ble2mqtt managed to control Neewer 660 Lights within Node-red(Google Assistant).

Config.json

{
  "network": {
    "wifi": {
      "ssid": "",
      "password": ""
    }
  },
  "mqtt": {
    "server": {
      "host": "192.168.1.50",
      "port": 1883,
      "username": "",
      "password": ""
    },
    "publish": {
      "retain": true
    },
    "topics": {
      "prefix": "",
      "get_suffix": "/Get",
      "set_suffix": "/Set"
    }
  },
  "log": {
    "host": "192.168.1.50",
    "port": 514
  },
  "ble": {
    "whitelist": [
      "c7:d0:8e:bc:df:33",
      "cb:78:91:6d:97:4e",
      "ea:75:7a:3b:18:bd"
    ],
    "services": {
      "definitions": {
        "69400001-b5a3-f393-e0a9-e50e24dcca99": {
          "name": "Request"
        }
      }
    },
    "characteristics": {
      "definitions": {
        "69400002-b5a3-f393-e0a9-e50e24dcca99": {
          "name": "Ctl"
        },
        "69400003-b5a3-f393-e0a9-e50e24dcca99": {
          "name": "Info"
        }
      }
    }
  }
}

Node-Red flow

[{"id":"7aaa3f3563b06eda","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"c8ebebd4ee5641c0","type":"google-light-rgb-temp","z":"7aaa3f3563b06eda","client":"ceaea6c.add92d8","name":"Kitchen Light","room_hint":"Kitchen","topic":"homeassistant/lights/kitchen","passthru":false,"x":190,"y":140,"wires":[["4404c28852473a0c"]]},{"id":"67493a7dc89f5e64","type":"google-light-rgb-temp","z":"7aaa3f3563b06eda","client":"ceaea6c.add92d8","name":"Dinining Room Light","room_hint":"Dining Room","topic":"homeassistant/lights/dining","passthru":false,"x":160,"y":200,"wires":[["9b138c55d6727b11"]]},{"id":"a98add36306b1bac","type":"function","z":"7aaa3f3563b06eda","name":"mqtt message compose","func":"msg.topic=\"cb:78:91:6d:97:4e/Request/Ctl/Set\"\nvar message=\"\" \n\nvar onoff=context.get(\"onoff\")||false\nif (msg.payload.on==true && !onoff ){\n    message=\"120,129,1,1,251\"\n    context.set(\"onoff\",true)\n}\nelse if (msg.payload.rgb!=undefined){\n     var hue= parseInt(msg.payload.hue) & 0xFF\n     var huecal=(parseInt(msg.payload.hue) & 0xFF00) >> 8\n     context.set(\"color\",msg.payload.rgb)\n     message=\"120,134,4,\"+hue+\",\"+huecal+\",\"+msg.payload.saturation+\",\"+msg.payload.brightness\n     message=appendchecksum(message)\n}\nelse if (msg.payload.temperature!= undefined && msg.payload.rgb==undefined && msg.payload.name!=\"snow\") {\n    cct=msg.payload.temperature\n    if (cct<3200){\n        cct=3200\n    }\n    if (cct>5600){\n        cct=5600\n    }\n    message=\"120,135,2,\"+msg.payload.brightness+\",\"+cct/100\n    message=appendchecksum(message)\n}\nelse if (msg.payload.name==\"snow\"){\n    valChange()\n    message=\"120,136,2,\" +msg.payload.brightness+\",\"+context.get('scene')\n    message=appendchecksum(message)\n    \n}\nif (msg.payload.on==false){\n    message=\"120,129,1,2,252\"\n    context.set(\"onoff\",false)\n}\n\nfunction appendchecksum(message){\n\n     var sum=0;\n     var data = message.split(',').map(Number);\n     for( var i=0; i<data.length; i++ )\n        sum += data[i];\n\n     var checksum=parseInt(sum & 0xFF);\n     message=message+\",\"+checksum\n     return message\n}\nfunction valChange() {\n  counter = context.get('scene') || 0\n  if (counter == 9) {\n    counter = 0;\n  }\n  counter++\n  context.set(\"scene\",counter)\n  \n}\n\nnode.warn(\"Message:\"+message)\nmsg.payload=message\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":260,"wires":[["e347aa1e3814edc7"]]},{"id":"81026545c4e601fe","type":"google-light-rgb-temp","z":"7aaa3f3563b06eda","client":"ceaea6c.add92d8","name":"Game Room Light","room_hint":"Game Room","topic":"homeassistant/lights/game","passthru":false,"x":170,"y":260,"wires":[["90e8bc0402c78272"]]},{"id":"90e8bc0402c78272","type":"function","z":"7aaa3f3563b06eda","name":"add HSV","func":"//  Edited/Added node-red/settings.js\n//   functionGlobalContext: {\n//      ...\n//     cc:require('color-convert'),\n//   },\nvar cc = global.get('cc');\n\nif (msg.payload.rgb!=undefined){\n    var [r, g, b] = numberToColour(msg.payload.rgb)\n    var [h,s,v] = cc.rgb.hsv([r, g, b])\n    node.warn(\"RGB:\"+ [r, g, b])\n    node.warn(\"HSV:\"+ [h, s, v])\n    msg.payload.hue=h\n    msg.payload.saturation=s\n    msg.payload.value=v\n}\n\n\nfunction numberToColour(number) {\n  const r = (number & 0xff0000) >> 16;\n  const g = (number & 0x00ff00) >> 8;\n  const b = (number & 0x0000ff);\n\n  return [r, g, b];\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":260,"wires":[["a98add36306b1bac"]]},{"id":"e347aa1e3814edc7","type":"mqtt out","z":"7aaa3f3563b06eda","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"e47bd4b.1dcf628","x":870,"y":200,"wires":[]},{"id":"4404c28852473a0c","type":"function","z":"7aaa3f3563b06eda","name":"add HSV","func":"//  Edited/Added node-red/settings.js\n//   functionGlobalContext: {\n//      ...\n//     cc:require('color-convert'),\n//   },\nvar cc = global.get('cc');\n\nif (msg.payload.rgb!=undefined){\n    var [r, g, b] = numberToColour(msg.payload.rgb)\n    var [h,s,v] = cc.rgb.hsv([r, g, b])\n    node.warn(\"RGB:\"+ [r, g, b])\n    node.warn(\"HSV:\"+ [h, s, v])\n    msg.payload.hue=h\n    msg.payload.saturation=s\n    msg.payload.value=v\n}\n\n\nfunction numberToColour(number) {\n  const r = (number & 0xff0000) >> 16;\n  const g = (number & 0x00ff00) >> 8;\n  const b = (number & 0x0000ff);\n\n  return [r, g, b];\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":140,"wires":[["2219646c6a966348"]]},{"id":"2219646c6a966348","type":"function","z":"7aaa3f3563b06eda","name":"mqtt message compose","func":"msg.topic=\"c7:d0:8e:bc:df:33/Request/Ctl/Set\"\nvar message=\"\" \nvar onoff=context.get(\"onoff\")||false\n\nif (msg.payload.on==true && !onoff ){\n    message=\"120,129,1,1,251\"\n    context.set(\"onoff\",true)\n}\nelse if (msg.payload.rgb!=undefined){\n     var hue= parseInt(msg.payload.hue) & 0xFF\n     var huecal=(parseInt(msg.payload.hue) & 0xFF00) >> 8\n     context.set(\"color\",msg.payload.rgb)\n     message=\"120,134,4,\"+hue+\",\"+huecal+\",\"+msg.payload.saturation+\",\"+msg.payload.brightness\n     message=appendchecksum(message)\n}\nelse if (msg.payload.temperature!= undefined && msg.payload.rgb==undefined && msg.payload.name!=\"snow\") {\n    cct=msg.payload.temperature\n    if (cct<3200){\n        cct=3200\n    }\n    if (cct>5600){\n        cct=5600\n    }\n    message=\"120,135,2,\"+msg.payload.brightness+\",\"+cct/100\n    message=appendchecksum(message)\n}\nelse if (msg.payload.name==\"snow\"){\n    valChange()\n    message=\"120,136,2,\" +msg.payload.brightness+\",\"+context.get('scene')\n    message=appendchecksum(message)\n    \n}\nif (msg.payload.on==false){\n    message=\"120,129,1,2,252\"\n    context.set(\"onoff\",false)\n}\n\nfunction appendchecksum(message){\n\n     var sum=0;\n     var data = message.split(',').map(Number);\n     for( var i=0; i<data.length; i++ )\n        sum += data[i];\n\n     var checksum=parseInt(sum & 0xFF);\n     message=message+\",\"+checksum\n     return message\n}\nfunction valChange() {\n  counter = context.get('scene') || 0\n  if (counter == 9) {\n    counter = 0;\n  }\n  counter++\n  context.set(\"scene\",counter)\n  \n}\n\nnode.warn(\"Message:\"+message)\nmsg.payload=message\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":140,"wires":[["e347aa1e3814edc7"]]},{"id":"9b138c55d6727b11","type":"function","z":"7aaa3f3563b06eda","name":"add HSV","func":"//  Edited/Added node-red/settings.js\n//   functionGlobalContext: {\n//      ...\n//     cc:require('color-convert'),\n//   },\nvar cc = global.get('cc');\n\nif (msg.payload.rgb!=undefined){\n    var [r, g, b] = numberToColour(msg.payload.rgb)\n    var [h,s,v] = cc.rgb.hsv([r, g, b])\n    node.warn(\"RGB:\"+ [r, g, b])\n    node.warn(\"HSV:\"+ [h, s, v])\n    msg.payload.hue=h\n    msg.payload.saturation=s\n    msg.payload.value=v\n}\n\n\nfunction numberToColour(number) {\n  const r = (number & 0xff0000) >> 16;\n  const g = (number & 0x00ff00) >> 8;\n  const b = (number & 0x0000ff);\n\n  return [r, g, b];\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":200,"wires":[["c8b704c4e9608d05"]]},{"id":"c8b704c4e9608d05","type":"function","z":"7aaa3f3563b06eda","name":"mqtt message compose","func":"msg.topic=\"ea:75:7a:3b:18:bd/Request/Ctl/Set\"\nvar message=\"\" \n\nvar onoff=context.get(\"onoff\")||false\nif (msg.payload.on==true && !onoff ){\n    message=\"120,129,1,1,251\"\n    context.set(\"onoff\",true)\n}\nelse if (msg.payload.rgb!=undefined){\n     var hue= parseInt(msg.payload.hue) & 0xFF\n     var huecal=(parseInt(msg.payload.hue) & 0xFF00) >> 8\n     context.set(\"color\",msg.payload.rgb)\n     message=\"120,134,4,\"+hue+\",\"+huecal+\",\"+msg.payload.saturation+\",\"+msg.payload.brightness\n     message=appendchecksum(message)\n}\nelse if (msg.payload.temperature!= undefined && msg.payload.rgb==undefined && msg.payload.name!=\"snow\") {\n    cct=msg.payload.temperature\n    if (cct<3200){\n        cct=3200\n    }\n    if (cct>5600){\n        cct=5600\n    }\n    message=\"120,135,2,\"+msg.payload.brightness+\",\"+cct/100\n    message=appendchecksum(message)\n}\nelse if (msg.payload.name==\"snow\"){\n    valChange()\n    message=\"120,136,2,\" +msg.payload.brightness+\",\"+context.get('scene')\n    message=appendchecksum(message)\n    \n}\nif (msg.payload.on==false){\n    message=\"120,129,1,2,252\"\n    context.set(\"onoff\",false)\n}\n\nfunction appendchecksum(message){\n\n     var sum=0;\n     var data = message.split(',').map(Number);\n     for( var i=0; i<data.length; i++ )\n        sum += data[i];\n\n     var checksum=parseInt(sum & 0xFF);\n     message=message+\",\"+checksum\n     return message\n}\nfunction valChange() {\n  counter = context.get('scene') || 0\n  if (counter == 9) {\n    counter = 0;\n  }\n  counter++\n  context.set(\"scene\",counter)\n  \n}\n\nnode.warn(\"Message:\"+message)\nmsg.payload=message\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":200,"wires":[["e347aa1e3814edc7"]]},{"id":"ceaea6c.add92d8","type":"googlesmarthome-client","name":"","enabledebug":true,"usegooglelogin":false,"usehttpnoderoot":false,"port":"3001","httppath":"/smarthome","ssloffload":true,"accesstokenduration":"6000","reportinterval":"60","request_sync_delay":"5","set_state_delay":"5"},{"id":"e47bd4b.1dcf628","type":"mqtt-broker","name":"","broker":"192.168.1.50","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]