diff --git a/data/web/collections.json b/data/web/collections.json index e8a21760a..6b0bef1f2 100644 --- a/data/web/collections.json +++ b/data/web/collections.json @@ -1,215 +1,179 @@ { - "113" : { - "link" : "/clock/index.html", - "icon" : "fa-clock-o", - "name" : "LED Clock" - }, - "69" : { - "mode" : "advanced", - "link" : "/ia5/security/webcam.shtml", - "name" : "Windowed Overview", - "icon" : "fa-th-large" + "59" : { + "name" : "Weather.com - Local", + "icon" : "fa-cloud", + "iframe" : "http://www.weather.com/weather/local/91403" }, - "11" : { - "name" : "Events, Calendar, & Clock", + "9" : { + "icon" : "fa-microphone", + "name" : "Speech", "children" : [ - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 113, - 115, - 112, - 114 - ], - "icon" : "fa-calendar" - }, - "29" : { - "icon" : "fa-globe", - "name" : "List Global Variables", - "link" : "/ia7/#path=/vars_global" + 97, + 98, + 100, + 99 + ] }, - "73" : { - "link" : "/ia5/security/backyardcam.shtml", - "icon" : "fa-pagelines", - "name" : "Backyard Camera" + "56" : { + "name" : "Browse Appliances", + "icon" : "fa-sitemap", + "link" : "/ia7/#path=/objects&parents=Appliances" }, - "116" : { - "link" : "/bin/weather_graph_zoom.pl", - "icon" : "fa-search", - "name" : "Weather Zoom" + "114" : { + "icon" : "fa-exclamation-circle", + "mode" : "advanced", + "name" : "Browse Timers", + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Timers" }, - "94" : { - "link" : "/ia5/entertain/internetradio.shtml", - "name" : "Internet Radio", - "icon" : "fa-sitemap" + "91" : { + "link" : "/ia5/entertain/shortcuts.shtml", + "icon" : "fa-bookmark-o", + "name" : "TV Shortcuts" }, - "7" : { - "children" : [ - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85 - ], - "name" : "Phone Calls & VoiceMail Msgs", - "icon" : "fa-phone" + "46" : { + "icon" : "fa-inbox", + "name" : "Postal Mailbox", + "link" : "/ia5/news/postalmail.shtml" }, - "19" : { - "icon" : "fa-gears", - "mode" : "advanced", - "name" : "Browse Widgets", + "8" : { + "icon" : "fa-music", + "name" : "TV/Radio Guide & MP3 Music", "children" : [ - 31, - 32, - 33, - 34, - 35 + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96 ] }, - "40" : { - "link" : "/ia5/statistics/webstats.shtml", - "icon" : "fa-link", - "mode" : "advanced", - "name" : "WebServer Statistics" - }, - "79" : { - "link" : "/bin/phone_search.pl", - "icon" : "fa-search", - "name" : "Search Calls" + "121" : { + "link" : "/ia7/#path=/rrd?now-6hour?wind_speed", + "icon" : "wi-strong-wind", + "name" : "Wind Speed" }, - "92" : { - "external" : "http://www.google.com/search?&q=movie%3A+91403", - "icon" : "fa-film", - "name" : "Local Movies" + "104" : { + "name" : "Browse Entertainment", + "icon" : "fa-gamepad", + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Entertainment" }, - "42" : { - "name" : "Browse This Category", - "icon" : "fa-ellipsis-v", - "mode" : "advanced", - "link" : "/ia7/widgets_checkbox" + "74" : { + "icon" : "fa-desktop", + "name" : "Desktop Camera", + "link" : "/ia5/security/desktopcam.shtml" }, - "41" : { - "name" : "HouseServer Statistics", - "icon" : "fa-home", - "mode" : "advanced", - "link" : "/ia5/statistics/housestats.shtml" + "75" : { + "link" : "/ia7/#path=/floorplan?Sample_Floorplan", + "icon" : "fa-building-o", + "name" : "Floorplan View" }, - "600" : { - "name" : "Status", - "link" : "/ia7/#path=/objects&parents=ia7_status_items" + "80" : { + "icon" : "fa-list", + "name" : "Phone List", + "link" : "/bin/phone_list.pl" }, "89" : { "name" : "TV Guide", - "icon" : "fa-book", - "external" : "http://tvguide.com/Listings/index.asp?I=70620&Zip=91403" + "external" : "http://tvguide.com/Listings/index.asp?I=70620&Zip=91403", + "icon" : "fa-book" }, - "34" : { - "link" : "/ia7/widgets_radiobutton", - "mode" : "advanced", - "icon" : "fa-dot-circle-o", - "name" : "Radiobutton Widgets" + "120" : { + "link" : "/ia7/#path=/rrd?now-6hour?indoor_humid", + "name" : "Indoor Humidity", + "icon" : "wi-humidity" }, - "63" : { - "icon" : "fa-moon-o", - "name" : "Sun & Moon Data", - "link" : "/ia5/outside/sunmoon.shtml" + "62" : { + "link" : "/ia7/#path=/objects&parents=HVAC", + "name" : "HVAC", + "icon" : "fa-dashboard" }, - "109" : { - "icon" : "fa-shopping-cart", - "name" : "Shopping List", - "link" : "/bin/shopping_list.pl" + "77" : { + "name" : "Recent Incoming Calls", + "icon" : "fa-arrow-down", + "link" : "/bin/phone_in.pl" }, - "124" : { - "link" : "/ia7/#path=/rrd?now-6hour?rain_rate", + "119" : { "icon" : "wi-sprinkle", - "name" : "Rain Rate" + "name" : "Outdoor Humidity", + "link" : "/ia7/#path=/rrd?now-6hour?outdoor_humid" }, - "meta" : { - "version" : "1.4" + "700" : { + "user" : "$Authorized" }, - "4" : { - "icon" : "fa-lightbulb-o", - "name" : "Lights & Appliances", - "children" : [ - 51, - 52, - 53, - 54, - 55, - 56, - 57 - ] + "67" : { + "link" : "/ia5/outside/browse.shtml", + "name" : "Browse Category", + "icon" : "fa-archive" }, - "44" : { - "name" : "Read CNN", - "icon" : "fa-book", - "external" : "http://www.cnn.com" + "60" : { + "name" : "Weather.com - National", + "external" : "http://www.weather.com/maps/maptype/currentweatherusnational/index_large.html", + "icon" : "fa-globe" }, - "30" : { - "link" : "/ia7/#path=/vars_save", - "icon" : "fa-save", - "name" : "List Save Variables" + "49" : { + "link" : "/bin/menu.pl", + "mode" : "advanced", + "icon" : "fa-list-alt", + "name" : "Menu Control" }, - "81" : { - "link" : "/ia5/phone/voicemail.shtml", - "icon" : "fa-envelope-o", - "name" : "VoiceMail Messages" + "36" : { + "name" : "View Print Log", + "icon" : "fa-list", + "link" : "/ia7/#path=/print_log" }, - "121" : { - "link" : "/ia7/#path=/rrd?now-6hour?wind_speed", - "name" : "Wind Speed", - "icon" : "wi-strong-wind" + "99" : { + "link" : "/ia5/speak/speechsettings.shtml", + "mode" : "advanced", + "icon" : "fa-cog", + "name" : "Speech Settings" }, - "78" : { - "name" : "Recent Outgoing Calls", - "icon" : "fa-arrow-up", - "link" : "/bin/phone_out.pl" + "45" : { + "name" : "Newsgroups", + "external" : "http://groups.google.com/grphp", + "icon" : "fa-group" }, - "53" : { - "link" : "/ia7/#path=objects&type=X10_Appliance", - "icon" : "fa-sitemap", - "name" : "Control X10 Appliances" + "90" : { + "external" : "http://tvguide.com/tv", + "name" : "What's On Now", + "icon" : "fa-desktop" }, - "28" : { - "icon" : "fa-desktop", - "mode" : "advanced", - "name" : "Setup TV Provider", - "link" : "/bin/set_parm_tv_provider.pl" + "86" : { + "name" : "MP3 Jukebox", + "icon" : "fa-music", + "link" : "/misc/mp3.html" }, - "119" : { - "name" : "Outdoor Humidity", - "icon" : "wi-sprinkle", - "link" : "/ia7/#path=/rrd?now-6hour?outdoor_humid" + "73" : { + "link" : "/ia5/security/backyardcam.shtml", + "name" : "Backyard Camera", + "icon" : "fa-pagelines" }, - "50" : { - "name" : "Browse Modes", - "icon" : "fa-th", - "link" : "/ia5/modes/browse.shtml" + "88" : { + "link" : "/tv", + "icon" : "fa-calendar-o", + "name" : "TV Today" }, - "60" : { - "icon" : "fa-globe", - "name" : "Weather.com - National", - "external" : "http://www.weather.com/maps/maptype/currentweatherusnational/index_large.html" + "83" : { + "name" : "White Pages", + "external" : "http://www.whitepages.com/", + "icon" : "fa-home" }, - "118" : { - "icon" : "wi-thermometer", - "name" : "Indoor Temp", - "link" : "/ia7/#path=/rrd?now-6hour?indoor_temp" + "98" : { + "icon" : "fa-volume-up", + "name" : "Speak Text", + "link" : "/ia7/house/speaktext.shtml" }, - "77" : { - "icon" : "fa-arrow-down", - "name" : "Recent Incoming Calls", - "link" : "/bin/phone_in.pl" + "96" : { + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Music", + "name" : "Browse Music", + "icon" : "fa-headphones" }, "20" : { + "icon" : "fa-wrench", "name" : "Setup MrHouse", "children" : [ 21, @@ -219,197 +183,214 @@ 25, 26, 27, - 28 - ], - "icon" : "fa-wrench" - }, - "114" : { - "icon" : "fa-exclamation-circle", - "name" : "Browse Timers", - "mode" : "advanced", - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Timers" + 28, + "126" + ] }, - "64" : { - "link" : "/ia5/outside/earthquakes.shtml", - "name" : "Earthquakes", - "icon" : "fa-bullseye" + "115" : { + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Timed_Events", + "name" : "Browse Timed_Events", + "icon" : "fa-archive" }, - "104" : { - "name" : "Browse Entertainment", - "icon" : "fa-gamepad", - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Entertainment" - }, - "38" : { - "icon" : "fa-warning", - "name" : "View Error Log", - "link" : "/ia5/statistics/errorlog.shtml" - }, - "46" : { - "link" : "/ia5/news/postalmail.shtml", - "name" : "Postal Mailbox", - "icon" : "fa-inbox" - }, - "112" : { - "name" : "Timers", - "icon" : "fa-exclamation", - "mode" : "advanced", - "link" : "/misc/timers.shtml" - }, - "12" : { - "icon" : "fa-bar-chart-o", + "6" : { "children" : [ - 36, - 37, - 38, - 39, - 40, - 41, - 42 + 68, + 70, + 72, + 73, + 74, + 75, + 76, + 69, + 71 ], - "name" : "Statistics & Logged Data" + "icon" : "fa-video-camera", + "name" : "Security Cameras" }, - "93" : { - "external" : "http://realguide.real.com/", - "name" : "Radio Guide", - "icon" : "fa-signal" + "42" : { + "icon" : "fa-ellipsis-v", + "mode" : "advanced", + "name" : "Browse This Category", + "link" : "/ia7/widgets_checkbox" }, - "17" : { - "link" : "/ia7/#path=objects&type=Group", - "icon" : "fa-group", - "name" : "Browse Groups" + "102" : { + "icon" : "fa-desktop", + "external" : "$config_parms{html_dir}/misc/photos.shtml", + "name" : "Picture Frame" }, "22" : { "name" : "User Code Activation", "icon" : "fa-code", "link" : "/bin/code_unselect.pl" }, - "99" : { - "name" : "Speech Settings", - "mode" : "advanced", - "icon" : "fa-cog", - "link" : "/ia5/speak/speechsettings.shtml" + "47" : { + "link" : "/ia5/news/browse.shtml", + "name" : "Browse News", + "icon" : "fa-list-alt" }, - "3" : { - "name" : "Modes", - "children" : [ - 48, - 50, - 49 - ], - "icon" : "fa-tasks" + "25" : { + "name" : "INI Editor", + "icon" : "fa-table", + "link" : "/bin/iniedit.pl" }, - "85" : { - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Phone", - "name" : "Browse Phone", - "icon" : "fa-archive" + "50" : { + "link" : "/ia5/modes/browse.shtml", + "icon" : "fa-th", + "name" : "Browse Modes" }, - "700" : { - "user" : "$Authorized" + "10" : { + "children" : [ + 101, + 102, + 103, + 104 + ], + "name" : "Comics & Pictures", + "icon" : "fa-picture-o" }, - "82" : { - "external" : "http://www.yellowpages.com/", - "name" : "Yellow Pages", - "icon" : "fa-building-o" + "92" : { + "icon" : "fa-film", + "external" : "http://www.google.com/search?&q=movie%3A+91403", + "name" : "Local Movies" }, - "75" : { - "link" : "/ia7/#path=/floorplan?Sample_Floorplan", - "icon" : "fa-building-o", - "name" : "Floorplan View" + "30" : { + "link" : "/ia7/#path=/vars_save", + "icon" : "fa-save", + "name" : "List Save Variables" }, - "115" : { - "icon" : "fa-archive", - "name" : "Browse Timed_Events", - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Timed_Events" + "meta" : { + "version" : "1.6" }, - "27" : { - "link" : "/bin/headercontrol.pl", - "icon" : "fa-wrench", + "65" : { + "icon" : "fa-fire", "mode" : "advanced", - "name" : "Header Control" + "name" : "Iridium Flares", + "link" : "/ia5/outside/sattelite.shtml" }, - "59" : { - "iframe" : "http://www.weather.com/weather/local/91403", - "icon" : "fa-cloud", - "name" : "Weather.com - Local" + "17" : { + "link" : "/ia7/#path=objects&type=Group", + "icon" : "fa-group", + "name" : "Browse Groups" }, - "102" : { - "icon" : "fa-desktop", - "name" : "Picture Frame", - "external" : "$config_parms{html_dir}/misc/photos.shtml" + "26" : { + "name" : "Program IRMAN", + "mode" : "advanced", + "icon" : "fa-rss", + "link" : "/ia5/house/irman.shtml" }, - "100" : { - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Speech", - "icon" : "fa-microphone", - "name" : "Browse Speech" + "81" : { + "icon" : "fa-envelope-o", + "name" : "VoiceMail Messages", + "link" : "/ia5/phone/voicemail.shtml" }, - "55" : { - "link" : "/ia7/#path=/objects&parents=All_Lights", - "name" : "Browse All Lights", - "icon" : "fa-lightbulb-o" + "71" : { + "link" : "/cameras/", + "icon" : "fa-film", + "mode" : "advanced", + "name" : "Camera Files" }, - "70" : { - "icon" : "fa-clock-o", - "name" : "Time Lapse Viewer", - "link" : "/ia5/security/wc_sshow.shtml" + "35" : { + "link" : "/ia7/widgets_checkbox", + "icon" : "fa-check-square-o", + "name" : "Checkbox Widgets" }, - "500" : { + "3" : { "children" : [ - 700 + 48, + 50, + 49 ], - "name" : "Gear Settings", - "icon" : "fa-home" - }, - "37" : { - "icon" : "fa-bullhorn", - "name" : "View Speech Log", - "link" : "/ia7/#path=/print_speaklog" + "name" : "Modes", + "icon" : "fa-tasks" }, "68" : { + "link" : "/ia5/security/main.shtml", "name" : "Basic Overview", - "icon" : "fa-video-camera", - "link" : "/ia5/security/main.shtml" + "icon" : "fa-video-camera" + }, + "64" : { + "link" : "/ia5/outside/earthquakes.shtml", + "name" : "Earthquakes", + "icon" : "fa-bullseye" }, "72" : { - "icon" : "fa-home", + "link" : "/ia5/security/frontdoor.shtml", "name" : "Frontdoor Camera", - "link" : "/ia5/security/frontdoor.shtml" + "icon" : "fa-home" }, - "26" : { - "mode" : "advanced", - "icon" : "fa-rss", - "name" : "Program IRMAN", - "link" : "/ia5/house/irman.shtml" + "1" : { + "children" : [ + 13, + 15, + 16, + 17, + 18, + 20, + 29, + 30, + 19 + ], + "icon" : "fa-home", + "name" : "Mr. House Home" }, - "16" : { - "icon" : "fa-archive", - "name" : "Browse Categories", - "link" : "/ia7/#path=/objects&type=Category" + "79" : { + "name" : "Search Calls", + "icon" : "fa-search", + "link" : "/bin/phone_search.pl" }, - "83" : { - "name" : "White Pages", - "icon" : "fa-home", - "external" : "http://www.whitepages.com/" + "61" : { + "children" : [ + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125 + ], + "name" : "Weather Station", + "icon" : "fa-bolt" }, - "107" : { - "link" : "/organizer/contacts.pl", - "name" : "Address Book", - "keys" : "true", - "icon" : "fa-book" + "106" : { + "icon" : "fa-calendar", + "name" : "Calendar", + "link" : "/organizer/calendar.pl", + "keys" : "true" }, - "110" : { - "name" : "List Manager", - "icon" : "fa-list-alt", - "link" : "/bin/ListManager.pl" + "101" : { + "name" : "Daily Comics", + "icon" : "fa-picture-o", + "link" : "/comics/index.html" }, - "125" : { - "name" : "Baro Pressure", - "icon" : "wi-cloud-refresh", - "link" : "/ia7/#path=/rrd?now-6hour?outside_press" + "78" : { + "link" : "/bin/phone_out.pl", + "name" : "Recent Outgoing Calls", + "icon" : "fa-arrow-up" }, - "80" : { - "link" : "/bin/phone_list.pl", - "name" : "Phone List", - "icon" : "fa-list" + "116" : { + "name" : "Weather Zoom", + "icon" : "fa-search", + "link" : "/bin/weather_graph_zoom.pl" + }, + "48" : { + "icon" : "fa-tasks", + "name" : "Control Modes & Events", + "link" : "/ia7/house/modes.shtml" + }, + "41" : { + "link" : "/ia5/statistics/housestats.shtml", + "name" : "HouseServer Statistics", + "mode" : "advanced", + "icon" : "fa-home" + }, + "69" : { + "name" : "Windowed Overview", + "mode" : "advanced", + "icon" : "fa-th-large", + "link" : "/ia5/security/webcam.shtml" }, "5" : { "children" : [ @@ -424,381 +405,406 @@ 65, 66 ], - "name" : "HVAC & Weather", - "icon" : "fa-umbrella" + "icon" : "fa-umbrella", + "name" : "HVAC & Weather" }, - "15" : { - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=MisterHouse", - "icon" : "fa-home", - "name" : "Browse MrHouse" - }, - "120" : { - "name" : "Indoor Humidity", - "icon" : "wi-humidity", - "link" : "/ia7/#path=/rrd?now-6hour?indoor_humid" + "28" : { + "link" : "/bin/set_parm_tv_provider.pl", + "name" : "Setup TV Provider", + "icon" : "fa-desktop", + "mode" : "advanced" }, - "91" : { - "name" : "TV Shortcuts", - "icon" : "fa-bookmark-o", - "link" : "/ia5/entertain/shortcuts.shtml" + "29" : { + "name" : "List Global Variables", + "icon" : "fa-globe", + "link" : "/ia7/#path=/vars_global" }, - "13" : { - "link" : "/ia7/house/main.shtml", - "name" : "About MrHouse", - "icon" : "fa-home" + "97" : { + "link" : "/ia7/#path=/print_speaklog", + "name" : "View Speech Log", + "icon" : "fa-bullhorn" }, - "57" : { - "link" : "/ia7/#path=/floorplan?Sample_Floorplan", - "name" : "Floorplan View", - "icon" : "fa-home" + "44" : { + "external" : "http://www.cnn.com", + "name" : "Read CNN", + "icon" : "fa-book" }, - "108" : { - "link" : "/organizer/tasks.pl", - "name" : "TODO List", - "keys" : "true", - "icon" : "fa-list" + "15" : { + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=MisterHouse", + "name" : "Browse MrHouse", + "icon" : "fa-home" }, - "105" : { - "icon" : "fa-check", - "name" : "Calendar Facts", - "link" : "/ia5/calendar/main.shtml" + "113" : { + "icon" : "fa-clock-o", + "name" : "LED Clock", + "link" : "/clock/index.html" }, - "117" : { - "icon" : "fa-leaf", - "name" : "Outdoor Temp", - "link" : "/ia7/#path=/rrd?now-6hour?outdoor_temp" + "63" : { + "link" : "/ia5/outside/sunmoon.shtml", + "name" : "Sun & Moon Data", + "icon" : "fa-moon-o" }, - "98" : { - "icon" : "fa-volume-up", - "name" : "Speak Text", - "link" : "/ia7/house/speaktext.shtml" + "107" : { + "keys" : "true", + "icon" : "fa-book", + "name" : "Address Book", + "link" : "/organizer/contacts.pl" }, - "97" : { - "link" : "/ia7/#path=/print_speaklog", - "name" : "View Speech Log", - "icon" : "fa-bullhorn" + "34" : { + "name" : "Radiobutton Widgets", + "mode" : "advanced", + "icon" : "fa-dot-circle-o", + "link" : "/ia7/widgets_radiobutton" }, - "61" : { - "children" : [ - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125 - ], - "name" : "Weather Station", - "icon" : "fa-bolt" + "125" : { + "name" : "Baro Pressure", + "icon" : "wi-cloud-refresh", + "link" : "/ia7/#path=/rrd?now-6hour?outside_press" }, - "51" : { - "icon" : "fa-group", - "name" : "Browse Groups", - "link" : "/ia7/#path=/objects&type=Group" + "118" : { + "icon" : "wi-thermometer", + "name" : "Indoor Temp", + "link" : "/ia7/#path=/rrd?now-6hour?indoor_temp" }, - "14" : { - "link" : "/ia7/house/aboutaudrey.shtml", - "name" : "About 3Com Audrey", - "icon" : "fa-desktop" + "52" : { + "link" : "/ia7/#path=objects&type=X10_Item", + "icon" : "fa-info", + "name" : "Control X10 Items" }, "31" : { "link" : "/ia7/widgets", "icon" : "fa-cogs", "name" : "All Widgets" }, - "56" : { - "link" : "/ia7/#path=/objects&parents=Appliances", - "icon" : "fa-sitemap", - "name" : "Browse Appliances" - }, - "6" : { - "icon" : "fa-video-camera", - "name" : "Security Cameras", - "children" : [ - 68, - 70, - 72, - 73, - 74, - 75, - 76, - 69, - 71 - ] - }, - "84" : { - "icon" : "fa-sort-alpha-desc", - "name" : "Reverse Lookup", - "external" : "http://www.whitepages.com/find_person.pl?fid=p" - }, - "74" : { - "link" : "/ia5/security/desktopcam.shtml", - "name" : "Desktop Camera", - "icon" : "fa-desktop" - }, - "33" : { - "link" : "/ia7/widgets_entry", - "icon" : "fa-pencil-square-o", - "name" : "Entry Widgets" + "14" : { + "icon" : "fa-desktop", + "name" : "About 3Com Audrey", + "link" : "/ia7/house/aboutaudrey.shtml" }, - "103" : { - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Photos", - "icon" : "fa-archive", - "name" : "Browse Photos" + "70" : { + "icon" : "fa-clock-o", + "name" : "Time Lapse Viewer", + "link" : "/ia5/security/wc_sshow.shtml" }, - "65" : { - "icon" : "fa-fire", - "name" : "Iridium Flares", + "40" : { + "link" : "/ia5/statistics/webstats.shtml", + "name" : "WebServer Statistics", "mode" : "advanced", - "link" : "/ia5/outside/sattelite.shtml" + "icon" : "fa-link" }, - "35" : { - "link" : "/ia7/widgets_checkbox", - "icon" : "fa-check-square-o", - "name" : "Checkbox Widgets" + "117" : { + "link" : "/ia7/#path=/rrd?now-6hour?outdoor_temp", + "name" : "Outdoor Temp", + "icon" : "fa-leaf" }, - "0" : { - "name" : "Home", - "children" : [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12 - ], - "icon" : "fa-home" + "51" : { + "icon" : "fa-group", + "name" : "Browse Groups", + "link" : "/ia7/#path=/objects&type=Group" }, - "86" : { - "icon" : "fa-music", - "name" : "MP3 Jukebox", - "link" : "/misc/mp3.html" + "123" : { + "link" : "/ia7/#path=/rrd?now-6hour?rain", + "icon" : "wi-rain", + "name" : "Total Rain" }, - "43" : { - "link" : "/ia5/news/main.shtml", - "name" : "Read e-mail", - "icon" : "fa-envelope" + "55" : { + "link" : "/ia7/#path=/objects&parents=All_Lights", + "name" : "Browse All Lights", + "icon" : "fa-lightbulb-o" }, - "88" : { - "link" : "/tv", - "icon" : "fa-calendar-o", - "name" : "TV Today" + "126" : { + "link" : "/ia7/#path=/security", + "icon" : "fa-users", + "name" : "Users and Groups" }, - "36" : { - "icon" : "fa-list", - "name" : "View Print Log", - "link" : "/ia7/#path=/print_log" + "112" : { + "name" : "Timers", + "icon" : "fa-exclamation", + "mode" : "advanced", + "link" : "/misc/timers.shtml" }, - "24" : { - "name" : "Edit Items", - "icon" : "fa-list", - "link" : "/bin/items.pl" + "600" : { + "link" : "/ia7/#path=/objects&parents=ia7_status_items", + "name" : "Status" }, - "123" : { - "link" : "/ia7/#path=/rrd?now-6hour?rain", - "name" : "Total Rain", - "icon" : "wi-rain" + "109" : { + "icon" : "fa-shopping-cart", + "name" : "Shopping List", + "link" : "/bin/shopping_list.pl" }, - "48" : { - "name" : "Control Modes & Events", - "icon" : "fa-tasks", - "link" : "/ia7/house/modes.shtml" + "58" : { + "name" : "Weather Underground", + "icon" : "fa-sun-o", + "iframe" : "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=91403" }, "87" : { "icon" : "fa-play-circle", "name" : "Media Center", "link" : "/media/mhmedia.html" }, - "9" : { - "name" : "Speech", - "children" : [ - 97, - 98, - 100, - 99 - ], - "icon" : "fa-microphone" + "18" : { + "link" : "/ia7/#path=/objects&type=Type", + "icon" : "fa-info", + "name" : "Browse Items" }, - "66" : { - "name" : "GPS/APRS Tracking", + "76" : { + "link" : "/ia5/security/floorplan.shtml", + "name" : "Floorplan View2", "mode" : "advanced", - "icon" : "fa-road", - "link" : "/ia5/outside/tracking.shtml" + "icon" : "fa-building-o" }, "95" : { "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Entertainment", "icon" : "fa-gamepad", "name" : "Browse Entertainment" }, - "106" : { - "link" : "/organizer/calendar.pl", - "keys" : "true", - "name" : "Calendar", - "icon" : "fa-calendar" + "21" : { + "link" : "/bin/code_select.pl", + "name" : "Common Code Activation", + "icon" : "fa-code" }, - "58" : { - "name" : "Weather Underground", - "iframe" : "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=91403", - "icon" : "fa-sun-o" + "124" : { + "icon" : "wi-sprinkle", + "name" : "Rain Rate", + "link" : "/ia7/#path=/rrd?now-6hour?rain_rate" }, - "67" : { - "link" : "/ia5/outside/browse.shtml", - "icon" : "fa-archive", - "name" : "Browse Category" + "27" : { + "link" : "/bin/headercontrol.pl", + "name" : "Header Control", + "mode" : "advanced", + "icon" : "fa-wrench" }, - "49" : { + "23" : { + "icon" : "fa-clock-o", + "name" : "Edit Triggers", + "link" : "/ia7/#path=triggers" + }, + "94" : { + "link" : "/ia5/entertain/internetradio.shtml", + "icon" : "fa-sitemap", + "name" : "Internet Radio" + }, + "38" : { + "link" : "/ia5/statistics/errorlog.shtml", + "name" : "View Error Log", + "icon" : "fa-warning" + }, + "110" : { + "link" : "/bin/ListManager.pl", "icon" : "fa-list-alt", + "name" : "List Manager" + }, + "19" : { + "children" : [ + 31, + 32, + 33, + 34, + 35 + ], "mode" : "advanced", - "name" : "Menu Control", - "link" : "/bin/menu.pl" + "icon" : "fa-gears", + "name" : "Browse Widgets" }, - "71" : { - "link" : "/cameras/", - "name" : "Camera Files", + "82" : { + "icon" : "fa-building-o", + "external" : "http://www.yellowpages.com/", + "name" : "Yellow Pages" + }, + "66" : { + "icon" : "fa-road", "mode" : "advanced", - "icon" : "fa-film" + "name" : "GPS/APRS Tracking", + "link" : "/ia5/outside/tracking.shtml" }, - "18" : { - "icon" : "fa-info", - "name" : "Browse Items", - "link" : "/ia7/#path=/objects&type=Type" + "100" : { + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Speech", + "name" : "Browse Speech", + "icon" : "fa-microphone" }, - "8" : { - "icon" : "fa-music", - "children" : [ - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96 - ], - "name" : "TV/Radio Guide & MP3 Music" + "16" : { + "name" : "Browse Categories", + "icon" : "fa-archive", + "link" : "/ia7/#path=/objects&type=Category" }, - "23" : { - "icon" : "fa-clock-o", - "name" : "Edit Triggers", - "link" : "/bin/triggers.pl" + "105" : { + "link" : "/ia5/calendar/main.shtml", + "icon" : "fa-check", + "name" : "Calendar Facts" }, - "47" : { - "link" : "/ia5/news/browse.shtml", - "icon" : "fa-list-alt", - "name" : "Browse News" + "85" : { + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Phone", + "icon" : "fa-archive", + "name" : "Browse Phone" + }, + "39" : { + "name" : "View Backup Log", + "icon" : "fa-floppy-o", + "link" : "/ia5/statistics/backuplog.shtml" + }, + "57" : { + "icon" : "fa-home", + "name" : "Floorplan View", + "link" : "/ia7/#path=/floorplan?Sample_Floorplan" }, "32" : { "link" : "/ia7/widgets_label", "name" : "Label Widgets", "icon" : "fa-square-o" }, - "1" : { - "icon" : "fa-home", + "93" : { + "icon" : "fa-signal", + "external" : "http://realguide.real.com/", + "name" : "Radio Guide" + }, + "12" : { "children" : [ - 13, - 15, - 16, - 17, - 18, - 20, - 29, - 30, - 19 + 36, + 37, + 38, + 39, + 40, + 41, + 42 ], - "name" : "Mr. House Home" + "name" : "Statistics & Logged Data", + "icon" : "fa-bar-chart-o" }, - "52" : { - "icon" : "fa-info", - "name" : "Control X10 Items", - "link" : "/ia7/#path=objects&type=X10_Item" + "122" : { + "link" : "/ia7/#path=/rrd?now-6hour?wind_dir", + "icon" : "fa-arrows", + "name" : "Wind Direction" }, - "90" : { - "external" : "http://tvguide.com/tv", - "name" : "What's On Now", - "icon" : "fa-desktop" + "33" : { + "name" : "Entry Widgets", + "icon" : "fa-pencil-square-o", + "link" : "/ia7/widgets_entry" }, - "96" : { - "name" : "Browse Music", - "icon" : "fa-headphones", - "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Music" + "108" : { + "name" : "TODO List", + "icon" : "fa-list", + "link" : "/organizer/tasks.pl", + "keys" : "true" }, - "25" : { - "icon" : "fa-table", - "name" : "INI Editor", - "link" : "/bin/iniedit.pl" + "24" : { + "link" : "/bin/items.pl", + "name" : "Edit Items", + "icon" : "fa-list" }, - "45" : { - "external" : "http://groups.google.com/grphp", - "icon" : "fa-group", - "name" : "Newsgroups" + "53" : { + "link" : "/ia7/#path=objects&type=X10_Appliance", + "icon" : "fa-sitemap", + "name" : "Control X10 Appliances" }, "111" : { - "link" : "/bin/triggers.pl", + "link" : "/ia7/#path=triggers", "name" : "Alarms", "icon" : "fa-bell-o" }, - "2" : { - "icon" : "fa-envelope", + "4" : { "children" : [ - 43, - 44, - 45, - 46, - 47 + 51, + 52, + 53, + 54, + 55, + 56, + 57 ], - "name" : "Mail and News" - }, - "62" : { - "link" : "/ia7/#path=/objects&parents=HVAC", - "name" : "HVAC", - "icon" : "fa-dashboard" + "name" : "Lights & Appliances", + "icon" : "fa-lightbulb-o" }, - "21" : { - "icon" : "fa-code", - "name" : "Common Code Activation", - "link" : "/bin/code_select.pl" + "11" : { + "children" : [ + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 113, + 115, + 112, + 114 + ], + "name" : "Events, Calendar, & Clock", + "icon" : "fa-calendar" }, - "122" : { - "link" : "/ia7/#path=/rrd?now-6hour?wind_dir", - "icon" : "fa-arrows", - "name" : "Wind Direction" + "103" : { + "name" : "Browse Photos", + "icon" : "fa-archive", + "link" : "/ia7/#path=/objects&type=Voice_Cmd&category=Photos" }, - "76" : { - "link" : "/ia5/security/floorplan.shtml", - "mode" : "advanced", - "name" : "Floorplan View2", - "icon" : "fa-building-o" + "13" : { + "link" : "/ia7/house/main.shtml", + "name" : "About MrHouse", + "icon" : "fa-home" }, - "101" : { - "name" : "Daily Comics", - "icon" : "fa-picture-o", - "link" : "/comics/index.html" + "0" : { + "children" : [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 + ], + "icon" : "fa-home", + "name" : "Home" }, - "10" : { - "icon" : "fa-picture-o", + "7" : { "children" : [ - 101, - 102, - 103, - 104 + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85 ], - "name" : "Comics & Pictures" + "icon" : "fa-phone", + "name" : "Phone Calls & VoiceMail Msgs" }, - "39" : { - "link" : "/ia5/statistics/backuplog.shtml", - "name" : "View Backup Log", - "icon" : "fa-floppy-o" + "37" : { + "link" : "/ia7/#path=/print_speaklog", + "icon" : "fa-bullhorn", + "name" : "View Speech Log" + }, + "84" : { + "icon" : "fa-sort-alpha-desc", + "external" : "http://www.whitepages.com/find_person.pl?fid=p", + "name" : "Reverse Lookup" + }, + "43" : { + "link" : "/ia5/news/main.shtml", + "name" : "Read e-mail", + "icon" : "fa-envelope" + }, + "2" : { + "name" : "Mail and News", + "icon" : "fa-envelope", + "children" : [ + 43, + 44, + 45, + 46, + 47 + ] + }, + "500" : { + "icon" : "fa-home", + "name" : "Gear Settings", + "children" : [ + 700 + ] } } diff --git a/lib/BondHome.pm b/lib/BondHome.pm new file mode 100644 index 000000000..4ace3ea83 --- /dev/null +++ b/lib/BondHome.pm @@ -0,0 +1,854 @@ + +=head1 B + + +=head2 DESCRIPTION + +Module for interfacing with the BondHome Hub to control IR/RF devices such as fans. + +=head2 CONFIGURATION + +At minimum, you must define the BondHome_ip in the mh.private.ini and the Interface +object. Once they are configured you can restart MH, once MH is back up restart the Bond Hub, +next set the Bond Hub interface object to "GetToken" within a few min after the Bond Hub reboot. +Once the token is successfuly retreved, set the Bond Hub interface object to "LogDevs" +and copy the device code from the MH logs and paste into a MH .mht file in your code directory. +You will need all the devices preconfigured in the BondHome Hub because MH pulls them including the names from it. +The BondHome_Device objects allow for the display and control of these objects as separate items +in the MH interface and allows users to interact directly with these objects +using the basic Generic_Item functions such as tie_event. + + +The BondHome_Device object is for tracking the state of and controlling +devices configured in the BondHome hub through the BondHome app and stored in +the local BondHome hub database. These devices are pulled from the BondHome Hub +with the local api. + +Misterhouse loads all devices and device commands from BondHome when it is started. +You must reload the BondHome device and trigger and retrieve an auth token by setting the +parent object to "GetToken" with in a few min after the reboot. + +The BondHome_Manual object is for manually recording remote signals and sending them from Misterhouse. +This bypasses the BondHome Hub database and just tells the BondHome Hub what signal to transmit directly. +To record a signal, set the Bond Hub interface object to ScanRF or ScanIR depending on what kind of remote +you are recording. Once scan is enabled, put the remote close to the hub and push the button you want to +record a few times and watch for the hub lights to change colors (This is the same process as the initial hub setup +through the app). Next set the Bond Hub interface object to ScanCheck and the .mht code for the recorded +command will be logged, update the device name IE: MasterFan and the command name IE: PowerOff and +paste the code in your .mht file. + +=head2 Interface Configuration + +mh.private.ini configuration: + +In order to allow for multiple BondHome Hubs, instance names are used. +the following are prefixed with the instance name (BondHome). + + + +The IP of the BondHome Hub: + BondHome_ip=192.168.1.50 + + +Max command retrys when a command fails to send to the BondHome Hub: + BondHome_maxretry=4 + + +=head2 Defining the Interface Object + +In addition to the above configuration, you must also define the interface +object. The object can be defined in the user code. + +In user code: + + $BondHomeHub = new BondHome('BondHome'); + +Wherein the format for the definition is: + + $BondHomeHub = new BondHome(INSTANCE); + +States: +GetToken,Reboot,LogDevs,ReloadCache,LogVersion,ScanRF,ScanIR,ScanStop,ScanCheck + + + +=head2 NOTES + +An example mh.private.ini: + + BondHome_maxretry=4 + BondHome_ip=192.168.1.50 + + +An example user code: + + #noloop=start + + use BondHome; + + $BondHomeHub = new BondHome('BondHome'); + + $MasterFan = new BondHome_Device('BondHome','MasterFan'); + + $TV = new BondHome_Manual('BondHome'); + $TV->addcmd('power', '38', 'OOK', 'hex', '40000', '1', '00000'); + + #noloop=stop + + +An example .mht code: + BONDHOME, BondHome, BondHome + BONDHOME_DEVICE, masterfan, BondHome, masterfan + BONDHOME_DEVICE, guestfan, BondHome, guestfan + + + BONDHOME_MANUAL, TV, BondHome + BONDHOME_MANUAL_CMD, TV, power, 38, OOK, hex, 40000, 1, 0000000 + + +=head2 INHERITS + +L + +=head2 METHODS + +=over + +=cut + +package BondHome; +@BondHome::ISA = ('Generic_Item'); + +use Data::Dumper; +use JSON; + +sub new { + my ( $class, $instance ) = @_; + $instance = "BondHome" if ( !defined($instance) ); + ::print_log("Starting $instance instance of BondHome interface module"); + + my $self = new Generic_Item(); + + # Initialize Variables + $$self{instance} = $instance; + $$self{maxretry} = $::config_parms{ $instance . '_maxretry' } || 4; + $$self{ip} = $::config_parms{ $instance . '_ip' }; + my $year_mon = &::time_date_stamp( 10, time ); + $$self{log_file} = $::config_parms{'data_dir'} . "/logs/BondHome.$year_mon.log"; + $$self{token_file} = $::config_parms{'data_dir'} . "/.bh-$instance"; + + bless $self, $class; + + #Store Object with Instance Name + $self->_set_object_instance($instance); + #$self->restore_data( 'token' ); + $$self{token} = $self->get_data($$self{token_file}); #The normal restore_data happens after new is called, so we have to save to a file. + @{$$self{states}} = ('GetToken','Reboot','LogDevs','ReloadCache', 'LogVersion', 'ScanRF', 'ScanIR', 'ScanStop', 'ScanCheck'); + return $self; +} + +sub get_object_by_instance { + my ($instance) = @_; + return $Interfaces{$instance}; +} + +sub _set_object_instance { + my ( $self, $instance ) = @_; + $Interfaces{$instance} = $self; +} + +sub init { + +} + +=item C + +Used to associate child objects with the interface. + +=cut + +sub register { + my ( $self, $object, $class ) = @_; + if ( $object->isa('BondHome_Device') ) { + ::print_log("Registering BondHome Device Child Object"); + push @{ $self->{device_object} }, $object; + } elsif ( $object->isa('BondHome_Manual') ) { + ::print_log("Registering BondHome Manual Child Object" ); + push @{ $self->{manual_object} }, $object; + } +} + + + +sub set { + my ( $self, $p_state, $p_setby, $p_response ) = @_; + if ( uc $p_state eq 'GETTOKEN' ) { + ::print_log( "[BondHome] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + $self->gettoken( $object, $class ); + } elsif ( uc $p_state eq 'REBOOT' ) { + ::print_log( "[BondHome] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + return unless $self->tokencheck; + $self->bondcmd( $class, '/v2/sys/reboot', 'PUT', '{}' ); + } elsif ( uc $p_state eq 'LOGDEVS' ) { + ::print_log( "[BondHome] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + $self->logdevs( $object, $class ); + } elsif ( uc $p_state eq 'LOGVERSION' ) { + ::print_log( "[BondHome] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + return unless $self->tokencheck; + ::print_log "[BondHome] version ".$self->bondcmd( $class, '/v2/sys/version', 'GET')->{_content}; + } elsif ( uc $p_state eq 'RELOADCACHE' ) { + ::print_log( "[BondHome] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + return unless $self->tokencheck; + ::print_log "[BondHome] Reloading local device cache from Bond"; + delete $self->{devicehash}->{devicename} if $self->{devicehash}->{devicename}; + $self->getbonddevs( $object, $class ); + foreach my $child ( @{ $self->{device_object} } ) { + $child->updatestates($class); + } + } elsif ( uc $p_state eq 'SCANRF' ) { + $self->scan('RF',$class ); + } elsif ( uc $p_state eq 'SCANIR' ) { + $self->scan('IR',$class ); + } elsif ( uc $p_state eq 'SCANSTOP' ) { + $self->scan('STOP',$class ); + } elsif ( uc $p_state eq 'SCANCHECK' ) { + $self->scan('CHECK',$class ); + } else { + ::print_log( "[BondHome] Unknown request " . $p_state . " for " . $self->get_object_name ); + } +} + +sub tokencheck { + my ( $self ) = @_; + unless ( $$self{token} ) { + ::print_log "[BondHome] You must reboot the Bond Home and set the parent BondHome object to gettoken to get/set the token"; + return; + } + return 1; +} + +sub devexists { + my ( $self, $object, $class ) = @_; + my $token = $$self{token}; + my $devicename = $$object{devicename}; + + return unless $self->tokencheck; + + unless ( $self->{devicehash}->{devicename} ) { + ::print_log ("[BondHome] There are no devices in cache, running reload cache ". $self->get_object_name ); + $self->getbonddevs( $object, $class ); + + unless ( $self->{devicehash}->{devicename} ) { + ::print_log ("[BondHome] (Sub devexists) There are no devices in cache after reloading the cache, something went wrong"); + return; + } + } + + return 1 if ( exists $self->{devicehash}->{devicename}->{$devicename} ); + + ::print_log ( "[BondHome] there is no device with the name \"$devicename\" configured on the BondHome Hub " . $self->get_object_name ); + return; +} + + +sub getdevstates { + my ( $self, $object, $class ) = @_; + my $token = $$self{token}; + my $devicename = $$object{devicename}; + + return unless $self->tokencheck; + + unless ( $self->{devicehash}->{devicename} ) { + ::print_log "[BondHome] There are no devices in cache, running reload cache"; + $self->getbonddevs( $object, $class ); + + unless ( $self->{devicehash}->{devicename} ) { + ::print_log "[BondHome] (Sub getdevstates) There are no devices in cache after reloading the cache, something went wrong"; + return; + } + } + + + my @states; + my @speeds; + foreach my $command (keys %{$self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}} ) { + push @states, ucfirst($command); + if ( normalize($command) =~ /speed(\d)/ ) { + push @speeds, $1; + } + } + @speeds = sort @speeds; + push @states, @speeds; + return @states; +} + + +sub normalize { + my ( $string ) = @_; + $string = lc $string; + $string =~ s/ //g; + return $string; +} + +sub get_data { + my ( $self, $file ) = @_; + if( -e $file ) { + open my $FH, '<', $file or ::print_log "[BondHome] failed to open token file $!"; + while (my $line = <$FH>) { + $line =~ s/^\s+//; + $line =~ s/\s+$//; + if ( length($line) > 1 ) { + close $FH; + return $line; + } + } + close $FH; + } +} + + +sub save_data { + my ( $self, $file, $data ) = @_; + ::print_log "[BondHome] saving token to $file"; + open my $FH, '>', $file or ::print_log "[BondHome] failed to save token to file $!"; + print $FH $data; + close($FH); +} + + +sub scan { + my ( $self, $type, $class ) = @_; + my $token = $$self{token}; + my $instance = $$self{instance}; + my $http; + my $content; + my $url = '/v2/signal/scan'; + + if ($type eq 'IR') { + $http = 'PUT'; + $content = '{ "freq": 38, "modulation": "OOK" }'; + } elsif ($type eq 'RF') { + $http = 'PUT'; + $content = '{ "modulation": "OOK" }'; #RF all frequencies + } elsif ($type eq 'STOP') { + $http = 'DELETE'; + } elsif ($type eq 'CHECK') { + $http = 'GET'; + } + + return unless $self->tokencheck; + + my $response = $self->bondcmd( $class, $url, $http, $content ); + + #BONDHOME_MANUAL, masterfan, BondHome + #BONDHOME_MANUAL_CMD, masterfan, power, 434000, OOK, cq, 1000, 12, 110100110110H + + + if ($type eq 'CHECK') { + return unless $response; + my $message = $response->decoded_content; + eval { $message = decode_json($message) }; + if ( $message->{success} ) { + my $response2 = $self->bondcmd( $class, $url.'/signal', 'GET' ); + return unless $response2; + my $message2 = $response2->decoded_content; + eval { $message2 = decode_json($message2) }; + + ::print_log "[BondHome] Listing mht code for manual device command"; + my $msg = "\nBONDHOME_MANUAL, dev_name_update_me, $instance"; + $msg .= "\nBONDHOME_MANUAL_CMD, dev_name_update_me, cmd_name_update_me, ".$message2->{freq}.", ".$message2->{modulation}.", ".$message2->{encoding}.", ".$message2->{bps}.", ".$message2->{reps}.", ".$message2->{data}; + ::print_log $msg; + } elsif ( $message->{running} ) { + ::print_log "[BondHome] Scan is still running and no signals have been seen"; + } else { + ::print_log "[BondHome] Scan has timed out and no signals have been seen"; + } + } +} + + + +sub logdevs { + my ( $self, $object, $class ) = @_; + my $token = $$self{token}; + my $instance = $$self{instance}; + my $name = $self->get_object_name; + $name =~ s/\$//; + + return unless $self->tokencheck; + + unless ( $self->{devicehash}->{devicename} ) { + ::print_log "[BondHome] There are no devices in cache, running reload cache"; + $self->getbonddevs( $object, $class ); + } + ::print_log "[BondHome] Listing mht code for Bond devices"; + my $message = "\nBONDHOME, $name, $instance"; + foreach my $devicename (keys %{$self->{devicehash}->{devicename}}) { + $message .= "\nBONDHOME_DEVICE, $devicename, $instance, $devicename"; + } + ::print_log $message; +} + + +sub gettoken { + my ( $self, $object, $class ) = @_; + ::print_log "[BondHome] Getting Token"; + my $response = $self->bondcmd( $class, '/v2/token', 'GET' ); + my $message = $response->decoded_content; + eval { $message = decode_json($message) }; + if ( $message->{locked} ) { + ::print_log "[BondHome] You must reboot the Bond Home before running gettoken"; + return; + } + $$self{token} = $message->{token}; + my $token = $message->{token}; + $self->save_data( $$self{token_file}, $token ); + ::print_log "[BondHome] Got Token: $$self{token}" if $$self{token}; + delete $self->{devicehash} if $self->{devicehash}; + $self->{devicehash} = $self->getbonddevs( $object, $class); +} + + +sub reloadcache { + my ( $self, $object, $class ) = @_; + + ::print_log "[BondHome] Reloading local device cache from Bond"; + delete $self->{devicehash}->{devicename} if $self->{devicehash}->{devicename}; + $self->getbonddevs( $object, $class ); +} + + +sub getbonddevs { + my ( $self, $class ) = @_; + my $maxretry = $$self{maxretry}; + my $token = $$self{token}; + my $response; + return unless $self->tokencheck; + for (0..$maxretry) { + $response = $self->bondcmd( $class, '/v2/devices', 'GET' ); + } + return unless $response; + my $message = $response->decoded_content; + eval { $message = decode_json($message) }; + + ::print_log("[BondHome] reloading MH device cache from bond"); + + foreach my $deviceid (keys %{$message}) { + next if $deviceid =~ /_/; + for (0..$maxretry) { + $response = $self->bondcmd( $class, "/v2/devices/$deviceid", 'GET' ); + last if $response; + } + next unless $response; + my $message = $response->decoded_content; + eval { $message = decode_json($message) }; + my $devicename = normalize($message->{name}); + $self->{devicehash}->{devicename}->{$devicename}->{id}=$deviceid; + + foreach my $action ( @{$message->{actions}} ) { + next if ( $action =~ /^Set/ ); #Skip the Set actions because they require arguments. + next if ( $action =~ /^IncreaseSpeed/ ); #Skip the IncreaseSpeed actions because they require arguments. + next if ( $action =~ /^DecreaseSpeed/ ); #Skip the DecreaseSpeed actions because they require arguments. + $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{normalize($action)}->{action}=$action; + } + #::print_log Dumper $message; + my $response2; + for (0..$maxretry) { + $response2 = $self->bondcmd( $class, "/v2/devices/$deviceid/commands", 'GET' ); + last if $response2; + } + next unless $response2; + my $message2 = $response2->decoded_content; + eval { $message2 = decode_json($message2) }; + foreach my $cmdid (keys %{$message2}) { + next if $cmdid =~ /_/; + for (0..$maxretry) { + $response = $self->bondcmd( $class, "/v2/devices/$deviceid/commands/$cmdid", 'GET' ); + last if $response; + } + next unless $response; + my $message = $response->decoded_content; + eval { $message = decode_json($message) }; + my $cmdname = normalize($message->{name}); + $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{$cmdname}->{id}=$cmdid; + #::print_log Dumper $$self{devicehash}; + } + + ::print_log "[BondHome] \n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n" if $debug; + } +} + + + +sub sendcmd { + my ( $self, $object, $cmdname, $class, $argument ) = @_; + + return unless $self->tokencheck; + + my $maxretry = $$self{maxretry}; + my $devicename = $$object{devicename}; + $cmdname = normalize($cmdname); + + if ( exists $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{$cmdname}->{id} ) { + my $deviceid = $self->{devicehash}->{devicename}->{$devicename}->{id}; + my $cmdid = $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{$cmdname}->{id}; + for (0..$maxretry) { + my $response = $self->bondcmd( $class, "/v2/devices/$deviceid/commands/$cmdid/tx", 'PUT', '{}' ); + last if $response; + } + } elsif ( exists $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{$cmdname}->{action} ) { + my $deviceid = $self->{devicehash}->{devicename}->{$devicename}->{id}; + my $action = $self->{devicehash}->{devicename}->{$devicename}->{commands}->{name}->{$cmdname}->{action}; + if ( $argument ) { + $argument = '{"argument": '.$argument.'}'; + } else { + $argument = '{}'; + } + for (0..$maxretry) { + my $response = $self->bondcmd( $class, "/v2/devices/$deviceid/actions/$action", 'PUT', $argument ); + last if $response; + } + } else { + ::print_log "[BondHome] Invalid command: $cmdname for " . $self->get_object_name; + } + +} + + +sub bondcmd { + my ( $self, $class, $url, $function, $content ) = @_; + use LWP::UserAgent; + use HTTP::Request; + my $ip = $$self{ip}; + my $token = $$self{token}; + my $userAgent = LWP::UserAgent->new(); + $userAgent->timeout(1); + my $request; + + if ( $function eq 'PUT' ) { + $request = HTTP::Request->new(PUT => 'http://'.$ip.$url); + $request->content($content); + } elsif ( $function eq 'POST' ) { + $request = HTTP::Request->new(POST => 'http://'.$ip.$url); + $request->content($content); + } elsif ( $function eq 'DELETE' ) { + $request = HTTP::Request->new(DELETE => 'http://'.$ip.$url); + } elsif ( $function eq 'GET' ) { + $request = HTTP::Request->new(GET => 'http://'.$ip.$url); + } + + unless ( $url =~ /\/token$/ ) { + $request->header('Host' => "$ip"); + $request->header('BOND-Token' => "$token"); + } + + #::print_log("[BondHome] request: ".Dumper $request); + my $response = $userAgent->request($request); + if ($response->is_error) { + ::print_log("[BondHome] http request: http://$ip$url failed - ". $response->status_line ." ". $response ->decoded_content); + if ($response->status_line =~ /read timeout/) { + ::print_log("[BondHome] retrying request: http://$ip$url"); + } + return 0; + } + return $response; +} + +=back + +=head1 B + +=head2 SYNOPSIS + +User code: + + $LivingRoomFan = new BondHome_Device('BondHome','livingroomfan'); + + Wherein the format for the definition is: + $BondHome_Device = new BondHome_Device(INSTANCE,BondHomeDeviceName); + +States: +Dynamic from BondHome Hub + +See C for a more detailed description of the arguments. + + +=head2 DESCRIPTION + + Configures a device from the BondHome to be controlled by MH. + +=head2 INHERITS + +L + +=head2 METHODS + +=over + +=cut + +package BondHome_Device; +@BondHome_Device::ISA = ('Generic_Item'); + +=item C + +Instantiates a new object. + +$instance = The instance of the parent BondHome hub object that this device is found on + +$devicename = The name of the device used on the bondhome hub + +=cut + + +sub new { + my ( $class, $instance, $devicename ) = @_; + my $self = new Generic_Item(); + bless $self, $class; + $$self{parent} = BondHome::get_object_by_instance($instance); + $$self{parent}->register( $self, $class ); + $$self{devicename} = normalize($devicename); + + $$self{parent}->devexists( $self, $class ); + + @{ $$self{states} } = $$self{parent}->getdevstates( $self, $class ); + + return $self; +} + + +sub set { + my ( $self, $p_state, $p_setby, $p_response ) = @_; + + $p_state = normalize($p_state); + if ( $p_state =~ /^\d+$/ ) { + $p_state = "speed$p_state"; + } + if ( $self->validstate( $p_state ) ) { + ::print_log( "[BondHome::Device] Received request " . $p_state . " for " . $self->get_object_name ); + $self->SUPER::set( $p_state, $p_setby ); + $$self{parent}->sendcmd( $self, normalize($p_state), $class ); + } + else { + ::print_log( "[BondHome::Device] Received INVALID request " . $p_state . " for " . $self->get_object_name ); + } +} + +sub validstate { + my ( $self, $p_state ) = @_; + + foreach my $state ( @{ $$self{states} } ) { + if ( normalize($state) eq $p_state ) { + return $p_state; + } + + } + return 0; +} + +sub getcmd { + my ( $self, $cmd ) = @_; + + foreach my $state ( @{ $$self{states} } ) { + if ( normalize($state) =~ /$cmd/ ) { + return normalize($state); + } + } + return 0; +} + + +sub updatestates { + my ( $self, $class ) = @_; + @{ $$self{states} } = $$self{parent}->getdevstates( $self, $class ); +} + + +sub normalize { + my ( $string ) = @_; + $string = lc $string; + $string =~ s/ //g; + return $string; +} + + +=back + +=head1 B + +=head2 SYNOPSIS + +User code: + + $LivingRoomFan = new BondHome_Manual('BondHome'); + + Wherein the format for the definition is: + $BondHomeManualObject = new BondHome_Manual(INSTANCE); + + Add discovered commands with: + $LivingRoomFan->addcmd('power', '434000', 'OOK', 'cq', '1000', '12', '110100110110H'); + + Wherein the format for the definition is: + $BondHomeManualObject->addcmd(CommandName, Frequency, Modulation, Encoding, Bps, Reps, Data); + +mht file code: + + BONDHOME_MANUAL, LivingRoomFan, BondHome + + Wherein the format for the definition is: + BONDHOME_MANUAL, ObjectName, INSTANCE + + Add discovered commands with: + BONDHOME_MANUAL_CMD, LivingRoomFan, power, 434000, OOK, cq, 1000, 12, 110100110110H + + Wherein the format for the definition is: + BONDHOME_MANUAL_CMD, BondHomeManualObject, CommandName, Frequency, Modulation, Encoding, Bps, Reps, Data + +States: +Created from the BondHome addcmd sub routine + + +See C for a more detailed description of the arguments. + + +=head2 DESCRIPTION + + Configures a device from the BondHome to be controlled by MH. + +=head2 INHERITS + +L + +=head2 METHODS + +=over + +=cut + +package BondHome_Manual; +@BondHome_Manual::ISA = ('Generic_Item'); + +=item C + +Instantiates a new object. + +$instance = The instance of the parent BondHome hub object that this device is found on + + +=cut + +use Data::Dumper; +use JSON; + +sub new { + my ( $class, $instance ) = @_; + my $self = new Generic_Item(); + bless $self, $class; + $$self{parent} = BondHome::get_object_by_instance($instance); + $$self{parent}->register( $self, $class ); + @{$$self{states}} = (' '); + + return $self; +} + + +sub set { + my ( $self, $p_state, $p_setby, $p_response ) = @_; + + $p_state = normalize($p_state); + if ( exists $self->{devicehash}->{commands}->{name}->{$p_state} ) { + ::print_log( "[BondHome::Manual] Received request " . $p_state . " for " . $self->get_object_name ); + + my $content; + $content->{freq} = $self->{devicehash}->{commands}->{name}->{$p_state}->{freq}; + $content->{modulation} = $self->{devicehash}->{commands}->{name}->{$p_state}->{modulation}; + $content->{data} = $self->{devicehash}->{commands}->{name}->{$p_state}->{data}; + $content->{encoding} = $self->{devicehash}->{commands}->{name}->{$p_state}->{encoding}; + $content->{bps} = $self->{devicehash}->{commands}->{name}->{$p_state}->{bps}; + $content->{reps} = $self->{devicehash}->{commands}->{name}->{$p_state}->{reps}; + $content->{use_scan}='false'; + + $content = encode_json($content); + #::print_log( "[BondHome::Manual] content: $content" ); + $$self{parent}->bondcmd( $class, '/v2/signal/tx', 'PUT', $content ); + + $self->SUPER::set( $p_state, $p_setby ); + + } + else { + ::print_log( "[BondHome::Manual] Received INVALID request " . $p_state . " for " . $self->get_object_name ); + } +} + + +sub addcmd { + my ($self, $cmdname, $frequency, $modulation, $encoding, $bps, $reps, $data) = @_; + + #::print_log "[BondHome::Manual] ". Dumper Dumper $self; + ::print_log( "[BondHome::Manual] adding new command $cmdname" ); + $cmdname = normalize($cmdname); + $self->{devicehash}->{commands}->{name}->{$cmdname}->{modulation} = $modulation; + $self->{devicehash}->{commands}->{name}->{$cmdname}->{data} = $data; + $self->{devicehash}->{commands}->{name}->{$cmdname}->{freq} = $frequency; + $self->{devicehash}->{commands}->{name}->{$cmdname}->{encoding} = $encoding; + $self->{devicehash}->{commands}->{name}->{$cmdname}->{bps} = $bps; + $self->{devicehash}->{commands}->{name}->{$cmdname}->{reps} = $reps; + + $self->{ 'cmdtimer' } = ::Timer::new(); + $self->{ 'cmdtimer' }->set( + 5, + sub { $self->updatestates; } + ); + +} + + +sub updatestates { + my ( $self ) = @_; + my @speeds; + foreach my $command (keys %{$self->{devicehash}->{commands}->{name}} ) { + push @{ $$self{states} }, ucfirst($command); + if ( normalize($command) =~ /speed(\d)/ ) { + push @speeds, $1; + } + } + @speeds = sort @speeds; + push @{ $$self{states} }, @speeds; + +} + + +sub normalize { + my ( $string ) = @_; + $string = lc $string; + $string =~ s/ //g; + return $string; +} + +=back + +=head2 INI PARAMETERS + +=head2 NOTES + +=head2 AUTHOR + +Wayne Gatlin + +=head2 SEE ALSO + +=head2 LICENSE + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +=cut + diff --git a/lib/read_table_A.pl b/lib/read_table_A.pl index b119b2c7e..89b56bc11 100644 --- a/lib/read_table_A.pl +++ b/lib/read_table_A.pl @@ -1840,7 +1840,45 @@ sub read_table_A { $object = ''; } #-------------- End Alexa Objects ---------------- - + #-------------- BondHome Objects ----------------- + elsif ( $type eq "BONDHOME" ) { + ## + require 'BondHome.pm'; + $code .= '#noloop=start'."\n"; + my ($instance); + ( $name, $instance, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); # Quote data + $object = "BondHome('$instance','$other')".';'."\n".'#noloop=stop'."\n"; + } + elsif ( $type eq "BONDHOME_DEVICE" ) { + ## + require 'BondHome.pm'; + $code .= '#noloop=start'."\n"; + my ($instance, $bonddevname); + ( $name, $instance, $bonddevname, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); + $object = "BondHome_Device('$instance','$bonddevname')".';'."\n".'#noloop=stop'."\n"; + $code .= '#noloop=stop'."\n"; + } + elsif ( $type eq "BONDHOME_MANUAL" ) { + ## + require 'BondHome.pm'; + $code .= '#noloop=start'."\n"; + my ($instance); + ( $name, $instance, $grouplist, @other ) = @item_info; + $other = join ', ', ( map { "'$_'" } @other ); + $object = "BondHome_Manual('$instance')".';'."\n".'#noloop=stop'."\n"; + $code .= '#noloop=stop'."\n"; + } + elsif ( $type eq "BONDHOME_MANUAL_CMD" ) { + ## + $code .= '#noloop=start'."\n"; + my ($parent, $cmdname, $frequency, $modulation, $encoding, $bps, $reps, $data) = @item_info;; + $code .= sprintf "\$%-35s -> addcmd('$cmdname', '$frequency', '$modulation', '$encoding', '$bps', '$reps', '$data');\n", $parent; + $object = ''; + $code .= '#noloop=stop'."\n"; + } + #-------------- End BondHome Objects ----------------- #-------------- AoGSmartHome Objects ----------------- elsif ( $type eq "AOGSMARTHOME_ITEMS" ) { ##