Giter VIP home page Giter VIP logo

zonyl / pytomation Goto Github PK

View Code? Open in Web Editor NEW
88.0 23.0 50.0 17.24 MB

Pytomation is an extensible device communication and automation system written in Python. It's uses include home automation and lighting control but is certainly not limited to that. It is supported on any platform that support Python ( Windows, Mac OS-X, Linux, etc )

Home Page: www.pytomation.com

License: Other

CSS 7.08% Java 23.66% JavaScript 14.40% Shell 0.47% Python 52.58% Arduino 0.94% HTML 0.87%

pytomation's Introduction

Pytomation


####This repository is out of date all development now takes place at: ####https://github.com/king-dopey/pytomation

. . . . . .

Pytomation is an extensible device communication and automation system written in Python. It's uses include home automation and lighting control but is certainly not limited to that. It is supported on any platform that support Python ( Windows, Mac OS-X, Linux, etc )

Supported

Pytomation currently has support for the following hardware interfaces with more planned in the future.

Future

  • Weeder Analog I/O board (Wtaio/RS232)
  • Ube Wifi Devices
  • CoralStar WiFi Devices

FEATURES

  • Written in Python
  • REST API
  • Mobile Web and Android clients w/ continuous device state updates (web-sockets)
  • Voice Commands from Android (“Home Control” app)
  • Local Telnet and Web access
  • Unique language to describe devices and actions
  • Smart objects: Doors, Lights, Motion, Photocell etc.
  • Optional “Mainloop” programming, for more complicated control
  • Optional “Event driven” programming, for complex actions when a device state changes
  • Time of day on and off control
  • Delays for time off
  • Idle command, device will return to "idle" state
  • Map one command to another with optional source and time
  • Good hardware support with more coming
  • Very easy to add new hardware drivers
  • Good documentation complete with examples
  • Much more

###INSTALLATION

DEPENDENCIES

Before you can create an instance and run Pytomation automation software you must satisfy a few dependencies. Pytomation is written in Python and currently has been tested under versions 2.6.x and 2.7.x.

Pytomation also requires the following packages to be installed for normal operation:

  • pySerial - Support for RS232 serial interfaces.
  • Pyephem - High-precision astronomy computations for sunrise/sunset.
  • Pytz - World timezone definitions.
  • APScheduler - Advanced Python Scheduler

Optional Packages:

  • python-gevent - A coroutine-based Python networking library (PytoWebSocketServer)
  • python-openssl - Allows the PytoWebSocketServer to use native SSL (https and wss connections)

Additional packages are required for development and testing. See requirements.txt for a more complete list.

Debian packages are available for pySerial, pytz, pythone-gevent, and python-openssl. They can be installed with :

sudo apt-get install git python-dev python-serial python-tz python-gevent python-openssl

For other operating systems, search your package manager for the equivalent packages or use pip to install the Python dependencies.

The remaining dependencies can be installed with pip. Pip is a tool for installing and managing Python packages, such as those found in the Python Package Index.

Again, under Debian distributions you can install the python-pip package:

sudo apt-get install python-pip

Once pip is installed it is easy to install the rest of the dependencies with the following commands:

sudo pip install pyephem 
sudo pip install APScheduler

To use the optional websocket server:

sudo pip install gevent-websocket

The gevent-websocket server is pretty fast, but can be accelerated further by installing wsaccel and ujson or simplejson

sudo pip install wsaccel ujson

Build openzwave and python-openzwave

Aeon Labs Z-Wave requires python-openzwave, which must be compiled from source. The instructions below list how to build from the development repositories. There is also prepared source avaiable at http://bibi21000.no-ip.biz/python-openzwave/python-openzwave-0.2.6.tgz, but that didn't work for me.

The following was extracted and adapted from the python-openzwave INSTALL_MAN.txt:

sudo apt-get install mercurial subversion python-pip python-dev python-setuptools python-louie python-sphinx make build-essential libudev-dev g++
sudo pip install cython==0.14
sudo pip install sphinxcontrib-blockdiag sphinxcontrib-actdiag
sudo pip install sphinxcontrib-nwdiag sphinxcontrib-seqdiag

