1. Turn off Hue light bulb as soon as possible.
2. Turn off Hue light bulb an hour after it is turned on.
3. Turn off Hue light bulb 10 minutes after a TV is turned on.
Controlling Hue light bulb is already explained before; [link to more info]. And I posted how to detect whether TV is turned on/off already; [link to more info]. This article is to show the actual working example of the plugins for "cron_native".
I have six files for this:
pi@fileserver 01:37:55 plugins$ ls Makefile plugin.hello.cpp plugin.hue_outdoor.cpp stdafx.h my_curl.hpp plugin.hue_bedroom.cpp plugin.hue_tv_room.cpp pi@fileserver 01:37:57 plugins$
The first program to show is "plugin.hue_bedroom.cpp", which turns off the light as soon as possible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // plugin.hue_bedroom.cpp written by JaeHyukKwak #include "stdafx.h" #include "my_curl.hpp" static string MAC_HUE = "00:17:88:09:ef:18"; static uint32_t s_last_checked; extern "C" void dl_init( uint32_t ) { s_last_checked = 0; } extern "C" void dl_shutdown( uint32_t ) {} extern "C" void dl_main( uint32_t cur_time ) { if ( cur_time - s_last_checked <= 10 ) return; // every 10 seconds s_last_checked = cur_time; ifstream fin( "/proc/net/arp" ); string ip_hue = "192.168.0.50"; for ( string line; !fin.eof(); ) { // ex: 192.168.0.50 0x1 0x2 00:17:88:09:ef:18 * eth0 getline( fin, line ); string ip = line.substr( 0, line.find( ' ' ) ); if ( line.find( MAC_HUE ) != string::npos ) ip_hue = ip; } string URL = string( "http://" ) + ip_hue; URL += "/api/newdeveloper/lights/1/state"; my_curl_t curl; curl.SetOpt_URL( URL ); curl.SetOpt_READDATA( "{\"on\":false}" ); curl.RequestPut(); } |
Since the goal of the program is to turn off the light as soon as possible, it doesn't need to check to see if the light is turned on or not. It will work either way.
I wanted to have the program running every 10 seconds; not every seconds. Each HTTP request is not much cost in terms of CPU and network bandwidth. I may add up and become big when I have many other plugins running together.
The second program, "plugin.hue_outdoor.cpp", is to turn off the hue light only if it is turned on for an hour. As the file name implies, this is to turn off the out door light automatically in case my wife forgot to turn it off.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | // plugin.hue_outdoor.cpp written by JaeHyukKwak #include "stdafx.h" #include "my_curl.hpp" static string MAC_HUE = "00:17:88:09:ef:18"; static uint32_t s_last_checked, s_last_inactive; extern "C" void dl_init( uint32_t cur_time ) { s_last_checked = 0; s_last_inactive = cur_time; } extern "C" void dl_shutdown( uint32_t ) {} extern "C" void dl_main( uint32_t cur_time ) { if ( cur_time - s_last_checked <= 60 ) return; // every minute s_last_checked = cur_time; ifstream fin( "/proc/net/arp" ); string ip_hue = "192.168.0.50"; for ( string line; !fin.eof(); ) { // ex: 192.168.0.50 0x1 0x2 00:17:88:09:ef:18 * eth0 getline( fin, line ); string ip = line.substr( 0, line.find( ' ' ) ); if ( line.find( MAC_HUE ) != string::npos ) ip_hue = ip; } string URL = string( "http://" ) + ip_hue; URL += "/api/newdeveloper/lights/4"; my_curl_t curl; curl.SetOpt_URL( URL ); string reply = curl.RequestGet(); bool turned_on = [&] { bool inaccessible = ( reply.find( "\"on\":" ) == string::npos ); if ( inaccessible ) return false; return ( reply.find( "\"on\":true" ) != string::npos ); }(); if ( !turned_on ) s_last_inactive = cur_time; else if ( cur_time - s_last_inactive >= 60 * 60 ) // 60min { syslog( LOG_NOTICE, "Light#4 is on for 60min and turning off" ); curl.SetOpt_URL( URL + "/state" ); curl.SetOpt_READDATA( "{\"on\":false}" ); curl.RequestPut(); s_last_inactive = cur_time; } else {} // turned on but not waited long enough } |
The last program, "plugin.hue_tv_room.cpp", is to turn off the light when TV is turned on. But it turns off only once, meaning TV is turned off, the light will be turned off but when I turn on the light manually while the TV is turned on, the light wouldn't be turned off again. I could also have it turn on the light back when TV is turned off, but I didn't find it useful because when I turn off the TV, most likely I was leaving the room.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | // plugin.hue_tv_room.cpp written by JaeHyukKwak #include "stdafx.h" #include "my_curl.hpp" static const char *MAC_TV = "f4:7b:5e:41:91:00"; static const char *MAC_HUE = "00:17:88:09:ef:18"; static uint32_t s_last_inactive, s_last_checked; static bool s_light_once; // turn off light only once extern "C" void dl_init( uint32_t cur_time ) { s_last_checked = 0; s_last_inactive = cur_time; s_light_once = true; } extern "C" void dl_shutdown( uint32_t ) {} extern "C" void dl_main( uint32_t cur_time ) { if ( cur_time - s_last_checked <= 60 ) return; // every 60 seconds s_last_checked = cur_time; my_curl_t curl; string ip_hue = "192.168.0.50"; // in case not found from ARP bool is_tv_on = [&] { ifstream fin( "/proc/net/arp" ); string ip_tv; for ( string line; !fin.eof(); ) { // ex: 192.168.0.53 0x1 0x0 f4:7b:5e:41:91:00 * eth0 getline( fin, line ); string ip = line.substr( 0, line.find( ' ' ) ); if ( line.find( MAC_TV ) != string::npos ) ip_tv = ip; else if ( line.find( MAC_HUE ) != string::npos ) ip_hue = ip; } if ( ip_tv.empty() ) return false; // TV IP not found. string msg = "Server returned nothing (no headers, no data)"; curl.SetOpt_URL( string( "http://" ) + ip_tv + ":55000" ); try { curl.RequestGet(); } // expected to throw always catch ( runtime_error &e ) { return ( msg == e.what() + 28 ); } catch ( ... ) { return false; } return false; }(); if ( !is_tv_on ) { // when TV is off s_last_inactive = cur_time; s_light_once = true; } else if ( cur_time - s_last_inactive >= 60 ) // wait 1 minute { // when TV is on and waited enough if ( !s_light_once ) return; s_light_once = false; syslog( LOG_NOTICE, "TV is turned on and turning off light#2" ); const string URL_set = "/api/newdeveloper/lights/2/state"; curl.SetOpt_URL( string( "http://" ) + ip_hue + URL_set ); curl.SetOpt_READDATA( "{\"on\":false}" ); curl.RequestPut(); } else {} // when TV is on but not waited enough } |
Makefile looks like this:
# Makefile written by Jae Hyuk Kwak CC=g++-4.7 CFLAGS=-Wall -g -std=c++11 all: plugin.hello.so plugin.hue_outdoor.so plugin.hue_tv_room.so plugin.hello.so: plugin.hello.cpp stdafx.h.gch -$(CC) $(CFLAGS) -shared -fPIC -o plugin.hello.so plugin.hello.cpp plugin.hue_outdoor.so: plugin.hue_outdoor.cpp my_curl.hpp stdafx.h.gch -$(CC) $(CFLAGS) -shared -fPIC -lcurl -o plugin.hue_outdoor.so plugin.hue_outdoor.cpp plugin.hue_tv_room.so: plugin.hue_tv_room.cpp my_curl.hpp stdafx.h.gch -$(CC) $(CFLAGS) -shared -fPIC -lcurl -o plugin.hue_tv_room.so plugin.hue_tv_room.cpp plugin.hue_bedroom.so: plugin.hue_bedroom.cpp my_curl.hpp stdafx.h.gch -$(CC) $(CFLAGS) -shared -fPIC -lcurl -o plugin.hue_bedroom.so plugin.hue_bedroom.cpp stdafx.h.gch : stdafx.h $(CC) $(CFLAGS) -c stdafx.h clean: rm stdafx.h.gch plugin.*.so
The file, "stdafx.h", looks like this:
1 2 3 4 5 6 7 8 9 10 | // stdafx.h written by JaeHyukKwak #include <syslog.h> #include <string> #include <fstream> #include <stdexcept> // runtime_error #include <memory> // shared_ptr #include <string.h> // memcpy using namespace std; #include <curl/curl.h> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | // Written by Jae Hyuk Kwak, raspberrypiprogramming.blogspot.com class my_curl_t { unique_ptr< CURL, decltype( curl_easy_cleanup )* > curl_; string html_; // temporary buffer to store HTML content string put_; public: my_curl_t() : curl_( curl_easy_init(), curl_easy_cleanup ) { if ( nullptr == curl_ ) throw runtime_error( "curl_easy_init() failed." ); SetOpt_( CURLOPT_WRITEDATA, &html_ ); SetOpt_( CURLOPT_WRITEFUNCTION, WriteMemoryCallback ); SetOpt_( CURLOPT_READDATA, &put_ ); SetOpt_( CURLOPT_READFUNCTION, ReadDataCallback ); } string RequestGet() { return Request_( false ); } string RequestPut() { return Request_( true ); } void SetOpt_URL( string url ) { SetOpt_( CURLOPT_URL, url.c_str() ); } void SetOpt_READDATA( string data ) { put_ = data; } private: string Request_( bool put ) { SetOpt_( CURLOPT_UPLOAD, put ); const CURLcode res = curl_easy_perform( curl_.get() ); if ( CURLE_OK != res ) throw runtime_error( string( "curl_easy_perform() failed: " ) + curl_easy_strerror( res ) ); const string htmlCopied = html_; html_.clear(); return htmlCopied; } template< typename ParamType > void SetOpt_( CURLoption option, const ParamType ¶meter ) { CURLcode res = curl_easy_setopt( curl_.get(), option, parameter ); if ( CURLE_OK != res ) throw runtime_error( string( "curl_easy_setopt failed: " ) + curl_easy_strerror( res ) ); } static size_t WriteMemoryCallback( void *contents, size_t size, size_t numberOfMemoryBlock, void *userChunk ) { const size_t realSize = size * numberOfMemoryBlock; string &html = *static_cast< string * >( userChunk ); html += string( static_cast< char* >( contents ), realSize ); return realSize; } static size_t ReadDataCallback( char *buf, size_t size, size_t nitems, void *instream ) { string &put = *static_cast< string * >( instream ); const size_t putSize = put.size(); const size_t minSize = min( putSize, size * nitems ); memcpy( buf, put.c_str(), minSize ); if ( minSize == putSize ) put.clear(); else put = put.substr( minSize ); return minSize; } }; |
No comments:
Post a Comment