Git Product home page Git Product logo

python-laurel's Introduction

Python control of C by GE Bluetooth lightbulbs

A simple Python API for controlling C by GE lighting devices.

This is not an officially supported Google product.

Example use

Retrieve authentication data from the remote API and automatically create devices:

import laurel

devices = laurel.laurel("username", "password")

Show device information:

print(devices[0].name)
print(devices[0].id)
print(devices[0].type)
print(devices[0].brightness)
print(devices[0].temperature)

Set the device brightness to 50%:

devices[0].set_brightness(50)

If the device supports colour temperature setting:

if devices[0].supports_temperature == True:
  devices[0].set_temperature(50)

Turn the device off:

devices[0].set_power(False)

Force an update of the device state (note that this is asynchronous):

devices[0].update_status()

python-laurel's People

Contributors

mjg59 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-laurel's Issues

Support for C by GE Direct Connect Bulbs

Using this library I was able to find the C by GE Direct Connect bulbs on my account but the library is current able not to do anything with them. I think that this should work as no hub is required and the API can pick up these devices, there is just currently no support for it.

Laurel object is not subscriptable

the code I have on my Rasberry pi 3
I am running it on python3 as bluepy would not install on python 2.7

import laurel

devices = laurel.laurel("myUserName","myPassword")

devices[0].set_power(False)

The error I get is
Traceback (most recent call last):
File"
devices[0]/set_power(False)
TyoeError: 'laurel' object is not subscriptable

"Setters" not doing anything

I'm trying to work with this repository. Using Ubuntu on my laptop. I have all the lights right around. I'm able to log in with my username and password and see my devices, then I'm even sometimes able to "connect" to the network calling devices.devices[0].network.connect() (it claims it works occasionally). But then when I go to interact with the devices, nothing ever happens. Sometimes it runs through the script, sometimes it just hangs at one of the "set" parts. Anyone have any suggestions?

The brightness and temperature components of "devices" are just the class defaults (i.e. 0). Nothing is being called to update those.

import laurel

with open('../login.txt') as f:
    login_data = f.read()
username = login_data.split(',')[0]
password = login_data.split(', ')[1]

print('Logging in...')
devices = laurel.laurel(username, password)
print('Successfully logged in!')
devices = devices.devices

idx = 5
print('Name: {}\nMAC: {}\nType: {}\nBrightness: {}\nTemperature: {}\n'.format(
    devices[idx].name,
    devices[idx].mac,
    devices[idx].type,
    devices[idx].brightness,
    devices[idx].temperature))

print('Connecting...')
devices[idx].network.connect(idx_start = idx)

print('Setting brightness')
devices[idx].set_brightness(10) # THIS DOES NOTHING OR HANGS SOMETIMES

print('Setting power')
devices[idx].set_power(False) # THIS DOES NOTHING OR HANGS SOMETIMES

KeyError: 'mac'

Traceback (most recent call last):
File "", line 1, in
File "/home/eli/.local/share/Trash/files/python-laurel/laurel/init.py", line 101, in init
mac = [bulb['mac'][i:i+2] for i in range(0, 12, 2)]
File "/home/eli/.local/share/Trash/files/python-laurel/laurel/init.py", line 101, in
mac = [bulb['mac'][i:i+2] for i in range(0, 12, 2)]
KeyError: 'mac'

AttributeError: 'NoneType' object has no attribute 'send_packet'

>>> devices.devices[0].set_power(False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/srv/homeassistant/python-laurel/laurel/__init__.py", line 177, in set_power
    self.network.send_packet(self.id, 0xd0, [int(power)])
  File "/srv/homeassistant/python-laurel/laurel/__init__.py", line 137, in send_packet
    self.link.send_packet(id, command, params)
AttributeError: 'NoneType' object has no attribute 'send_packet'

Any ideas what could be causing this error?

No Support for External Control?

It appears from reading through your code that this library relies on a local network connection to the devices in order to issue any control commands, even though the C by GE app does not have this same constraint, and will in fact allow me to control my devices from anywhere in the world with an internet connection. Is this correct, that you didn't explore the control mechanisms which rely on the hosted API to communicate the commands down to the devices?

I found your library when searching for any info on the API used to remotely control these switches, bulbs, plugs, etc, so that I can add some automation which is not currently available with the C by GE app or via Google/Amazon and their integrations. If your library relies on the device executing the commands being on the same network segment as the devices being controlled in order to actually make changes, then it will unfortunately not be able to inform my own automation work, beyond the initial auth and device detail retrieval.

Thanks for your work; I look forward to your reply, and I'm hoping I missed something! ๐Ÿ˜

No Error Control if there are defunct devices

Got it working!!! Submitted changes...

The biggest change was error handling for properties as I was getting this error:

Failed to connect to F4:BC:DA:3F:E4:39 Failed to connect to peripheral F4:BC:DA:3F:E4:39, addr type: public
Traceback (most recent call last):
File "ge_plug.py", line 7, in
devices.devices[0].network.connect()
File "/home/wkenny/.local/lib/python3.8/site-packages/laurel/init.py", line 140, in connect
raise Exception("Unable to connect to mesh %s" % self.address)
Exception: Unable to connect to mesh 90E6BE00E748
if 'error' in properties:
                continue
            else:
                for bulb in properties['bulbsArray']:
                    id = int(bulb['deviceID'][-3:])
                    mac = [bulb['mac'][i:i+2] for i in range(0, 12, 2)]
                    mac = "%s:%s:%s:%s:%s:%s" % (mac[5], mac[4], mac[3], mac[2], mac[1], mac[0])
                    if network is None:
                        network = laurel_mesh(mesh['mac'], mesh['access_key'], self.mesh)
                    device = laurel_device(network, {'name': bulb['displayName'], 'mac': mac, 'id': id, 'type': bulb['deviceType']})
                    network.devices.append(device)
                    self.devices.append(device)

Here's what I'm doing with it...

Using the C by GE Smart Plug to flash Halloween lights on and off to "look like lightning" on my front porch. It should work with the bulbs too, but I don't have any to test it on.

This is my absolute first time writing anything in Python. I think I'm hooked.

#!/usr/bin/env python3
# flashy_lights.py
# William Kenny
# [email protected]
# 
# Utility to flash "holiday" lights using C by GE Smart Plugs
# Load up required modules first...
import laurel #module to communicate with C by GE products
import time
import random
import sys
import shelve
import argparse


# Let's deal with command line variables...
parser = argparse.ArgumentParser()
parser.add_argument("-tm","--timing_min", type=float, default=0.1, help='time lights will be flashed on for (0.1)')
parser.add_argument("-tmm","--timing_max", type=float, default=0.6, help='time lights will be flashed off for (0.6)')
parser.add_argument("-lm","--loop_min", type=int, default=3, help='min number of times lights will be flashed (3)')
parser.add_argument("-lmm","--loop_max", type=int, default=7, help='max number of times lights will be flashed (7)')
parser.add_argument("-r","--resting_state",action='store_true',default=False, help='state lights will be in when not flashing -- True is on, False is off. (False)')
parser.add_argument("-rt","--run_time", type=int, default=5, help='time in minutes script will run for in minutes (5)')
parser.add_argument("-p","--print_args",action='store_true', help='print arguments (False)')
parser.add_argument("-t", "--test_timing",action='store_true', help='test timing functions only (False)')
parser.add_argument("-f", "--flash", action='store_true', help='flash the lights once for testing (False)')
parser.add_argument("-v", "--verbose", action='store_true', help='sets verbose mode (False)')
parser.add_argument("-l", "--loop", action='store_true', help='do one loop and exit (False)')
parser.add_argument("-blm", "--between_loops_min", type=int, default=5,help='minimum time between loops in seconds (5)')
parser.add_argument("-blmm", "--between_loops_max", type=int, default=15,help='maximum time between loops in seconds (25)')
args = parser.parse_args()

#List out the arguments if we tell it to...
if args.print_args == True or args.verbose == True:
    print(f'timing_min      = {args.timing_min}')
    print(f'timing_max      = {args.timing_max}')
    print(f'loop_min        = {args.loop_min}')
    print(f'loop_max        = {args.loop_max}')
    print(f'resting_state   = {args.resting_state}')
    print(f'run_time        = {args.run_time}')
    print(f'print_args      = {args.print_args}')
    print(f'test_timing     = {args.test_timing}')
    print(f'flash           = {args.flash}')
    print(f'verbose         = {args.verbose}')



#Let's flash the lights
# function flash_it
#    turns light on, then off using predetermined lengths of on and off times
#    timing_on  - float - time lights will be on (resolution to 2 decimal points, no less than .10
#    timing_off - float - time lights will be off (resolution to 2 decimal points, no less than .10
def flash_it(timing_on,timing_off,v):
    #turn lights on
    if v == True:
        print(f' ON for {timing_on} seconds.')
    if args.test_timing == False:
        devices.devices[0].set_power(True)
    #wait before we turn it off for <timeon> seconds
    time.sleep(timing_on)
    #turn lights off
    if v == True:
        print(f' OFF for {timing_off} seconds.')
        print()
    if args.test_timing == False:
        devices.devices[0].set_power(False)
    #wait before we move on for <timeoff> seconds
    time.sleep(timing_off)

# function get_flash_it_timing
#    randomly generates timing for "flash_it" using 2 floating point numbers
#    tm - float - minimum time lights will be on/off
#    tmm - float - maximum time lights will be on/off
def get_flash_it_timing(tm,tmm):
    return round(random.uniform(tm,tmm),2)

# function get_loop_amount
#    randomly sets number of times "flash_it" will loop
#    loop_min - int - minimum times "flash_it" will loop
#    loop_max - int - maximum times "flash_it" will loop
def get_loop_amount(lm,lmm):
    return random.randint(lm,lmm)

# function get_loop_timing
#    randomly sets time between loops
#    between_loops_min - int - minimum time between loops
#    between_loops_max - int - maximum time between loops
def get_loop_timing(blm,blmm):
    return random.randint(blm,blmm)

# function cache_it
#    caches variables to file so we can save some time
#    file_name - path to file where data will be cached
param = ""
def cache_it(file_name):
    d = shelve.open(file_name)
    def decorator(func):
        def new_func(param):
            if param not in d:
                d[param] = func(param)
            return d[param]
        return new_func
    return decorator

# function get_devices(param) -- this is cached by using @chache_it(file_name)
#    This function will usually take 5-20 seconds depending on internet latency and server response times.
#    param - cached data...
#    Let's set our filename to use for caching our data...
file_name = "flashy_lights_devices.cache"
@cache_it(file_name)
def get_devices(param):
    #I want to find a way to hash the password...  More to come...
    return(laurel.laurel("MyEmail","MyPassword",True))


# Let's get to it already!!!

#First, we need to connect...
if args.test_timing == False:
    devices = get_devices(param)
    devices.devices[0].network.connect()
#Should we do a test flash?
if args.flash == True:
    timing_on = get_flash_it_timing(args.timing_min,args.timing_max)
    timing_off = get_flash_it_timing(args.timing_min,args.timing_max)
    flash_it(timing_on,timing_off,args.verbose)

if args.loop == True:
    num_loops = get_loop_amount(args.loop_min,args.loop_max)
    if args.verbose == True:
        print(f'looping {num_loops} times')
    i = 0
    while i < num_loops:
        timing_on = get_flash_it_timing(args.timing_min,args.timing_max)
        timing_off = get_flash_it_timing(args.timing_min,args.timing_max)
        flash_it(timing_on,timing_off,args.verbose)
        i+=1

if args.loop == True or args.flash == True:
    args.run_time = 0

if args.run_time > 0:
    if args.verbose == True:
        if args.resting_state == False:
            state = "off"
            if args.test_timing == False:
                devices.devices[0].set_power(False)
        else:
            state = "on"
            if args.test_timing == False:
                devices.devices[0].set_power(False)
        print(f'Lights will be {state} between flash_it loops.')
    t_end = time.time() + 60*args.run_time
    while time.time() < t_end:
        num_loops = get_loop_amount(args.loop_min,args.loop_max)
        if args.verbose == True:
            print(f'looping {num_loops} times')
        i = 0
        while i < num_loops:
            timing_on = get_flash_it_timing(args.timing_min,args.timing_max)
            timing_off = get_flash_it_timing(args.timing_min,args.timing_max)
            flash_it(timing_on,timing_off,args.verbose)
            i+=1
        pause_time = get_loop_timing(args.between_loops_min,args.between_loops_max)
        if args.verbose == True:
            print(f'Waiting for {pause_time} seconds.')
        time.sleep(pause_time)

If anyone has any suggestions to improve this, please let me know...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.