hg clone https://code.google.com/p/python-openzwave/
cd python-openzwave
svn checkout http://open-zwave.googlecode.com/svn/trunk/ openzwave

Method 1 (Install Everything via Scripts)

./compile.sh
sudo ./install.sh

Method 2 (Install Manually)

If you installed everthing, stop here. Otherwise, go to the openzwave directory and build it:

cd openzwave/cpp/build
make
cd ../../..

Build python-openzwave:

python setup-lib.py build
python setup-api.py build

And install them:

sudo python setup-lib.py install
sudo python setup-api.py install

Permissions

Like with all other interfaces. Make sure the pyto user account owns or otherwise has permissions to use the device. You may want to give your own usr account access as well.

sudo chown youruseraccount:pyto /dev/yourzwavestick
sudo chmod 770 /dev/yourzwavestick

or

sudo chown pyto:pyto /dev/yourzwavestick
sudo chmod 770 /dev/yourzwavestick

Make Permissions Permanent

Add the following either /etc/udev/rules.d or /lib/udev/rules.d (Simmilar procedure can be used for other serial interfaces. lsusb -v can grab the neccessary ATTRS info.)

SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="0001", SYMLINK+="zwave", GROUP="pyto", OWNER="pyto"

ozwsh (OpenZWave Shell, for testing)

sudo pip install urwid louie
/ozwsh.sh --device=/dev/yourzwavestick

INSTALL

You are now ready to install pytomation. First, clone the pytomation git repository. Change into the pytomation repo directory and run ./install.sh. You may have to make it executable with the command chmod +x ./install.sh first. Install.sh can take an optional argument which points to an alternate installation directory:

 ./install.sh /some/other/folder/pytomation

The install.sh command does the following:

  • Confirms where you are installing Pytomation to.
  • Makes a "pyto" user and creates the home directory.
  • Copies all the necessary files into Pytomations HOME.
  • Creates an /etc/init.d/pytomation init script for starting Pytomation on boot.
  • Configures pytomation to start automatically at boot time

You are now ready to configure pytomation and create an instance for your devices.

pytomation's People

Contributors

akameswaran avatar akashgupta02 avatar gac410 avatar horga83 avatar jcollie avatar king-dopey avatar mattlevesque avatar mlb5000 avatar prjctgeek avatar rogersmj avatar texnofobix avatar tomstokes avatar zonyl 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pytomation's Issues

Linuxisms

I'm trying to get Pytomation functioning on my Mac for use with a University project, but am having a lot of issues trying to get the installation scripts to play nice.

I've noticed that at least install.sh uses Linuxisms (useradd). This prevents non-Linux users from installing by using the script. I attempted to get around this by manually creating the user and commenting out that portion of the script.

I now get two errors at the very end as a result.

Setting install location in pytomation.sh
Copying init script to /etc/init.d...
Making sure scripts are excutable...
chmod: /etc/init.d/pyto: Not a directory
Setting Pytomation to start from run level 2
ln: /etc/rc2.d/S99pyto: No such file or directory
Finished...

This is generated by a Mac running Mountain Lion.

lightStatusRequest

I don't know how to 'submit' this because git still confuses me, but I might have fixed the light request thing. Problem is, of course, that the command1 response can be anything and different for all devices. It can even look the same as an existing command. The only way to deal with it is to remember you asked for a light status, and the next response will be the light status report.

This works for me, and I don't think you can get ACKs out of order, but it might make better sense to store the device's address somewhere and the next response from that device will be the light / contact_sensor report.

commit 8be08749cb5e43f86426559f0f7c5378e8b0d41a
Author: Grant
Date:   Sat Jan 5 14:28:33 2013 -0500

    Added beep command. LightStatus works no matter what CMD1 is, even if it happens to coincide with different valid command.

diff --git a/pytomation/interfaces/insteon.py b/pytomation/interfaces/insteon.py
index 986c6de..c30c1ce 100644
--- a/pytomation/interfaces/insteon.py
+++ b/pytomation/interfaces/insteon.py
@@ -83,7 +83,10 @@ def simpleMap(value, in_min, in_max, out_min, out_max):

 class InsteonPLM(HAInterface):
     VERSION = '1.6'
