Skip to content

Instantly share code, notes, and snippets.

@i8beef
Last active June 6, 2018 01:35
Show Gist options
  • Save i8beef/55fc207efbafd6f37cb96d182ef9f026 to your computer and use it in GitHub Desktop.
Save i8beef/55fc207efbafd6f37cb96d182ef9f026 to your computer and use it in GitHub Desktop.
Node-Red TV Listing
# TV Listing
Reads TV listing from Zap2It on a schedule, and stores locally for usage. Exposes an endpoint that will generate sequential messages for MQTT to change channel.
# Usage
1. Ensure you have the node-red-contrib-primitives package installed, as this uses the iterator it provides.
2. Paste into a new flow.
3. Change the URL in the "Get current guide" node for your TV listings.
4. Update the "Set our shows" node to a list of shows you like (all lower-case, no punctuation with '&' converted to the word 'and').
5. Hook up "Send message" node to your implementation for actuall controlling the target. I have a Harmony to MQTT bridge running so I just spit out the messages one after another with a delay of the individual button presses (i.e., channel 162 becomes four button presses, '1', '6', '2', 'Enter'). This part will obviously need to change per implementation.
6. Hook up an IFTTT command to hit your endpoint with something like "find [[query]] on TV". This flow will listen for either a specific show name, or the query "something" or "something to watch". In the former case, it will attempt to find the actual show requested, in the latter it will scan the "Our shows" list and pick a random one that is currently on. If no show is found, nothing is done.
[{"id":"89e9588f.788598","type":"http response","z":"d24444ff.c18338","name":"Response","x":340,"y":1060,"wires":[]},{"id":"eeb21ba8.1e4268","type":"http in","z":"d24444ff.c18338","name":"Play Show Request","url":"/someEndoint","method":"post","upload":false,"swaggerDoc":"","x":150,"y":1060,"wires":[["d1f467aa.42a488","b1fc4355.f553d","89e9588f.788598"]]},{"id":"d1f467aa.42a488","type":"debug","z":"d24444ff.c18338","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":350,"y":1020,"wires":[]},{"id":"7f1ce302.1a894c","type":"http request","z":"d24444ff.c18338","name":"Get current guide","method":"GET","ret":"txt","url":"http://api.zap2it.com/tvlistings/ZCGrid.do?zipcode=XXXXX&lineupId=XXXXXX:X&setMyPreference=false&aid=zap2it","tls":"","x":510,"y":820,"wires":[["afe0b72d.e7f408"]]},{"id":"76f5bd6a.39d4e4","type":"html","z":"d24444ff.c18338","name":"","property":"payload","tag":"table.zc-row","ret":"html","as":"single","x":910,"y":800,"wires":[["24d6ed9e.b65382"]]},{"id":"24d6ed9e.b65382","type":"array-iterate","z":"d24444ff.c18338","name":"","source_object":"msg","source_property":"payload","x":1090,"y":800,"wires":[["adadd001.76b51"],[]]},{"id":"adadd001.76b51","type":"xml","z":"d24444ff.c18338","name":"","attr":"","chr":"","x":1070,"y":860,"wires":[["a5e0afc8.fbd78"]]},{"id":"a5e0afc8.fbd78","type":"function","z":"d24444ff.c18338","name":"Parse","func":"let showTimes = flow.get(\"showTimesTemp\") || {};\n\n// Get current channel number\nlet channel = parseInt(msg.payload.tr.td[0].span[0].a[0][\"_\"]);\n\n// Loop through shows in current lineup and parse\nfor (let i = 1; i < msg.payload.tr.td.length; i++) {\n let showNode = msg.payload.tr.td[i];\n \n // Get show name\n let fullShowName = \"\";\n if (typeof showNode.a !== \"undefined\") {\n fullShowName = showNode.a[0][\"_\"];\n } else {\n fullShowName = showNode.span[0][\"_\"]; \n }\n \n if (typeof fullShowName === \"undefined\" || fullShowName === \"\")\n continue;\n\n // Cleanup show names\n let showName = fullShowName;\n showName = showName.replace(\"&\", \"and\");\n showName = showName.replace(/[^a-zA-Z0-9 -]/g, \"\");\n showName = showName.toLowerCase();\n \n // Get start time\n let onClickRegex = /return zctv\\.gd\\.a\\(this,'\\w+',(\\d+),(\\d+)\\)/g;\n let onClickMatches = onClickRegex.exec(showNode.$.onclick);\n\n let startTimeEpochSeconds = parseInt(onClickMatches[1].substring(0, onClickMatches[1].length - 3));\n let runTimeSeconds = parseInt(onClickMatches[2]) * 60;\n let startTime = new Date(0);\n startTime.setUTCSeconds(startTimeEpochSeconds);\n \n // Get end time\n let endTime = new Date(0);\n endTime.setUTCSeconds(startTimeEpochSeconds + runTimeSeconds);\n \n // Build shotime object\n let showTime = {\n \"channel\": channel,\n \"showName\": fullShowName,\n \"startTime\": startTime,\n \"endTime\": endTime\n };\n\n // Init showtime array if not present\n if (showTimes[showName] === undefined) {\n showTimes[showName] = [];\n }\n \n showTimes[showName].push(showTime);\n\n flow.set(\"showTimesTemp\", showTimes);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":1210,"y":860,"wires":[["24d6ed9e.b65382"]]},{"id":"ed3783f1.413d4","type":"inject","z":"d24444ff.c18338","name":"Every 30 minutes","topic":"","payload":"","payloadType":"date","repeat":"1800","crontab":"","once":true,"x":150,"y":820,"wires":[["418e5638.12d6c8"]]},{"id":"11efa026.28e05","type":"function","z":"d24444ff.c18338","name":"Reset","func":"flow.set(\"showTimes\", {});\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":860,"wires":[[]]},{"id":"2a3b282b.6cd728","type":"inject","z":"d24444ff.c18338","name":"Button","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":860,"wires":[["11efa026.28e05"]]},{"id":"fda8be57.a438","type":"debug","z":"d24444ff.c18338","name":"","active":true,"console":"false","complete":"false","x":410,"y":900,"wires":[]},{"id":"d16de5fe.c26b18","type":"inject","z":"d24444ff.c18338","name":"Button","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":900,"wires":[["3a61eec4.abfba2"]]},{"id":"3a61eec4.abfba2","type":"function","z":"d24444ff.c18338","name":"Debug","func":"let showTimes = flow.get(\"showTimes\");\nreturn { \"payload\": showTimes };\n","outputs":1,"noerr":0,"x":250,"y":900,"wires":[["fda8be57.a438"]]},{"id":"418e5638.12d6c8","type":"function","z":"d24444ff.c18338","name":"Init","func":"flow.set(\"showTimesTemp\", {});\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":820,"wires":[["7f1ce302.1a894c"]]},{"id":"b1fc4355.f553d","type":"function","z":"d24444ff.c18338","name":"Find random show","func":"let showTimes = flow.get(\"showTimes\");\nlet now = Date.now();\n\n// Sanitize show name\nlet requestedShow = msg.payload.query;\nrequestedShow = requestedShow.replace(\"&\", \"and\");\nrequestedShow = requestedShow.replace(/[^a-zA-Z0-9 -]/g, \"\");\nrequestedShow = requestedShow.toLowerCase();\n\nlet searchShows = (requestedShow !== \"something\" && requestedShow !== \"something to watch\") ?\n [ requestedShow ] :\n flow.get(\"ourShows\");\n\n// Find things that are on right now\nlet foundShows = [];\nfor (let i = 0; i < searchShows.length; i++) {\n let show = searchShows[i];\n if (showTimes[show] !== undefined) {\n // Show found, filter for current running\n let currentSDShowTimes = showTimes[show].filter(function(showTime) {\n return showTime.startTime <= now && showTime.endTime >= now &&\n showTime.channel >= 2 && showTime.channel <= 99;\n });\n \n let currentHDShowTimes = showTimes[show].filter(function(showTime) {\n return showTime.startTime <= now && showTime.endTime >= now &&\n showTime.channel >= 100 && showTime.channel <= 202;\n });\n \n let currentExtendedShowTimes = showTimes[show].filter(function(showTime) {\n return showTime.startTime <= now && showTime.endTime >= now &&\n showTime.channel >= 203 && showTime.channel <= 499;\n });\n \n // Prefer higher number channels\n if (currentHDShowTimes.length > 0)\n foundShows.push(...currentHDShowTimes);\n //else if (currentExtendedShowTimes.length > 0)\n // channel = currentExtendedShowTimes[0].channel;\n else if (currentSDShowTimes.length > 0)\n foundShows.push(...currentSDShowTimes);\n }\n}\n\n// If nothing is found do nothing\nif (foundShows.length === 0)\n return null;\n\n// Randomize and take first\nlet foundShow = foundShows[Math.floor(Math.random()*foundShows.length)];\n\n// Map target to an MQTT topic\nlet topic = \"\";\nswitch (msg.payload.target.toLowerCase()) {\n case \"bedroom tv\":\n topic = \"harmony/bedroom/devices/PaceDVR/NumericBasic/Number{{number}}/set\";\n break;\n default:\n topic = \"harmony/default/devices/PaceDVR/NumericBasic/Number{{number}}/set\";\n}\n\nreturn {\n \"topic\": topic,\n \"payload\": foundShow.channel,\n \"delay\": 200\n};","outputs":1,"noerr":0,"x":370,"y":1100,"wires":[["d7745ed8.39616"]]},{"id":"f6a899c0.f4cd58","type":"comment","z":"d24444ff.c18338","name":"Show Time Management","info":"","x":170,"y":780,"wires":[]},{"id":"b7fd5ae6.32a068","type":"comment","z":"d24444ff.c18338","name":"Play Show Request Pipeline","info":"","x":180,"y":980,"wires":[]},{"id":"cd7839bd.f552f8","type":"inject","z":"d24444ff.c18338","name":"Init","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":720,"wires":[["e537c386.24212"]]},{"id":"e537c386.24212","type":"function","z":"d24444ff.c18338","name":"Set ourShows","func":"flow.set(\"ourShows\", [\n \"american dad\",\n \"archer\",\n \"bobs burgers\",\n \"everybody loves raymond\",\n \"family feud\",\n \"family guy\",\n \"friends\",\n \"futurama\",\n \"greys anatomy\",\n \"how i met your mother\",\n \"impractical jokers\",\n \"its always sunny in philadelphia\",\n \"jeopardy\",\n \"king of the hill\",\n \"mike and molly\",\n \"mom\",\n \"rick and morty\",\n \"robot chicken\",\n \"that 70s show\",\n \"the big bang theory\",\n \"the daily show\",\n \"the detour\",\n \"the king of queens\",\n \"the middle\",\n \"the office\",\n \"the simpsons\",\n \"wheel of fortune\"\n]);","outputs":1,"noerr":0,"x":280,"y":720,"wires":[[]]},{"id":"afe0b72d.e7f408","type":"switch","z":"d24444ff.c18338","name":"Success check","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":720,"y":820,"wires":[["76f5bd6a.39d4e4"],["1f37848e.4c3cbb"]]},{"id":"1f37848e.4c3cbb","type":"debug","z":"d24444ff.c18338","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":890,"y":840,"wires":[]},{"id":"d7745ed8.39616","type":"array-iterate","z":"d24444ff.c18338","name":"","source_object":"msg","source_property":"payload","x":590,"y":1100,"wires":[["45c810ec.24fd4"],[]]},{"id":"1582d2ba.84b51d","type":"delay","z":"d24444ff.c18338","name":"","pauseType":"delayv","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":760,"y":1060,"wires":[["ef899121.8fe25","d7745ed8.39616"]]},{"id":"ef899121.8fe25","type":"function","z":"d24444ff.c18338","name":"Send message","func":"return msg.payload;","outputs":1,"noerr":0,"x":980,"y":1060,"wires":[[]]},{"id":"45c810ec.24fd4","type":"function","z":"d24444ff.c18338","name":"Copy delay","func":"msg.delay = msg.payload.delay;\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":1060,"wires":[["1582d2ba.84b51d"]]}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment