Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

xmbc plugin

  • Loading branch information...
commit 5501cbb6d1d36f33af39dc62c9a2cd93fc1729d2 1 parent 0701d23
@awentz awentz authored
Showing with 3,839 additions and 0 deletions.
  1. +201 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Channel.py
  2. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Channel.pyc
  3. +151 −0 src/xmbc/plugin.video.interactivelighting/Lumos/ControllerUnit.py
  4. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/ControllerUnit.pyc
  5. +63 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/Controllers.py
  6. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/Controllers.pyc
  7. +151 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/FireGodControllerUnit.py
  8. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/FireGodControllerUnit.pyc
  9. +225 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/FirecrackerX10ControllerUnit.py
  10. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/FirecrackerX10ControllerUnit.pyc
  11. +146 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/LumosControllerUnit.py
  12. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/LumosControllerUnit.pyc
  13. +308 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/LynX10ControllerUnit.py
  14. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/LynX10ControllerUnit.pyc
  15. +143 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/Olsen595ControllerUnit.py
  16. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/Olsen595ControllerUnit.pyc
  17. +177 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/RenardControllerUnit.py
  18. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/RenardControllerUnit.pyc
  19. +60 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Device/X10ControllerUnit.py
  20. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/X10ControllerUnit.pyc
  21. 0  src/xmbc/plugin.video.interactivelighting/Lumos/Device/__init__.py
  22. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/__init__.pyc
  23. +63 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Event.py
  24. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Event.pyc
  25. +171 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Extras/VixenSequence.py
  26. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/VixenSequence.pyc
  27. 0  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/__init__.py
  28. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/__init__.pyc
  29. +82 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/Network.py
  30. +62 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/Networks.py
  31. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Network/Networks.pyc
  32. +105 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/ParallelBitNetwork.py
  33. +98 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/ParallelNetwork.py
  34. +94 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/SerialBitNetwork.py
  35. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Network/SerialBitNetwork.pyc
  36. +140 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/SerialNetwork.py
  37. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Network/SerialNetwork.pyc
  38. +68 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Network/__init__.py
  39. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Network/__init__.pyc
  40. +86 −0 src/xmbc/plugin.video.interactivelighting/Lumos/PowerSource.py
  41. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/PowerSource.pyc
  42. +368 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Sequence.py
  43. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Sequence.pyc
  44. +587 −0 src/xmbc/plugin.video.interactivelighting/Lumos/Show.py
  45. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Show.pyc
  46. +69 −0 src/xmbc/plugin.video.interactivelighting/Lumos/TimeRange.py
  47. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/TimeRange.pyc
  48. 0  src/xmbc/plugin.video.interactivelighting/Lumos/__init__.py
  49. BIN  src/xmbc/plugin.video.interactivelighting/Lumos/__init__.pyc
  50. +109 −0 src/xmbc/plugin.video.interactivelighting/addon.py
  51. +18 −0 src/xmbc/plugin.video.interactivelighting/addon.xml
  52. 0  src/xmbc/plugin.video.interactivelighting/resources/__init__.py
  53. +5 −0 src/xmbc/plugin.video.interactivelighting/resources/language/English/strings.xml
  54. 0  src/xmbc/plugin.video.interactivelighting/resources/lib/__init__.py
  55. +75 −0 src/xmbc/plugin.video.interactivelighting/resources/lib/utils.py
  56. +14 −0 src/xmbc/plugin.video.interactivelighting/resources/settings.xml