-    
+    LIGHTLEVEL = 1
+    SENSOR = 2
+    statusRequest = 0
+
     #(address:engineVersion) engineVersion 0x00=i1, 0x01=i2, 0x02=i2cs
     deviceList = {};
     currentCommand = ""
@@ -160,6 +163,10 @@ class InsteonPLM(HAInterface):
                                     'SD03': {        #Product Data Request (generally an Ack)
                                         'callBack' : self._handle_StandardDirect_IgnoreAck
                                     },
+                                    'SDXX': {        #Get Light Level
+                                        'callBack' : self._handle_StandardDirect_LightStatusResponse,
+                                        'validResponseCommands' : ['SDXX']
+                                    },
                                     'SD0D': {        #Get InsteonPLM Engine
                                         'callBack' : self._handle_StandardDirect_EngineResponse,
                                         'validResponseCommands' : ['SD0D']
@@ -198,7 +205,11 @@ class InsteonPLM(HAInterface):
                                     },
                                     'SD19': {        #Light Status Response
                                         'callBack' : self._handle_StandardDirect_LightStatusResponse,
-                                        'validResponseCommands' : ['SD19']
+                                        'validResponseCommands' : ['SDXX']
+                                    },
+                                    'SD30': {        #Beep Response
+                                        'callBack' : self._handle_StandardDirect_AckCompletesCommand,
+                                        'validResponseCommands' : ['SD30']
                                     },
                                     #Broadcast Messages/Responses
                                     'SB01': {
@@ -326,6 +337,11 @@ class InsteonPLM(HAInterface):
             time.sleep(self.spinTime)

     def _sendStandardP2PInsteonCommand(self, destinationDevice, commandId1, commandId2):
+        if commandId1 == '19': #Light level request. Next inbound will be the response.
+            if commandId2 == '01' : 
+                self.statusRequest = self.SENSOR
+            else:
+                self.statusRequest = self.LIGHTLEVEL
         self._logger.debug("Command: %s %s %s" % (destinationDevice, commandId1, commandId2))
         return self._sendInterfaceCommand('62', _stringIdToByteIds(destinationDevice) + _buildFlags() + binascii.unhexlify(commandId1) + binascii.unhexlify(commandId2), extraCommandDetails = { 'destinationDevice': destinationDevice, 'commandId1': 'SD' + commandId1, 'commandId2': commandId2})

@@ -422,8 +438,9 @@ class InsteonPLM(HAInterface):
         else:
             #standard direct
             insteonCommandCode = 'SD' + insteonCommandCode
-
-        if insteonCommandCode == 'SD00':
+        if self.statusRequest > 0:
+            insteonCommandCode = 'SDXX'
+        #if insteonCommandCode == 'SD00':
             #this is a strange special case...
             #lightStatusRequest returns a standard message and overwrites the cmd1 and cmd2 bytes with "data"
             #cmd1 (that we use here to sort out what kind of incoming message we got) contains an 
@@ -432,7 +449,7 @@ class InsteonPLM(HAInterface):
             #for now my testing has show that its 0 (at least with my dimmer switch - my guess is cause I haven't linked it with anything)
             #so we treat the SD00 message special and pretend its really a SD19 message (and that works fine for now cause we only really
             #care about cmd2 - as it has our light status in it)
-            insteonCommandCode = 'SD19'
+        #    insteonCommandCode = 'SD19'

         #print insteonCommandCode

@@ -560,19 +577,22 @@ class InsteonPLM(HAInterface):
         # find the device and light level
         destDeviceId = _byteIdToStringId(fromIdHigh, fromIdMid, fromIdLow).upper()
         # For now lets just handle on and off until the new state code is ready.
-        for d in self._devices:
-            if d.address == destDeviceId:
-                if command1 == 0x12:
-                    d.state = State.ON
-                elif command1 == 0x14:
-                    d.state = State.OFF
-                
-        # Old stuff, don't use this at the moment
-        #lightLevelRaw = messageBytes[10]
-        #map the lightLevelRaw value to a sane value between 0 and 1
-        #normalizedLightLevel = simpleMap(ord(lightLevelRaw), 0, 255, 0, 1)
-
-        #return (True, {'lightStatus': round(normalizedLightLevel, 2) })
+        #for d in self._devices:
+        #    if d.address == destDeviceId:
+        #        if command1 == 0x12:
+        #            d.state = State.ON
+        #        elif command1 == 0x14:
+        #            d.state = State.OFF
+        lightLevelRaw = messageBytes[10] #sorry
+        normalizedLightLevel = simpleMap(ord(lightLevelRaw), 0, 255, 0, 1)
+        if self.statusRequest == self.SENSOR:
+            self.statusRequest = 0
+            return (True, {'sensorStatus': ord(lightLevelRaw) })
+        elif self.statusRequest == self.LIGHTLEVEL:
+            self.statusRequest = 0
+            return (True, {'lightStatus': round(normalizedLightLevel, 2) })
+        else:
+            return False

     #public methods
     def getPLMInfo(self, timeout = None):
@@ -609,6 +629,14 @@ class InsteonPLM(HAInterface):
         commandExecutionDetails = self._sendStandardP2PInsteonCommand(deviceId, '19', '00')
         return self._waitForCommandToFinish(commandExecutionDetails, timeout = timeout)

+    def sensorStatusRequest(self, deviceId, timeout = None): #I/O Linc Sensor.
+        commandExecutionDetails = self._sendStandardP2PInsteonCommand(deviceId, '19', '01')
+        return self._waitForCommandToFinish(commandExecutionDetails, timeout = timeout)
+
+    def beep(self, deviceId, timeout = None):
+        commandExecutionDetails = self._sendStandardP2PInsteonCommand(deviceId, '30', '00')
+        return self._waitForCommandToFinish(commandExecutionDetails, timeout = timeout)
+
     def command(self, device, command, timeout=None):
         command = command.lower()
         if isinstance(device, InsteonDevice):

Installation problem

Good afternoon,

When installing pytomation it appears, the project is no more in line with the apscheduler library. Indeed, the class Scheduler doesn't exist anymore.

I suppose the BackgroundScheduler as to be used instead ?

Thanks to share this nice piece of code.

Jean-Pascal

Insteon USB?

I see there is support for the serial device. Is this not the case for the USB version?

ImportError: cannot import name config

import pytomation.interfaces
Traceback (most recent call last):
File "", line 1, in
File "pytomation/interfaces/init.py", line 1, in
from .common import *
File "pytomation/interfaces/common.py", line 42, in
from pytomation.common.pytomation_object import PytomationObject
File "pytomation/common/init.py", line 1, in
from .pytomation_system import *
File "pytomation/common/pytomation_system.py", line 3, in
from .pytomation_object import PytomationObject
File "pytomation/common/pytomation_object.py", line 1, in
from .pyto_logging import PytoLogging
File "pytomation/common/pyto_logging.py", line 4, in
from ..common import config
ImportError: cannot import name config

I tried but still can not solve it. Please give me some help.

Questions from a potential contributor

Hello!

I've been looking for a HA solution for Linux for a while now, and this looks like exactly what I've been waiting for. A few questions:

  1. How are things with Insteon? I notice retransmit problems and v2 mentioned on the todo, but I'm unfamiliar with the Insteon protocol.
  2. Because Insteon is what I had planned on using, how difficult is it to figure out how their stuff works? I'm experienced in Python, but not reverse-engineering protocols. :P
  3. More of a personal question, I guess. Would Insteon be the best choice for HA? I'm up for anything, especially if it means easier contribution.

Thanks for your time!

  • Tyler

Mochad not sending commands

I'm new to this and own a cm19a but I tried the following in my own instance:

tcp = TCP('127.0.0.1', 1099)
mochad = Mochad(tcp)

In pylog.txt:

[2014/01/22 22:23:50] [ERROR] [Mochad] Problem with interface: read() takes exactly 2 arguments (1 given)

I'm confused what argument I may be missing or is it something in the mochad code. I based my code off the unit test code.

Restrict should not restrict HTTP commands

When a device ends up in an undesired state, there appears to be no way to correct the issue without resorting to configuration changes. For ex, if a light is restricted because of location or a photocell, there is no way to let there be light.

It would be nice if the command restrictions could have exceptions.

Applied a local change:

--- a/pytomation/devices/light.py
+++ b/pytomation/devices/light.py
@@ -48,7 +48,7 @@ class Light(InterfaceDevice):
             primary_command = m_command[0]
         try:
             if source and (primary_command in [Command.ON, Command.LEVEL]):
-                if self.restricted and source not in self._interfaces:
+                if self.restricted and source not in self._interfaces and source.name != "HTTPServer2":
                     m_command = None
                     m_state = None 

threads can only be started once

Following basic example:

from pytomation.config import *
from pytomation.interfaces import InsteonPLM, TCP

import time
tcp = TCP('192.168.1.200', 9761)
insteon = InsteonPLM(tcp)
debug['Insteon'] = 4
insteon.start()

response = insteon.on('1b.7a.50')
time.sleep(1)
response = insteon.off('1b.7a.50')
insteon.shutdown()
tcp.shutdown()

Throws the following error


connect 192.168.1.200:9761
Traceback (most recent call last):
File "/home/borand/projects/pytomation/issue1.py", line 8, in
insteon.start()
File "/usr/lib/python2.7/threading.py", line 488, in start
raise RuntimeError("threads can only be started once")

RuntimeError: threads can only be started once

Commenting the offending line

insteon.start()

Allows the program execution but I endup with timeout errors :

connect 192.168.1.200:9761
Timed out for eaab7d1da1799a7ca0d4a25b08096ccc - Requeueing (already had 0 retries)
Timed out for eaab7d1da1799a7ca0d4a25b08096ccc - Requeueing (already had 1 retries)
Timed out for eaab7d1da1799a7ca0d4a25b08096ccc - Requeueing (already had 2 retries)
Timed out for eaab7d1da1799a7ca0d4a25b08096ccc - Requeueing (already had 3 retries)
Timed out for eaab7d1da1799a7ca0d4a25b08096ccc - Requeueing (already had 4 retries)
Timed out for 3fa5eff4ae9efcded70940930caa2641 - Requeueing (already had 0 retries)
Timed out for 3fa5eff4ae9efcded70940930caa2641 - Requeueing (already had 1 retries)
Timed out for 3fa5eff4ae9efcded70940930caa2641 - Requeueing (already had 2 retries)
Timed out for 3fa5eff4ae9efcded70940930caa2641 - Requeueing (already had 3 retries)
Timed out for 3fa5eff4ae9efcded70940930caa2641 - Requeueing (already had 4 retries)


pylog.txt
[2012/11/30 23:09:57] Insteon Pytomation driver version 1.3
[2012/11/30 23:13:36] Insteon Pytomation driver version 1.3
[2012/11/30 23:13:36] [Insteon] Command: 1b.7a.50 11 ff
[2012/11/30 23:13:36] [Insteon] < 0000 02 62 1B 7A 50 0F 11 FF 06 .b.zP....
209aee16af889e269fb05afa1f7ed8dd

[2012/11/30 23:13:36] [Insteon] < 0000 02 50 1B 7A 50 1C 00 38 2B 11 FF .P.zP..8+..
c06993260da3b662c76d3e4fb8442535

[2012/11/30 23:13:36] [Insteon] Unhandled packet (couldn't find any pending command to deal with it)

[2012/11/30 23:13:36] [Insteon] This could be an unsolocicited broadcast message

BUT,
The devices do turn ON and OFF

This is a recent feature of the code.

start / end not working for ignore option

The ignore= setting appears to support start/end, and it gets written into the object, however by the time the _match operation runs, the value is not available.

In state.py method ignore, the start/end attributes are set. I added them to print in the debug message, and they are there:

2013/11/14 18:18:11] [DEBUG] [Light] Living room Light add ignore for off from Dining Room Motion start 4:30pm end 5.00pm

and in state.py, method: def _match_condition_item, a debug log message shows:

2013/11/14 18:26:36] [DEBUG] [Light] Living room Light match ignore start (0, 30, 16, AllMatch([]), AllMatch([]), AllMatch([])) end None

So the start time made it through, but the end time did not.

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.