Xiaomi Mijia LYWSD03MMC, Hygro- / Thermometer, BLE via gatttool
Additional software to install
- gatttool
Customization to your own system
- payload: MAC address of your LYWSD03MMC
- Temperature offset
- Humidity offset
- fahrenheit: true | false [default: false = °C]
Output
Object
- temperature: Celsius | Fahrenheit
- humidity: %
- absoluteHumidty: g/m³
Subflow
Update
2024/10/13:
- added: absoluteHumidity to output
- changed: subflow
[{"id":"75b862f2e565d8a9","type":"subflow","name":"Xiaomi Mijia LYWSD03MMC","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"27f818c0c30c8245"}]}],"out":[{"x":1190,"y":200,"wires":[{"id":"1920b289697ce10b","port":0}]},{"x":1190,"y":160,"wires":[{"id":"f4ce523156a84b83","port":0}]}],"env":[],"meta":{"version":"3.0.0","author":"[email protected]","license":"MIT"},"color":"#3FADB5","outputLabels":["Error","Result"],"icon":"node-red-huemag/hue-temperature.png","status":{"x":1160,"y":260,"wires":[{"id":"a466615ee41c3608","port":0}]}},{"id":"27f818c0c30c8245","type":"exec","z":"75b862f2e565d8a9","command":"gatttool --char-write-req --handle='0x0038' --value=\"0100\" --listen -b ","addpay":"payload","append":"","useSpawn":"false","timer":"30","winHide":false,"oldrc":false,"name":"","x":340,"y":80,"wires":[["ae7848a2da5e081e"],[],[]]},{"id":"ace0bd267fa454d7","type":"delay","z":"75b862f2e565d8a9","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":780,"y":240,"wires":[["1920b289697ce10b"]]},{"id":"1920b289697ce10b","type":"delay","z":"75b862f2e565d8a9","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"4","nbRateUnits":"5","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":1020,"y":200,"wires":[[]]},{"id":"c9e485707ed13e20","type":"change","z":"75b862f2e565d8a9","name":"","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":200,"wires":[["ace0bd267fa454d7","1920b289697ce10b"]]},{"id":"2f1f540eb0f9d407","type":"switch","z":"75b862f2e565d8a9","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":410,"y":240,"wires":[["c9e485707ed13e20"],["ace0bd267fa454d7"]]},{"id":"ae7848a2da5e081e","type":"function","z":"75b862f2e565d8a9","name":"Xiaomi Mijia LYWSD03MMC","func":"// by Thorsten Willert\n// V3.0 2024/10/12\n\nconst regex = /Notification handle = 0x0036 value: ([0-9a-f ]+)/g;\n\nlet sColor;\nlet match;\nlet temperature;\nlet humidity;\nlet unit = \"C\";\nlet status = 0;\nlet text;\n\nconst values = [];\n\nconst fahrenheit = msg.fahrenheit || false;\nconst offsetHum = msg.offsetHumidity || 0;\nconst offsetTemp = msg.offsetTemperature || 0;\n//-----------------------------------------------------------------\n\nwhile ((match = regex.exec(msg.payload)) !== null) {\n values.push(match[1].trim()); // Trimmt Whitespace\n}\n\nmsg.payload = {};\n\n//-----------------------------------------------------------------\nif (values.length === 0) {\n sColor = \"red\";\n msg.payload.temperature = 'err';\n msg.payload.humidity = 'err';\n msg.payload.absoluteHumidity = 'err';\n \n text = 'No valid values found.';\n node.warn(text);\n status = 1;\n} else {\n // Verarbeiten der extrahierten Werte\n const value = values[0];\n const hexValues = value.split(' ');\n const tempHexa = hexValues[1] + hexValues[0]; \n const humHexa = hexValues[2];\n\n const temperature100 = parseInt(tempHexa, 16);\n temperature = parseFloat( (temperature100 / 100).toFixed(2) );\n humidity = Math.max(Math.min(100,parseInt(humHexa, 16) + offsetHum),0);\n \n msg.payload.humidity = humidity;\n msg.payload.absoluteHumidity = parseFloat( calculateAbsoluteHumidity(temperature + offsetTemp, humidity).toFixed(2) );\n\n // Füge Temperatur (C/F) und Luftfeuchtigkeit als Eigenschaften in msg.payload hinzu\n if (fahrenheit == false) {\n msg.payload.temperature = temperature + offsetTemp;\n } else {\n msg.payload.temperature = celtofar(temperature) + offsetTemp;\n unit = 'F';\n }\n\n sColor = \"green\";\n text = `${msg.payload.temperature} °${unit} | ${humidity} % | ${msg.payload.absoluteHumidity} g/m³`\n status = 0;\n}\n\n//-----------------------------------------------------------------\nfunction celtofar (value) {\n\treturn parseFloat( (value * 1.8 + 32).toFixed(2) );\n}\n\n//-----------------------------------------------------------------\nfunction calculateAbsoluteHumidity(temperature, relativeHumidity) {\n // Sättigungsdampfdruck (Magnus-Formel)\n const es = 6.112 * Math.exp((17.62 * temperature) / (243.12 + temperature));\n \n // atsächlicher Dampfdruck\n const e = (relativeHumidity / 100) * es;\n \n // absolute Luftfeuchtigkeit\n const absoluteHumidity = (216.7 * e) / (temperature + 273.15);\n \n return absoluteHumidity; // g/m³\n}\n\n//-----------------------------------------------------------------\n// Setze den Node-Status mit Temperatur und Luftfeuchtigkeit\nnode.status({\n fill: sColor,\n shape: \"dot\",\n text: text\n});\n//-----------------------------------------------------------------\n\nvar msg2 = { payload: status };\n\nreturn [msg, msg2];\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":200,"wires":[["f4ce523156a84b83"],["2f1f540eb0f9d407"]],"inputLabels":["Output from gatttool"],"outputLabels":["Result","Error",""]},{"id":"a466615ee41c3608","type":"status","z":"75b862f2e565d8a9","name":"","scope":["ae7848a2da5e081e","27f818c0c30c8245"],"x":1060,"y":260,"wires":[[]]},{"id":"f4ce523156a84b83","type":"switch","z":"75b862f2e565d8a9","name":"","property":"payload.temperature","propertyType":"msg","rules":[{"t":"istype","v":"number","vt":"number"}],"checkall":"true","repair":false,"outputs":1,"x":410,"y":160,"wires":[[]]},{"id":"6f4c4f0f072a5a72","type":"catch","z":"75b862f2e565d8a9","name":"","scope":["27f818c0c30c8245"],"uncaught":false,"x":1070,"y":300,"wires":[["7e4bd42a8a35df23"]]},{"id":"7e4bd42a8a35df23","type":"debug","z":"75b862f2e565d8a9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1190,"y":300,"wires":[]},{"id":"2090e87d807f3c9a","type":"subflow:75b862f2e565d8a9","z":"6a68389a91938bd8","name":"","x":320,"y":1360,"wires":[["a43b32e9ba1823c5"],["293d067377f4b125","74e91ad52ff54dab"]]},{"id":"a43b32e9ba1823c5","type":"change","z":"6a68389a91938bd8","name":"A4:C1:38:9A:B5:EF","rules":[{"t":"set","p":"payload","pt":"msg","to":"A4:C1:38:9A:B5:EF","tot":"str"},{"t":"set","p":"offsetHumidity","pt":"msg","to":"-10","tot":"num"},{"t":"set","p":"offsetTemperature","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":1280,"wires":[["2090e87d807f3c9a"]]},{"id":"9658ea816669f2f2","type":"inject","z":"6a68389a91938bd8","name":"6 min.","props":[{"p":"reset","v":"true","vt":"bool"},{"p":"topic","vt":"str"}],"repeat":"360","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":100,"y":1280,"wires":[["a43b32e9ba1823c5"]]}]