View
201 src/xmbc/plugin.video.interactivelighting/Lumos/Channel.py
@@ -0,0 +1,201 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS CHANNEL CLASS
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Channel.py,v 1.4 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+
+class DimmerError (Exception):
+ "Exception thrown for improper usage of dimmer settings."
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return str(self.value)
+
+class Channel (object):
+ """
+ Class for power output channels.
+
+ This just describes some behavior and attributes common to all kinds
+ of channels.
+ """
+
+ def __init__(self, id, name=None, load=None, dimmer=True, warm=None, resolution=100, power=None):
+ """
+ Constructor for Channel objects:
+
+ Channel(id, [name], load, [dimmer], [warm], [resolution], power)
+
+ id: unique (within a given controller) ID
+ for this channel.
+ name: descriptive name for channel (optional).
+ load: amperage of load assigned to this channel.
+ dimmer: (bool) is channel capable of dimming?
+ warm: minimum dimmer level to maintain for this
+ channel, as an integer percentage of full
+ brightness. If you set this to None, no
+ minimum is set, and turning the channel off
+ will turn it completely off. Alternatively,
+ if you set it to 0, then turning the channel
+ off will dim it to 0%, which is an important
+ distinction on some devices (e.g., X10).
+ [default=None]
+ resolution: number of discrete dimmer steps supported.
+ [default=100]
+ power: PowerSource object supplying power to this channel
+ """
+
+ if name is None:
+ self.name = 'Channel %s' % id
+ else:
+ self.name = name
+ if load is None:
+ raise ValueError("Channel %s load parameter is required" % id)
+ if power is None:
+ raise ValueError("Channel %s power parameter is required" % id)
+
+
+ self.load = float(load)
+ self.dimmer = bool(dimmer)
+ self.resolution = int(resolution)
+ self.level = None
+ self.power_source = power
+ if warm is not None:
+ self.warm = self.raw_dimmer_value(warm)
+ if not 0 <= self.warm < self.resolution:
+ self.warm = None
+ raise ValueError, "Channel %s warm percentage out of range (%d)" % (id, warm)
+
+ if not self.dimmer:
+ self.warm = None
+ raise DimmerError, "Channel %s specifies warm value but is not dimmable." % id
+ else:
+ self.warm = None
+
+ def raw_dimmer_value(self, n):
+ '''Translate brightness percentage to device-specific "raw"
+ dimmer level value.
+
+ Note that this is for determining *dimmer* values; if you
+ give it a value of None (which normally indicates the "off"
+ state), this function returns 0 (zero).'''
+
+ if n is None: return 0
+ return int(((self.resolution-1)/100.0) * n)
+
+ def pct_dimmer_value(self, n):
+ '''Translate device-specific "raw" dimmer value to percentage.
+
+ The None value (i.e., "off") is returned as 0% as well.'''
+
+ if n is None: return 0
+ return int((100.0/(self.resolution-1)) * n)
+
+ def normalized_value(self, n, override_warm=False):
+ '''Normalize percentage brightness level to percentage
+ which matches dimmer resolution steps. Takes into account
+ the "warm" attribute unless told to override that.
+
+ An input value of None is considered to be a dimmer level
+ of 0 for this function.'''
+
+ n = self.raw_dimmer_value(n)
+ if self.warm is not None and not override_warm and n < self.warm:
+ n = self.warm
+ return self.pct_dimmer_value(n)
+
+ def set_level(self, level, override_warm=False):
+ '''
+ Set channel to a raw (device-specific) dimmer level.
+
+ For convenience, you can set the level to None, which
+ will turn the device off completely. This is different
+ than dimming it to 0%. (More importantly different with
+ some controllers than others.)
+
+ Returns a tuple of raw (device-specific) level values for the
+ channel. The first is the original value before the change,
+ and the second is the new value being set. Either value could
+ be None to indicate a completely "off" condition.
+ '''
+ previous = self.level
+ #
+ # if we are not capable of dimming, we make any level
+ # less than 50% "off" and anything greater "on".
+ #
+ if not self.dimmer:
+ self.level = None if level < self.resolution / 2.0 else self.resolution-1
+ return (previous, self.level)
+ #
+ # otherwise, figure out proper dimmer handling.
+ #
+ if level is None:
+ if override_warm or self.warm is None:
+ self.level = None
+ return (previous, None)
+ else:
+ level = 0
+
+ level = int(level)
+ if not override_warm and self.warm is not None:
+ if level < self.warm:
+ level = self.warm
+ elif level < 0:
+ level = 0
+
+ if level >= self.resolution:
+ level = self.resolution-1
+
+ self.level = level
+ return (previous, self.level)
+
+ def set_on(self):
+ '''Turn channel fully on (full brightness).
+ Returns same values as setLevel()'''
+ return self.set_level(self.resolution-1)
+
+ def set_off(self):
+ 'Turn channel fully off. Returns same values as setLevel()'
+ return self.set_level(self.warm)
+
+ def kill(self):
+ '''Turn channel COMPLETELY off, disregarding "warm" setting.
+ Returns same values as setLevel()'''
+ return self.set_level(None, override_warm=True)
+
+ def current_drain(self):
+ "Report current load in amps based on output level right now -> (amps, PowerSource)"
+ if self.level is None:
+ return (0, self.power_source)
+
+ return ((float(self.level) / (self.resolution-1)) * self.load, self.power_source)
+
+ def current_load(self):
+ "Report the load assigned to this channel as a tuple (amps, PowerSource)"
+ return (self.load, self.power_source)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.3 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Channel.pyc
Binary file not shown
View
151 src/xmbc/plugin.video.interactivelighting/Lumos/ControllerUnit.py
@@ -0,0 +1,151 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS CONTROLLER UNIT BASE CLASS
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/ControllerUnit.py,v 1.6 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.Channel import Channel
+
+class ControllerUnit (object):
+ """
+ Generic controller unit virtual class. Don't create these directly;
+ derive a subclass describing some real type of hardware device.
+ This class describes attributes and behaviors common to all controller
+ unit types.
+ """
+ def __init__(self, id, power, network, resolution=100):
+ """
+ Constructor for basic controller units.
+ id: the ID tag this unit instance is known by.
+ power: a PowerSource instance describing the power
+ feed for this unit This will be the default
+ source for all channels unless a channel
+ explicitly overrides this.
+ network: a Network instance describing the communications
+ network and protocol connected to this unit.
+ resolution: default dimmer resolution for channels of this
+ device. [def=100]
+ """
+ self.id = id
+ self.power_source = power
+ self.channels = {}
+ self.resolution = resolution
+ self.network = network
+
+ def channel_id_from_string(self, channel):
+ '''Given a string with a channel name, return the proper channel
+ ID this device wants to see. This may be, for example, an
+ integer value instead of a character string with digits in it.'''
+
+ raise NotImplementedError("Subclasses of ControllerUnit must define a channel_id_from_string() method.")
+
+ def add_channel(self, id, name=None, load=None, dimmer=True, warm=None, resolution=None, power=None):
+ """
+ Add an active channel for this controller.
+ add_channel(id, [name], [load], [dimmer], [warm], [resolution], [power])
+
+ This constructs a channel object of the appropriate type and
+ adds it to this controller unit. This may be of the the base
+ Channel class, or it may be something derived from that if the
+ controller has special channel features. The parameters are
+ the same as for the Channel object constructor.
+
+ The resolution parameter will default to the value specified to
+ the ControllerUnit constructor.
+ """
+
+ if resolution is None: resolution = self.resolution
+ if power is None: power = self.power_source
+ self.channels[id] = Channel(id, name, load, dimmer, warm, resolution, power)
+
+ def set_channel(self, id, level, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own set_channel method.")
+
+ def set_channel_on(self, id, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own set_channel_on method.")
+
+ def set_channel_off(self, id, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own set_channel_off method.")
+
+ def kill_channel(self, id, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own kill_channel method.")
+
+ def kill_all_channels(self, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own kill_all_channels method.")
+
+ def all_channels_off(self, force=False):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own all_channels_off method.")
+
+ def initialize_device(self):
+ raise NotImplementedError("Subclasses of ControllerUnit must define their own initialize_device method.")
+
+ def flush(self, force=False):
+ """
+ Flush any pending device changes to the hardware. The default action
+ is to do nothing, which is appropriate for devices which will transmit
+ their commands immediately. Other devices may need to track commands
+ internally and then send a controller-wide all-channel update sequence
+ when flush() is called.
+ """
+ pass
+
+ def iter_channels(self):
+ "Iterate over the list of channel IDs, not necessarily in any order."
+ return iter(self.channels)
+
+ def _iter_non_null_channel_list(self):
+ '''For units where the channel list is a linear list of objects,
+ this method implements an generator function which will return the
+ channel IDs which are actually initialized. So, for example, if
+ a unit had 48 physical channels but only 13 were set up in a show
+ profile, only those 13 would be returned.'''
+
+ for i in range(len(self.channels)):
+ if self.channels[i] is not None:
+ yield i
+
+ def current_drain(self):
+ '''Report on the current load drawn by this controller at this point in
+ time, based on chanel output levels. (This assumes any pending flush
+ has happened and channels are already doing what they were last commanded
+ to do.) The return value is a dictionary mapping channel ID to a tuple
+ of (amps, PowerSource) for each channel in this controller.'''
+ return dict([
+ (k,self.channels[k].current_drain())
+ for k in self.iter_channels()
+ ])
+
+ def current_loads(self):
+ '''Report on the loads assigned to this controller during the show.
+ The return value is a dictionary mapping channel ID to a tuple of
+ (amps, PowerSource) for each channel in this controller.'''
+ return dict([
+ (k,self.channels[k].current_load())
+ for k in self.iter_channels()
+ ])
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.5 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/ControllerUnit.pyc
Binary file not shown
View
63 src/xmbc/plugin.video.interactivelighting/Lumos/Device/Controllers.py
@@ -0,0 +1,63 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS CONTROLLER LIST
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/Controllers.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+#
+from Lumos.Device.LynX10ControllerUnit import LynX10ControllerUnit
+from Lumos.Device.LumosControllerUnit import LumosControllerUnit
+from Lumos.Device.FirecrackerX10ControllerUnit import FirecrackerX10ControllerUnit
+from Lumos.Device.RenardControllerUnit import RenardControllerUnit
+from Lumos.Device.Olsen595ControllerUnit import Olsen595ControllerUnit
+from Lumos.Device.FireGodControllerUnit import FireGodControllerUnit
+#
+# List of supported controller device drivers, mapping the name as used
+# in the show.conf file (and other interfaces) to the actual class
+# implementing the driver for that device.
+#
+supported_controller_types = {
+ 'lynx10': LynX10ControllerUnit,
+ '48ssr': LumosControllerUnit,
+ 'lumos': LumosControllerUnit,
+ 'cm17a': FirecrackerX10ControllerUnit,
+ 'renard': RenardControllerUnit,
+ 'olsen595': Olsen595ControllerUnit,
+ 'firegod': FireGodControllerUnit,
+}
+
+def controller_unit_factory(type, **kwargs):
+ """
+ Create and return a controller object of the requested type.
+ Usage: controller_unit_factory(typename, [args...])
+ where: typename is one of the defined keywords for a device
+ type (as usable in the show configuration file), and
+ [args] are whatever constructor arguments are applicable
+ to that type of device class.
+ """
+
+ type = type.lower().strip()
+ if type not in supported_controller_types:
+ raise ValueError, "Invalid controller unit type '%s'" % type
+
+ return supported_controller_types[type](**kwargs)
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/Controllers.pyc
Binary file not shown
View
151 src/xmbc/plugin.video.interactivelighting/Lumos/Device/FireGodControllerUnit.py
@@ -0,0 +1,151 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: FIREGOD DIY SSR CONTROLLER
+# ***UNTESTED*** SPECULATIVE CODE. NOT READY FOR USE!
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/FireGodControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.ControllerUnit import ControllerUnit
+
+#
+# The FireGod is a classic DIY SSR controller. This driver is
+# an experimental design based on the protocol description posted
+# on doityourselfchristmas.com. The author does not have the hardware
+# available to verify that this works. Hopefully someone with a FireGod
+# can verify this and give feedback about it to the author.
+#
+# The serial protocol used by this device updates all channels at one
+# time, in packets which look like this:
+# <0x55><address><level0><level1>...<level31>
+#
+# The <address> byte may be 0x01-0x04, to address the 32-channel module
+# being updated.
+#
+# <level> bytes are in the range <0x64> (fully off) to <0xc8> (fully on).
+# there are 101 levels, so you get 0%-100% inclusive.
+#
+
+class FireGodControllerUnit (ControllerUnit):
+ """
+ ControllerUnit subclass for the Renard DIY SSR unit.
+
+ ***THE STATUS OF THIS DRIVER IS EXPERIMENTAL***
+ THIS HAS NOT YET BEEN VERIFIED TO WORK WITH ACTUAL HARDWARE
+
+ If you have a FireGod or compatible controller, we would
+ appreciate any feedback you'd like to offer about this driver,
+ if you're willing to try it and help us test/debug this code.
+ """
+ def __init__(self, id, power, network, address, resolution=101, channels=32):
+ """
+ Constructor for a FireGod dimmable 128-channel SSR board object:
+ FireGodControllerUnit(power, id, network, address, [resolution], [channels])
+
+ Specify the correct PowerSource object for this unit and
+ the module address (1-4). The number of channels defaults to 32, but this
+ can be changed if you have a controller which implements a different number
+ of channels per module. This will change the number of levels transmitted
+ in the command packets.
+
+ The resolution probably won't ever need to be changed. The FireGod units
+ use 101 dimmer levels (0%-100%), so that's the default for that parameter.
+ """
+
+ ControllerUnit.__init__(self, id, power, network, resolution)
+ self.address = int(address)
+ self.type = 'FireGod SSR Controller (%d channels)' % channels
+ self.channels = [None] * channels
+ self.update_pending = False
+ self.iter_channels = self._iter_non_null_channel_list
+
+ if not 1 <= self.address <= 4:
+ raise ValueError("Address %d out of range for a FireGod SSR Controller Module" % self.address)
+
+ def __str__(self):
+ return "%s, module #%d (%d channels)" % (self.type, self.address, len(self.channels))
+
+ def channel_id_from_string(self, channel):
+ return int(channel)
+
+ #def iter_channels(self):
+ #return range(len(self.channels))
+
+ def add_channel(self, id, name=None, load=None, dimmer=True, warm=None, resolution=None, power=None):
+ try:
+ id = int(id)
+ assert 0 <= id < len(self.channels)
+ except:
+ raise ValueError("%d-channel FireGod channel IDs must be integers from 0-%d"
+ % (len(self.channels), len(self.channels)-1))
+
+ if resolution is not None:
+ resolution = int(resolution)
+ else:
+ resolution=self.resolution
+
+ ControllerUnit.add_channel(self, id, name, load, dimmer, warm, resolution, power)
+
+ def set_channel(self, id, level, force=False):
+ self.channels[id].set_level(level)
+ self.update_pending = True
+
+ def set_channel_on(self, id, force=False):
+ self.channels[id].set_on()
+ self.update_pending = True
+
+ def set_channel_off(self, id, force=False):
+ self.channels[id].set_off()
+ self.update_pending = True
+
+ def kill_channel(self, id, force=False):
+ self.channels[id].kill()
+ self.update_pending = True
+
+ def kill_all_channels(self, force=False):
+ for ch in self.channels:
+ if ch is not None:
+ ch.kill()
+ self.update_pending = True
+
+ def all_channels_off(self, force=False):
+ for ch in self.channels:
+ if ch is not None:
+ ch.set_off()
+ self.update_pending = True
+
+ def initialize_device(self):
+ self.all_channels_off(True)
+ self.flush(True)
+
+ def flush(self, force=False):
+ if self.update_pending or force:
+ self.network.send('U%c' % self.address)
+ for channel in self.channels:
+ if channel is None:
+ self.network.send(chr(0x64))
+ elif channel.level is None:
+ self.network.send(chr(0x64))
+ else:
+ self.network.send(chr(0x64 + channel.level))
+ self.update_pending = False
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/FireGodControllerUnit.pyc
Binary file not shown
View
225 src/xmbc/plugin.video.interactivelighting/Lumos/Device/FirecrackerX10ControllerUnit.py
@@ -0,0 +1,225 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: X-10 CM17A "FIRECRACKER"
+# ***UNTESTED*** SPECULATIVE CODE. NOT READY FOR USE!
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/FirecrackerX10ControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.Device.X10ControllerUnit import X10ControllerUnit
+
+# **MUST** be plugged into a FirecrackerSerialNetwork device.
+
+#
+# Protocol:
+#
+# transmit MSB first in 40-bit packets per command:
+# D5 AA xx xx AD
+# 11010101 10101010 xxxx0x00 xxxxx000 10101101
+# \____header_____/ \____command____/ \footer/
+#
+# commands:
+# House Code A 0110xxxx xxxxxxxx 6000
+# House Code B 0111xxxx xxxxxxxx 7000
+# House Code C 0100xxxx xxxxxxxx 4000
+# House Code D 0101xxxx xxxxxxxx 5000
+# House Code E 1000xxxx xxxxxxxx 8000
+# House Code F 1001xxxx xxxxxxxx 9000
+# House Code G 1010xxxx xxxxxxxx A000
+# House Code H 1011xxxx xxxxxxxx B000
+# House Code I 1110xxxx xxxxxxxx E000
+# House Code J 1111xxxx xxxxxxxx F000
+# House Code K 1100xxxx xxxxxxxx C000
+# House Code L 1101xxxx xxxxxxxx D000
+# House Code M 0000xxxx xxxxxxxx 0000
+# House Code N 0001xxxx xxxxxxxx 1000
+# House Code O 0010xxxx xxxxxxxx 2000
+# House Code P 0011xxxx xxxxxxxx 3000
+# Unit Code 1 xxxxx0xx x0x00xxx 0000
+# Unit Code 2 xxxxx0xx x0x10xxx 0010
+# Unit Code 3 xxxxx0xx x0x01xxx 0008
+# Unit Code 4 xxxxx0xx x0x11xxx 0018
+# Unit Code 5 xxxxx0xx x1x00xxx 0040
+# Unit Code 6 xxxxx0xx x1x10xxx 0050
+# Unit Code 7 xxxxx0xx x1x01xxx 0048
+# Unit Code 8 xxxxx0xx x1x11xxx 0058
+# Unit Code 9 xxxxx1xx x0x00xxx 0400
+# Unit Code 10 xxxxx1xx x0x10xxx 0410
+# Unit Code 11 xxxxx1xx x0x01xxx 0408
+# Unit Code 12 xxxxx1xx x0x11xxx 0418
+# Unit Code 13 xxxxx1xx x1x00xxx 0440
+# Unit Code 14 xxxxx1xx x1x10xxx 0450
+# Unit Code 15 xxxxx1xx x1x01xxx 0448
+# Unit Code 16 xxxxx1xx x1x11xxx 0458
+# BRIGHT hhhhxxxx 1x001xxx 0088 +5%; NO UNIT CODE
+# DIM hhhhxxxx 1x011xxx 0098 -5%; NO UNIT CODE
+# ON hhhhxuxx 0u0uuxxx 0000
+# OFF hhhhxuxx 0u1uuxxx 0020
+#
+# These are transmitted serially on the DTR and RTS lines.
+# One of the lines MUST be asserted at all times to power the
+# Firecracker unit. The encodings are:
+#
+# RTS DTR
+# 0 0 Reset (power off)
+# 1 0 Transmit 1
+# 0 1 Transmit 0
+# 1 1 Idle
+#
+# Wait at least .5mS between each transition.
+#
+
+bits = {
+# HOUSE CODES UNIT CODES H/U COMMANDS HOUSE COMMANDS
+ 'A': 0x6000, '1': 0x0000, 'on': 0x0000, 'up': 0x0088,
+ 'B': 0x7000, '2': 0x0010, 'off': 0x0020, 'dn': 0x0098,
+ 'C': 0x4000, '3': 0x0008,
+ 'D': 0x5000, '4': 0x0018,
+ 'E': 0x8000, '5': 0x0040,
+ 'F': 0x9000, '6': 0x0050,
+ 'G': 0xa000, '7': 0x0048,
+ 'H': 0xb000, '8': 0x0058,
+ 'I': 0xe000, '9': 0x0400,
+ 'J': 0xf000, '10': 0x0410,
+ 'K': 0xc000, '11': 0x0408,
+ 'L': 0xd000, '12': 0x0418,
+ 'M': 0x0000, '13': 0x0440,
+ 'N': 0x1000, '14': 0x0450,
+ 'O': 0x2000, '15': 0x0448,
+ 'P': 0x3000, '16': 0x0458,
+}
+
+def packet(id, op):
+ if op in ('up','dn'):
+ b = bits[id[0]] | bits[op]
+ else:
+ b = bits[id[0]] | bits[id[1:]] | bits[op]
+
+ return chr(0xd5)+chr(0xaa)+chr((b>>8)&0xff)+chr(b&0xff)+chr(0xad)
+
+class FirecrackerX10ControllerUnit (X10ControllerUnit):
+ def __init__(self, id, power, network, resolution=21):
+ '''
+ Constructor for a CM17a X10 object:
+ FirecrackerX10ControllerUnit(power, [resolution])
+
+ Specify the correct PowerSource object for this unit.
+ The resolution defaults to 21, which matches the 5%
+ delta mentioned in the CM17a protocol spec from X-10.
+ '''
+ X10ControllerUnit.__init__(self, id, power, network, resolution)
+ self.type = 'X-10 CM17a "Firecracker" Controller'
+
+ def _proto_set_channel(self, id, old_level, new_level, force=False):
+ if new_level == old_level and not force: return ''
+ #
+ # We only turn fully off when we are told to,
+ # since if we dim to zero we keep the ability to
+ # fade back up, too.
+ #
+ # Going from anything to OFF is easy, just send
+ # the OFF code. However, this means our only
+ # next option is fully ON. You can't fade up
+ # from off, at least not on all X10 dimmers.
+ #
+ if new_level is None:
+ return packet(id, 'off')
+ #
+ # Any other change in level is going to require
+ # starting off by getting the unit's attention by
+ # sending it an ON command. If the unit used to
+ # be OFF, that will turn it fully on (see below);
+ # but otherwise it won't affect the brightness but
+ # will make it listen to subsequent DIM or BRIGHT
+ # commands.
+ #
+ response = packet(id, 'on')
+ if old_level is None:
+ #
+ # Going from OFF to ON is easy. And since going
+ # from OFF to anything else involves first turning
+ # on the unit, we'll start by turning it fully on.
+ #
+ # If we wanted a dimmer level less than that, we'll
+ # have to dim down to it. (So if we're going from
+ # "OFF" to "dimmer level zero" we still have to turn
+ # fully on, then fade to black. It's just how the
+ # X10 dimmers are designed. But THEN we can fade
+ # the dimmer back up from zero at will as long as
+ # it doesn't get turned OFF again.)
+ #
+ if new_level < self.resolution-1:
+ response += (self.resolution-1-new_level) * packet(id, 'dn')
+ elif new_level > old_level:
+ #
+ # If we're going UP from an old dimmer level to a new
+ # dimmer level, we send ON + some number of BRIGHT
+ # commands.
+ #
+ response += (new_level-old_level) * packet(id, 'up')
+ else:
+ #
+ # Going DOWN from an old dimmer level to a new
+ # level.
+ #
+ response += (old_level-new_level) * packet(id, 'dn')
+
+ return response
+
+
+ def set_channel(self, id, level, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_level(level), force=force))
+
+ def set_channel_on(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_on(), force=force))
+
+ def set_channel_off(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_off(), force=force))
+
+ def kill_channel(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].kill(), force=force))
+
+ def kill_all_channels(self, force=False):
+ for ch in self.channels:
+ self.kill_channel(ch, force)
+
+ def all_channels_off(self, force=False):
+ for ch in self.channels:
+ self.set_channel_off(ch, force)
+
+ def initialize_device(self):
+ self.kill_all_channels(True)
+ # Force OFF commands to be transmitted out
+ # we need to do this because kill_all_channels() won't
+ # send anything to change channels it thinks are already
+ # off.
+ #for ch in self.channels:
+ # self.network.send(self._proto_set_channel(ch, 0, None))
+ # Then adjust dimmer levels for warmed devices
+ self.all_channels_off()
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.4 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/FirecrackerX10ControllerUnit.pyc
Binary file not shown
View
146 src/xmbc/plugin.video.interactivelighting/Lumos/Device/LumosControllerUnit.py
@@ -0,0 +1,146 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: LUMOS DIY SSR CONTROLLER
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/LumosControllerUnit.py,v 1.6 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.ControllerUnit import ControllerUnit
+
+class LumosControllerUnit (ControllerUnit):
+ """
+ ControllerUnit subclass for my custom 48-channel SSR boards.
+ """
+ def __init__(self, id, power, network, address, resolution=32):
+ """
+ Constructor for a 48-Channel SSR board object:
+ LumosControllerUnit(id, power, address, [resolution])
+
+ Specify the correct PowerSource object for this unit and
+ the unit address (0-15). The resolution defaults to 32,
+ which is correct for the 3.1 revision of the boards.
+ """
+
+ ControllerUnit.__init__(self, id, power, network, resolution)
+ self.address = int(address)
+ self.type = 'Lumos 48-Channel SSR Controller'
+ self.iter_channels = self._iter_non_null_channel_list
+ if not 0 <= self.address <= 15:
+ raise ValueError, "Address %d out of range for Lumos 48-Channel SSR Controller" % self.address
+
+ def __str__(self):
+ return "%s, address=%d" % (self.type, self.address)
+
+ def channel_id_from_string(self, channel):
+ return int(channel)
+
+ #def iter_channels(self):
+ # return range(48)
+
+ def add_channel(self, id, name=None, load=None, dimmer=True, warm=None, resolution=None, power=None):
+ try:
+ id = int(id)
+ assert 0 <= id <= 47
+ except:
+ raise ValueError, "Lumos 48SSR channel IDs must be integers from 0-47"
+
+ if resolution is not None:
+ resolution = int(resolution)
+ else:
+ resolution=self.resolution
+
+ ControllerUnit.add_channel(self, id, name, load, dimmer, warm, resolution, power)
+
+ #
+ # hardware protocol, for a unit with address <a>
+ # Kill all channels 1000aaaa
+ # Chan c off 1001aaaa 00cccccc
+ # Chan c on 1001aaaa 01cccccc
+ # Chan c to level v 1010aaaa 00cccccc 000vvvvv
+ # Disable admin cmds 1111aaaa 01100001
+
+ def _proto_set_channel(self, id, old_level, new_level, force=False):
+ if old_level == new_level and not force: return ''
+ if new_level is None or new_level <= 0:
+ return chr(0x90 | self.address) + chr(id & 0x3f)
+ elif new_level >= self.resolution-1:
+ return chr(0x90 | self.address) + chr(0x40 | (id & 0x3f))
+ else:
+ return chr(0xa0 | self.address) + chr(id & 0x3f) + chr(new_level & 0x1f)
+
+ def set_channel(self, id, level, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_level(level), force=force))
+
+ def set_channel_on(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_on(), force=force))
+
+ def set_channel_off(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_off(), force=force))
+
+ def kill_channel(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].kill(), force=force))
+
+ def kill_all_channels(self, force=False):
+ for ch in self.channels:
+ self.channels[ch].kill()
+ self.network.send(chr(0x80 | self.address))
+
+ def all_channels_off(self, force=False):
+ for ch in self.channels:
+ self.set_channel_off(ch, force)
+
+ def initialize_device(self):
+ self.network.send(chr(0xf0 | self.address) + chr(0x61)) # go to normal run mode
+ self.kill_all_channels(True)
+ self.all_channels_off(True)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.5 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
+#
+# LUMOS CHANNEL MAP HANDLER CLASS
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/SSR48ControllerUnit.py,v 1.6 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/LumosControllerUnit.pyc
Binary file not shown
View
308 src/xmbc/plugin.video.interactivelighting/Lumos/Device/LynX10ControllerUnit.py
@@ -0,0 +1,308 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: X-10 TW523 + LynX10
+# ***UNTESTED*** SPECULATIVE CODE. NOT READY FOR USE!
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/LynX10ControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.Device.X10ControllerUnit import X10ControllerUnit
+
+# XXX How do we pause waiting for the unit to be reset/ready? XXX
+# XXX We can refactor some of this, including recognizing and XXX
+# XXX batching up dimmer changes on the same house code which XXX
+# XXX can be done simultaneously. XXX
+#
+# The LynX10 X-10 controller product accepts serial commands
+# (described below) and manipulates a TW523 line interface
+# module to send and receive X-10 command codes on the AC
+# power lines.
+#
+# For Lumos, we don't presently handle error codes from our
+# devices, in the interest of keeping the real-time show
+# output flowing out. This means we may run into dropped
+# events, particularly with X-10 devices where a collision
+# may occur or other transmission issues. X-10 technology
+# reliability being what it is, we could still have signals
+# not reach their destination and not be aware of that fact
+# anyway. We'll say it again... X-10 is useful for many
+# things, and we'll certainly talk to those devices from
+# Lumos; however it's not really the recommended type of
+# controller device for Lumos.
+#
+# Commands are sent as a sequence of printable ASCII
+# characters followed by a carriage return ('\r').
+# Responses are ASCII strings terminated by a return
+# as well. For successful commands, this will be a
+# star ("*"). For errors, one of the error codes
+# listed below may appear. Note that error codes or
+# other inputs may appear on their own in some
+# cases. For example, if an input is received on
+# an I/O port, the LynX10 will transmit the string:
+#
+# I<pp>=<vv>\r
+# where:
+# <pp>=port (00..FF); <vv>=byte (00..FF)
+#
+# The I/O ports are for experimental use (I built
+# a quiz show control circuit on my LynX10 board,
+# for example). The are NOT used for normal X-10
+# control purposes, and are not referenced in this
+# driver.
+#
+# Commands may be "batched" by concatenating them
+# before the return. For example, a command XX
+# and a command YY may be sent separately as:
+# XX\r
+# YY\r
+# or may be sent together as:
+# XXYY\r
+#
+# X-10 units sharing a common power circuit are
+# addressed individually. Up to 256 devices may
+# be addressed, but these units are grouped into
+# 16 groups (called "house codes") of 16 units
+# per group. The house code is a letter "A".."P"
+# while the unit code is a number 1..16. For
+# example, a device may be addressed as "A1",
+# "C10", or "P16".
+#
+# The LynX10 controller uses hexadecimal codes to
+# specify these device addresses, which works well
+# since a single hex digit represents the house
+# code, and the other digit is for the unit
+# code. However, by expressing these as hexadecimal
+# values, some translation between standard X-10
+# address nomenclature (which Lumos presents to the
+# user) and the codes sent to the device, is necessary.
+#
+# In the descriptions which follos, <h> represents the
+# house code as a single hex character:
+# <h> is the house code 0-F (=A-P)
+# 0=A 1=B 2=C 3=D 4=E 5=F 6=G 7=H
+# 8=I 9=J A=K B=L C=M D=N E=O F=P
+# while <u> is the unit code as a hex character:
+# <u> is the unit code 0-F (=1-16)
+# (Note that the value of <u> has a zero origin rather
+# than the one-origin used by X-10, so these are always
+# one less than the X-10 unit code number.)
+#
+# F0<h><u> turn off unit
+# F1<h> turn off all lights
+# F2<h> turn off all units
+# F3 turn off all lights, all house codes
+# F4 turn off all units, all house codes
+# N0<h><u> turn on unit
+# N1<h> turn on all lights
+# N2 turn on all lights, all house codes
+# D<n><h><u> MODE 0: Set unit to brightness level <n> (0-F)
+# D0<h><n> MODE 1: Send <n> dims to house code (n: 1-F, 0=1)
+# D1<h><n> MODE 1: Send <n> brights to house code (n: 1-F, 0=1)
+# S0<h> Send OFF STATUS to last unit in house code <h>
+# S1<h> Send ON STATUS to last unit in house code <h>
+# S2<h><u> Request status of unit
+# S3 Get LynX-10 status as "S=xx\r":
+# 7 6 5 4 3 2 1 0
+# RESERVED XON XOFF EEPROM FREQ TW523
+# X X X NO NO BAD 60Hz OK <-- 0
+# X X X YES YES OK 50Hz No Power <-- 1
+# X0<h><u> Raw X10 code: address unit for next command
+# Multiple X0<h><u>'s will address a set of units to respond
+# X1<h>0 Raw X10 code: Send ALL UNITS OFF to house <h>
+# X1<h>1 Raw X10 code: Send ALL LIGHTS ON to house <h>
+# X1<h>2 Raw X10 code: Send ON to house <h>
+# X1<h>3 Raw X10 code: Send OFF to house <h>
+# X1<h>4 Raw X10 code: Send one DIM to house <h>
+# X1<h>5 Raw X10 code: Send one BRIGHT to house <h>
+# X1<h>6 Raw X10 code: Send ALL LIGHTS OFF to house <h>
+# X1<h>7 Raw X10 code: Send EXTENDED CODE to house <h>
+# X1<h>8 Raw X10 code: Send HAIL REQUEST to house <h>
+# X1<h>9 Raw X10 code: Send HAIL ACK to house <h>
+# X1<x>A Raw X10 code: Send PRESET DIM 0x0<x>
+# X1<x>B Raw X10 code: Send PRESET DIM 0x1<x>
+# X1<h>C Raw X10 code: Send EXTENDED DATA to house <h>
+# X1<h>D Raw X10 code: Send STATUS ON to house <h>
+# X1<h>E Raw X10 code: Send STATUS OFF to house <h>
+# X1<h>F Raw X10 code: Send STATUS REQUEST to house <h>
+# H<h> Hail house code (get HAIL ACK, X1<h>9, if a controller seen)
+# R Reset LynX-10; clear timer; reset EEPROM
+# T Get time since power restored/reset as "dd:hh:mm:ss\r"
+# V0 Get LynX-10 version number string
+# V1 Get LynX-10 copyright string
+# V\r Get both version number and copyright strings
+# C0<e>\r Get count of E<e> errors as "C0<t>=xxxx\r" (xxxx in hex)
+# C0<e>=0 Reset counter
+# M00\r Get MODE register as "M00=xx\r":
+# 7 6 5 4 3 2 1 0
+# LINE RESRV REPEATER XON/XOFF DIM DEBUG
+# TERM X X NO OFF 0 OFF <-- 0
+# * X X YES ON 1 ON <-- 1
+# *00=\r 01=\n 10=\r\n 11=none
+# M00=<xx> Set MODE register
+# M01\r SIO FIFO threshold
+# M01=<xx> Set FIFO threshold (0x10 is good) (01-1C)
+# M02\r Host retransmit attempts after collision
+# M02=<xx> Set retransmits (00-FE; FF=infinite--DONT USE FF)
+# M03\r Command receive timeout in seconds
+# M03=<xx> Set timeout (00-FF; 00=disable)
+# O<pp>=<xx> Output byte <xx> to port <pp>. FF is front panel lights.
+# I<pp> Input byte from port <pp> as "I<pp>=<xx>\r"
+# W0 Save current settings to EEPROM.
+# WF Restore factory defaults; clear all counters and timers.
+#
+#
+# Return codes:
+# E0\r X10 reception error
+# E1\r invalid command
+# E2\r invalid data for command
+# E3\r X10 collision detected
+# E4\r X10 transmission timed out (failed)
+# E5\r X10 lost reception
+# E6\r SIO RX FIFO overrun
+# E7\r Carrier lost
+# X<n><h><m>\r Raw X10 command received (out of band)
+#
+#
+# Diagnostic LEDS:
+# ERROR Bad accumulator
+# ERROR+RX Bad register
+# ERROR+TX Bad memory location
+#
+# Communications:
+# 1200bps, no parity, 8 bits, 1 stop
+# rts/cts handshaking, also xon/xoff if enabled in M00 register
+#
+#
+# Dimming:
+# X10 dimmers have a peculiarity where they can be
+# incrementally bumped up or down in brightness, but
+# only if it's "on". If the unit is "off", then the
+# only option is to turn it fully "on". For devices
+# which are just being switched on and off, this isn't
+# a problem. If they're being dimmed, though, we'll
+# need to keep them "on" but dimmed to 0% output if
+# they're going to later be gradually brightened.
+#
+# We handle this by assuming that the channel level of
+# None means fully off, while 0-100 indicates some
+# dimmer level. We'll apply the appropriate logic to
+# adjust them. This will use the flush function
+
+class LynX10ControllerUnit (X10ControllerUnit):
+ def __init__(self, id, power, network, resolution=16):
+ X10ControllerUnit.__init__(self, id, power, network, resolution)
+ self.type = 'LynX-10/TW523 Controller'
+
+ def _x10_to_lynx_id(self, x10_id):
+ return self._x10_housecode_to_lynx(x10_id[0]) \
+ + self._x10_unitcode_to_lynx(x10_id[1:])
+
+ def _x10_housecode_to_lynx(self, x10_id):
+ return str("ABCDEFGHIJKLMNOP".index(x10_id.upper()))
+
+ def _x10_unitcode_to_lynx(self, x10_id):
+ return '%X' % (int(x10_id)-1)
+
+ def _proto_set_channel(self, id, old_level, new_level, force=False):
+ #
+ # Implement logic for what commands we output to move
+ # channels from one dimmer level to another. These are
+ # already translated (via the Channel object) to device
+ # specific dimmer values, and warm limits or undimmable
+ # channels already taken into account.
+ #
+ if new_level == old_level and not force:
+ # The level didn't end up changing on the actual
+ # device, so don't send anything.
+ return ''
+
+ if new_level is None:
+ # We're going to a fully off state, so just turn off
+ # the device and be done with it.
+ return 'F0' + self._x10_to_lynx_id(id) + '\r'
+
+ # ...so we're changing to an ON state. We know then
+ # no matter what we'll start off with an ON code.
+ # We might add more, depending on what else is going on...
+ command = 'N0' + self._x10_to_lynx_id(id) + '\r'
+
+ if old_level is None:
+ # We're going from a fully OFF position. If we're
+ # going to something less that fully ON, we'll have
+ # to turn it all the way on, then dim DOWN to the
+ # desired level. Sorry, that's just how (some?)
+ # X10 dimmer modules work...
+ # Or, we can just send some number of dim UP or DOWN
+ # commands...
+ if new_level < self.resolution - 1:
+ command += 'D0%s%X\r' % (
+ self._x10_housecode_to_lynx(id[0]),
+ self.resolution - 1 - new_level
+ )
+ elif new_level > old_level:
+ command += 'D1%s%X\r' % (
+ self._x10_housecode_to_lynx(id[0]),
+ new_level - old_level
+ )
+ else:
+ command += 'D0%s%X\r' % (
+ self._x10_housecode_to_lynx(id[0]),
+ old_level - new_level
+ )
+
+ return command
+
+
+ def set_channel(self, id, level, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_level(level), force=force))
+
+ def set_channel_on(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_on(), force=force))
+
+ def set_channel_off(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].set_off(), force=force))
+
+ def kill_channel(self, id, force=False):
+ self.network.send(self._proto_set_channel(id, *self.channels[id].kill(), force=force))
+
+ def kill_all_channels(self, force=False):
+ self.network.send("F4\r")
+ for ch in self.channels:
+ self.channels[ch].kill()
+
+ def all_channels_off(self, force=False):
+ for ch in self.channels:
+ self.set_channel_off(ch, force)
+
+ def initialize_device(self):
+ "Reset device state to reasonable default settings."
+ self.network.send("R\r")
+ self.network.send("M00=02\r")
+ self.kill_all_channels(True)
+ self.all_channels_off(True)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.4 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/LynX10ControllerUnit.pyc
Binary file not shown
View
143 src/xmbc/plugin.video.interactivelighting/Lumos/Device/Olsen595ControllerUnit.py
@@ -0,0 +1,143 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: OLSEN 595 DIY SSR CONTROLLER
+# ***UNTESTED*** SPECULATIVE CODE. NOT READY FOR USE!
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/Olsen595ControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.ControllerUnit import ControllerUnit
+
+#
+# The Olsen 595 is a classic DIY SSR controller. This driver is
+# an experimental design based on the protocol description posted
+# on computerchristmas.com. The author does not have the hardware
+# available to verify that this works. Hopefully someone with a '595
+# can verify this and give feedback about it to the author.
+#
+# This driver should also drive a Grinch board.
+#
+# This is a parallel protocol (well, actually it's "serial" but we
+# use the PC's parallel port, manipulating three of the output pins
+# to implement a synchronous serial bitstream). These bits are
+# ultimately shifted out to the output SSR channels.
+#
+# For each bit you wish to send, set up the bit in D0 and set the
+# /STROBE line high to clock it out. Repeat until all bits have
+# been sent to the hardware.
+#
+# When they've all been transmitted, clock the /AUTO_FEED line high
+# to have the controller latch all those bits out to the output lines
+# driving the SSRs.
+#
+# This does not use Channel objects since all we're doing is changing
+# them from on to off. This may change in the future, though.
+#
+
+class Olsen595ControllerUnit (ControllerUnit):
+ """
+ ControllerUnit subclass for the classic "Olsen 595" and
+ "Grinch" DIY SSR controllers.
+
+ ***THE STATUS OF THIS DRIVER IS EXPERIMENTAL***
+ THIS HAS NOT YET BEEN VERIFIED TO WORK WITH ACTUAL HARDWARE
+
+ If you have an Olsen 595 and/or a Grinch controller, we would
+ appreciate any feedback you'd like to offer about this driver,
+ if you're willing to try it and help us test/debug this code.
+ """
+
+ def __init__(self, id, power, network, channels=64):
+ """
+ Constructor for an Olsen 595/Grinch controller object:
+ Olsen595ControllerUnit(power, network, [channels])
+
+ Specify the correct PowerSource object for this unit,
+ the network this board (or chained set of boards) talks
+ through. Also specify the number of channels implemented
+ on this (set of) controller(s). The default is 64. Lumos
+ will transmit that many bits on each update.
+ """
+
+ ControllerUnit.__init__(self, id, power, network)
+ self.type = 'Olsen595 Controller'
+ self.channels = [0] * channels
+ self.needs_update = False
+
+ def __str__(self):
+ return "%s (%d channels)" % (self.type, len(self.channels))
+
+ def channel_id_from_string(self, channel):
+ return int(channel)
+
+ def add_channel(self, id, name=None, load=None):
+ try:
+ id = int(id)
+ assert 0 <= id < len(self.channels)
+ except:
+ raise ValueError("This Olsen595's channel IDs must be integers from 0-%d" % self.max_bits)
+
+ def set_channel(self, id, level, force=False):
+ if level == 0:
+ self.set_channel_off(id, force)
+ else:
+ self.set_channel_on(id, force)
+
+ def set_channel_on(self, id, force=False):
+ self.channels[int(id)] = 1
+ self.needs_update = True
+
+ def set_channel_off(self, id, force=False):
+ self.channels[int(id)] = 0
+ self.needs_update = True
+
+ def kill_channel(self, id, force=False):
+ self.set_channel_off(id, force)
+
+ def kill_all_channels(self, force=False):
+ self.channels = [0] * len(self.channels)
+ self.needs_update = True
+
+ def all_channels_off(self, force=False):
+ self.kill_all_channels(force)
+
+ def initialize_device(self):
+ self.all_channels_off(True)
+ self.flush(True)
+
+ def flush(self, force=False):
+ if self.needs_update or force:
+ for bit in self.channels:
+ self.network.send(bit)
+ self.network.latch()
+ self.needs_update = False
+
+ def iter_channels(self):
+ return range(len(self.channels))
+
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.4 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/Olsen595ControllerUnit.pyc
Binary file not shown
View
177 src/xmbc/plugin.video.interactivelighting/Lumos/Device/RenardControllerUnit.py
@@ -0,0 +1,177 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: RENARD DIY SSR CONTROLLER
+# ***UNTESTED*** SPECULATIVE CODE. NOT READY FOR USE!
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/RenardControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.ControllerUnit import ControllerUnit
+
+#
+# The Renard is a classic DIY SSR controller. This driver is
+# an experimental design based on the protocol description posted
+# on computerchristmas.com. The author does not have the hardware
+# available to verify that this works. Hopefully someone with a Renard
+# can verify this and give feedback about it to the author.
+#
+# The serial protocol used by this device updates all channels at one
+# time, in packets which look like this:
+# <0x7e><address><level0><level1>...<leveln>
+#
+# literal 0x7d, 0x7e and 0x7f bytes are sent using an escape mechanism:
+# <0x7f><0x2f> == <0x7d>
+# <0x7f><0x30> == <0x7e>
+# <0x7f><0x31> == <0x7f>
+#
+# The <address> byte always has MSB set (so the address of the first unit
+# is really 0x80, the second is 0x81, etc.).
+#
+# <level> bytes are in the range <0x00> (fully off) to <0xff> (fully on).
+#
+
+class RenardControllerUnit (ControllerUnit):
+ """
+ ControllerUnit subclass for the Renard DIY SSR unit.
+
+ ***THE STATUS OF THIS DRIVER IS EXPERIMENTAL***
+ THIS HAS NOT YET BEEN VERIFIED TO WORK WITH ACTUAL HARDWARE
+
+ If you have a Renard or compatible controller, we would
+ appreciate any feedback you'd like to offer about this driver,
+ if you're willing to try it and help us test/debug this code.
+ """
+ def __init__(self, id, power, network, address, resolution=256, channels=64):
+ """
+ Constructor for a Renard SSR board object:
+ RenardControllerUnit(id, power, network, address, [resolution], [channels])
+
+ Specify the correct PowerSource object for this unit and
+ the unit address (0-127). (We will call the first unit address 0,
+ even though behind the scenes the address byte's MSB will be sent
+ when transmitted (so unit address 0 transmits as 0x80, address 1
+ transmits as 0x81, etc.) The number of channels defaults to 64, but there
+ are known Renard implementations with 8, 16, 24, 32 and 64 channels and
+ probably others. This sets the upper bound for channel IDs on this
+ controller unit, and also the packet size which will always be sent
+ when pending changes are flushed out to the hardware.
+
+ The resolution probably won't ever need to be changed. The Renards
+ use 256 dimmer levels, so that's the default for that parameter.
+ """
+
+ ControllerUnit.__init__(self, id, power, network, resolution)
+ self.address = int(address)
+ self.type = 'Renard SSR Controller (%d channels)' % channels
+ self.channels = [None] * channels
+ self.update_pending = False
+ self.iter_channels = self._iter_non_null_channel_list
+
+ if not 0 <= self.address < 127:
+ raise ValueError("Address %d out of range for a Renard SSR Controller" % self.address)
+
+ def channel_id_from_string(self, channel):
+ return int(channel)
+
+ def __str__(self):
+ return "%s, address=%d (%d channels)" % (self.type, self.address, len(self.channels))
+
+ def add_channel(self, id, name=None, load=None, dimmer=True, warm=None, resolution=None, power=None):
+ try:
+ id = int(id)
+ assert 0 <= id < len(self.channels)
+ except:
+ raise ValueError("%d-channel Renard channel IDs must be integers from 0-%d"
+ % (len(self.channels), len(self.channels)-1))
+
+ if resolution is not None:
+ resolution = int(resolution)
+ else:
+ resolution=self.resolution
+
+ ControllerUnit.add_channel(self, id, name, load, dimmer, warm, resolution, power)
+
+ def set_channel(self, id, level, force=False):
+ self.channels[id].set_level(level)
+ self.update_pending = True
+ self.flush()
+
+ def set_channel_on(self, id, force=False):
+ self.channels[id].set_on()
+ self.update_pending = True
+ self.flush()
+
+ def set_channel_off(self, id, force=False):
+ self.channels[id].set_off()
+ self.update_pending = True
+ self.flush()
+
+ def kill_channel(self, id, force=False):
+ self.channels[id].kill()
+ self.update_pending = True
+ self.flush()
+
+ def kill_all_channels(self, force=False):
+ for ch in self.channels:
+ if ch is not None:
+ ch.kill()
+ self.update_pending = True
+ self.flush()
+
+ def all_channels_off(self, force=False):
+ for ch in self.channels:
+ if ch is not None:
+ ch.set_off()
+ self.update_pending = True
+ self.flush()
+
+ def initialize_device(self):
+ self.all_channels_off(True)
+ self.flush(True)
+
+ def flush(self, force=False):
+ if self.update_pending or force:
+ #self.network.send('~%c' % (0x80 | self.address))
+ self.network.send(bytearray([0x7e, 0x80]))
+ for channel in self.channels:
+ if channel is None:
+ self.network.send(chr(0))
+ else:
+ self.send_escaped_int(channel.level)
+ self.update_pending = False
+
+ #def iter_channels(self):
+ #return range(len(self.channels))
+
+ def send_escaped_int(self, i):
+ if i is None: i = 0
+ if i == 0x7d: self.network.send(bytearray([0x7c]))
+ elif i == 0x7e: self.network.send(bytearray([0x7c]))
+ elif i == 0x7f: self.network.send(bytearray([0x80]))
+ else: self.network.send(bytearray([i]))
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.4 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/RenardControllerUnit.pyc
Binary file not shown
View
60 src/xmbc/plugin.video.interactivelighting/Lumos/Device/X10ControllerUnit.py
@@ -0,0 +1,60 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS DEVICE DRIVER: X-10 BASE CLASS
+#
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Device/X10ControllerUnit.py,v 1.5 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.ControllerUnit import ControllerUnit
+
+class X10ControllerUnit (ControllerUnit):
+ def __init__(self, id, power, network, resolution=16):
+ ControllerUnit.__init__(self, id, power, network, resolution)
+ self.type = 'X-10 Controller'
+
+ def __str__(self):
+ return self.type
+
+ def channel_id_from_string(self, channel):
+ return str(channel).upper().strip()
+
+ def add_channel(self, id, name=None, load=None, dimmer=True, warm=None, resolution=None, power=None):
+ try:
+ id = self.channel_id_from_string(id)
+ assert('A' <= id[0] <= 'P')
+ assert(1 <= int(id[1:]) <= 16)
+ except:
+ raise ValueError, "X-10 channels must be 'A1'..'P16'"
+
+ if resolution is not None:
+ resolution = int(resolution)
+ else:
+ resolution = self.resolution
+
+ ControllerUnit.add_channel(self, id, name, load, dimmer, warm, resolution, power)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.4 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/X10ControllerUnit.pyc
Binary file not shown
View
0  src/xmbc/plugin.video.interactivelighting/Lumos/Device/__init__.py
No changes.
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Device/__init__.pyc
Binary file not shown
View
63 src/xmbc/plugin.video.interactivelighting/Lumos/Event.py
@@ -0,0 +1,63 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS EVENT CLASS
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Event.py,v 1.3 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+class Event:
+ """An Event object describes a single event in the sequence
+ being played out. It essentially represents a specific point in time,
+ at which a given channel is moved to a given output level, during a
+ transition period of delta time."""
+
+ def __init__(self, unit, channel, level=0, delta=0):
+ self.unit = unit
+ self.channel = channel
+ self.level = level
+ self.delta = delta
+
+ def __eq__(self, other):
+ #if not isinstance(other, Event):
+ #return False
+
+ return self.unit == other.unit \
+ and self.channel == other.channel \
+ and self.level == other.level \
+ and self.delta == other.delta
+
+# def __ne__(self, other):
+# return not self.__eq__(other)
+
+ def __repr__(self):
+ return "<Event %s.%s->%.2f%% (%dmS)>" % (
+ '*' if self.unit is None else self.unit.id,
+ '*' if self.channel is None else self.channel,
+ self.level,
+ self.delta
+ )
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.2 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Event.pyc
Binary file not shown
View
171 src/xmbc/plugin.video.interactivelighting/Lumos/Extras/VixenSequence.py
@@ -0,0 +1,171 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS: VIXEN SEQUENCE CLASS
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Extras/VixenSequence.py,v 1.3 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from xml.etree.ElementTree import ElementTree
+import base64
+
+class InvalidVixenSequenceFile (Exception):
+ pass
+
+class VixenChannel (object):
+ """Describes a channel from a Vixen sequence. Has the following
+ attributes:
+
+ name: text label for this channel
+ color: tuple of (red, green, blue) values
+ enabled: boolean (not currently used by Lumos)
+ output: string (not currently used by Lumos)
+ """
+
+ def __init__(self, name, color, enabled, output):
+ """color may be tuple (r,g,b) or 24-bit signed value
+
+ enabled may be boolean or string with "true/false" text"""
+
+ self.name = name
+ if isinstance(color, tuple):
+ self.color = color
+ else:
+ color = int(color)
+ self.color = ((color >> 16) & 0xff,
+ (color >> 8) & 0xff,
+ color & 0xff)
+
+ if isinstance(enabled, bool):
+ self.enabled = enabled
+ else:
+ self.enabled = enabled.lower() != 'false'
+
+ self.output = output
+
+class VixenSequence (object):
+ """Objects of this class represent sequences prepared by the
+ Vixen sequence editor. This is intended for experimental purposes
+ or for importing scenes from Vixen to Lumos.
+
+ This has been tested with standard Vixen sequences on version 2.XXX
+ and may not work with all possible types of sequences understood by
+ Vixen.
+
+ Once a sequence is loaded, the following attributes are publicly
+ available:
+
+ total_time: the duration of the sequence. Note that the
+ duration may extend past the last event
+ timestamp if there are any events which are
+ not instantaneous and still running at that
+ point. Integer, measured in milliseconds.
+
+ channels: a list of VixenChannel objects defined by the
+ sequence file (q.v.).
+
+ min_val: minimum range for channel values
+ max_val: maximum range for channel values
+
+ events: a compressed list of (time, channel#, value) tuples
+ representing the sequence. "Compressed" is a word which
+ here means, "converted from Vixen's 'time-slice/frame'
+ format to our event-based one, without showing duplicate
+ events when a channel remains steady from one frame to the
+ next."
+
+ audio_filename: None or the name of the audio clip defined for this
+ sequence.
+
+ This is a fairly simplistic algorithm at the moment. It needs to
+ be completed, and could become confused by too many variations in
+ the file parameters. It does NOT currently take into account
+ channels which are disabled or min/max values other than 0-255.
+ """
+
+ def __init__(self, vixen_file = None):
+ """Create a representation of a sequence obtained from importing
+ a Vixen file."""
+ self.clear()
+ if vixen_file is not None:
+ self.load_file(vixen_file)
+
+ def clear(self):
+ self.total_time = 0
+ self.channels = []
+ self.min_val = None
+ self.max_val = None
+ self.audio_filename = None
+ self.events = []
+
+ def load_file(self, filename):
+ "Load a Vixen sequence file into this object."
+ self.load(file(filename, 'r'))
+
+ def load(self, fileobj):
+ "Load a Vixen sequence from a file object."
+ self.clear()
+ file_data = ElementTree()
+
+ try:
+ file_data.parse(fileobj)
+ self.total_time = int(file_data.find('Time').text)
+ self.min_val = int(file_data.find('MinimumLevel').text)
+ self.max_val = int(file_data.find('MaximumLevel').text)
+
+ for channel in file_data.find('Channels').getiterator('Channel'):
+ self.channels.append(VixenChannel(channel.get('name'), channel.get('color'), channel.get('enabled'), channel.get('output')))
+
+ audio_node = file_data.find('Audio')
+ if audio_node is not None:
+ self.audio_filename = audio_node.text
+
+ # The events are a giant base-64-encoded block of value
+ # bytes giving a view of the state of each channel at each
+ # time slice.
+ event_raw_data = base64.b64decode(file_data.find('EventValues').text)
+ event_period = int(file_data.find('EventPeriodInMilliseconds').text)
+
+ # run through each channel data, record *changes* as events
+ # for Lumos
+ current_time = 0
+ current_channel = 0
+ current_value = None
+ for v in event_raw_data:
+ value = ord(v)
+ if value != current_value:
+ current_value = value
+ self.events.append((current_time, current_channel, value))
+
+ current_time += event_period
+ if current_time >= self.total_time:
+ current_time = 0
+ current_channel += 1
+ current_value = None
+
+ except Exception, error:
+ raise InvalidVixenSequenceFile('Unable to understand Vixen sequence file (%s)' % error)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.2 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/VixenSequence.pyc
Binary file not shown
View
0  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/__init__.py
No changes.
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Extras/__init__.pyc
Binary file not shown
View
82 src/xmbc/plugin.video.interactivelighting/Lumos/Network/Network.py
@@ -0,0 +1,82 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS NETWORK CLASS
+# $Header: /usr/local/cvsroot/lumos/lib/Lumos/Network/Network.py,v 1.3 2008/12/31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+class Network (object):
+ """
+ This class describes each network of controllers. Each network
+ driver class derived from this base class describes a type of
+ communications interface and protocol for communicating with a
+ set of controllers.
+
+ For now, this is single-threaded. Output blocks until sent
+ to the devices, and input is polled for. A future version
+ of Lumos may implement threaded I/O with asynchronous data
+ handling.
+
+ This is a virtual base class from which all actual network drivers
+ are derived.
+ """
+
+ def __init__(self, id, description=None):
+ """
+ Constructor for the Network class:
+ Network(ID, [description], ...)
+
+ ID: The ID tag for this network.
+
+ description: A short description of what this network is
+ responsible for.
+
+ Derived classes will have additional keyword arguments
+ appropriate for the specific kind of hardware they will
+ communicate with.
+ """
+ self.id = id
+ self.description = description
+ if self.description is None: self.description = 'Network "%s"' % id
+ self.units = {}
+
+ def add_unit(self, id, unit):
+ "Add a new controller unit to the network."
+ self.units[id] = unit
+
+ def send(self, cmd):
+ "Send a command string to the hardware device."
+ raise NotImplementedError, "You MUST redefine this method in each Network class."
+
+ def close(self):
+ """Close the network device; no further operations are
+ possible for it."""
+ raise NotImplementedError, "You MUST redefine this method in each Network class."
+#
+# $Log: Network.py,v $
+# Revision 1.3 2008/12/31 00:25:19 steve
+# Preparing 0.3a1 release
+#
+# Revision 1.2 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
62 src/xmbc/plugin.video.interactivelighting/Lumos/Network/Networks.py
@@ -0,0 +1,62 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS NETWORK LIST
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Network/Networks.py,v 1.4 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+from Lumos.Network.SerialNetwork import SerialNetwork
+from Lumos.Network.SerialBitNetwork import SerialBitNetwork
+#from Lumos.Network.ParallelNetwork import ParallelNetwork
+#from Lumos.Network.ParallelBitNetwork import ParallelBitNetwork
+
+#
+# List of supported network drivers, mapping the name as used
+# in the show.conf file (and other interfaces) to the actual
+# class implementing the driver for that network.
+#
+supported_network_types = {
+ 'serial': SerialNetwork,
+ 'serialbit': SerialBitNetwork
+}
+
+def network_factory(type, **kwargs):
+ """
+ Create and return a network object of the requested type.
+ Usage: network_factory(typename, [args...])
+ where: typename is one of the defined keywords for a network
+ type (as usable in the show configuration file), and
+ [args] are whatever constructor arguments are applicable
+ to that type of network class.
+ """
+
+ type = type.lower().strip()
+ if type not in supported_network_types:
+ raise ValueError, "Invalid network/protocol type '%s'" % type
+
+ return supported_network_types[type](**kwargs)
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.3 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
BIN  src/xmbc/plugin.video.interactivelighting/Lumos/Network/Networks.pyc
Binary file not shown
View
105 src/xmbc/plugin.video.interactivelighting/Lumos/Network/ParallelBitNetwork.py
@@ -0,0 +1,105 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS NETWORK DRIVER: BIT-AT-A-TIME OVER PARALLEL PORT
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Network/ParallelBitNetwork.py,v 1.3 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+import parallel
+from Lumos.Network import Network
+
+class ParallelBitNetwork (Network):
+ """
+ This class describes each parallel-port network of controllers.
+ Unlike ParallelNetwork, though, the interface is designed for
+ bit-at-a-time devices such as the Olsen 595, Grinch, etc.
+
+ There are two event methods provided. send() transmits a
+ single bit on pin D0, strobed via the /STROBE pin. When
+ all the bits are sent, call the latch() method to assert
+ the /AUTOFEED and /STROBE lines once. This signals the
+ device to latch the shifted bits out to the output channels.
+ """
+
+ def __init__(self, description=None, port=0, open_device=True):
+ """
+ Constructor for the ParallelBitNetwork class.
+
+ description: A short description of what this network is
+ responsible for.
+
+ port: The system designation for the parallel device
+ connecting the host computer to the network.
+ Can be system-specific like "LPT1" or
+ "/dev/lp0", or can be a simple integer--0
+ should be the "first" parallel port on your
+ system, 1 the next, etc. [0]
+
+ open_device: Boolean: whether to actually open the parallel
+ device upon construction. If you don't let it
+ open here, you'll need to call the open()
+ method later. [True]
+ """
+
+ Network.__init__(self, description)
+
+ self.port = port
+
+ # If the port can be a simple integer, make it so.
+ try:
+ self.port = int(self.port)
+ except:
+ pass
+
+ self.dev = None
+ if open_device:
+ self.open()
+
+ def open(self):
+ self.dev = parallel.Parallel(port=self.port)
+ self.dev.setDataDir(1)
+ self.dev.setDataStrobe(0)
+
+ def send(self, bit):
+ if self.dev is not None:
+ self.dev.setData(bit)
+ self.dev.setDataStrobe(1)
+ self.dev.setDataStrobe(0)
+
+ def latch(self):
+ raise NotImplementedError("LATCH")
+
+ def close(self):
+ if self.dev is not None:
+ self.dev.setDataDir(0)
+ self.dev.close()
+ self.dev = None
+
+ def __str__(self):
+ if self.description is not None: return self.description
+ return "ParallelBitNetwork (port %s)" % self.port
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.2 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
98 src/xmbc/plugin.video.interactivelighting/Lumos/Network/ParallelNetwork.py
@@ -0,0 +1,98 @@
+# vi:set ai sm nu ts=4 sw=4 expandtab:
+#
+# LUMOS NETWORK DRIVER: PARALLEL PORT
+# $Header: /tmp/cvsroot/lumos/lib/Lumos/Network/ParallelNetwork.py,v 1.3 2008-12-31 00:25:19 steve Exp $
+#
+# Lumos Light Orchestration System
+# Copyright (c) 2005, 2006, 2007, 2008 by Steven L. Willoughby, Aloha,
+# Oregon, USA. All Rights Reserved. Licensed under the Open Software
+# License version 3.0.
+#
+# This product is provided for educational, experimental or personal
+# interest use, in accordance with the terms and conditions of the
+# aforementioned license agreement, ON AN "AS IS" BASIS AND WITHOUT
+# WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+# THE WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
+# PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+# WORK IS WITH YOU. (See the license agreement for full details,
+# including disclaimer of warranty and limitation of liability.)
+#
+# Under no curcumstances is this product intended to be used where the
+# safety of any person, animal, or property depends upon, or is at
+# risk of any kind from, the correct operation of this software or
+# the hardware devices which it controls.
+#
+# USE THIS PRODUCT AT YOUR OWN RISK.
+#
+import parallel
+from Lumos.Network import Network
+
+class ParallelNetwork (Network):
+ """
+ This class describes each parallel-port network of controllers.
+ Typically this will be a parallel port on the host computer,
+ and attached to that parallel port will be one or more
+ controller devices. Commands are sent as a sequence of
+ bytes using the standard control bits (STROBE, etc).
+ """
+
+ def __init__(self, description=None, port=0, open_device=True):
+ """
+ Constructor for the ParallelNetwork class.
+
+ description: A short description of what this network is
+ responsible for.
+
+ port: The system designation for the parallel device
+ connecting the host computer to the network.
+ Can be system-specific like "LPT1" or
+ "/dev/lp0", or can be a simple integer--0
+ should be the "first" parallel port on your
+ system, 1 the next, etc. [0]
+
+ open_device: Boolean: whether to actually open the parallel
+ device upon construction. If you don't let it
+ open here, you'll need to call the open()
+ method later. [True]
+ """
+
+ Network.__init__(self, description)
+
+ self.port = port
+
+ # If the port can be a simple integer, make it so.
+ try:
+ self.port = int(self.port)
+ except:
+ pass
+
+ self.dev = None
+ if open_device:
+ self.open()
+
+ def open(self):
+ self.dev = parallel.Parallel(port=self.port)
+ self.dev.setDataDir(1)
+ self.dev.setDataStrobe(0)
+
+ def send(self, cmd):
+ if self.dev is not None:
+ self.dev.setData(cmd)
+ self.dev.setDataStrobe(1)
+ self.dev.setDataStrobe(0)
+
+ def close(self):
+ if self.dev is not None:
+ self.dev.setDataDir(0)
+ self.dev.close()
+ self.dev = None
+
+ def __str__(self):
+ if self.description is not None: return self.description
+ return "ParallelNetwork (port %s)" % self.port
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.2 2008/12/30 22:58:02 steve
+# General cleanup and updating before 0.3 alpha release.
+#
+#
View
94 src/xmbc/plugin.video.interactivelighting/Lumos/Network/SerialBitNetwork.py