Skip to content

Instantly share code, notes, and snippets.

@stephenlb
Forked from domadev812/01.py
Created November 15, 2017 21:06
Show Gist options
  • Save stephenlb/518b8312393d43c23336791105c4ac6f to your computer and use it in GitHub Desktop.
Save stephenlb/518b8312393d43c23336791105c4ac6f to your computer and use it in GitHub Desktop.
[Blog] DIY Hue Lightbulb with Android Control
#!/usr/bin/python
import time
import RPi.GPIO as GPIO
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
## Make your pin assignments
red_gpio = 18
green_gpio = 23
blue_gpio = 24
## Setup GPIO Board and Pins
GPIO.setmode(GPIO.BCM) # BCM for GPIO numbering
GPIO.setup(red_gpio, GPIO.OUT)
GPIO.setup(green_gpio, GPIO.OUT)
GPIO.setup(blue_gpio, GPIO.OUT)
## Init the GPIO PWMs
Freq = 100 #Hz
RED = GPIO.PWM(red_gpio, Freq)
RED.start(0)
GREEN = GPIO.PWM(green_gpio, Freq)
GREEN.start(0)
BLUE = GPIO.PWM(blue_gpio, Freq)
BLUE.start(0)
# Update the hue with R G B values
def updateHue(R, G, B):
rVal = 100 - (R/255.0)*100 # Will have to change these values depending on
gVal = 100 - (G/255.0)*100 # whether your LED has a common cathode or common
bVal = 100 - (B/255.0)*100 # anode. This code is for common anode.
print "rgb(%.2f, %.2f, %.2f)" % (rVal, gVal, bVal)
RED.ChangeDutyCycle(rVal)
GREEN.ChangeDutyCycle(gVal)
BLUE.ChangeDutyCycle(bVal)
rVal = (R/255.0)*100 # Common Cathode (Ground)
rVal = 100 - (R/255.0)*100 # Common Anode (5V)
def rgb():
updateHue(255,0,0); time.sleep(2)
updateHue(0,255,0); time.sleep(2)
updateHue(0,0,255); time.sleep(2)
def main():
rgb()
updateHue(0,0,0); # Light off
main()
def main():
...
# Instantiate Pubnub
pnconfig = PNConfiguration()
pnconfig.subscribe_key = "my_subkey"
pnconfig.publish_key = "my_pubkey"
pnconfig.ssl = False
pubnub = PubNub(pnconfig)
# This is the channel your Pi will be listening on for RGB values
channel = 'phue'
# This callback handles any messages received on subscribed channel
def _callback(msg, n):
print(msg)
updateHue(msg["RED"], msg["GREEN"], msg["BLUE"])
def _error(m):
print(m)
class MySubscribeCallback(SubscribeCallback):
def presence(self, pubnub, presence):
pass # handle incoming presence data
def status(self, pubnub, status):
if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory:
pass # This event happens when radio / connectivity is lost
elif status.category == PNStatusCategory.PNConnectedCategory:
# Connect event. You can do stuff like publish, and know you'll get it.
# Or just use the connected event to confirm you are subscribed for
# UI / internal notifications, etc
pubnub.publish().channel("awesomeChannel").message("hello!!").async(my_publish_callback)
elif status.category == PNStatusCategory.PNReconnectedCategory:
pass
# Happens as part of our regular operation. This event happens when
# radio / connectivity is lost, then regained.
elif status.category == PNStatusCategory.PNDecryptionErrorCategory:
pass
# Handle message decryption error. Probably client configured to
# encrypt messages and on live data feed it received plain text.
def message(self, pubnub, message):
print(message)
updateHue(message["RED"], message["GREEN"], message["BLUE"])
pubnub.subscribe().channels(channel).execute()
pubnub.add_listener(MySubscribeCallback())
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile group: 'com.pubnub', name: 'pubnub-gson', version: '4.14.0'
}
package me.kevingleason.phue;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.pubnub.api.Callback;
import com.pubnub.api.Pubnub;
import com.pubnub.api.PubnubError;
import org.json.JSONException;
import org.json.JSONObject;
public class MainActivity extends Activity {
public static final int COLOR_RED = 0; // Arbitrary ID's
public static final int COLOR_BLUE = 1; // that are used
public static final int COLOR_GREEN = 2; // later in the code
SeekBar mRedSeek, mGreenSeek, mBlueSeek; // All our layout pieces
LinearLayout mRGBValueHolder; // that we will be editing
// Pubnub instance and all the keys you will need to stream data
private Pubnub mPubNub;
public static final String PUBLISH_KEY = "your-pub-key";
public static final String SUBSCRIBE_KEY = "your-sub-key";
public static final String CHANNEL = "phue";
private long lastUpdate = System.currentTimeMillis(); // Used to thereshold
...
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Instantiate PubNub. We will cover this shortly.
initPubNub();
// Get Seek Bars. They will be at R.id.<your-seekbar-id>
mRedSeek = (SeekBar) findViewById(R.id.red_seek);
mGreenSeek = (SeekBar) findViewById(R.id.green_seek);
mBlueSeek = (SeekBar) findViewById(R.id.blue_seek);
mRGBValueHolder = (LinearLayout) findViewById(R.id.rgb_value_holder);
// Setup Seek Bars. We will cover this function shortly.
setupSeekBar(mRedSeek, COLOR_RED);
setupSeekBar(mGreenSeek, COLOR_GREEN);
setupSeekBar(mBlueSeek, COLOR_BLUE);
}
public void initPubNub(){
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.setSubscribeKey("demo");
pnConfiguration.setPublishKey("demo");
this.mPubNub = new PubNub(pnConfiguration);
this.mPubNub.setUUID("AndroidPHue");
subscribe();
}
public void subscribe(){
try {
this.mPubNub.subscribe()
.channels(CHANNEL) // subscribe to channels
.withPresence() // also subscribe to related presence information
.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setupSeekBar(SeekBar seekBar, final int colorID){
seekBar.setMax(255); // Seek bar values goes 0-255
seekBar.setProgress(255); // Set the knob to 255 to start
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
publish(mRedSeek.getProgress(), mGreenSeek.getProgress(), mBlueSeek.getProgress());
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
publish(mRedSeek.getProgress(), mGreenSeek.getProgress(), mBlueSeek.getProgress());
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
TextView colorValueText;
switch (colorID){ // Get the TextView identified by the colorID
case COLOR_RED:
colorValueText = (TextView)findViewById(R.id.red_value);
break;
case COLOR_GREEN:
colorValueText = (TextView) findViewById(R.id.green_value);
break;
case COLOR_BLUE:
colorValueText = (TextView)findViewById(R.id.blue_value);
break;
default:
Log.e("SetupSeek","Invalid color.");
return;
}
colorValueText.setText(String.valueOf(progress)); // Update the 0-255 text
int red = mRedSeek.getProgress(); // Get Red value 0-255
int green = mGreenSeek.getProgress(); // Get Grn value 0-255
int blue = mBlueSeek.getProgress(); // Get Blu value 0-255
updateRGBViewHolderColor(red, green, blue); // Change the background of the viewholder
long now = System.currentTimeMillis(); // Only allow 1 pub every 100 milliseconds
if (now - lastUpdate > 100 && fromUser) { // Threshold and only send when user sliding
lastUpdate = now;
publish(red, green, blue); // Stream RGB values to the Pi
}
}
});
}
private void updateRGBViewHolderColor(int red, int green, int blue){
int alpha = 255; // No opacity
int color = (alpha << 24) | (red << 16) | (green << 8) | blue;
mRGBValueHolder.setBackgroundColor(color);
}
public void publish(int red, int green, int blue){
JSONObject js = new JSONObject();
try {
js.put("RED", red);
js.put("GREEN", green);
js.put("BLUE", blue);
} catch (JSONException e) { e.printStackTrace(); }
pubnub.publish().channel(CHANNEL).message(js).async(new PNCallback<PNPublishResult>() {
@Override
public void onResponse(PNPublishResult result, PNStatus status) {
// Check whether request successfully completed or not.
if (!status.isError()) {
Log.d("PUBNUB" result.toString());
}
// Request processing failed.
else {
// Handle message publish error. Check 'category' property to find out possible issue
// because of which request did fail.
//
// Request can be resent using: [status retry];
}
}
});
}
public void setRGBSeeks(int red, int green, int blue){
mRedSeek.setProgress(red);
mGreenSeek.setProgress(green);
mBlueSeek.setProgress(blue);
}
public void lightOff(View view){
publish(0, 0, 0); // 0,0,0 is black or OFF
setRGBSeeks(0, 0, 0);
}
public void lightOn(View view){
publish(255, 255, 255); // 255,255,255 is White or ON
setRGBSeeks(255, 255, 255);
}
public void initPubNub(){
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.setSubscribeKey("SubscribeKey");
pnConfiguration.setPublishKey("PublishKey");
pnConfiguration.setSecure(false);
this.mPubNub = new PubNub(pnConfiguration);
this.mPubNub.setUUID("AndroidPHue");
hereNow(); // Check if any light is on the channel before you join
subscribe();
}
public void hereNow(){
this.mPubNub.hereNow()
// tailor the next two lines to example
.channels(CHANNEL)
.includeUUIDs(true)
.async(new PNCallback<PNHereNowResult>() {
@Override
public void onResponse(PNHereNowResult result, PNStatus status) {
if (status.isError()) {
// handle error
return;
}
Log.d("PUBNUB", response.toString());
try {
JSONObject json = (JSONObject) result;
if (json.getInt("occupancy") == 0){
setRGBSeeks(0,0,0);
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"No Light Detected.", Toast.LENGTH_LONG).show();
// Do something to views...
}
});
}
} catch (JSONException e){ e.printStackTrace(); }
}
});
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.kevingleason.phue" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
...
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<!-- colors.xml -->
<resources>
<color name="red">#FFFF0000</color>
<color name="green">#00FF00</color>
<color name="blue">#0000FF</color>
</resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="72dp">
<Button
android:onClick="lightOn"
style="@style/AppTheme.Button"
android:text="@string/on_button"/>
<ImageButton
android:src="@drawable/phue"
android:scaleType="centerCrop"
style="@style/AppTheme.Button"/>
<Button
android:onClick="lightOff"
style="@style/AppTheme.Button"
android:text="@string/off_button"/>
</LinearLayout>
<SeekBar
android:id="@+id/red_seek"
style="@style/AppTheme.SeekBar"
android:background="@color/red" />
<SeekBar
android:id="@+id/green_seek"
style="@style/AppTheme.SeekBar"
android:background="@color/green" />
<SeekBar
android:id="@+id/blue_seek"
style="@style/AppTheme.SeekBar"
android:background="@color/blue" />
<LinearLayout
android:id="@+id/rgb_value_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/red_value"
android:textColor="@color/red"
android:text="255"
style="@style/AppTheme.RGB"/>
<TextView
android:id="@+id/green_value"
android:textColor="@color/green"
android:text="255"
style="@style/AppTheme.RGB"/>
<TextView
android:id="@+id/blue_value"
android:textColor="@color/blue"
android:text="255"
style="@style/AppTheme.RGB"/>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- main/res/values/strings.xml -->
<resources>
<string name="app_name">PHue</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="on_button">ON</string>
<string name="off_button">OFF</string>
</resources>
<resources>
<!-- main/res/values/styles.xml -->
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
<!-- This is the container or RGB values -->
<style name="AppTheme.RGB">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">center</item>
<item name="android:textSize">40sp</item>
<item name="android:textStyle">bold</item>
</style>
<!-- The seek bar slider styles -->
<style name="AppTheme.SeekBar">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">25dp</item>
</style>
<!-- The on and off button styles -->
<style name="AppTheme.Button">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:background">#000</item>
<item name="android:textSize">20sp</item>
<item name="android:textColor">#fff</item>
</style>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment