Closed HJ959 closed 3 years ago
Hi there @HJ959, it's nice to see someone else using this code.
There currently exists a DMXLight
abstract class which all other lights need to inherit from and implement some methods for: https://github.com/JMAlego/PyDMX/blob/846a50a46df7cd82920b053532ef9e75dc50a1fc/dmx/light.py#L11-L38
Two examples of this can be seen in DMXLight3Slot
and DMXLight7Slot
, here's what DMXLight3Slot
looks like: https://github.com/JMAlego/PyDMX/blob/846a50a46df7cd82920b053532ef9e75dc50a1fc/dmx/light.py#L41-L60
You can see there are 2 main methods that need to be overridden in subclasses:
serialise
is the method which actually takes the state of the light and transforms it into a series of bytes (as a list of integers each from 0 to 255 inclusive) to put out on the DMX bus at some point.slot_count
is the property method which needs to return the number of "slots" (I use the term "slots as it's what the network standard calls them but most people call them "channels") that a light takes up.For basically all use cases you'll want to also add other methods for interacting with the light (for instance set_colour
or similar) and also want to override the initialiser to setup relevant internal state for the light.
The current way that the raw data for channels travels is as follows:
serialise
is called on a DMXUniverse
class, each light in that universe has it's serialise
method called.serialise
logic (which can include calling serialise
on a Colour
class as you noticed) and returns it's channel data.serialise
on the universe returns a complete picture of what all 512 channels are set to as a list of integers from 0-255 inclusive (e.g. basically bytes).As can be seen in the example code from the readme (pasted below) this channel data is then typically passed to the interface by calling set_frame
which updates the frame to be sent out next time the send_update
method on the interface is called.
from dmx import Colour, DMXInterface, DMXLight3Slot, DMXUniverse
PURPLE = Colour(255, 0, 255)
# Open an interface
with DMXInterface("FT232R") as interface:
# Create a universe
universe = DMXUniverse()
# Define a light
light = DMXLight3Slot(address=8)
# Add the light to a universe
universe.add_light(light)
# Update the interface's frame to be the universe's current state
interface.set_frame(universe.serialise())
# Send an update to the DMX network
interface.send_update()
# Set light to purple
light.set_colour(PURPLE)
# Update the interface's frame to be the universe's current state
interface.set_frame(universe.serialise())
# Send an update to the DMX network
interface.send_update()
The idea behind this architecture is that once a light has a class implemented then it can simply be initialised (with an address specified) and then added to the DMX universe, after that all further interactions can be with this light class which can provide a nicer interface than directly settings channel data (this can be seen in the above example). The only assumption made by the architecture is that each light will always use contiguous addresses e.g. a single light won't listen to 2 entirely separate address ranges, say 2,3, and 4 for RGB data and 20 for dimmer data.
Not sure what data the lights you describe actually want but here's an example of implementing something that looks like the first light you described:
class DMXLightExample(DMXLight3Slot):
"""Represents an DMX light with brightness, RGB, Strobe and presets."""
def __init__(self, address: int = 1):
"""Initialise the light."""
super().__init__(address=address)
self._brightness = 255
self._strobe = 0
self._presets = (0,0)
def set_brightness(self, value: int):
"""Set the brightness of the light between 0 and 255 (inclusive)."""
self._brightness = int(max(0, min(value, 255)))
def set_strobe(self, value: int):
"""Set the strobe of the light between 0 and 255 (inclusive)."""
self._strobe = int(max(0, min(value, 255)))
def set_presets(self, values: List[int]):
"""Set the presets of the light."""
# NB: I have no idea how these work on the light so just left it as two arbitrary bytes.
self._presets = tuple(values[:2])
@property
def slot_count(self) -> int:
"""Get the number of slots used by this light."""
return 7
def serialise(self) -> List[int]:
"""Serialise the DMX light to a sequence of bytes."""
# Colour is handled by the superclass `DMXLight3Slot`.
return [self._brightness] + super().serialise() + [self._strobe] + list(self._presets)
I have no way of testing it (as I don't have your lights) so I'm not sure if it works (there may even be a syntax error hiding in there) but hopefully that gives you an idea of what you can do to implement a light using it.
Hope all of that helps you with using this library!
Thanks for your reply! Give me a couple of days to digest your post and to do some tests! Cheers dude.
Got it working! Many many many many thanks for your help and great explanation. I also feel like I understand classes in python a lot better now.
Hey DMAlego! First let me say thank you so much for this code! It's given me some hope with a project I am currently working on and works with my cheap DMX interface. Brilliant.
My feature idea is to create a DMXLight class that can be customised to fit the fixtures you may come across. For example I have some cheap U'King DMX par cans that have 7 channels, Brightness, RGB, Strobe, presets, presets. Also another spot light that has 6 channels.
I must admit I'm not the best at OOP and am struggling to adapt your code. I can't seem to find where the value of the channels are actually set as well. I'm thinking it's the Colour class that's responsible.
I wonder if you could give me a steer and to whether or not you'd like this implemented in the repo.
Maybe another class called channels or something that lets you access whatever channel within the light object.
Thanks!