2 * Copyright 2015 SmartThings
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
10 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
11 * for the specific language governing permissions and limitations under the License.
15 * Author: Steve Vlaminck
21 name: "SmartBlock Notifier",
22 namespace: "vlaminck/Minecraft",
23 author: "SmartThings",
24 description: "A SmartApp that notifies you when things are happening around your SmartBlocks",
25 iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
26 iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png"
30 page(name: "firstPage")
31 page(name: "redstonePage")
32 page(name: "neighborBlockPage")
33 page(name: "messageBuilderPage")
34 page(name: "destroyedPage")
39 def defaultLabelValue = smartBlock ? (smartBlock.label ?: smartBlock.name) : null
42 def destroyedPageName = "destroyedPage"
43 def destroyedComplete = pageStateComplete(destroyedPageName)
44 def destroyedState = destroyedComplete ? "complete" : null
45 def destroyedDescription = destroyedComplete ? messageDescriptionForPage(destroyedPageName) : null
47 def redstonePageName = "redstonePage"
48 def redstoneComplete = pageStateComplete(redstonePageName)
49 def redstoneState = redstoneComplete ? "complete" : null
50 def redstoneDescription = redstoneComplete ? messageDescriptionForPage(redstonePageName) : null
52 def neighborPageName = "neighborBlockPage"
53 def neighborComplete = pageStateComplete(neighborPageName)
54 def neighborState = neighborComplete ? "complete" : null
55 def neighborDescription = neighborComplete ? messageDescriptionForPage(neighborPageNamePageName) : null
57 dynamicPage(name: "firstPage", title: "Setup your notifications", install: true, uninstall: true) {
59 section("Get notifications for this SmartBlock") {
60 input(name: "smartBlock", type: "capability.switch", title: "Which SmartBlock would you like to monitor?", multiple: false)
61 // TODO: type: "device.smartBlock",
64 section("Why would you like to be notified?") {
65 href(name: "toDestroyedPage", page: destroyedPageName, title: "Because it was destroyed", description: destroyedDescription, state: destroyedState)
67 href(name: "toRedstonePage", page: redstonePageName, title: "Because its redstone signal changed", description: redstoneDescription, state: redstoneState)
68 href(name: "toNeighborPage", page: neighborPageName, title: "Because a block next to it changed", description: neighborDescription, state: neighborState)
71 section("Other Options") {
72 label(title: "Label this notification", description: app.name, required: false, defaultValue: defaultLabelValue)
73 mode(title: "Only send notifications when in one of these modes", description: "All modes")
79 def pageName = "destroyedPage"
80 dynamicPage(name: pageName, title: "For when your block is destroyed") {
81 smartPhoneNotificationSection(pageName)
83 messageBuilderSection(pageName)
84 chatClosestPlayerSection(pageName)
89 def pageName = "redstonePage"
90 dynamicPage(name: pageName, title: "Get Notified For Redstone Changes") {
91 section("When Redstone Is") {
92 input(name: "redstoneGreaterThan", type: "enum", required: false, title: "Greater Than", options: (0..15).collect {
95 input(name: "redstoneLessThan", type: "enum", required: false, title: "Less than", options: (0..15).collect {
98 input(name: "redstoneEqualTo", type: "enum", required: false, title: "Equal to", options: (0..15).collect {
102 smartPhoneNotificationSection(pageName)
103 chatSection(pageName)
104 messageBuilderSection(pageName)
108 def neighborBlockPage() {
109 def pageName = "neighborBlockPage"
110 dynamicPage(name: pageName, title: "Get Notified When a neighbor block updates") {
111 section("Not all blocks send updates, but Chests definitely do") {
112 input(type: "enum", name: "neighborBlockParsed", title: "When any of these blocks are updated", required: false, multiple: true, options: allBlocksParsed())
115 smartPhoneNotificationSection(pageName)
116 chatSection(pageName)
117 messageBuilderSection(pageName)
118 chatClosestPlayerSection(pageName)
120 section(title: "More Info", hideable: true, hidden: true) {
121 href(name: "allIds", title: "A full list of blocks and items can be found here", url: "http://minecraft.gamepedia.com/Ids", style: "external", description: null)
126 def messageBuilderPage(params) {
128 def pageName = params.pageName
129 def size = messageBuilderOptions().size() * 2
131 dynamicPage(name: "messageBuilderPage", title: "Build your message") {
132 section("These will be combined to form the final message.") {
135 name: "${pageName}MessagePart${it}",
136 type: (it % 2) ? "enum" : "text",
137 defaultValue: messagePartDefaultValue(pageName, it),
138 options: (it % 2) ? messageBuilderOptions() : null,
139 title: null, description: null, required: false, multiple: false
146 def smartPhoneNotificationSection(pageName) {
147 section("SmartPhone notifications") {
148 input("recipients", "contact", title: "Send notifications to") {
149 input(name: "${pageName}WantsPush", title: "Push Notification", description: null, type: "bool", required: false, defaultValue: "false")
150 input(name: "${pageName}WantsSms", title: "Text Message", description: "phone number", type: "phone", required: false)
152 input(name: "${pageName}WantsHH", title: "Hello Home only", description: null, type: "bool", required: false)
156 def chatSection(pageName) {
157 section("Minecraft Chat Message") {
158 input(name: "${pageName}ChatAllUsers", title: "Chat all users", type: "bool", required: false)
159 input(name: "${pageName}ChatUsername", title: "Or chat to a specific username", type: "text", required: false)
163 def messageBuilderSection(pageName) {
164 section("What should your message say?") {
165 messageBuilderHref(pageName)
169 def messageBuilderHref(pageName) {
170 def partsAreSet = messagePartsSet(pageName)
171 def messageState = partsAreSet ? "complete" : ""
172 def messageDescription = partsAreSet ? messageDescriptionForPage(pageName) : defaultMessageDescription(pageName)
176 page: "messageBuilderPage",
178 description: messageDescription ?: "Construct your message",
180 params: [pageName: pageName]
184 def chatClosestPlayerSection(pageName) {
185 section("Chat the closest player to the block. (usually the player that destroyed it)") {
186 messageBuilderHref("${pageName}ClosestPlayer")
190 def pageStateComplete(pageName) {
192 if (pageName == "redstonePage") {
193 if (redstoneGreaterThan) return true
194 if (redstoneLessThan) return true
195 if (redstoneEqualTo) return true
199 if (pageName == "neighborBlockPage") {
200 if (neighborBlockParsed) return true
204 if (app."${pageName}WantsPush") return true
205 if (app."${pageName}WantsSms") return true
206 if (app."${pageName}WantsHH") return true
207 if (app."${pageName}ChatAllUsers") return true
208 if (app."${pageName}ChatUsername") return true
209 if (app."${pageName}ClosestPlayer") return true
219 log.debug "Installed with settings: ${settings}"
225 log.debug "Updated with settings: ${settings}"
232 log.debug "initializing"
233 subscribe(smartBlock, "redstoneSignalStrength", redstoneSignalStrengthHandler)
234 subscribe(smartBlock, "smartBlockNeighborChanged", smartBlockNeighborChangedHandler, [filterEvents: false])
235 subscribe(smartBlock, "smartBlockNeighborChanged", smartBlockNeighborChangedHandler, [filterEvents: false])
236 subscribe(smartBlock, "blockDestroyed.true", smartBlockDestroyedHandler, [filterEvents: false])
243 def smartBlockDestroyedHandler(evt) {
244 log.debug "smartBlockDestroyedHandler evt.value: ${evt.value}"
246 def pageName = "destroyedPage"
247 def message = message(pageName)
248 notifyUser(pageName, message)
251 def smartBlockNeighborChangedHandler(evt) {
252 log.debug "smartBlockNeighborChangedHandler evt.value: ${evt.value}"
253 log.debug "neighborBlockParsed: ${neighborBlockParsed}"
255 if (neighborBlockParsed?.contains(evt.value)) {
256 notifyUserOfNeighborChange(evt.value)
260 def redstoneSignalStrengthHandler(evt) {
261 log.debug "redstoneSignalStrengthHandler: ${evt.value}"
263 int newValue = evt.value as int
264 int lastValue = smartBlock.latestState("redstoneSignalStrength").value as int
266 if (redstoneGreaterThan) {
267 int gt = redstoneGreaterThan as int
268 // log.debug "$newValue > $gt"
270 log.debug "greater than ${gt}. send notification"
271 notifyUserOfRedstoneChange(newValue)
275 if (redstoneLessThan) {
276 int lt = redstoneLessThan as int
277 // log.debug "$newValue < $lt"
279 log.debug "less than ${lt}. send notification"
280 notifyUserOfRedstoneChange(newValue)
284 if (redstoneEqualTo) {
285 int et = redstoneEqualTo as int
286 // log.debug "$newValue == $et"
287 if (newValue == et) {
288 log.debug "equal to ${et}. send notification"
289 notifyUserOfRedstoneChange(newValue)
299 def notifyUserOfRedstoneChange(value) {
300 def msg = message("redstonePage")
301 log.debug "message: ${msg}"
302 def notificationMessage = msg ?: "${smartBlock} redstone signal is ${value}"
303 notifyUser(notificationMessage)
306 def notifyUserOfNeighborChange(value) {
307 def msg = message("neighborPage")
308 log.debug "message: ${msg}"
309 def notificationMessage = msg ?: "${smartBlock} was updated by ${value}"
310 notifyUser(notificationMessage)
313 def notifyUser(pageName, messageToSend) {
314 log.debug "notifyUser pageName: ${pageName}"
316 def closestPlayerMessage = message("${pageName}ClosestPlayer")
317 log.debug "closestPlayerMessage = ${closestPlayerMessage}"
318 def latestClosePlayer = getLatestClosePlayer()
319 log.debug "latestClosePlayer = ${latestClosePlayer}"
320 if (closestPlayerMessage && latestClosePlayer != "unknown") {
321 log.debug "chatting closestPlayer"
322 chatMessageToMC(closestPlayerMessage, latestClosePlayer)
326 def wantsHH = app."${pageName}WantsHH"
327 log.debug "wantsHH = ${wantsHH}"
330 log.debug "sending HH"
331 sendNotificationEvent(messageToSend)
334 if (location.contactBookEnabled) {
335 sendNotificationToContacts(messageToSend, recipients)
339 def wantsPush = app."${pageName}WantsPush"
340 log.debug "wantsPush = ${wantsPush}"
341 if (wantsPush && wantsPush != "false") {
342 log.debug "sending push"
343 sendPush(messageToSend)
346 def wantsSms = app."${pageName}WantsSms"
347 log.debug "wantsSms = ${wantsSms}"
349 log.debug "sending sms to: ${wantsSms}"
350 sendSms(wantsSms, messageToSend)
355 def username = app."${pageName}ChatUsername"
356 def allUsers = app."${pageName}ChatAllUsers"
358 log.debug "username = ${username}"
359 log.debug "allUsers = ${allUsers}"
361 if (username && username != "") {
362 log.debug "chatting username: ${username}"
363 chatMessageToMC(messageToSend, username)
364 } else if (allUsers) {
365 log.debug "chatting all users"
366 chatMessageToMC(messageToSend, null)
371 def chatMessageToMC(message, username) {
372 log.debug "chatMessageToMC"
374 def url = "${app.getParent().getServerURL()}/chat?message=${message.encodeAsURL()}"
376 url = "${url}&username=${username.encodeAsURL()}"
379 log.debug "POST to ${url}"
381 httpPost(url, "foo=bar") {}
384 def messageDescriptionPartsForPage(pageName) {
385 def size = messageBuilderOptions().size() * 2
386 (0..size).collect { app."${pageName}MessagePart${it}" }
389 def messagePartsSet(pageName) { // are any set?
390 messageDescriptionPartsForPage(pageName).collect { !it }.unique().contains(false)
393 def defaultMessageDescription(pageName) {
396 if (pageName == "destroyedPage" || pageName == "redstonePage" || pageName == "neighborBlockPage") {
397 def second = messageBuilderOptions()[messagePartDefaultValue(pageName, 1)]
398 if (second) description = "\${${second}}"
400 def third = messagePartDefaultValue(pageName, 2)
401 if (third) description = "${description} ${third}"
403 def fourth = messageBuilderOptions()[messagePartDefaultValue(pageName, 3)]
404 if (fourth) description = "${description} \${${fourth}}"
410 def messageDescriptionForPage(pageName) {
412 def parts = messageDescriptionPartsForPage(pageName)
413 def messageParts = []
414 parts.eachWithIndex { part, idx ->
415 if (part != null && part != "null") {
417 messageParts << "\${${messageBuilderOptions()[part]}}"
425 return messageParts.join(" ").trim()
427 return defaultMessageDescription()
431 def messagePartDefaultValue(pageName, part) {
432 if (pageName == "destroyedPage") {
433 if (part == 1) return "name"
434 if (part == 2) return "was destroyed by"
435 if (part == 3) return "closestPlayer"
438 if (pageName == "neighborBlockPage") {
439 if (part == 1) return "name"
440 if (part == 2) return "has a redstone signal of"
441 if (part == 3) return "redstoneSignalStrength"
444 if (pageName == "redstonePage") {
445 if (part == 1) return "name"
446 if (part == 2) return "was updated by"
447 if (part == 3) return "closestPlayer"
453 def message(pageName) {
454 log.debug "building message"
455 def messageParts = []
457 messageDescriptionPartsForPage(pageName).eachWithIndex { part, idx ->
459 // def option = messageBuilderOptions()[part]
460 def optionPart = getMessagePartFromOption(part)
461 if (optionPart) messageParts << optionPart
463 if (part) messageParts << part
467 def message = messageParts.join(" ").trim()
468 log.debug "message: ${message}"
472 def messageBuilderOptions() {
474 "name": "SmartBlock name",
475 "neighborBlockName": "Neighbor block name",
476 "blockDestroyed": "Destroyed State ('destroyed' / 'OK')",
477 "redstoneSignalStrength": "Redstone signal strength",
478 "worldSeed": "World seed",
479 "dimensionName": "Dimension name (World, Nether, End)",
480 "coordinates": "Block coordinates",
481 "closestPlayer": "Username of Closest player (within the past minute)",
482 "placedBy": "Username of who placed the block"
486 def getMessagePartFromOption(optionKey) {
487 log.debug "optionKey: ${optionKey}"
488 if (optionKey == "name") return smartBlock.label ?: smartBlock.name
489 if (optionKey == "closestPlayer") return getLatestClosePlayer()
490 if (optionKey == "blockDestroyed") return smartBlock.latestValue("blockDestroyed") ? "OK" : "destroyed"
491 return smartBlock.latestValue(optionKey)
494 def getLatestClosePlayer() {
496 def minusOne = new Date(minutes: now.minutes - 1)
497 def latestStates = smartBlock.statesSince("closestPlayer", minusOne)
498 if (latestStates.size) {
499 return latestStates[0].value
508 def settingsAsIds() {
509 log.debug "settingsAsIds"
510 log.debug "neighborBlockParsed: $neighborBlockParsed"
512 def subscribedIds = []
514 neighborBlockParsed.each {
515 subscribedIds << convertBlockSettingToBlockId(it)
521 def convertBlockSettingToBlockId(setting) {
522 def id = setting.substring(0, setting.indexOf(" "))
523 def name = allBlocks()[id]
524 log.debug "id: $id, name:${name}"
528 def allBlocksParsed() {
529 allBlocks().collect { k, v -> "${k} ${v}" }
539 "5": "Oak Wood Plank",
540 "5:1": "Spruce Wood Plank",
541 "5:2": "Birch Wood Plank",
542 "5:3": "Jungle Wood Plank",
544 "6:1": "Spruce Sapling",
545 "6:2": "Birch Sapling",
546 "6:3": "Jungle Sapling",
549 "9": "Stationary Water",
551 "11": "Stationary Lava",
558 "17:1": "Spruce Wood",
559 "17:2": "Birch Wood",
560 "17:3": "Jungle Wood",
562 "18:1": "Spruce Leaves",
563 "18:2": "Birch Leaves",
564 "18:3": "Jungle Leaves",
567 "21": "Lapis Lazuli Ore",
568 "22": "Lapis Lazuli Block",
571 "24:1": "Chiseled Sandstone",
572 "24:2": "Smooth Sandstone",
575 "27": "Powered Rail",
576 "28": "Detector Rail",
577 "29": "Sticky Piston",
586 "35:1": "Orange Wool",
587 "35:2": "Magenta Wool",
588 "35:3": "Light Blue Wool",
589 "35:4": "Yellow Wool",
593 "35:8": "Light Gray Wool",
595 "35:10": "Purple Wool",
596 "35:11": "Blue Wool",
597 "35:12": "Brown Wool",
598 "35:13": "Green Wool",
600 "35:15": "Black Wool",
603 "39": "Brown Mushroom",
604 "40": "Red Mushroom",
607 "43": "Double Stone Slab",
608 "43:1": "Double Sandstone Slab",
609 "43:2": "Double Wooden Slab",
610 "43:3": "Double Cobblestone Slab",
611 "43:4": "Double Brick Slab",
612 "43:5": "Double Stone Brick Slab",
613 "43:6": "Double Nether Brick Slab",
614 "43:7": "Double Quartz Slab",
616 "44:1": "Sandstone Slab",
617 "44:2": "Wooden Slab",
618 "44:3": "Cobblestone Slab",
619 "44:4": "Brick Slab",
620 "44:5": "Stone Brick Slab",
621 "44:6": "Nether Brick Slab",
622 "44:7": "Quartz Slab",
626 "48": "Mossy Cobblestone",
630 "52": "Monster Spawner",
631 "53": "Oak Wood Stairs",
633 "55": "Redstone Wire",
635 "57": "Diamond Block",
640 "62": "Burning Furnace",
642 "64": "Wooden Door Block",
645 "67": "Cobblestone Stairs",
648 "70": "Stone Pressure Plate",
649 "71": "Iron Door Block",
650 "72": "Wooden Pressure Plate",
651 "73": "Redstone Ore",
652 "74": "Glowing Redstone Ore",
653 "75": "Redstone Torch(off)",
654 "76": "Redstone Torch(on)",
655 "77": "Stone Button",
669 "91": "Jack - O - Lantern",
671 "93": "Redstone Repeater Block(off)",
672 "94": "Redstone Repeater Block(on)",
673 "95": "Locked Chest",
675 "97": "Stone(Silverfish)",
676 "97:1": "Cobblestone(Silverfish)",
677 "97:2": "Stone Brick(Silverfish)",
679 "98:1": "Mossy Stone Brick",
680 "98:2": "Cracked Stone Brick",
681 "98:3": "Chiseled Stone Brick",
682 "99": "Red Mushroom Cap",
683 "100": "Brown Mushroom Cap",
686 "103": "Melon Block",
687 "104": "Pumpkin Stem",
691 "108": "Brick Stairs",
692 "109": "Stone Brick Stairs",
695 "112": "Nether Brick",
696 "113": "Nether Brick Fence",
697 "114": "Nether Brick Stairs",
698 "115": "Nether Wart",
699 "116": "Enchantment Table",
700 "117": "Brewing Stand",
703 "120": "End Portal Frame",
706 "123": "Redstone Lamp(inactive)",
707 "124": "Redstone Lamp(active)",
708 "125": "Double Oak Wood Slab",
709 "125:1": "Double Spruce Wood Slab",
710 "125:2": "Double Birch Wood Slab",
711 "125:3": "Double Jungle Wood Slab",
712 "126": "Oak Wood Slab",
713 "126:1": "Spruce Wood Slab",
714 "126:2": "Birch Wood Slab",
715 "126:3": "Jungle Wood Slab",
716 "127": "Cocoa Plant",
717 "128": "Sandstone Stairs",
718 "129": "Emerald Ore",
719 "130": "Ender Chest",
720 "131": "Tripwire Hook",
722 "133": "Emerald Block",
723 "134": "Spruce Wood Stairs",
724 "135": "Birch Wood Stairs",
725 "136": "Jungle Wood Stairs",
726 "137": "Command Block",
727 "138": "Beacon Block",
728 "139": "Cobblestone Wall",
729 "139:1": "Mossy Cobblestone Wall",
733 "143": "Wooden Button",
736 "146": "Trapped Chest",
737 "147": "Weighted Pressure Plate(light)",
738 "148": "Weighted Pressure Plate(heavy)",
739 "149": "Redstone Comparator(inactive)",
740 "150": "Redstone Comparator(active)",
741 "151": "Daylight Sensor",
742 "152": "Redstone Block",
743 "153": "Nether Quartz Ore",
745 "155": "Quartz Block",
746 "155:1": "Chiseled Quartz Block",
747 "155:2": "Pillar Quartz Block",
748 "156": "Quartz Stairs",
749 "157": "Activator Rail",
751 "159": "White Stained Clay",
752 "159:1": "Orange Stained Clay",
753 "159:2": "Magenta Stained Clay",
754 "159:3": "Light Blue Stained Clay",
755 "159:4": "Yellow Stained Clay",
756 "159:5": "Lime Stained Clay",
757 "159:6": "Pink Stained Clay",
758 "159:7": "Gray Stained Clay",
759 "159:8": "Light Gray Stained Clay",
760 "159:9": "Cyan Stained Clay",
761 "159:10": "Purple Stained Clay",
762 "159:11": "Blue Stained Clay",
763 "159:12": "Brown Stained Clay",
764 "159:13": "Green Stained Clay",
765 "159:14": "Red Stained Clay",
766 "159:15": "Black Stained Clay",
768 "171": "White Carpet",
769 "171:1": "Orange Carpet",
770 "171:2": "Magenta Carpet",
771 "171:3": "Light Blue Carpet",
772 "171:4": "Yellow Carpet",
773 "171:5": "Lime Carpet",
774 "171:6": "Pink Carpet",
775 "171:7": "Gray Carpet",
776 "171:8": "Light Gray Carpet",
777 "171:9": "Cyan Carpet",
778 "171:10": "Purple Carpet",
779 "171:11": "Blue Carpet",
780 "171:12": "Brown Carpet",
781 "171:13": "Green Carpet",
782 "171:14": "Red Carpet",
783 "171:15": "Black Carpet",
784 "172": "Hardened Clay",
785 "173": "Block of Coal",
786 "256": "Iron Shovel",
787 "257": "Iron Pickaxe",
789 "259": "Flint and Steel",
799 "268": "Wooden Sword",
800 "269": "Wooden Shovel",
801 "270": "Wooden Pickaxe",
803 "272": "Stone Sword",
804 "273": "Stone Shovel",
805 "274": "Stone Pickaxe",
807 "276": "Diamond Sword",
808 "277": "Diamond Shovel",
809 "278": "Diamond Pickaxe",
810 "279": "Diamond Axe",
813 "282": "Mushroom Soup",
815 "284": "Gold Shovel",
816 "285": "Gold Pickaxe",
824 "293": "Diamond Hoe",
826 "295": "Wheat Seeds",
829 "298": "Leather Helmet",
830 "299": "Leather Chestplate",
831 "300": "Leather Leggings",
832 "301": "Leather Boots",
833 "302": "Chainmail Helmet",
834 "303": "Chainmail Chestplate",
835 "304": "Chainmail Leggings",
836 "305": "Chainmail Boots",
837 "306": "Iron Helmet",
838 "307": "Iron Chestplate",
839 "308": "Iron Leggings",
841 "310": "Diamond Helmet",
842 "311": "Diamond Chestplate",
843 "312": "Diamond Leggings",
844 "313": "Diamond Boots",
845 "314": "Gold Helmet",
846 "315": "Gold Chestplate",
847 "316": "Gold Leggings",
850 "319": "Raw Porkchop",
851 "320": "Cooked Porkchop",
853 "322": "Golden Apple",
854 "322:1": "Enchanted Golden Apple",
856 "324": "Wooden Door",
858 "326": "Water Bucket",
859 "327": "Lava Bucket",
867 "335": "Milk Bucket",
874 "342": "Storage Minecart",
875 "343": "Powered Minecart",
878 "346": "Fishing Rod",
880 "348": "Glowstone Dust",
882 "350": "Cooked Fish",
885 "351:2": "Cactus Green",
886 "351:3": "Coco Beans",
887 "351:4": "Lapis Lazuli",
888 "351:5": "Purple Dye",
890 "351:7": "Light Gray Dye",
893 "351:10": "Lime Dye",
894 "351:11": "Dandelion Yellow",
895 "351:12": "Light Blue Dye",
896 "351:13": "Magenta Dye",
897 "351:14": "Orange Dye",
898 "351:15": "Bone Meal",
903 "356": "Redstone Repeater",
908 "361": "Pumpkin Seeds",
909 "362": "Melon Seeds",
912 "365": "Raw Chicken",
913 "366": "Cooked Chicken",
914 "367": "Rotten Flesh",
915 "368": "Ender Pearl",
918 "371": "Gold Nugget",
919 "372": "Nether Wart Seeds",
921 "374": "Glass Bottle",
923 "376": "Fermented Spider Eye",
924 "377": "Blaze Powder",
925 "378": "Magma Cream",
926 "379": "Brewing Stand",
928 "381": "Eye of Ender",
929 "382": "Glistering Melon",
930 "383:50": "Spawn Creeper",
931 "383:51": "Spawn Skeleton",
932 "383:52": "Spawn Spider",
933 "383:54": "Spawn Zombie",
934 "383:55": "Spawn Slime",
935 "383:56": "Spawn Ghast",
936 "383:57": "Spawn Pigman",
937 "383:58": "Spawn Enderman",
938 "383:59": "Spawn Cave Spider",
939 "383:60": "Spawn Silverfish ",
940 "383:61": "Spawn Blaze",
941 "383:62": "Spawn Magma Cube ",
942 "383:65": "Spawn Bat",
943 "383:66": "Spawn Witch",
944 "383:90": "Spawn Pig",
945 "383:91": "Spawn Sheep",
946 "383:92": "Spawn Cow",
947 "383:93": "Spawn Chicken",
948 "383:94": "Spawn Squid",
949 "383:95": "Spawn Wolf",
950 "383:96": "Spawn Mooshroom",
951 "383:98": "Spawn Ocelot",
952 "383:100": "Spawn Horse",
953 "383:120": "Spawn Villager",
954 "384": "Bottle o' Enchanting",
955 "385": "Fire Charge",
956 "386": "Book and Quill",
957 "387": "Written Book",
963 "393": "Baked Potato",
964 "394": "Poisonous Potato",
966 "396": "Golden Carrot",
967 "397": "Mob Head (Skeleton)",
968 "397:1": "Mob Head (Wither Skeleton)",
969 "397:2": "Mob Head (Zombie)",
970 "397:3": "Mob Head (Human)",
971 "397:4": "Mob Head (Creeper)",
972 "398": "Carrot on a Stick",
973 "399": "Nether Star",
974 "400": "Pumpkin Pie",
975 "401": "Firework Rocket",
976 "402": "Firework Star",
977 "403": "Enchanted Book",
978 "404": "Redstone Comparator",
979 "405": "Nether Brick",
980 "406": "Nether Quartz",
981 "407": "Minecart with TNT",
982 "408": "Minecart with Hopper",
983 "417": "Iron Horse Armor",
984 "418": "Gold Horse Armor",
985 "419": "Diamond Horse Armor",